Memory CUDA-合并内存访问和总线宽度

Memory CUDA-合并内存访问和总线宽度,memory,cuda,coalescing,Memory,Cuda,Coalescing,因此,我关于在CUDA中合并内存访问的想法是,warp中的线程应该访问连续的内存地址,因为这只会导致单个内存事务(每个地址上的值然后广播到线程),而不是以串行方式执行的多个内存事务 现在,我的总线宽度是48字节。这意味着我可以在每个内存事务上传输48个字节,对吗?因此,为了充分利用总线,我需要能够一次读取48个字节(通过每个线程读取超过一个字节,内存事务由warp执行)。然而,假设一个线程一次读取48个字节不会提供同样的优势吗(我假设我可以通过读取一个大小为48字节的结构一次读取48个字节) 我

因此,我关于在CUDA中合并内存访问的想法是,warp中的线程应该访问连续的内存地址,因为这只会导致单个内存事务(每个地址上的值然后广播到线程),而不是以串行方式执行的多个内存事务

现在,我的总线宽度是48字节。这意味着我可以在每个内存事务上传输48个字节,对吗?因此,为了充分利用总线,我需要能够一次读取48个字节(通过每个线程读取超过一个字节,内存事务由warp执行)。然而,假设一个线程一次读取48个字节不会提供同样的优势吗(我假设我可以通过读取一个大小为48字节的结构一次读取48个字节)

我的合并问题是我必须对数据进行转置。我有很多数据,所以转置它需要时间,如果可以的话,我宁愿用它来做其他事情


我正在研究Compute Capability 2.0。

为了合并,正如您所说的,您应该将重点放在使扭曲中的32个线程访问相邻位置,最好是32字节或128字节对齐。除此之外,不必担心DRAM内存的物理地址总线。内存控制器主要由独立分区组成,每个分区的宽度为64位。内存控制器将尽可能快地满足您从warp出来的合并访问。对于访问整型或浮点型的完整扭曲(32个线程)的单个联合访问,无论如何都需要检索128个字节,即DRAM物理总线上的多个事务。当您在缓存模式下运行时,无论如何,您无法将对全局内存的请求粒度控制在每次128字节以下

不可能在单个事务中导致单个线程请求48字节或类似的内容。即使在c代码级别,如果您认为您正在同时访问整个数据结构,那么在机器代码级别,它也会转换为一次读取32或64位的指令


如果您觉得每次128字节的缓存限制正在惩罚您的代码,您可以尝试在未缓存模式下运行,这将把全局内存请求的粒度降低到每次32字节。如果您有一个分散的访问模式(没有很好地合并),此选项可能会提供更好的性能。

您的GPU的内存总线不仅仅是48字节宽(这将是相当麻烦的,因为它不是2的幂)。相反,它由6个内存通道组成,每个通道8字节(64位)。为了利用内存的突发模式,内存事务通常比通道宽度宽得多。好的事务大小从64字节开始产生一个大小为8的突发,这与compute capability 1.x设备上的16个32位半扭曲字非常匹配

128字节范围的事务仍然要快一点,与compute capability 2.0(及更高版本)设备的warp-wide 32位字访问相匹配。缓存线也有128字节宽以匹配。请注意,所有这些访问必须在事务宽度的倍数上对齐,以便映射到单个内存事务

现在,关于您的实际问题,最好的办法可能是什么都不做,让缓存来解决它。这与在共享内存中显式执行的方式相同,只是由缓存硬件为您执行,不需要任何代码,这将使它稍微快一点。唯一需要担心的是要有足够的缓存可用,以便每个扭曲都可以有必要的32×32×4字节=4KB的缓存用于字范围(例如浮点)或8KB的双访问。这意味着,限制同时处于活动状态的扭曲的数量以防止它们相互碰撞缓存线是有益的

对于特殊优化,还可以使用向量类型,如
float2
float4
,因为所有支持CUDA的GPU都有加载和存储指令,这些指令将8或16个字节映射到同一线程中。然而,在compute capability 2.0及更高版本上,我并没有看到在一般矩阵转置情况下使用它们的任何优势,因为它们会进一步增加每个扭曲的缓存占用空间

由于16kB缓存/48kB共享内存的默认设置仅允许每个SM的四个扭曲在任何时候执行转置(前提是您同时没有其他内存访问),因此,在使用默认16kB/48kB拆分的基础上选择48kB缓存/16kB共享内存设置可能是有益的

为了完整起见,我还将提到compute capability 3.0引入的warp shuffle指令允许在warp内交换寄存器数据,而无需通过缓存或共享内存。有关详细信息,请参阅CUDA C编程指南的第页
(请注意,《编程指南》的一个版本没有本附录。因此,如果您的副本中附录B.13涉及其他内容,请通过提供的链接重新加载它)