流式多处理器
**流式多处理器(SM,streaming multiprocessor)**是 NVIDIA GPU 的主要计算单元。一个 GPU 包含许多 SM,每个 SM 接收线程块、调度它们的 warp,并使用片上计算和内存资源执行指令。AMD 硬件使用不同的名称,并以不同的方式分组工作,但大体思想相同:许多并行计算单元共同分担工作负载。
SM 包含什么
具体设计随 GPU 世代而变化,但 SM 通常包含:
- 一组用于整数和浮点运算的算术执行单元(NVIDIA GPU 上的 CUDA Core,AMD GPU 上的执行单元)
- 用于加速矩阵运算的 Tensor Core(在现代架构上)
- 一个在每个周期挑选就绪 warp 并发出指令的 warp 调度器
- 一个寄存器文件、共享内存和 L1 缓存。在许多架构上,共享内存和 L1 共享片上资源,并且可以配置。更多信息请参见 GPU 内存层级。
片上(on-chip)指物理上位于 GPU 硅片(Die)本身、紧邻计算单元的位置。片外(off-chip)指位于 GPU 芯片之外,需要穿过内存接口(线缆、控制器)。
现代数据中心 GPU 拥有许多 SM。例如,NVIDIA H100 SXM 有 132 个 SM,A100 有 108 个。GPU 的总吞吐量取决于你的内核能让这些 SM 忙碌于有用工作的程度。
只要有足够的寄存器、共享内存和 warp 槽位,每个 SM 就可以同时容纳并执行多个线程块。
warp 调度如何隐藏延迟
SM 可以让许多 warp 保持驻留。warp 调度器选择一个就绪的 warp,并向所需的执行单元发出指令。如果某个 warp 在等待内存加载或依赖,调度器可以从另一个就绪的 warp 发出工作。
这种快速切换不需要操作系统级上下文切换。驻留 warp 的寄存器和调度状态已经存在于 SM 上。GPU 利用这池就绪工作来隐藏延迟并维持吞吐量。
更多驻留 warp 只有在能提供有用替代时才有帮助。如果每个 warp 都在等待同一个瓶颈,或者内核已经饱和了某条计算流水线,那么增加更多 warp 可能收效甚微。
线程块驻留
GPU 会把每个线程块分配给一个 SM。在正常执行下,线程块会一直驻留在该 SM 上直到完成,线程块中的所有线程都使用该 SM 上的资源。
有几个限制决定了可以同时驻留多少个线程块和 warp:
- 每个 SM 的线程数和 warp 数
- 每个 SM 的线程块数
- 每个 SM 的寄存器数以及每个线程使用的寄存器数
- 每个 SM 的共享内存量以及每个线程块使用的共享内存量
- 架构专属的调度限制
通常总有一个限制最先成为瓶颈。例如,一个分配了大块共享内存分块的线程块,可能会阻止另一个线程块驻留在同一个 SM 上,即使线程槽位仍然可用。
占用率
**占用率(occupancy)**是 SM 上活动 warp 数与该 SM 支持的最大活动 warp 数之比:
更高的占用率为调度器提供更大的 warp 池,有助于隐藏延迟。每个线程的寄存器使用量和每个线程块的共享内存使用量常常决定了占用率上限。
占用率不是一个可以盲目最大化的分数。内核可能会接受较低的占用率,以在共享内存中保留有用的分块、在寄存器中持有中间值,或减少重复的 HBM 流量。FlashAttention 就遵循这一模式:更多的片上存储可以降低占用率,同时削减代价高昂得多的片外内存搬运。
要用占用率来解释性能,而不是取代测量。性能分析可以显示 SM 是缺少合格的 warp、因内存而停顿,还是已经饱和了相关的执行流水线。
为什么 SM 利用率对推理很重要
推理工作负载很少只由一次理想的矩阵乘法构成。注意力、归一化、采样、数据搬运和许多小内核都会竞争 SM 时间。糟糕的启动几何结构或不足的并行工作会让 SM 空闲,增加延迟。过度的资源使用会减少驻留线程块的数量,使延迟更难隐藏。
批大小和序列形状也会影响并行工作的数量。一个在预填充期间填满 GPU 的内核,可能在单 token 解码期间无法充分利用同一个 GPU。这就是为什么必须针对实际的推理阶段来解释利用率,而不是把它看作一个笼统的百分比。
常见问题
拥有更多 SM 的 GPU 总是能让内核运行得更快吗?
不一定。启动需要足够多的独立线程块才能用上新增的 SM,而且内核还必须避免遇到另一个瓶颈,如 HBM 带宽。不同世代的 GPU,SM 设计和时钟频率也不相同,所以单凭 SM 数量无法完整比较性能。
低占用率总是问题吗?
不一定。只有当 SM 缺少就绪 warp、无法隐藏延迟时,低占用率才成为问题。一个计算受限的内核,或一个具有较强片上数据复用的内核,在中等的占用率下也能表现出色。