CUDA最佳内存访问布局:全局内存合并和共享内存库冲突

时间:2012-10-23 05:44:20

标签: cuda

我对CUDA中最方便的全局和共享内存访问布局有些怀疑。

全球记忆

1)以下内存地址(0,0)(0,1)(1,0)(1,1)如何排列在CPU内存和GPU内存中?换句话说,它们存储的顺序是什么?

2)哪个是行索引,哪个是(m, n)中的列索引?

3)通过访问列主要顺序或行主要顺序中的元素来实现全局内存合并吗?

共享内存

1)银行冲突是如何产生的?请通过示例/案例告诉我。

2)在总64K中配置共享内存和L1的命令是什么以及在哪里找到该命令?

1 个答案:

答案 0 :(得分:4)

您的问题的很多部分已在上述评论中得到解答。我只想提供一些对您有用的规则,以及一般对下一个用户有关合并内存访问的规则,共享内存库冲突的一些示例以及避免共享内存库冲突的一些规则

合并内存访问

1D数组 - 1D线程网格

gmem[blockDim.x * blockIdx.x + threadIdx.x]

2D数组 - 2D线程网格

int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int elementPitch = blockDim.x * gridDim.x;
gmem[y][x] or gmem[y * elementPitch + x]

共享内存银行冲突

为了实现高带宽,共享内存分为独立的银行。通过这种方式,共享内存可以服务于线程的同时访问。每个流式多处理器(SM)具有在32个存储体中组织的共享存储器。每个存储区的带宽为每两个时钟周期32位,主机字长为4个字节(32位):连续32位字地址分配给连续的存储区。

当两个不同的线程访问同一个银行中的不同的字时,会发生银行冲突。银行冲突会对性能产生负面影响,因为它们会强制执行硬件以序列化对共享内存的访问。请注意,如果不同的线程访问同一个单词中的任何字节,则不会发生冲突。另请注意,之间没有银行冲突 属于不同经线的线程。

快速访问

  • 如果warp的所有线程访问不同的银行,则没有银行冲突;
  • 如果warp的所有线程都为获取操作访问相同的地址,则没有 银行冲突(广播)。

慢访问

  • 32个线程访问同一个银行中的32个不同的字词,以便所有访问都被序列化;
  • 一般来说,访问共享内存的成本与同时访问单个银行的最大数量成正比。

示例1

smem[4]:   accesses bank #4  (physically, the fifth one – first row)

smem[31]:  accesses bank #31 (physically, the last one  – first row)

smem[50]:  accesses bank #18 (physically, the 19th one  – second row)

smem[128]: accesses bank #0  (physically, the first one – fifth row)

smem[178]: accesses bank #18 (physically, the 19th one  – sixth row)

如果warp中的第三个线程访问myShMem[50]和warp访问myShMem[178]中的八个线程,那么你就会发生双向冲突并且两个事务被序列化。

示例2

考虑以下类型的访问

__shared__ float smem[256];
smem[b + s * threadIdx.x]

要在同一个warp的两个线程t1t2之间发生银行冲突,必须满足以下条件

b + s * t2 = b + s * t1 + 32 * k, with k positive integer
0 <= t2 - t1 < 32

以上意思

32 * k = s * (t2 - t1)
0 <= t2 - t1 < 32

如果s是奇数,这两个条件不成立,即没有银行冲突。

示例3

示例2 ,进行以下访问

smem[b + threadIdx.x]
如果smem属于32 - 位数据类型,

不会导致冲突。但也

extern __shared__ char smem[];
foo = smem[baseIndex + threadIdx.x];

extern __shared__ short smem[];
foo = smem[baseIndex + threadIdx.x];

导致没有存储体冲突,因为访问了一个字节/线程,因此访问了同一个字的不同的字节。