线程 / block / grid

CUDA 把并行线程组织成三层:

grid
 ├── block
 │    ├── thread
 │    ├── thread
 │    └── ...
 ├── block
 │    └── ...
 └── ...
  • thread —— 最小执行单位,跑一份 kernel 代码
  • block —— 一组 thread,能共享 shared memory + 互相 __syncthreads
  • grid —— 一组 block,block 之间不能直接通信(只能通过 global memory)

1. 启动一个 kernel

my_kernel<<<gridDim, blockDim>>>(args...);

gridDimblockDim 都是 dim3(可以是 1D / 2D / 3D)。

my_kernel<<<10, 256>>>(...);                  // 1D:grid=10 个 block,每 block 256 thread
my_kernel<<<dim3(4, 4), dim3(16, 16)>>>(...); // 2D:grid=4×4=16 个 block,每 block 16×16=256 thread
my_kernel<<<dim3(2, 2, 2), 64>>>(...);        // 3D grid,1D block

总线程数 = gridDim.x × gridDim.y × gridDim.z × blockDim.x × blockDim.y × blockDim.z

2. 在 kernel 里查询自己的位置

__global__ void my_kernel() {
    // block 在 grid 里的坐标
    int bx = blockIdx.x;
    int by = blockIdx.y;
    
    // thread 在 block 里的坐标
    int tx = threadIdx.x;
    int ty = threadIdx.y;
    
    // block / grid 的尺寸(整个 kernel 一致)
    int bw = blockDim.x;
    int gw = gridDim.x;
}

5 个内建变量:blockIdxthreadIdxblockDimgridDimwarpSize。前四个都是 dim3,带 .x .y .z;warpSize 是常量 32(从 Tesla 一直到 Blackwell 都是 32)。

3. 三层各自的内存可见性

内存类型谁能访问速度
寄存器单 thread最快
局部内存(.local)单 thread慢(DRAM)
共享内存(__shared__)一个 block 内所有 thread快(SRAM)
全局内存(cudaMalloc)所有 block,所有 grid
常量内存(__constant__)所有 thread,只读缓存优化

关键认知:block 内可以同步、可以共享 shared memory;block 之间不行。这就是为什么很多算法要”按 block 拆分”,每个 block 完成一小块独立的工作。

4. 块内同步:__syncthreads()

__global__ void example() {
    __shared__ float buf[256];
    
    int tid = threadIdx.x;
    buf[tid] = global_in[tid];      // 每 thread 写一个元素
    __syncthreads();                 // ★ 等整个 block 都写完
    
    // 现在可以读别 thread 写的数据了
    float v = buf[(tid + 1) % 256];
}

__syncthreads()block 内屏障——所有 thread 都到达这一行才一起继续。跨 block 没有对应物——想跨 block 同步只能拆成两个 kernel 启动。

5. 为什么不让 block 之间通信

设计原则:block 必须能独立调度、独立完成

GPU 一次能在所有 SM 上同时跑几百个 block,调度顺序未定义。block A 可能先跑、block B 可能后跑,也可能同时跑。如果你假设 “block A 先写,block B 后读”,会出错——它们随时可能交换顺序。

这个限制看起来不便,但带来的好处是 kernel 自动可扩展:同一段代码在 GTX 1050(只有几个 SM)上能跑,在 H100(132 个 SM)上也能跑,只是后者并行度更高、跑得更快。

6. 一个完整例子:向量加法

__global__ void vec_add(const float* a, const float* b, float* c, int n) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    if (tid >= n) return;             // tail guard,见下一篇
    c[tid] = a[tid] + b[tid];
}

int main() {
    int n = 1'000'000;
    int block = 256;
    int grid  = (n + block - 1) / block;   // 向上取整
    vec_add<<<grid, block>>>(a_d, b_d, c_d, n);
    cudaDeviceSynchronize();
}

n = 1_000_000,block = 256grid = 3907。一共启动 3907 × 256 = 1,000,192 个线程,处理 100 万元素 —— 多出来的 192 个线程被 tail guard 拦住直接 return

7. 一句话

thread / block / grid 是 CUDA 的三层并行抽象。block 内能同步 + 共享 shared memory,block 间只能通过 global memory 通信。这个限制让 kernel 自动可扩展到任意大小的 GPU。

下一篇:warp ——硬件真正调度的单位(不是 block,也不是 thread)。

评论区
评论功能即将上线, 敬请期待。