CS336 · 第 5 课

GPU、TPU 架构与性能

同样数量的 FLOPs,为什么有的算子接近硬件峰值,有的却大部分时间都在等待数据?本讲从计算单元、memory hierarchy 和执行模型出发,建立判断 kernel 瓶颈的硬件视角,并比较 GPU 与 TPU 的共同目标和不同取舍。

授课:Tatsu Hashimoto 预计用时:80 分钟 难度:中等

Spring 2026 官方来源:Lecture 5 PDF · 课程日程

本页是中文学习导读;讲次主题、教师与材料版本以 Stanford 官方页面和讲义为准。


这一讲到底解决什么问题?

Lecture 2 已经教会我们核算 FLOPs 和 bytes,但公式还没有解释:为什么相同 FLOPs 在不同算子上会得到完全不同的实际时间?答案取决于数据在硬件中的位置,以及程序能否让足够多的计算同时进行。

30 秒知识地图

并行 workload → SM / tensor core 执行 → registers / shared memory / HBM 数据移动 → arithmetic intensity 与 roofline → coalescing / tiling / occupancy → CUDA、Triton 和 TPU 如何映射同一组约束。

问题与 prerequisite refresher

GPU 快不是因为单个 core 神奇,而是大量线程用相似控制流处理数据。先区分 FLOPs(工作量)、FLOP/s(计算速率)、bytes(容量/流量)与 bytes/s(带宽),本课才回答“慢在算,还是慢在搬”。

数据离计算越近,容量通常越小

HBM → L2 → shared memory / L1 → registers → arithmetic units
大/慢                                                   小/快

kernel workload
├─ math FLOPs
├─ memory traffic bytes
├─ parallel work / synchronization
└─ resource use per block(registers, shared memory)
     ↓
occupancy 与 achieved throughput

先算一个 vector add

对 BF16 的 \(y=x+z\),每元素约 1 FLOP,却至少读 \(x,z\) 各 2 bytes、写 \(y\) 2 bytes,算术强度约 \(1/6\) FLOP/byte;再多 ALUs 也无法消除这 6 bytes。相比之下,tile matmul 可让加载的元素参与多次 multiply-add,提高 reuse。Roofline 的意义正是把 toy traffic 推到真实 hardware ceiling。

反例:GPU utilization 100% 仍可能很慢

一个 bandwidth-bound kernel 可以持续占用 GPU,使 utilization 显示 100%,却只实现很低的峰值 FLOP/s;occupancy 也不是越高越好,若更大的 tile 用更多 registers、减少 occupancy,却显著增加数据复用,最终反而可能更快。

先看 GPU 为什么适合这类 workload

CPU vs GPU

特性CPUGPU
核心数~16-64 复杂核心~1000s 简单核心
优化目标低延迟(latency)高吞吐(throughput)
适合任务串行、分支密集并行、计算密集
内存低延迟,大容量高带宽,较大容量
Reference · A100、H100 与 B200 硬件数字

A100 GPU 架构

A100 SM (Streaming Multiprocessor) 结构:
├── 108 个 SM
├── 每个 SM 包含:
│   ├── 64 FP32 CUDA cores (INT32 也可)
│   ├── 4 个 Tensor Cores (第三代)
│   │   └── 支持 TF32, FP16, BF16, FP64 等矩阵指令模式
│   ├── 192 KB L1 cache / Shared Memory (可配置)
│   └── 寄存器文件 (256 KB)
├── 40 MB L2 cache (共享)
└── 80 GB HBM2e (高带宽内存)
    带宽: 2 TB/s

H100 GPU 架构(Hopper 示例)

H100 SM (Streaming Multiprocessor) 结构:
├── H100 SXM 产品启用 132 个 SM
├── 每个 SM 包含:
│   ├── 128 FP32 CUDA cores
│   ├── 4 个 Tensor Cores (第四代)
│   │   └── 支持 FP8, FP16, BF16, TF32
│   ├── 256 KB unified L1 / Texture / Shared Memory
│   │   └── 其中 shared-memory carveout 最高 228 KB/SM
│   └── 寄存器文件 (256 KB)
├── 50 MB L2 cache
└── 80 GB HBM3
    带宽: 3.35 TB/s

