1. 概念
Shared memory是片上存储器,因此与local memory或global memory相比更高的带宽和更低的延迟。前提是线程之间没有 bank conflicts。
为了实现高带宽,共享内存被划分为大小相等的内存模块,称为Banks,可以同时访问。因此,任何由 n 个地址组成的内存读取或写入请求都可以同时提供服务,从而产生比单个模块带宽高 n 倍的总带宽。
但是,如果warp内多个线程的内存请求的两个地址位于同一Bank中,则存在bank conflict,并且必须序列化访问。硬件将具有bank conflict的内存请求拆分为根据需要尽可能多的单独的无冲突请求,从而将吞吐量降低一个系数,该系数等于单独的内存请求数。如果单独内存请求的数量为 n,则初始内存请求称为导致 n 路存储体冲突。此处的一个例外是,当 warp 中的多个线程对同一共享内存位置进行寻址时,从而导致广播。在这种情况下,来自不同存储区的多个广播被合并到从请求的共享内存位置到线程的单个多播中。(注意区分:同一Bank(冲突) 和 同一位置(广播))
因此,为了获得最佳性能,了解内存地址如何映射到内存存储体非常重要,以便安排内存请求,从而最大限度地减少存储体冲突。
在计算能力为 5.x 或更高版本的设备上,每个Bank的每个时钟周期的带宽为 32 位,并且连续的 32 位字被分配给连续的Bank。Warp大小为 32 个线程,Bank的数量也为 32 个,因此Warp中的任何线程之间都可能发生Bank冲突。
2. NVIDIA Tesla V100的Bank组织形式
计算能力7.5,Maximum amount of shared memory per SM为64KB,最大可配置为96KB。Bank数量为32,Bank宽度为32-bit(4B)。故每个Bank可保存:
个整型或单精度浮点型数。或者说:Bank组织成了512行32列的矩阵。
3. Bank Conflict示例
bank conflict:同一warp的多个线程访问一个bank内的不同地址。
下图显示了共享内存跨步访问(strided access)的一些示例。适用于计算能力为 3.x(在 32 位模式下)或计算能力为 5.x 、 6.x、7.x 的设备。

左:线性寻址,步幅为一个 32 -bit(无bank conflict)。
中间:线性寻址,步幅为两个 32 -bit(双向bank conflict)。
右:线性寻址,步幅为三个 32 -bit(无bank conflict)。
下图显示了涉及广播机制的一些内存读取访问示例。计算能力为 3.x、5.x 、 6.x、7.x 的设备的示例。

左:通过随机排列实现无冲突访问。
中间:无冲突访问,因为线程 3、4、6、7 和 9 访问Bank5 中的同一位置。属于广播
右:无冲突广播访问(线程访问Bank内的相同位置)。
4. 用pad方式避免Bank Conflict
如下,分配一块共享内存:

__shared__ int a[5][32];
int b = a[thx * 2];
会产生2 bank conflict,可以通过memory padding的方式来避免bank conflict,如下图:

按bank方式排列的数据如下:

从代码角度:
__shared__int sMem[5][33];
int row = tid / 32;
int col = tid % 32;
sMem[row*33+col] = global_mem[tid];
b = sMem[tid*2 + tid*2/32];
(更多可参考:https://blog.csdn.net/endlch/article/details/47043069)
本文深入探讨了CUDA中的共享内存,它提供高带宽和低延迟,但受bank冲突影响。银行冲突发生在多个线程访问同一bank的不同位置时,导致访问序列化。在NVIDIA Tesla V100上,每个SM有64KB共享内存,32个bank,每个bank宽度为32位。通过理解内存地址映射和避免bank冲突,可以优化性能。示例展示了不同步幅的访问模式及其对bank冲突的影响。

1130

被折叠的 条评论
为什么被折叠?



