以下内核执行我在本文中遇到的矩阵副本: https://devblogs.nvidia.com/efficient-matrix-transpose-cuda-cc/
__global__ void copy(float *odata, const float *idata)
{
int x = blockIdx.x * TILE_DIM + threadIdx.x;
int y = blockIdx.y * TILE_DIM + threadIdx.y;
int width = gridDim.x * TILE_DIM;
for (int j = 0; j < TILE_DIM; j+= BLOCK_ROWS)
odata[(y+j)*width + x] = idata[(y+j)*width + x];
}
我对使用的符号感到困惑。据我所知,数据采用行主格式。 “y”对应于行,“x”对应于列。因此,线性指数计算为data [y] [x] = data [y * width + x];
odata [(y + j)* width + x]如何合并?在行主要中,同一行中的元素位于连续的位置。因此,以时尚方式访问元素,(y,x)(y,x + 1)(y,x + 2)......是连续的。
然而,上面的“j”被添加到似乎没有合并的“y”。 我对这种符号的理解是不正确的还是我在这里遗漏了什么?
答案 0 :(得分:1)
合并内存事务只需要来自同一warp的线程读取和写入连续的内存块,这可以由单个事务提供。你的代码
int x = blockIdx.x * TILE_DIM + threadIdx.x;
int y = blockIdx.y * TILE_DIM + threadIdx.y;
odata[(y+j)*width + x] = idata[(y+j)*width + x];
生成合并访问,因为j
在warp中的每个线程中都是常量。因此访问模式变为:
0. (y * width); (y * width + 1); (y * width + 2); .....
1. (y * width + width); (y * width + width + 1); (y * width + width + 2); .....
2. (y * width + 2 * width); (y * width + 2 * width + 1); (y * width + 2 * width + 2); .....
在每次warp中,J访问的任何值仍然是带内存的顺序元素,因此读取和写入将合并。