你最终想要的取决于你的输入数据是一维数组还是二维数组,以及你的网格和块是否为一维或二维。最简单的情况是两者都是一维的:
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + threadIdx.x];
这是合并的操作。 我使用的经验法则是,最快变化的坐标(threadIdx)作为偏移量添加到块偏移量(blockDim * blockIdx)上。 最终结果是块内线程之间的索引跨度为1。 如果跨度变大,则会失去合并。
简单的规则(在Fermi和更高版本的GPU上)是:如果warp中所有线程的地址都落入同一对齐的128字节范围内,则会产生单个内存事务(假设启用了缓存以进行加载,默认情况下)。 如果它们落入两个对齐的128字节范围内,则会产生两个内存事务,依此类推。
在GT2xx及更早期的GPU上,情况变得更加复杂。 但是,您可以在编程指南中找到有关此问题的详细信息。
其他示例:
不合并:
shmem[threadIdx.x] = gmem[blockDim.x + blockIdx.x * threadIdx.x];
在GT200及之后的显卡中,虽然不是最优的,但仍算不错:
stride = 2;
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + stride * threadIdx.x];
完全未合并:
stride = 32;
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + stride * threadIdx.x];
合并、二维网格、一维块:
int elementPitch = blockDim.x * gridDim.x;
shmem[threadIdx.x] = gmem[blockIdx.y * elementPitch +
blockIdx.x * blockDim.x + threadIdx.x];
合并、二维网格和块:
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int elementPitch = blockDim.x * gridDim.x;
shmem[threadIdx.y * blockDim.x + threadIdx.x] = gmem[y * elementPitch + x];