我有'N'个线程要在设备上同时执行,它们需要全局内存中的M*N个浮点值。访问合并的全局内存的正确方法是什么?在这种情况下,共享内存有什么帮助?
通常,当相邻线程访问内存中的相邻单元时,可以实现良好的联合访问。因此,如果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编程指南"中阅读更多详细信息,该主题有整整一章:"5.3。最大限度地提高内存吞吐量"。
此外,我听说了一些关于共享内存的本地化内存访问。这是记忆聚合的首选还是有其自身的困难共享内存的速度要快得多,因为它位于芯片上,但其大小有限。内存不像全局内存那样是分段的,你几乎可以随机访问,没有任何惩罚成本。但是,内存组行的宽度为4字节(大小为32位int(。每个线程访问的内存地址应该是不同的模16(或32,取决于GPU(。因此,地址[tid*4]
将比[tid*5]
慢得多,因为第一个地址只访问存储体0、4、8、12,而后者访问0、5、10、15、4、9、14。。。(存储体id=地址模16(。
同样,您可以在CUDA编程指南中阅读更多内容。