CS152 · 第 18 课

GPUs:把向量机重新包装成"多线程 SIMD"

这一课把第 17 课的向量机器和 GPU 直接对照起来看——你会发现 GPU 的核心执行模型几乎就是"向量处理器 + 多线程"的另一种包装,只是用了一套不同的编程模型语言(线程、warp、grid)来描述同样的底层硬件思想。

授课:Sophia Shao 预计用时:55 分钟 难度:中等 → 较难

三种并行度的区分

图形处理、机器视觉、语音识别、机器学习等应用驱动了 DLP 的重新兴起——这正是本课要讲的 GPU 崛起的应用背景。


从固定功能图形管线到通用计算

GPU 的历史演进:1990 年代中后期的固定功能 3D 图形管线 → 2001-2005 年可编程着色器时代(Cg 语言,用于编写运行在每个顶点/像素上的小程序)→ 通用 GPU 计算。里程碑:"GP-GPU":NVIDIA GeForce 8800(2006)+ CUDA(2007,"Compute Unified Device Architecture");OpenCL 作为厂商中立的同类方案。这套模式被称为"挂载处理器模型":主机 CPU 把数据并行的内核(kernel)派发给 GP-GPU 执行。


SIMT:单指令、多线程

GPU 把许多独立的标量"微线程(µthread,即 CUDA 线程)"组织成线程块(thread block),NVIDIA 把 32 个 CUDA 线程编成一个warp——同一条指令流被 warp 内全部 32 个微线程同时执行。这正是"SIMT(单指令多线程)"名字的由来:"或者说是多线程 SIMD/流式多处理器(SM)"。整个覆盖输入数据的线程块集合被 NVIDIA 称为 Grid(可以是二维的);每块线程数 blockDim 由程序员选择(DAXPY 示例中取 256)。

SIMT 模型的两个直接推论:地址合并(address coalescing)——所有"向量"式的 load/store 本质上都是逐个微线程的标量访存(scatter/gather),硬件需要一个专门的地址合并单元动态地把这些标量访存拼凑成看起来像向量访存的效果;冗余的条带化检查——每个微线程需要重复做"我是否还处于活跃状态?"这类检查(用于处理输入规模不是 blockDim 整数倍的边界情况)。


硬件执行模型:多线程 SIMD 处理器

一个真实的多线程 SIMD 处理器核心(教材 Fig. 4.14):指令缓存 → warp 调度器 → 指令寄存器,扇出到 16 条 SIMD lane,每条 lane 有自己的 1024×32 位寄存器,所有 lane 共享地址合并单元、互联网络、本地内存。SIMD 线程调度器同时管理多达 64 个独立的 SIMD 指令线程(用一张 64 项 PC 表调度)——这正是"warp 在核心上被多线程化":调度器在不同 warp 的指令之间交替发射,用充足的并行 warp 来隐藏访存延迟,而不需要任何一个 warp 单独等待。

寄存器容量交叉验证:16 条 lane × 每 lane 1024 个 32 位寄存器 = 16×1024=16,384 个寄存器,即 16,384×32位 = 524,288 位 = 64KB 的寄存器堆容量——这与幻灯片里 NVIDIA Ampere A100 的 SM 图给出的"Register File (16,384 × 32-bit)"规格完全吻合,说明教材上这份通用示意图和真实 A100 硬件规格在数量级和具体数字上是一致的。


向量处理器 vs. 多线程 SIMD:本质是同一件事,换了层皮

教材 Fig. 4.22 把两者并排对比:向量处理器有一个专门的控制处理器,负责给标量-向量运算提供标量操作数、为单位/非单位步长访存递增地址、执行其他"记账"类操作;GPU 的多线程 SIMD 处理器里,这个控制处理器被 warp 调度器/派发单元取代——本质上是把"控制处理器"这个角色,从一个专用硬件模块变成了多线程调度逻辑的一部分。两者都有"掩码"寄存器来控制哪些通道/线程真正生效:只有当地址合并单元能发现局部化的访存模式时,GPU 才能达到峰值访存性能;只有当所有内部掩码位一致时,才能达到峰值计算性能——这两句话直接呼应第 17 课"死区时间"和"密度时间掩码实现"的效率讨论。


分支发散(Branch Divergence)

SIMT 模型给程序员一种"每个线程都独立"的假象,但当 warp 内的微线程在同一条分支上走向不同结果时,就会发生分支发散:硬件追踪哪些微线程走了哪个分支方向,为未走的那条路径生成一个掩码向量,把"被采纳分支的 PC+掩码"压入一个硬件栈,稍后再执行——本质上是用类似第 17 课"简单掩码实现"的思路(关闭不活跃通道的写回),只是换成了硬件栈来管理"稍后回来执行另一条路径"这件事。程序员要保持微线程步调一致(单位步长访存、避免分支发散)才能获得效率——这正是 SIMT 编程模型"看起来通用、实际上有性能陷阱"的地方。


常见误区


知识检查

问题 1:教材 Fig. 4.14 描述的多线程 SIMD 处理器有 16 条 SIMD lane,每条 lane 有 1024 个 32 位寄存器。总共有多少个寄存器?这个数字与 NVIDIA Ampere A100 的真实规格是否吻合?

问题 2:根据教材 Fig. 4.22 的对比,向量处理器里的"控制处理器"这个角色,在 GPU 的多线程 SIMD 处理器里被什么取代了?

问题 3:当一个 warp 内的微线程在同一条分支上走向不同结果(分支发散)时,硬件是如何处理的?


本课要点

ILP/TLP/DLP

三种并行度的区分,GPU 主打 DLP + 大量线程隐藏延迟

SIMT

32 个 CUDA 线程编成一个 warp,同步执行同一条指令流

GPU ≈ 向量机 + 多线程

控制处理器角色被 warp 调度器取代,掩码/地址合并思想相通

分支发散

SIMT 模型最重要的性能陷阱,需要程序员保持步调一致


参考资料与延伸阅读