人人都会AI编程

15.4 SIMT 执行模型:GPU 并行计算的底层逻辑

更新时间:2026-07-09

在 15.1 节中,我们对比了 GPU 与 CPU 的架构差异:CPU 是“少量精兵”,擅长复杂逻辑与分支预测;GPU 是“海量步兵”,专为并行吞吐而生。但“有很多核心”只是硬件表象,真正让这成千上万个核心能够协同完成一幅图像渲染或一次矩阵乘法的,是 GPU 的执行模型——SIMT(Single Instruction, Multiple Threads,单指令多线程)。

理解 SIMT,是理解 CUDA 编程、Kernel 性能调优,以及为什么大模型训练/推理能把 GPU 榨干还是榨不满的关键钥匙


一、SIMT 是什么:从“一个指挥官”到“一群克隆兵”

SIMT 的核心思想可以用一句话概括:一条指令广播给一组线程,每个线程用自己的数据独立执行。

想象一个 Warp(线程束,NVIDIA GPU 上的基本调度单位,通常包含 32 个线程)。当 GPU 要执行一条加法指令时:

  • 控制单元只取指、译码一次
  • 同一条加法指令被同时广播给 Warp 里的 32 个线程;
  • 线程 0 给数据 A0 加 B0,线程 1 给数据 A1 加 B1……线程 31 给数据 A31 加 B31;
  • 32 个计算在 32 个不同的 CUDA Core(或 CUDA 核心组)上同步执行

这就像一个教官喊一声“出拳”,32 名士兵同时出拳,但每人打向各自面前的沙袋。教官不需要喊 32 次。

与 CPU 的本质区别:CPU 的一个核心在一个时钟周期里通常只推进一个线程的一条指令(配合超线程最多 2 个)。而 GPU 的一个 Warp Scheduler 在一个周期里可以推进 32 个线程的同一条指令。这种吞吐量正是大模型中矩阵运算所需要的。


二、SIMT vs SIMD:常被混淆的兄弟俩

很多人把 SIMT 和 SIMD(Single Instruction, Multiple Data)混为一谈。两者确实像,但对开发者而言,差异直接影响代码写法:

| 维度 | SIMD(如 CPU 的 AVX/NEON) | SIMT(GPU CUDA) |
|------|---------------------------|------------------|
| 编程抽象 | 向量寄存器(128/256/512 bit) | 线程(Thread) |
| 数据组织 | 显式打包成向量 | 隐式分散在线程中 |
| 分支处理 | 需手动用 mask 向量处理 | 硬件自动管理线程掩码(Predication) |
| 灵活性 | 数据宽度固定,对齐要求严格 | 线程可独立寻址、独立执行,更灵活 |
| 代表硬件 | Intel AVX、Apple AMX | NVIDIA CUDA Core |

实用认知:CPU 的 SIMD 是“你把数据排好队,我一次性算 8 个浮点数”;GPU 的 SIMT 是“你把任务拆成几万个线程,每个线程各自取自己的数据,我每 32 个一组批量执行”。前者是数据级并行,后者是线程级并行。SIMT 更适合处理不规则但可并行的任务,比如顶点着色、神经网络中的稀疏索引。


三、Warp:SIMT 的物理节拍器

在 NVIDIA GPU 上,SIMT 不是以单个线程为单位调度的,而是以 Warp(32 线程) 为单位。

关键机制

  • Warp 内同步执行:一个 Warp 里的 32 个线程共享同一个程序计数器(PC)。它们必须步调一致地执行同一条指令。
  • 线程独立状态:尽管执行同一条指令,每个线程有自己的寄存器堆、栈指针和线程 ID(threadIdx.x)。这意味着每个线程可以访问不同的内存地址。
  • Warp Scheduler:每个 SM(Streaming Multiprocessor)上有多个 Warp Scheduler。当某个 Warp 因为等待内存数据而阻塞时,硬件可以零开销切换到另一个就绪 Warp。这种“多线程级并行隐藏延迟”的机制,是 GPU 在高带宽延迟下仍能满载运转的核心。

与大模型的关联:Transformer 中的矩阵乘法(GEMM)和注意力计算,天然可以被拆成成千上万个独立的乘加操作。CUDA 程序(如 cuBLAS、FlashAttention)会把这些操作映射成海量线程,再由 Warp Scheduler 疯狂切换,把算力单元的闲置率压到最低。


四、分支发散:SIMT 的阿喀琉斯之踵

SIMT 看似完美,但有一个致命约束:一个 Warp 里的 32 个线程,任何时刻必须执行同一条指令。 如果代码里出现了 if-else 分支,会发生什么?

if (x > 0)
    a = a * 2;   // 路径 A
