CUDA全局内存事务中的“合并”是什么?经过我的CUDA指南后,我无法理解。怎么做?在CUDA编程指南矩阵示例中,逐行访问矩阵称为“coalesced”或col .. by col ..称为合并? 哪个是正确的,为什么?
答案 0 :(得分:133)
此信息可能仅适用于计算能力1.x或cuda 2.0。更新的架构和cuda 3.0拥有更复杂的全局内存访问,事实上“这些芯片的合并全局负载”甚至都没有。
此外,此逻辑可应用于共享内存以避免银行冲突。
合并的内存事务是半转换中的所有线程同时访问全局内存的事务。这是过于简单的,但正确的方法是让连续的线程访问连续的内存地址。
因此,如果线程0,1,2和3读取全局内存0x0,0x4,0x8和0xc,则应该是合并读取。
在矩阵示例中,请记住您希望矩阵线性驻留在内存中。您可以随意执行此操作,并且您的内存访问应反映矩阵的布局方式。那么,下面的3x4矩阵
0 1 2 3
4 5 6 7
8 9 a b
可以一行一行地完成,这样(r,c)映射到内存(r * 4 + c)
0 1 2 3 4 5 6 7 8 9 a b
假设您需要访问一次元素,并说您有四个线程。哪个线程将用于哪个元素?可能要么
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。连续!合并!耶!
最好的方法可能是编写内核,然后对其进行概要分析,看看你是否有非合并的全局加载和存储。
答案 1 :(得分:10)
内存合并是一种允许最佳使用全局内存带宽的技术。 也就是说,当运行相同指令的并行线程访问全局存储器中的连续位置时,可以实现最有利的访问模式。
上图中的示例有助于解释合并的安排:
在图(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矢量的第一元素等,可以完成合并数据排列。其余的向量元素以类似的方式存储,如图(b)所示。如果 n (向量的数量)不是bd
的因子,则需要用一些微不足道的值填充最后一个块中的剩余数据,例如, 0
在图(a)中的线性数据存储器中,向量 i (0≤ i < m ) > INDX
(0≤ indx < n )由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年)。
答案 2 :(得分:7)
如果块中的线程正在访问连续的全局内存位置,则所有访问都由硬件组合成单个请求(或合并)。在矩阵示例中,行中的矩阵元素线性排列,然后是下一行,依此类推。 对于例如块中的2x2矩阵和2个线程,存储器位置排列为:
(0,0)(0,1)(1,0)(1,1)
在行访问中,thread1访问(0,0)和(1,0),它们无法合并。 在列访问中,thread1访问(0,0)和(0,1),它们可以合并,因为它们是相邻的。
答案 3 :(得分:2)
CUDA 3.2 Programming Guide,第G.3.2节中很好地记录了合并的标准。短版本如下:warp中的线程必须按顺序访问存储器,被访问的字应该> = 32位。此外,warp访问的基址应分别为64位,128位或256位字节,分别用于32位,64位和128位访问。
Tesla2和Fermi硬件可以很好地合并8位和16位访问,但如果你想要峰值带宽,最好避免使用它们。
请注意,尽管Tesla2和Fermi硬件有所改进,但是合并是过时的。即使在Tesla2或Fermi类硬件上,未能合并全局内存事务也会导致2倍的性能损失。 (在Fermi类硬件上,只有在启用ECC时才会出现这种情况。连续但未解析的内存事务对Fermi的影响大约为20%。)