如果将整个3D网格 三维对三维(网格,块)
三维网格中每个网格的坐标为 (i,j,k),其中 i 表示沿 x 轴的索引,j 表示沿 y 轴的索引,k 表示沿 z 轴的索引。nx、ny、nz 分别为数组在 x、y、z 轴上的长度
i = blockIdx.x * blockDim.x + threadIdx.x;
j = blockIdx.y * blockDim.y + threadIdx.y;
k = blockIdx.z * blockDim.z + threadIdx.z;
其中,blockIdx.x、blockDim.x、threadIdx.x 表示当前线程所在的块在 x 轴上的索引、每个块在 x 轴上的线程数、当前线程在所在块中在 x 轴上的索引,其他变量类似。这些变量通常在 CUDA 编程中使用。
则 (i, j, k) 在一维数组中的索引为
idx = i + j * nx + k * nx * ny;
如果将整个网格按照 z 方向分成若干个块
int i = blockDim.x * blockIdx.x + threadIdx.x;
int j = (blockDim.y * blockIdx.y + threadIdx.y) / nz;
int k = (blockDim.y * blockIdx.y + threadIdx.y) % nz;
(blockIdx.y * blockDim.y + threadIdx.y) / nz
表示
当前线程的 y 坐标在整个网格中对应的 z 坐标
。其中 blockIdx.y * blockDim.y + threadIdx.y 表示当前线程在整个网格中的唯一标识,
除以 nz 则可以得到在 z 方向上的位置。这样设计的目的是将整个网格按照 z 方向分成若干个块,方便进行并行计算。
*(blockDim.y * blockIdx.y + threadIdx.y) % nz;*其中 % 表示取模运算,返回的结果是在 0 到 nz-1 之间的整数,表示当前线程在 z 方向上的偏移量。
其中,blockDim.x、blockDim.y 和 blockDim.z 分别表示每个线程块中线程在 x、y 和 z 方向上的数量。blockIdx.x、blockIdx.y 和 blockIdx.z 分别表示当前线程块在 x、y 和 z 方向上的索引。因此,blockDim.x * blockIdx.x 和 blockDim.y * blockIdx.y 可以计算出当前线程块在三维网格中的偏移量。threadIdx.x 和 threadIdx.y 表示当前线程在线程块中的索引,因此 blockDim.x * blockIdx.x + threadIdx.x 和 blockDim.y * blockIdx.y + threadIdx.y 可以计算出当前线程在三维网格中的偏移量。最后,通过除法和取模运算可以得到当前线程在 y 和 z 方向上的坐标 j 和 k。
如果将整个网格按照 y 方向分成若干个块
int i = blockDim.x * blockIdx.x + threadIdx.x;
int j = (blockDim.y * blockIdx.y + threadIdx.y) % ny;
int k = (blockDim.y * blockIdx.y + threadIdx.y) / ny;
如果将整个网格按照 x 方向分成若干个块
int i = (blockDim.z * blockIdx.z + threadIdx.z) % nx;
int j = blockDim.y * blockIdx.y + threadIdx.y;
int k = (blockDim.z * blockIdx.z + threadIdx.z) / nx;
三维网格映射到二维的grid和block。nx
ny
nz 为整体网格大小
grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y)
grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y)
对于三维网格映射到三维的grid和二维的block,
- 假设三维的 grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y),nxnynz 为整体网格大小,则二维的 block 和三维的 grid 可以使用以下表达式进行映射:
blockIdx.x = i / (blockDim.x * blockDim.y)
blockIdx.y = (i / blockDim.x) % gridDim.y
blockIdx.z = i % gridDim.z
其中,i 为二维的 block 的唯一位置索引,可以使用以下表达式计算:
i = blockIdx.x * gridDim.y * gridDim.z + blockIdx.y * gridDim.z + blockIdx.z
在核函数中,可以使用以下表达式来计算唯一位置索引:
int i = blockIdx.x * gridDim.y * gridDim.z + blockIdx.y * gridDim.z + blockIdx.z;
int j = blockDim.x * blockDim.y * threadIdx.z + blockDim.x * threadIdx.y + threadIdx.x;
int index = i * blockDim.x * blockDim.y * blockDim.z + j;
其中,index 就是唯一位置索引,可以用于访问对应的数组元素。
2. grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y)
2. grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y)
dim3 gridDim(gridDim.x, gridDim.y, nz);
dim3 blockDim(blockDim.x, blockDim.y, 1);
核函数中,可以使用以下的方式计算唯一位置的索引
int i = blockDim.x * blockIdx.x + threadIdx.x;
int j = blockDim.y * blockIdx.y + threadIdx.y;
int k = blockIdx.z;
int index = k * blockDim.x * gridDim.x * blockDim.y * gridDim.y + j * blockDim.x * gridDim.x + i;
3. grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y)
3. grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y)
假设三维的 grid 为 (gridDim.x, gridDim.y,nz),block 为 (blockDim.x, blockDim.y),则三维网格映射到三维的 grid 和二维的 block 的表达式为:
int gridX = (nx + blockDim.x * gridDim.x - 1) / (blockDim.x * gridDim.x);
int gridY = (ny + blockDim.y * gridDim.y - 1) / (blockDim.y * gridDim.y);
int gridZ = (nz + blockDim.z * gridDim.z - 1) / (blockDim.z * gridDim.z);
dim3 grid(gridX, gridY, gridZ);
dim3 block(blockDim.x, blockDim.y);
在核函数中,可以通过如下表达式得到唯一位置的索引:
int i = blockDim.x * (blockIdx.x + gridDim.x * blockIdx.y) + threadIdx.x;
int j = blockDim.y * blockIdx.z + threadIdx.y;
int k = blockDim.z * (blockIdx.y % gridDim.y) + threadIdx.z;
其中,blockIdx.x + gridDim.x * blockIdx.y 可以将二维的 blockIdx 转换为一维的索引,从而计算出沿着 x 轴的位置;blockIdx.z 代表沿着 z 轴的位置
1. grid 为 (gridDim.x, gridDim.y, gridDim.z),block 为 (blockDim.x, blockDim.y)
假设三维的 grid 为 (gridDim.x, gridDim.y, gridDim.z),block 为 (blockDim.x, blockDim.y),则三维网格映射到三维的 grid 和二维的 block 的表达式为
int grid_x = (nx + blockDim.x * gridDim.x - 1) / (blockDim.x * gridDim.x);
int grid_y = (ny + blockDim.y * gridDim.y * gridDim.z - 1) / (blockDim.y * gridDim.y * gridDim.z);
int grid_z = gridDim.z;
int block_x = blockDim.x;
int block_y = blockDim.y * gridDim.y;
在核函数中,可以通过以下方式计算唯一位置的索引:
int i = blockDim.x * (blockIdx.x + blockIdx.y * gridDim.x) + threadIdx.x;
int j = blockDim.y * blockDim.z * blockIdx.z + blockDim.y * threadIdx.y + threadIdx.z;
2. grid 为 (gridDim.x, gridDim.y, gridDim.z),block 为 (blockDim.x, blockDim.y)
假设三维的 grid 为 (gridx, gridy, gridz),block 为 (blockx, blocky),三维的网格大小为 (nx, ny, nz),
每个 block 大小为 (blockDim.x, blockDim.y, blockDim.z),
第 i 个 block 在三维 grid 中的坐标为 (blockIdx.x, blockIdx.y, blockIdx.z),
第 j 个 thread 在 block 中的坐标为 (threadIdx.x, threadIdx.y, threadIdx.z),
则三维网格映射到三维的 grid 和二维的 block 的表达式为:
int idx = threadIdx.x + blockDim.x * blockIdx.x;
int idy = threadIdx.y + blockDim.y * blockIdx.y;
int idz = threadIdx.z + blockDim.z * blockIdx.z;
int grid_xy = gridx * gridy;
int block_xy = blockx * blocky;//每个block中线程的数量
int gid = idx + idy * blockDim.x * gridx + idz * block_xy;
int bid = (idx + idy * blockDim.x) % block_xy;
int zid = (idx + idy * blockDim.x) / block_xy;
idx、idy、idz分别是在当前 block 中的线程在 x、y、z 方向的索引;
grid_xy是 grid 在 x 和 y 方向上的大小的乘积;
block_xy是 block 在 x 和 y 方向上的大小的乘积;
gid是当前线程的全局索引;
bid是当前线程在二维 block 中的索引;
zid是当前线程在三维 grid 中的 z 方向上的索引。
三维的 grid:(gridDim.x, gridDim.y, gridDim.z) = (gridx, gridy, gridz)
二维的 block:(blockDim.x, blockDim.y) = (blockx, blocky)
dim3 grid(gridx, gridy, gridz);
dim3 block(blockx, blocky, 1);
在核函数中
int i = blockDim.x * (gridDim.y * blockIdx.z + blockIdx.y) + threadIdx.x;
int j = blockDim.y * blockIdx.x + threadIdx.y;
int k = blockDim.z * blockIdx.z + threadIdx.z;
int idx = i * ny * nz + j * nz + k;
或者第二种
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
int k = blockIdx.z * blockDim.z + threadIdx.z;
int index = i + j * gridDim.x * blockDim.x + k * gridDim.x * blockDim.x * gridDim.y * blockDim.y;
i,j,k 分别表示当前线程在 x、y、z 方向的索引,
idx 表示线程对应的唯一位置的索引。
需要注意的是,
此处假设网格按照 zyx 的顺序排列
,如果按照其他顺序排列,则公式中的对应系数需要做相应的调整
若二维的grid和block映射的表达式为:
dim3 grid_dim(nx*ny, 1, 1);
dim3 block_dim(nz, 1, 1);
在核函数中正确得到唯一位置的索引的表达式为:
int i = blockIdx.x / ny;
int j = blockIdx.x % ny;
int k = threadIdx.x;
int idx = k + j * nz + i * ny * nz;
其中 i 表示沿着 x 轴的索引,j 表示沿着 y 轴的索引,k 表示沿着 z 轴的索引,idx 表示对应在一维数组中的索引位置。
假设二维的 grid 为 (gridDim.x, gridDim.y),block 为 (blockDim.x, blockDim.y)
,则三维网格映射到二维的 grid 和 block 的表达式分别为:(未进行考证)
grid
- grid_x = (nx + blockDim.x * gridDim.x – 1) / (blockDim.x * gridDim.x)
- grid_y = (ny * nz + blockDim.y * gridDim.y – 1) / (blockDim.y * gridDim.y)
- gridDim = dim3(grid_x, grid_y, 1)
block
- block_x = (nx + blockDim.x – 1) / blockDim.x
- block_y = (ny + blockDim.y – 1) / blockDim.y
- blockDim = dim3(block_x, block_y, nz)
可以使用以下表达式在核函数中计算唯一的位置索引:
int idx = blockIdx.x * blockDim.x * blockDim.y * gridDim.y + blockIdx.y * blockDim.y * blockDim.x + threadIdx.y * blockDim.x + threadIdx.x;
其中,blockIdx.x 和 blockIdx.y 分别表示当前线程所在的二维 block 的 x 和 y 坐标,blockDim.x 和 blockDim.y 分别表示当前 block 的 x 和 y 维度大小,gridDim.y 表示二维 grid 的 y 维度大小,threadIdx.x 和 threadIdx.y 分别表示当前线程在 block 中的 x 和 y 坐标。
这个表达式
首先计算了所有已经处理过的 block 和线程数量的总和,然后加上当前 block 和线程中的索引,就得到了唯一位置的索引
。
需要注意的是,在使用这个表达式之前,需要保证整体网格大小 nx
ny
nz 不超过 2^31-1,否则可能会导致整数溢出。此外,还需要确保线程块的大小和网格大小与实际情况相符。