9

我有 'N' 个线程要在设备上同时执行,它们需要从全局内存中浮动 M*N。访问合并的全局内存的正确方法是什么?在这件事上,共享内存如何提供帮助?

4

1 回答 1

17

通常,当相邻线程访问内存中的相邻单元时,可以实现良好的合并访问。因此,如果tid保存您的线程的索引,则访问:

  • arr[tid]--- 提供完美的结合
  • arr[tid+5]--- 几乎完美,可能未对齐
  • arr[tid*4]---不再那么好,因为差距
  • arr[random(0..N)]--- 可怕!

我是从 CUDA 程序员的角度说的,但类似的规则也适用于其他地方,即使是在简单的 CPU 编程中,尽管影响并不大。


“但是我有这么多数组,每个人的线程数都比我的线程数长大约 2 或 3 倍,使用像“arr[tid*4]”这样的模式是不可避免的。有什么办法可以解决这个问题?

如果偏移量是某个较高 2 次方的倍数(例如 16*x 或 32*x),则不是问题。所以,如果你必须在 for 循环中处理一个相当长的数组,你可以这样做:

for (size_t base=0; i<arraySize; i+=numberOfThreads)
    process(arr[base+threadIndex])

(以上假设数组大小是线程数的倍数)

因此,如果线程数是 32 的倍数,则内存访问会很好。

再次注意:我是从 CUDA 程序员的角度说的。对于不同的 GPU/环境,您可能需要更少或更多线程来实现完美的内存访问合并,但应该适用类似的规则。


“32”是否与并行访问全局内存的扭曲大小有关?

虽然不是直接的,但有一定的联系。全局内存分为 32、64 和 128 字节的段,由半扭曲访问。对于给定的内存获取指令,您访问的段越多,执行的时间就越长。您可以在“CUDA Programming Guide”中阅读更多详细信息,该主题有一整章:“5.3. Maximize Memory Throughput”。

另外,我听说过一些关于共享内存的本地化内存访问。这是内存合并的首选还是有其自身的困难? 共享内存位于芯片上,速度要快得多,但它的大小是有限的。内存没有像全局那样分段,您几乎可以随机访问,没有惩罚成本。但是,存在宽度为 4 字节(32 位 int 大小)的内存条行。每个线程访问的内存地址应该是不同的模 16(或 32,取决于 GPU)。因此, address[tid*4]将比 慢得多[tid*5],因为第一个访问仅银行 0、4、8、12 和后者 0、5、10、15、4、9、14,...(银行 id = 地址模 16 )。

同样,您可以在 CUDA 编程指南中阅读更多内容。

于 2011-07-03T15:09:39.943 回答