B200 / Blackwell:从 FP8 走向 MXFP8 / MXFP4

Spring 2026 官方讲义把 Blackwell 作为新的硬件案例:B200 提供 148 个 SM、192 GB HBM 与约 8 TB/s HBM 带宽,并引入更强的低精度矩阵计算路径。这里最重要的不是背峰值数字,而是理解 MXFP8 / MXFP4 使用多个局部 scale,用额外元数据换取低 bit 宽下更可用的动态范围。

新鲜度边界:MXFP4/NVFP4 的支持不代表任意 PyTorch kernel 会自动得到 Blackwell 峰值。布局、scale recipe、累加精度、Tensor Core 指令与库版本必须同时匹配。


GPU 内存层次

从慢到快,从大到小

层次H100 SXM 容量示例访问范围性能特征
HBM (Global Memory)80 GB,峰值 3.35 TB/s整张 GPU容量最大,片外访问;延迟和带宽受访问模式影响
L2 Cache50 MB所有 SM片上缓存,命中时避免 HBM 流量
L1 / Shared Memory合计 256 KB/SM;shared 最多 228 KB/SMSM / thread block软件管理的 shared memory 可用于显式数据复用
Registers每 SM 64K 个 32-bit registers单个 thread最快但稀缺;过多使用会限制 occupancy 或发生 spill

仓库 vs 工厂类比(Horace He):

HBM = 仓库:容量大,但搬运慢(带宽有限)

SRAM = 工厂车间:容量小,但搬运快(带宽极高)

Registers = 工人手:容量极小,但最快

优化目标:最小化从"仓库"(HBM)搬运数据的次数。

内存带宽 vs 计算速度

H100 (SXM5) 关键数字:
  计算峰值 (bf16): 989 TFLOP/s
  内存带宽:       3.35 TB/s
  Ridge point:    989e12 / 3.35e12 ≈ 295 FLOP/byte

  这意味着: 在只考虑峰值算力与 HBM 带宽的理想 Roofline 中,
  算术强度约高于 295 FLOP/byte 才可能进入 compute-bound 区域。
  它描述的是吞吐平衡点,不是单次访存延迟。

GPU 执行模型

Thread, Block, Grid

# GPU 编程的基本抽象
Grid
 └── Thread Block (CTA)  ← 在单个 SM 上执行
      └── Warp (32 threads)  ← 以 SIMD 方式执行
           └── Thread        ← 基本执行单位

# 为什么是 Warp?
# Warp 调度器向 32 个线程发出共同指令,各线程保有独立状态
# NVIDIA 将这种抽象称为 SIMT,而不是传统向量机意义的 SIMD

SIMT 执行模型

SIMT = Single Instruction, Multiple Thread

Warp 中的 32 个线程同时执行同一条指令
但操作不同的数据

例如: 向量加法
Thread 0: a[0] + b[0] → c[0]
Thread 1: a[1] + b[1] → c[1]
...
Thread 31: a[31] + b[31] → c[31]
从编程模型看是一条 warp 指令;实际完成周期取决于指令、流水线和硬件吞吐。

分支发散(Branch Divergence):如果 warp 中的线程走不同控制流路径,硬件通常需要用不同 active mask 分阶段执行这些路径,因此有效吞吐可能下降;具体代价取决于分支长度、编译结果和架构。

# 坏: 分支发散
if threadIdx.x % 2 == 0:
    do_A()  # 16 个线程执行
else:
    do_B()  # 16 个线程执行
# 两条路径可能被分别执行,不能再让全部 lane 同时做有效工作

# 好: 无分支
# 让所有线程走相同路径

Occupancy

