在CUDA中,什么是内存聚合,它是如何实现的?
CUDA全局内存事务中的“合并”是什么? 即使通过我的CUDA指南,我也无法理解。 怎么做? 在CUDA编程指南matrix的例子中,一行一行地访问matrix称为“合并”(coalesced)或col .. col。被称为合并(coalesced) 这是正确的,为什么?
这些信息可能仅适用于计算function1.x或cuda 2.0。 更新的体系结构和cuda 3.0具有更复杂的全局内存访问,实际上“合并的全局负载”甚至没有针对这些芯片进行分析。
而且,这个逻辑可以应用于共享内存以避免银行冲突。
一个合并的内存事务是一个在同一时间半全局访问全局内存中的所有线程。 这是过于简单,但正确的方法是连续的线程访问连续的内存地址。
因此,如果线程0,1,2和3读取全局内存0x0,0x4,0x8和0xc,它应该是一个合并读取。
在matrix示例中,请记住,您希望matrix在内存中线性驻留。 你可以做到这一点,但你的内存访问应该反映你的matrix是如何布局的。 所以,下面的3×4matrix
0 1 2 3 4 5 6 7 8 9 ab
(r,c)映射到内存(r * 4 + c)
0 1 2 3 4 5 6 7 8 9 ab
假设你需要访问元素一次,并说你有四个线程。 哪个线程将用于哪个元素? 可能也是
thread 0: 0, 1, 2 thread 1: 3, 4, 5 thread 2: 6, 7, 8 thread 3: 9, a, b
要么
thread 0: 0, 4, 8 thread 1: 1, 5, 9 thread 2: 2, 6, a thread 3: 3, 7, b
哪个更好? 这将导致合并阅读,哪些不会?
无论哪种方式,每个线程进行三次访问。 我们来看看第一次访问,看看线程是否连续访问内存。 在第一个选项中,第一个访问权限是0,3,6,9。不连续,不合并。 第二个选项是0,1,2,3。连续! 合并! 好极了!
最好的方法是编写你的内核,然后对其进行configuration,看看你是否有非整合的全局加载和存储。
内存合并是一种允许最佳使用全局内存带宽的技术。 也就是说,当运行相同指令的并行线程访问全局存储器中的连续位置时,实现最有利的访问模式。
上图中的示例帮助解释了合并安排:
在图(a)中,长度为m的 n个向量以线性方式存储。 向量j的元素i由v j i表示。 GPU内核中的每个线程都被分配一个m-长度向量。 CUDA中的线程被分组成块,GPU中的每个线程都有一个唯一的id,可以定义为indx=bd*bx+tx
,其中bd
表示块维度, bx
表示块索引, tx
表示线程索引每块。
垂直箭头表示并行线程访问每个向量的第一个分量,即地址0, m , 2m …的情况。 如图(a)所示,在这种情况下,存储器访问不是连续的。 通过调整这些地址之间的差距(如上图所示的红色箭头),内存访问将变为合并。
但是,这里的问题稍微有些棘手,因为每个GPU块的允许的线程大小限制在bd
。 因此,可以通过以连续顺序存储第一bd
向量的第一元素,然后存储第二bd向量的第一元素等等来完成合并的数据排列。 其余vector元素以类似的方式存储,如图(b)所示。 如果n (向量的数量)不是bd
一个因子,则需要使用一些不重要的值填充最后一个块中的剩余数据,例如0。
在图(a)的线性数据存储中,向量indx ( 0≤indx < n )的分量i ( 0≤i < m )由m × indx +i
来寻址; 图(b)中的合并存储模式中的相同组件被解决为
(m × bd) ixC + bd × ixB + ixA
,
其中ixC = floor[(m.indx + j )/(m.bd)]= bx
, ixB = j
并且ixA = mod(indx,bd) = tx
。
总之,在存储大小为m的向量的示例中,线性索引被映射到根据以下的合并索引:
m.indx +i −→ m.bd.bx +i .bd +tx
这种数据重新排列可能会导致GPU全局内存的显着更高的内存带宽。
来源:“基于GPU的非线性有限元计算加速度变形分析”。 国际生物医学工程数值方法杂志(2013)。
如果块中的线程正在访问连续的全局存储器位置,则所有的访问都被硬件组合成单个请求(或合并)。 在matrix示例中,行中的matrix元素线性排列,接着是下一行,依此类推。 对于例如一个块中的2×2matrix和2个线程,存储器位置如下排列:
(0,0)(0,1)(1,0)(1,1)
在行访问中,thread1访问无法合并的(0,0)和(1,0)。 在列访问中,thread1访问(0,0)和(0,1),因为它们相邻,所以可以合并。
“ CUDA 3.2编程指南”的第G.3.2节中很好地logging了合并标准。 简短版本如下:warp中的线程必须按顺序访问内存,被访问的单词应该> = 32位。 另外,由warp访问的基地址应分别为64,128或256字节,分别对应32,64和128位访问。
Tesla2和Fermi硬件可以合并8位和16位访问,但是如果您希望获得峰值带宽,则最好避免使用它们。
请注意,尽pipeTesla2和Fermi硬件有所改进,但合并是BY NO MEANS已过时。 即使在Tesla2或Fermi级别的硬件上,如果无法合并全局内存事务,也可能导致性能下降2倍。 (在费米级的硬件上,这只有在启用了ECC的情况下才是真实的,连续的但未合并的内存交易在Fermi上大约占20%)。