else
    a = a + 3;   // 路径 B

当 Warp 内的 32 个线程中,一部分满足条件走路径 A,另一部分不满足走路径 B 时,硬件无法同时执行两条不同指令。它的处理方式是:

  1. 掩码(Mask/Predication):先执行路径 A,不满足条件的线程被掩码掉(不参与写回);
  2. 再执行路径 B,刚才满足条件的线程被掩码掉;
  3. 两条路径串行化执行,执行时间叠加。

这被称为 Branch Divergence(分支发散)。最坏情况下,一个 Warp 要执行 32 条不同路径,彻底退化成串行。

实用认知

  • 在写 CUDA Kernel 或阅读大模型推理框架源码时,要警惕 Warp 内的分支。例如,对 threadIdx.x % 32 做不同处理,往往就会引发发散。
  • 在深度学习场景里,这解释了为什么 ReLU(无分支,max(0, x))比带条件判断的自定义激活函数在 GPU 上更快;也解释了为什么 结构化稀疏(Structured Sparsity)比非结构化稀疏更容易加速——后者导致 Warp 内线程执行路径不一致。

五、内存访问合并:Warp 的集体行动原则

SIMT 不仅约束了执行,也约束了访存。GPU 显存的读取以 事务(Transaction) 为单位,一个 Warp 里的 32 个线程如果各自随机访问内存,会产生 32 次独立事务,带宽利用率暴跌。

但当 Warp 内的线程访问连续且对齐的内存地址时,硬件可以把这些访问合并(Coalesced)成一次或几次事务。例如:

  • 线程 0 读地址 base + 0
  • 线程 1 读地址 base + 4
  • ……
  • 线程 31 读地址 base + 124

这 32 个 32-bit 读取会被合并为一次 128-byte 的突发传输(Burst),效率逼近显存带宽上限。

与大模型的关联:矩阵乘法和注意力机制中的内存布局(Row-major vs Column-major)、分块大小(Tile Size)、以及 FlashAttention 中的重计算策略,本质上都是在精心安排 Warp 的访存模式,以最大化合并访问、减少显存事务。15.5 节将展开显存层级,而 SIMT 的合并访问规则,决定了你能否把 HBM 的带宽真正喂饱。


六、对大模型开发者的三条实战启示

1. Occupancy(占用率)不是越高越好,但要避开“Warp 饥饿”

一个 SM 上可以同时驻留多个 Warp(比如 64 个)。如果因为寄存器用量太多或共享内存太大,导致 SM 上只能驻留极少数 Warp,那么当这些 Warp 全部卡在等显存时,SM 就会空转。调优 Kernel 时,需要在资源占用和并行度之间找平衡。

2. Tensor Core 与 SIMT 是互补关系

15.3 节提到 Tensor Core 专门做矩阵乘加(MMA)。实际上,Tensor Core 的 WMMA 或 MMA 指令,仍然是在 Warp 级别(甚至 Warp Group 级别)同步发射的。SIMT 负责调度 Warp,Tensor Core 负责在 Warp 内做高密度矩阵运算。两者结合,才实现了大模型训练中的 TFLOPS 量级算力。

3. 写 CUDA 时要“Warp 思维”,而非“线程思维”

优化 GPU 程序时,最小优化单元往往是 Warp(32 线程)。设计线程网格时,让相邻线程处理相邻数据,保证合并访存;避免 Warp 内分支;利用 Warp-level Primitives(如 __shfl_sync__reduce_add_sync)做低延迟数据交换——这些技巧在 DeepSpeed、Megatron-LM、vLLM 等框架的底层 Kernel 中无处不在。


七、小结

SIMT 是 GPU 从“有很多核”走向“能有效干活”的桥梁。它用 Warp 作为调度单元,把单条指令广播给数十个线程,同时用硬件掩码机制容忍轻量级分支,用多 Warp 交错执行隐藏内存延迟。

核心 takeaway:

  • SIMT = 单指令广播 + 多线程独立数据 + Warp 级调度
  • 分支发散会让并行退化为串行,Kernel 设计需尽量避免 Warp 内的路径分歧;
  • 合并内存访问是榨干显存带宽的前提,对访存密集的大模型推理尤为关键;
  • 无论是调用 cuBLAS 还是手写 CUDA,底层都在遵循 SIMT 规则,理解它才能看懂性能剖析(Profile)中的瓶颈。

在 15.5 节中,我们将继续下沉,剖析 GPU 的显存架构——HBM 与 GDDR 的物理差异、带宽与延迟的权衡,以及为什么大模型训练中“显存容量”和“显存带宽”常常比“算力 FLOPS”更早成为瓶颈。