Occupancy = 活跃 warp 数 / 最大支持 warp 数

高 occupancy 意味着 SM 有更多的 warp 可以调度——当一个 warp 等待内存时,SM 可以切换到另一个 warp。

影响 occupancy 的因素:
1. 每个 thread 使用的寄存器数
2. 每个 block 使用的 shared memory
3. block 大小

实践原则:
- block size 通常取 warp size (32) 的倍数
- 128 或 256 threads/block 是常见起点,不是固定最优值
- 更高 occupancy 有助于隐藏等待,但最高 occupancy 不保证最高性能;
还要平衡寄存器、shared memory、指令级并行和数据复用


Roofline Model

概念

Roofline model 直观地展示一个操作的性能上限:

性能 (FLOP/s)
    ↑
    │        ┌──────── 计算上限 (compute ceiling)
    │       /
    │      /  ← 斜率 = 内存带宽
    │     /
    │    /
    │───/───────────────→ 算术强度 (FLOP/byte)
    │  ridge point

常见操作的 Roofline 位置

操作算术强度瓶颈
Element-wise通常很低:每个元素只做少量运算常见为 Memory-bound
Reduction / normalization取决于融合、重读次数和统计量计算常见为 Memory-bound
矩阵-向量乘(如 batch=1 decode)权重复用有限常见为 Memory-bound
大矩阵乘随 M、N、K 和数据复用增加尺寸足够大且形状友好时可 Compute-bound

优化策略

Memory-bound 操作的优化:

Compute-bound 操作的优化:


Tensor Cores

什么是 Tensor Core?

Tensor Core 是 GPU 上的专用硬件单元,专门做矩阵乘法:

Tensor Core 执行小矩阵分块的 multiply-accumulate:
D = A × B + C
支持的指令形状与输入/累加精度取决于 GPU 架构

H100 第四代 Tensor Core:
- 支持 FP8, FP16, BF16, TF32, FP64
- 每个 SM 有 4 个 Tensor Core
- 峰值吞吐还取决于数据类型、稠密/2:4 稀疏模式和时钟

使用 Tensor Core 的条件

TF32:两全其美

TF32 (TensorFloat-32): A100 引入
1 位符号 + 8 位指数 + 10 位尾数 = 19 bits
范围和 FP32 一样(8 位指数),精度稍低(10 vs 23 尾数)
但用 Tensor Core 加速!

PyTorch 中应显式选择期望的 float32 matmul 精度策略:
torch.set_float32_matmul_precision("high")
旧接口也可控制 CUDA matmul 的 TF32 行为;默认值会随版本变化,
不应假定所有环境都自动开启。


内存访问模式

Coalesced Access(合并访问)

Global memory 请求会被合并为尽可能少的内存事务;现代 NVIDIA GPU 的缓存行由 32-byte sectors 组成。warp 访问连续且对齐的地址时,所需 sector 更少:

Good: Coalesced (连续访问)
Thread 0 → address 0
Thread 1 → address 4
Thread 2 → address 8
... → 通常只需覆盖一个连续的 128B 区域

Bad: Strided (跨步访问)
Thread 0 → address 0
Thread 1 → address 1024
Thread 2 → address 2048
... → 可能触及大量分散的 sectors,浪费带宽

Shared Memory Bank Conflicts

Shared memory 被分为 32 个 bank,每个 bank 宽 4 字节:

如果 warp 中多个线程访问同一 bank 的不同地址
会发生 bank conflict,串行化访问

Good: 无 conflict
Thread 0 → bank 0
Thread 1 → bank 1
...

Bad: 2-way bank conflict
Thread 0 → bank 0, address 0
Thread 16 → bank 0, address 1024
这两个访问会被串行化


GPU 与 TPU:相同约束,不同执行抽象

