1 threads相关概念

CUDA里面用Grid和Block作为线程组织的组织单位,一个Grid可包含了N个Block,一个Block包含N个thread。

相关单位参数:

  • gridDim: blocks在grid里面的数量维度;dim3;
  • blockDim: threads在一个block的数量维度;dim3;
  • blockIdx: block在grid里面的索引;dim3;
  • threadIdx:thread在block里面的索引;dim3;

gridDim,规定blocks的形状,blockDim规定了threads的形状。 这些参数在kernel写好后,global/___device__定义的函数里面能够直接访问到变量。我们在创建kernel时,通常会传入blocks的维度、threads维度,示例如下

__global__ void kernel(float *src){
   //do something
}
 
// 定义grid 和block的形状:
dim3 BlocksPerGrid(N, N, N);  // gridDim 对应gridDim.x、gridDim.y、gridDim.z
dim3 threadsPerBlock(M, M, M);  // blockDim 对应blockDim.x、blockDim.y、blockDim.z
// invoke code:
kernel<<<BlocksPerGrid, threadsPerBlock>>>(*src);

下图给出了一个实例,其中gridDim .x=2, .y=2, .z=3; blockDim .x=4, .y=2, .z=4

这样,一共包含的block数量:223 = 12;每个block线程总数:424 = 32; 线程总数:12 * 32 = 384

若要索引一个threads,可通过索引坐标, 如图标记为蓝色的线程,其索引表示方式:blockIdx的 x=1, y=0, z=2; threadIdx的x=3, y=0; z=3

而线程的全局idx的求解,还需要按照坐标计算公式求得:

2 坐标计算公式

现在需要让线程通过全局索引拿取各自的数据,则需要通过公式转换计算求得。首先需要知道全部3D的坐标下的idx如何计算。3D3D的索引计算即找出一个线程在所有线程中的位置。由于线程的组织结构包含了两层,所以可以拆分计算:

  • step1: 计算线程thread在block的位置:
  • step2: 计算该block在grid中的位置;
  • step3: 计算block有多少线程;求解位置索引。

2.1 3D结构

2.2 2D结构

__global__ void kernel2D2D(float *input, int dataNum)
{
    // int threadInBlock = threadIdx.x + threadIdx.y*blockDim.x + threadIdx.z*blockDim.x*blockDim.y;
    // int blockInGrid = blockIdx.x + blockIdx.y*gridDim.x + blockIdx.z*gridDim.x*gridDim.y;
    // int oneBlockSize = blockDim.x*blockDim.y*blockDim.z;
    // int idx = threadInBlock + oneBlockSize*blockInGrid;
    // when:
    // threadIdx.z = 0; blockIdx.z = 0;
    // blockDim.z = 1; gridDim.z = 1;
    // then:
    // int threadInBlock = threadIdx.x + threadIdx.y*blockDim.x;
    // int blockInGrid = blockIdx.x + blockIdx.y*gridDim.x;
    // int oneBlockSize = blockDim.x*blockDim.y;
    int idx = threadIdx.x + threadIdx.y*blockDim.x + blockDim.x*blockDim.y*(blockIdx.x + blockIdx.y*gridDim.x);
    // thread overflow offset = blockDim.x*blockDim.y*gridDim.x*gridDim.y;
}

2.3 1D结构

令 threadIdx.y = 0; threadIdx.z = 0; blockIdx.y= 0; blockIdx.z = 0; blockDim.y = 1; blockDim.z = 1; gridDim.y = 1; gridDim.z = 1; 带入3D公式中简化得到1D计算:

__global__ void kernel1D1D(float *input, int dataNum)
{
    // int threadInBlock = threadIdx.x + threadIdx.y*blockDim.x + threadIdx.z*blockDim.x*blockDim.y;
    // int blockInGrid = blockIdx.x + blockIdx.y*gridDim.x + blockIdx.z*gridDim.x*gridDim.y;
    // int oneBlockSize = blockDim.x*blockDim.y*blockDim.z;
    // int idx = threadInBlock + oneBlockSize*blockInGrid;
    // when:
    // threadIdx.y = 0; threadIdx.z = 0; blockIdx.y= 0;  blockIdx.z = 0;
    // blockDim.y = 1; blockDim.z = 1; gridDim.y = 1; gridDim.z = 1;
    // then:
    // int threadInBlock = threadIdx.x;
    // int blockInGrid = blockIdx.x;
    // int oneBlockSize = blockDim.x;
    int idx = threadIdx.x + blockIdx.x * blockDim.x;
    // thread overflow offset = blockDim.x*gridDim.x;
}