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

如果将整个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。nxnynz 为整体网格大小

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 和线程中的索引,就得到了唯一位置的索引

需要注意的是,在使用这个表达式之前,需要保证整体网格大小 nxnynz 不超过 2^31-1,否则可能会导致整数溢出。此外,还需要确保线程块的大小和网格大小与实际情况相符。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值