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

时间:2012-09-25 19:00:42

标签: memory cuda coalescing

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

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

我的合并问题是我必须对数据进行转置。我有很多数据,所以转置它需要时间,如果可能的话我宁愿用于别的东西。

我正在使用Compute Capability 2.0。

2 个答案:

答案 0 :(得分:7)

GPU的内存总线不仅仅是48字节宽(由于它不是2的幂,因此非常麻烦)。相反,它由6个8字节(64位)的存储器通道组成。内存事务通常比通道宽度宽得多,以便利用内存的突发模式。良好的事务大小从64字节开始产生大小为8的突发,与计算能力1.x设备上的半经线的16个32位字很好地匹配。

128字节宽的事务仍然快一点,并且匹配计算能力2.0(和更高)设备的warp-wide 32位字访问。高速缓存行也是128字节宽以匹配。请注意,所有这些访问必须在事务宽度的倍数上对齐,以便映射到单个内存事务。

现在关于你的实际问题,最好的事情可能是什么也不做,让缓存排序。这与您在共享内存中明确执行的操作方式相同,只是它由缓存硬件为您完成,并且不需要代码,这应该使其稍微快一些。唯一需要担心的是拥有足够的缓存,以便每个warp可以具有必要的32×32×4字节= 4k字节的缓存用于字宽(例如浮点)或8k字节用于双访问。这意味着限制同时处于活动状态的warp数量可能是有益的,以防止它们颠簸彼此的缓存行。

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

由于16kB缓存/ 48kB共享内存的默认设置只允许每个SM四个warp在任何时候执行转置(假设您没有同时没有其他内存访问),选择它可能是有益的使用cudaDeviceSetCacheConfig()在默认的16kB / 48kB分割上设置48kB缓存/ 16kB共享内存。

为了完整起见,我还要提到计算能力3.0引入的warp shuffle指令允许在warp内交换寄存器数据,而无需通过缓存或共享内存。有关详细信息,请参阅“CUDA C编程指南”的Appendix B.14
(请注意,编辑指南的版本没有本附录。因此,如果您的副本附录B.13中有其他内容,请通过提供的链接重新加载。)

答案 1 :(得分:6)

出于合并的目的,如您所述,您应该专注于在warp访问连续位置中创建32个线程,最好是32字节或128字节对齐。除此之外,不要担心到DRAM内存的物理地址总线。存储器控制器主要由独立的分区组成,每个分区的宽度为64位。存储器控制器将尽快满足您从warp中出来的合并访问。对于访问int或float的完整warp(32个线程)的单个合并访问将需要检索128个字节,即物理总线上的多个事务到DRAM。当您在缓存模式下运行时,无论如何,您无法真正控制一次低于128字节的全局内存请求的粒度。

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

如果您认为一次128字节的缓存限制会损害您的代码,您可以尝试以非缓存模式运行,这会将全局内存请求的粒度一次减少到32个字节。如果您具有分散的访问模式(未很好地合并),则此选项可以提供更好的性能。