在CUDA中,什么是内存合并,它是如何实现的?

kar*_*kar 68 cuda definition memory-access

什么是CUDA全局内存事务中的"合并"?经过我的CUDA指南后,我无法理解.怎么做?在CUDA编程指南矩阵示例中,逐行访问矩阵称为"coalesced"或col .. by col ..称为合并?哪个是正确的,为什么?

jmi*_*loy 136

这些信息可能仅适用于计算能力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
Run Code Online (Sandbox Code Playgroud)

可以一行一行地完成,这样(r,c)映射到内存(r*4 + c)

0 1 2 3 4 5 6 7 8 9 a b
Run Code Online (Sandbox Code Playgroud)

假设你需要访问一次元素,并说你有四个线程.哪个线程将用于哪个元素?可能要么

thread 0:  0, 1, 2
thread 1:  3, 4, 5
thread 2:  6, 7, 8
thread 3:  9, a, b
Run Code Online (Sandbox Code Playgroud)

要么

thread 0:  0, 4, 8
thread 1:  1, 5, 9
thread 2:  2, 6, a
thread 3:  3, 7, b
Run Code Online (Sandbox Code Playgroud)

哪个更好?哪个会导致合并读取,哪个不会?

无论哪种方式,每个线程进行三次访问.让我们看看第一次访问,看看线程是否连续访问内存.在第一个选项中,第一个访问是0,3,6,9.不连续,不合并.第二个选项,它是0,1,2,3.连续!合并!好极了!

最好的方法可能是编写内核然后对其进行概要分析,看看你是否有非合并的全局加载和存储.


ram*_*ino 10

内存合并是一种允许最佳使用全局内存带宽的技术.也就是说,当运行相同指令的并行线程访问全局存储器中的连续位置时,实现了最有利的访问模式.

在此输入图像描述

上图中的示例有助于解释合并的排列:

在图(a)中,长度为m的n个矢量以线性方式存储.矢量j的元素iv j i表示.GPU内核中的每个线程被分配给一个m长度矢量.CUDA中的线程被分组在一个块数组中,GPU中的每个线程都有一个唯一的id,可以定义为,其中表示块维度,表示块索引,是每个块中的线程索引. indx=bd*bx+txbdbxtx

垂直箭头表示并行线程访问每个向量的第一个分量的情况,即存储器的地址0,m, 2m .... 如图(a)所示,在这种情况下,存储器访问不是连续的.通过将这些地址之间的间隙归零(上图中显示的红色箭头),内存访问将合并.

但是,这里的问题稍微有点棘手,因为每个GPU块的驻留线程的允许大小限制为bd.因此,可以通过bd以连续顺序存储第一矢量的第一元素,然后存储第二矢量矢量的第一元素等来完成合并数据排列.其余的向量元素以类似的方式存储,如图(b)所示.如果n(向量的数量)不是因子bd,则需要用一些微不足道的值(例如0)填充最后一个块中的剩余数据.

在直线的数据存储在图(a),成分. (0≤ < )载体的INDX (0≤ INDX < Ñ)由寻址m × indx +i; 图(b)中的合并存储模式中的相同组件被称为

(m × bd) ixC + bd × ixB + ixA,

在哪里ixC = floor[(m.indx + j )/(m.bd)]= bx,ixB = jixA = mod(indx,bd) = tx.

总之,在存储大小为m的多个向量的示例中,线性索引根据以下内容映射到合并索引:

m.indx +i ?? m.bd.bx +i .bd +tx

这种数据重新排列可以导致GPU全局存储器的显着更高的存储器带宽.


资料来源:"非线性有限元变形分析中基于GPU的计算加速." 国际生物医学工程数值方法杂志(2013年).


小智 8

如果块中的线程正在访问连续的全局存储器位置,则所有访问由硬件组合成单个请求(或合并).在矩阵示例中,行中的矩阵元素线性排列,然后是下一行,依此类推.对于例如块中的2x2矩阵和2个线程,存储器位置排列为:

(0,0)(0,1)(1,0)(1,1)

在行访问中,thread1访问(0,0)和(1,0),它们无法合并.在列访问中,thread1访问(0,0)和(0,1),它们可以合并,因为它们是相邻的.

  • 不错,简洁,但..记住,凝聚的是不是两个串行访问由线程1,而是由线程1并联的同时访问及线程.在你行存取例如,如果线程1访问(0,0)和(1,0),那么我想线程2正在访问(0,1)和(1,1).因此,第一个并行访问是1:(0,0)和2:(0,1) - >合并! (6认同)