Reference · A100 / H100 完整规格对比
规格A100 80GB(dense)H100 SXM 80GB(dense)H100 SXM(2:4 sparse)
FP64 CUDA cores9.7 TFLOP/s34 TFLOP/s—
FP64 Tensor Core19.5 TFLOP/s67 TFLOP/s—
FP32 CUDA cores19.5 TFLOP/s67 TFLOP/s—
TF32156 TFLOP/s494 TFLOP/s989 TFLOP/s
BF16312 TFLOP/s989 TFLOP/s1979 TFLOP/s
FP8—1979 TFLOP/s3958 TFLOP/s
HBM80 GB80 GB80 GB
HBM 带宽2.0 TB/s3.35 TB/s3.35 TB/s
L2 Cache40 MB50 MB50 MB
SM 数108132132
TDP400W700W700W

表中 Tensor Core 稀疏列要求满足 NVIDIA 的 2:4 structured sparsity;不能把该峰值当作任意稀疏模型都能获得的吞吐。

TPU:相似目标,不同执行模型

Spring 2026 官方 Lecture 5 明确把 TPU 放进 GPU 旁边比较。两者都把轻量控制、大型矩阵单元、片上快速存储与 HBM组合在一起,但 TPU 的核心抽象更偏向编译器调度的大块矩阵程序,而不是 GPU 的 warp/SIMT 线程层次。

维度GPUTPU
主要计算单元SM 内 CUDA cores + Tensor CoresMXU(Matrix Multiply Unit,通常以 systolic-array 思路实现)+ VPU
执行抽象thread → warp → block → grid;SIMT 与分支发散很重要较粗粒度 block/program;没有 GPU 式 warp 抽象
片上存储register、shared memory/L1、L2vector memory / on-chip buffers,围绕 MXU 数据流组织
软件栈CUDA、Triton、PyTorch compilerJAX/XLA 常把模型与 sharding 一起交给编译器
扩展方式NVLink/NVSwitch + InfiniBand/RoCETPU interconnect / pod;同样需要拓扑感知的 sharding
Workload fit从 irregular custom kernels 到 dense matmul 都有较成熟的显式控制路径规则、静态、矩阵密集且可由 compiler 整体规划的数据流更容易发挥矩阵单元

CourseStack 扩展边界:systolic array、JAX/XLA、ICI/NVLink 细节是用于建立工程对照的补充,不应被误读为 Lecture 5 对具体 TPU generation 的完整规格说明。官方讲义的核心结论是 GPU 与 TPU 都受矩阵单元、HBM 带宽和互联约束,但编程抽象与网络组织不同。具体 sharding 选择在第 7–8 课继续展开。

Deep Dive · 用峰值和 MFU 估算训练时间
仅用于展示 units:GPT-3 175B 报告的训练计算量约 3.14e23 FLOPs,
# 1024 张 GPU 全程保持 MFU=0.5,且忽略停机、评估和 I/O。

# A100 (312 TFLOP/s dense bf16, MFU=0.5):
#   单卡实际: 156 TFLOP/s
#   1024 卡: 1.6e17 FLOP/s
#   时间: ~22.8 天

# H100 (989 TFLOP/s dense bf16, MFU=0.5):
#   单卡实际: 495 TFLOP/s
#   1024 卡: 5.07e17 FLOP/s
#   时间: ~7.2 天

# 峰值 dense bf16 比值约 3.17×;真实端到端加速不会只由峰值算力决定。

多 GPU 互联

节点内互联

NVLink (节点内 GPU 间)
H100: 18 条 NVLink 4.0, 每条 50 GB/s
总带宽: 900 GB/s (双向)

NVSwitch fabric
在 HGX/DGX H100 中连接多张 SXM GPU,支持 GPU 间高带宽通信
900 GB/s 是每张 H100 的聚合双向 NVLink 带宽,
不能直接解释为任意一对 GPU 的单向 payload 带宽。

节点间互联

InfiniBand / RoCE (节点间)
一条 400 Gb/s 链路的理论 line rate = 50 GB/s;
实际节点可有多张 NIC,应用带宽还会受协议与拓扑影响。

