☰
第2节:线程组织与索引计算【CUDA高性能编程实战‑板块一】
2026/10/20 16:41:59 网站建设 项目流程

第2节:线程组织与索引计算【CUDA高性能编程实战‑板块一】

📚 专栏:《CUDA 高性能编程实战:从 Kernel 到 FlashAttention》
✨ 本篇为板块一第2节,详解Grid/Block层级线程架构、三维索引、全局唯一线程ID计算,配套完整可运行代码示例
🎯 阅读目标:掌握任意维度下线程索引推导,能独立实现一维、二维数据并行读写

学习目标

学完本节你将掌握:

  1. 理解 Grid、Block、Thread 三层层级架构逻辑
  2. 分清gridIdx/blockIdx/threadIdx三组内置变量含义
  3. 熟练计算一维、二维场景下全局唯一线程ID
  4. 写出通用索引计算模板,避免数组越界访问
  5. 理解dim3三维启动参数的使用规则

1. CUDA三层线程架构

GPU并行执行单元分为三层嵌套结构,从上至下:

  1. Grid(网格):一次Kernel启动的全部线程集合,由多个Block组成
  2. Block(线程块):一组共享片上共享内存的线程,单个Block最大1024线程
  3. Thread(线程):最小执行单元,对应SIMT流水线单条执行通道

层级关系示意图:

Grid ├─ Block(0,0) │ ├─ Thread(0,0,0) │ ├─ Thread(1,0,0) │ └─ ... ├─ Block(1,0) │ ├─ Thread(0,0,0) │ └─ ... └─ ...

核心特性:

  • 同一个Block内线程可同步、访问共享内存;不同Block线程无通信机制
  • Grid、Block均支持三维定义:x/y/z,日常一维计算只用x维度

2. 内置索引变量(只读)

CUDA自动为每个线程分配三组内置dim3类型索引:

变量名作用域含义
gridDim全局Grid整体尺寸,gridDim.x=Block总数
blockDim单个Block每个Block内线程总数,blockDim.x=单块线程数
blockIdxBlock内所有线程当前线程所在Block编号
threadIdx单个线程当前线程在Block内部的局部编号

一维场景索引逻辑(最常用)

一维下仅使用x分量,全局唯一线程ID计算公式:

int global_tid = blockIdx.x * blockDim.x + threadIdx.x;

公式拆解:

  • blockIdx.x * blockDim.x:当前Block前面所有Block的线程总量
  • + threadIdx.x:叠加当前Block内部局部偏移

3. dim3 三维启动参数

dim3是CUDA内置三维结构体,默认未赋值维度自动填充为1。
两种启动写法等价:

// 写法1:纯int一维启动 kernel<<<32, 128>>>(); // 写法2:dim3标准三维启动 dim3 grid_size(32, 1, 1); dim3 block_size(128, 1, 1); kernel<<<grid_size, block_size>>>();

二维矩阵计算标准dim3示例:

// 二维场景:每行一个Block,每个Block 256线程 dim3 grid(rows, 1); dim3 block(256, 1);

4. 完整代码示例1:一维数组索引遍历

#include <cstdio> __global__ void indexTestKernel(int data_len) { // 计算全局唯一线程ID int tid = blockIdx.x * blockDim.x + threadIdx.x; // 边界判断:防止线程超出数组长度,避免越界 if (tid < data_len) { printf("Block:%d Thread:%d GlobalTid:%d\n", blockIdx.x, threadIdx.x, tid); } } int main() { int data_size = 20; // 启动配置:4个Block,每个Block 8线程,总32线程 indexTestKernel<<<4, 8>>>(data_size); cudaDeviceSynchronize(); cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { printf("Kernel启动异常:%s\n", cudaGetErrorString(err)); return -1; } return 0; }

编译运行指令:

nvcc thread_index.cu -o thread_index ./thread_index

5. 二维矩阵索引计算

图像处理、矩阵运算常用二维Grid/Block,全局坐标推导:

// 当前像素在矩阵中的x、y坐标 int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; // 一维展平数组下标(行优先存储) int global_idx = y * matrix_width + x;

二维Kernel完整示例片段:

__global__ void matrixKernel(float* mat, int width, int height) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; if (x < width && y < height) { int idx = y * width + x; mat[idx] = x + y * 0.5f; } }

6. 索引避坑核心要点

  1. 必须加边界判断if(tid < length)
    总线程数 =gridDim.x * blockDim.x,通常会大于数据长度,多余线程不处理数据,不加判断会数组越界、内存报错。
  2. Block单块线程上限1024
    blockDim.x * blockDim.y * blockDim.z ≤ 1024,超过直接启动失败。
  3. 索引变量只读,不可修改
    blockIdx.x = 5;这类代码编译报错。
  4. 全局ID公式固定不变
    一维计算统一使用blockIdx.x * blockDim.x + threadIdx.x,不要颠倒乘数顺序。

7. 课后练习

  1. 修改一维示例启动参数为<<<5, 6>>>,手动计算总线程数,观察输出的最大global_tid。
  2. 改写代码实现二维512×512矩阵遍历,dim3设置为dim3 block(16,16),推导grid尺寸。
  3. 移除边界判断语句,传入较小data_len,观察程序是否出现内存错误。
  4. 尝试将blockDim设置为1025,查看Kernel启动报错信息,巩固Block线程限制知识点。

🔗专栏上下篇

  • 上一篇:第1节:CUDA 编程模型与核函数【CUDA高性能编程实战‑板块一】
  • 下一篇:第3节:SIMT与Warp基础【CUDA高性能编程实战‑板块一】

💡提示:所有代码复制后可直接nvcc编译运行,建议手动修改启动参数直观感受线程索引变化。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询