如何使用合并内存访问

Beh*_*adX 8 gpu coalesce shared-memory

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

Cyg*_*sX1 15

通常,当相邻线程访问存储器中的相邻单元时,可以实现良好的合并访问.所以,如果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])
Run Code Online (Sandbox Code Playgroud)

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

因此,如果线程数是32的倍数,则内存访问将是好的.

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


"32"与经线大小有关,它与全局存储器并行访问吗?

虽然不是直接的,但有一些联系.全局内存分为32,64和128字节的段,可通过半warp访问.您为给定的内存提取指令访问的段越多,它就越长.您可以在"CUDA编程指南"中阅读更多详细信息,这一主题有一整章:"5.3.最大化内存吞吐量".

另外,我听说了一些关于共享内存来本地化内存访问.这是内存合并的首选还是有其自身的困难? 共享内存在芯片上更快,但其大小有限.内存不像全局一样被分段,你可以几乎随机访问,无需花费.但是,存在宽度为4字节的存储体线(32位int的大小).每个线程访问的内存地址应该是16(或32,取决于GPU)不同的模块.所以,地址[tid*4]会慢得多[tid*5],因为第一个只能访问0,4,8,12和后者0,5,10,15,4,9,14 ......(bank id = address modulo 16) ).

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