这意味着:
通信密集的 tensor/expert parallel 通常优先放在高带宽域内;
data parallel 是否跨节点仍取决于通信量、网络和并行分组。

通信层次

层次带宽典型用途
H100 NVLink (节点内)每 GPU 聚合双向 900 GB/sTensor / Expert Parallel
NVSwitch fabric取决于系统拓扑与交换结构节点内 collective 与 peer traffic
InfiniBand (节点间)按 NIC 数量与代际变化;400 Gb/s = 50 GB/s line rate跨节点 collective

Implementation · CUDA、Triton 与 torch.compile 编程层次

GPU 编程范式

CUDA C++

// 最底层,最灵活,最复杂
__global__ void vector_add(float *a, float *b, float *c, int n) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < n) {
        c[i] = a[i] + b[i];
    }
}

// 启动 kernel
vector_add<<<num_blocks, block_size>>>(d_a, d_b, d_c, n);

Triton

# Python 风格的 GPU kernel DSL;仍需设计 program shape 与访存
@triton.jit
def vector_add_kernel(x_ptr, y_ptr, out_ptr, n, BLOCK: tl.constexpr):
    pid = tl.program_id(0)
    offsets = pid * BLOCK + tl.arange(0, BLOCK)
    mask = offsets < n
    x = tl.load(x_ptr + offsets, mask=mask)
    y = tl.load(y_ptr + offsets, mask=mask)
    tl.store(out_ptr + offsets, x + y, mask=mask)

torch.compile

# 不手写 kernel,让编译器尝试图优化与融合
@torch.compile
def fused_operation(x, w):
    return gelu(x @ w)  # 是否做 epilogue/kernel fusion 取决于后端与形状
方法抽象级别控制范围主要取舍
手写 CUDA最低最大工程成本高;不保证自动快于成熟库
Triton中program/block 级更易实验自定义 kernel,仍需调优
torch.compile高计算图与编译后端低改造成本,收益依赖图、形状和后端
PyTorch eager高算子组合开发直观;算子边界可能产生额外 launch 与中间流量

知识检查

问题 1:按 dense BF16 峰值估算,H100 SXM 的理想 ridge point 大约是多少?

问题 2:为什么 kernel fusion 能加速 memory-bound 操作?

问题 3:GPU 中的 warp 是什么?

问题 4:TF32 和 FP32 的区别是什么?

问题 5:为什么 expert parallel 通常优先放在节点内的高带宽域?


Concept Check · FLOPs 少为什么仍会慢

BF16 y=x+z 每元素约 1 FLOP,最少读写 6 bytes。算术强度是多少,增加 Tensor Cores 能直接解决吗?

答案:约 \(1/6\) FLOP/byte;主要瓶颈是数据流量时,增加计算峰值不会消除 HBM bytes。

若只因 FLOPs 少便判断“必然快”,你遗漏了 roofline 的 bandwidth ceiling。

你现在应该能够解释

通往第 6 课

硬件层次给出了限制;下一课把一个 PyTorch expression 拆成 kernel launches,亲手用 benchmark、fusion 和 tiling改变数据路径。

如果只能记住 5 件事

  1. GPU 通过大量并行执行单元追求 throughput;性能来自并行度,不是单个 thread 更快。
  2. Registers、shared memory/L1、L2 与 HBM 构成容量和速度不同的数据层次,优化往往是在减少更慢层级的流量。
  3. Roofline 用 arithmetic intensity 连接 FLOPs 与 bytes:瓶颈位置决定应该优化计算还是数据移动。
  4. Tensor Core 峰值只有在 dtype、shape、layout 和软件实现匹配时才有意义;硬件规格不是应用性能保证。
  5. GPU 与 TPU 都围绕矩阵单元、片上存储、HBM 和互联设计,但执行抽象与编译器分工不同。

延伸阅读

Reference · 硬件规格、调优指南与补充材料