cuda三维索引与一维索引(一)

  • Post author:
  • Post category:其他




如果将整个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和二维的block,

  1. 假设三维的 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)

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)

假设三维的 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,否则可能会导致整数溢出。此外,还需要确保线程块的大小和网格大小与实际情况相符。



版权声明:本文为qq_43594926原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。