【问题标题】:CUDA thread local arrayCUDA线程本地数组
【发布时间】:2012-09-20 19:40:23
【问题描述】:

我正在编写一个 CUDA 内核,它需要为每个线程维护一个小的关联数组。小,我的意思是最坏情况下最多 8 个元素,以及两个左右的预期条目数;所以没什么花哨的;只是一个键数组和一个值数组,索引和插入是通过对所述数组的循环进行的。

现在我通过线程本地内存来做到这一点;即标识符[大小];其中 size 是编译时间常数。现在听说在某些情况下,该存储器存储在片外,而在某些情况下,它存储在片上。显然我想要后者,在任何情况下。我知道我可以通过一个共享内存块来实现这一点,我让每个线程在自己的私有块上工作;但真的吗?我不想在线程之间共享任何东西,这将是一个可怕的组合。

这个内存去向的具体规则是什么?我似乎找不到任何来自 nvidia 的词。作为记录,我正在使用 CUDA5 并针对 Kepler。

【问题讨论】:

    标签: cuda


    【解决方案1】:

    局部变量要么存储在寄存器中,要么(为计算能力而缓存 >=2.0)片外内存。

    仅当所有数组索引都是常量并且可以在编译时确定时,寄存器才用于数组,因为架构无法在运行时对寄存器进行索引访问。

    在您的情况下,键的数量可能足够小以使用寄存器(并容忍寄存器压力的增加)。展开数组访问上的所有循环,以允许编译器将键放在寄存器中,并使用cuobjdump -sass 来检查它是否确实如此。

    如果您不想使用寄存器,您可以选择具有每个线程偏移量的共享内存(但请检查用于将每个线程索引保存到共享内存中的附加寄存器不会超过键的数量您使用)如您所提到的那样,或者什么都不做并使用片外“本地”内存(真正的“全局”内存,只是具有不同的寻址方案)希望缓存能够发挥作用。

    如果您希望缓存保存键和值,并且不使用太多共享内存,则使用cudaDeviceSetCacheConfig() 选择 48kB 缓存/16kB 共享内存设置而不是默认的 16kB/48kB 拆分可能会有所帮助.

    【讨论】:

    • 谢谢,很好的回答。我实际上正在编写基于 python 的 CUDA 预处理器;抽象出“共享”数组机制以获取每个线程的片上本地数组正是该项目的小巷,所以很可能会这样做。不完全确定这是我的瓶颈并且会使我的内核更快,但只有一种方法可以找到。
    • 最好的选择显然是确保 8 个元素驻留在寄存器中。此外,线程本地数组也是一种安全的替代方案,因为它很可能完全缓存在 L1 中。我对为此使用共享内存持保留意见,我预计不会比 L1 缓存带来性能优势,也不会实现代码简单性。
    • 就代码的简洁性而言,注册选项似乎是最不吸引人的,不是吗?另外,我应该补充一点,我可能想要扩展到 30 个左右的元素;对于我的 255 个开普勒寄存器,这实际上可能是可行的,但仍然...... L1 和共享访问时间如何比较?如果 L1 甚至相当快,那将是强烈的首选;请记住,在这 30 个分配的元素中,任何时候可能只有 3 或 4 个在使用。特别是如果我交错键/值对,我是否可以在 99% 的时间内从单行 L1 缓存中读取?如果是这样,我称自己为过早优化......
    • 一级缓存和共享内存具有相同的访问时间(它们实际上是相同的内存,这就是为什么它们之间的比率是可配置的)。并且也不需要交错键/值对:从一个 warp 映射到一个 128 字节高速缓存行的 32 个连续字宽访问。唯一值得确保的是,warp 中的所有线程同时从同一索引读取到键数组中,这样您就不会在每个 warp 中获得分散的缓存读取。
    • 我知道它们是相同的内存,但我认为缓存逻辑可能会有一些开销。无论如何,事实证明这显然不是我的瓶颈;简化我的算法以完全删除关联数组并没有真正改变执行时间,所以据说它们很高兴在缓存中。更令人失望的是;我以为小开普勒也从只读缓存中读取了 const restrict ptrs,但事实并非如此。那会为我节省很多工作......
    猜你喜欢
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-08
    • 2012-04-12
    相关资源
    最近更新 更多