Roofline:算力、带宽与算术强度

同样是 \(10^{12}\) 次运算,一个矩阵乘法在 H100 上可能 1 毫秒就做完,一串逐元素运算却可能慢几十倍。只知道 FLOPs 不够,差别在于数据要搬多少、从哪里搬。roofline 分析回答的就是「一个运算的时间到底花在计算上,还是花在搬数据上」,所有并行策略的分析都建立在它之上。

做 roofline 估算的原材料是硬件的规格数字:TPU 和 GPU 的计算单元有多快,存储层次和网络的每条通道有多宽,决定了一个运算会卡在哪里。

1. 时间花在哪:计算、搬数据、通信

先问一个朴素的问题:为什么一个算法要 50 ms,而不是 50 s 或 5 ms?时间只可能花在两类事情上。

计算。深度学习模型基本就是一堆矩阵乘法,每个由浮点乘和加组成。硬件每秒能做的浮点运算数(FLOP/s)是固定的,于是:

$$T_\text{math} = \frac{\text{计算的 FLOPs}}{\text{硬件 FLOP/s}}$$

例如 H100 的 BF16 稠密峰值约 \(9.89\times 10^{14}\) FLOP/s,TPU v6e 约 \(9.2\times10^{14}\)。做 \(10^{12}\) 次运算在 H100 上大约 1.01 ms。这是按标称峰值算的理想值:H100、B200 上的矩阵乘法通常只能达到标称峰值的 80–85%,TPU 正常使用时可以接近 95%。

通信,分两种:

两种都按同一个式子估算:

$$T_\text{comms} = \frac{\text{通信字节数}}{\text{带宽(字节/秒)}}$$

计算和通信通常可以重叠:在算这一块的同时,下一块数据已经在路上了。所以总时间有一个下界和一个上界:

$$T_\text{lower} = \max(T_\text{math}, T_\text{comms}),\qquad T_\text{upper} = T_\text{math} + T_\text{comms}$$
串行(上界) T_math T_comms 完全重叠(下界) T_math T_comms 完全重叠时,总时间由较长的那个决定:这里计算更长,是「计算受限」(compute-bound)。

因为 \(T_\text{math} + T_\text{comms} \le 2\max(T_\text{math}, T_\text{comms})\),上下界最多差 2 倍。实际估算时按 \(\max\) 来,因为代数简单,而且好的实现确实能接近它;之后再根据 profiling 补上重叠不完美的部分。

假设完全重叠:\(T_\text{math} > T_\text{comms}\) 时硬件算力被用满,叫计算受限(compute-bound);反之叫通信受限(communication-bound;瓶颈在 HBM 时也常叫 memory-bound 或 bandwidth-bound),一部分算力在等数据。

2. 算术强度与临界强度

判断一个运算会受哪个限制,看它的算术强度(arithmetic intensity,也叫 operational intensity):

$$\text{Intensity} = \frac{\text{计算的 FLOPs}}{\text{通信的字节数}}$$

单位是 FLOPs/byte,含义是「每搬一个字节,做多少次运算」。推导计算受限的条件只需要一行:

$$T_\text{math} > T_\text{comms} \iff \frac{\text{FLOPs}}{\text{硬件 FLOP/s}} > \frac{\text{Bytes}}{\text{带宽}} \iff \underbrace{\frac{\text{FLOPs}}{\text{Bytes}}}_{\text{算法的强度}} > \underbrace{\frac{\text{硬件 FLOP/s}}{\text{带宽}}}_{\text{硬件的临界强度}}$$

右边这个只取决于硬件的量叫临界算术强度(critical / peak arithmetic intensity)。它的意思是:硬件要跑满算力,每从 HBM 读一个字节,就必须做这么多次运算。几个芯片(BF16 矩阵单元峰值 / HBM 带宽,稠密值):

芯片BF16 FLOP/sHBM 带宽临界强度
NVIDIA A1003.1e142.0e12≈ 156
NVIDIA H100 SXM9.89e143.35e12≈ 295
NVIDIA H2009.89e144.8e12≈ 206
NVIDIA B2002.25e158.0e12≈ 281
TPU v5e1.97e148.2e11≈ 240
TPU v5p4.59e142.77e12≈ 166
TPU v6e9.18e141.64e12≈ 560

表里都是稠密值。读 NVIDIA 规格表时要小心:Tensor Core 的数字通常是带结构化稀疏的,正好是稠密值的 2 倍。例如 H100 SXM 规格表上 BF16 写的是 1,979 TFLOPS(带星号),稠密值是 989;HGX B200 写 8 卡 BF16 36 PFLOPS(稀疏),折合每卡稠密 2.25 PFLOPS,HBM 带宽每卡 8 TB/s。

可以看到,主流加速器的临界强度大多在 150–600 之间,而且有意思的是,几代硬件之间它变化不大:算力和带宽大体同步增长。

3. Roofline 图

把上面的关系画出来就是 roofline 图:横轴是算术强度,纵轴是可达到的吞吐(FLOP/s),两个轴都取对数。对强度为 \(I\) 的算法,能达到的吞吐是

$$\text{FLOP/s}_\text{achievable} = \min\big(\text{峰值 FLOP/s},\; I \times \text{带宽}\big)$$

图形像一个屋顶:左边是一条斜率为 1 的斜线(对数坐标下,斜线的高度由带宽决定),右边是一条水平线(峰值算力)。两线交点就是临界强度,也叫 ridge point。点落在斜线下方的区域是带宽受限,落在水平线下方是计算受限。提升性能只有两种方式:让算法的强度变大(点往右移),或者换更高的带宽(斜线往上移)。

选芯片和运算,拖动参数看点怎么移动。除了 HBM 的屋顶,还画了芯片间网络的屋顶(虚线),和片上高速存储的屋顶(点线,有数据时才画)。

交互演示 · Roofline 图
芯片
运算

几个值得自己拖出来的现象:

4. 低强度的运算:点积与逐元素运算

点积

计算 BF16 向量的点积 \(x\cdot y\):\(x, y\) 各 \(N\) 个元素,读 \(2N + 2N\) 字节,写回 2 字节;做 \(N\) 次乘、\(N-1\) 次加:

$$\text{Intensity}(\text{dot}) = \frac{2N-1}{4N+2} \xrightarrow{N\to\infty} \frac{1}{2}$$

每读一个字节只做半次运算。拿它和矩阵单元的临界强度(几百)比没有意义,因为点积本来就不在矩阵单元上跑,而在向量单元上(TPU 的 VPU、GPU 的 CUDA core)。但向量单元的算力低得多,它们的临界强度也低:TPU v5p 的 VPU 每核约 \(7\times10^{12}\) FLOP/s,整芯片两个核约 \(1.4\times10^{13}\),临界强度约 5;H100 的 CUDA core 不算 FMA 时约 33.5 TFLOP/s,除以 3.35 TB/s,临界强度约 10。即便如此,1/2 依然远低于它们,所以点积是带宽受限的。

逐元素运算

两个 FP32 向量相加:读 \(2\times 4N\)、写 \(4N\) 字节,做 \(N\) 次加法,强度 \(N/12N = 1/12\),糟糕至极。在 H100 上:

$$T \approx \frac{12N}{3.35\times10^{12}}$$

在 H100 上用 JAX 实测一下:\(N = 1024^3\) 时 roofline 预测约 3.84 ms,实测 4.1 ms,很接近;但 \(N=65{,}536\) 时预测 0.23 μs,实测约 1.5 μs——数据太小时,内核启动和延迟才是主要成本。

这个结论的实际意义很大:LayerNorm、激活函数、残差加、dropout、softmax……这些逐元素或归约运算的 FLOPs 在总账里可以忽略,但每一个都要把整个张量从 HBM 读一遍、写一遍。所以要么把它们融合(fuse)进相邻的矩阵乘法内核里(读进来顺手算掉,不单独写回),要么至少把几个逐元素运算融合成一个内核。融合内核(fused kernel)的价值、FlashAttention 不把注意力矩阵写回 HBM 的原因,背后都是这一张图。

5. 矩阵乘法:为什么 batch 要过 ~240 个 token

推导

\(X\) 是 \(\text{bf16}[B, D]\),\(Y\) 是 \(\text{bf16}[D, F]\),求 \(Z = XY\),\(Z\) 是 \(\text{bf16}[B,F]\)。要读 \(2BD + 2DF\) 字节,写 \(2BF\) 字节(输出在累加时是 FP32,但写回 HBM 前一般转成 BF16),做 \(2BDF\) 次运算:

$$\text{Intensity}(\text{matmul}) = \frac{2BDF}{2BD + 2DF + 2BF} = \frac{BDF}{BD + DF + BF}$$

在 Transformer 里,\(X\) 是激活(\(B\) 是 token 数),\(Y\) 是权重(\(D, F\) 是模型维度)。如果 \(B\) 远小于 \(D\) 和 \(F\),分母里 \(DF\) 占主导:

$$\frac{BDF}{BD + DF + BF} \approx \frac{BDF}{DF} = B$$

于是计算受限的条件变成一个极其简单的规则:

$$B > \frac{\text{硬件 FLOP/s}}{\text{HBM 带宽}}\quad\Longrightarrow\quad B_\text{TPU v5e} > \frac{1.97\times10^{14}}{8.2\times10^{11}} \approx 240,\quad B_\text{H100} > \frac{9.89\times10^{14}}{3.35\times10^{12}} \approx 295$$

直觉上:权重 \(DF\) 从 HBM 读一次的成本是固定的,它要被 \(B\) 个 token 复用;每个 token 对每个权重做 2 次运算(乘和加)而权重占 2 字节,所以强度就是 \(B\)。token 越多,读一次权重摊到的计算越多。

这里的 \(B\) 是 token 数,不是序列数。而且是每个模型副本(per-replica)的本地 token 数。例如在 2,048 张 GPU 上训练、全局 batch 是 512 条 × 4,096 token = 2M token,每张卡的本地 batch 是 1k token。为什么是「每副本」:如果把模型切到更多芯片上(模型并行),算力和带宽同比例增加,临界值不变;而数据并行把 token 分给不同副本,每个副本的 \(B\) 变小。

这就是训练和推理里一系列选择的来源:

精确的临界值与形状的影响

\(B \ll D, F\) 是近似。不做近似,直接解 \(2BDF/P = (2BD + 2DF + 2BF)/W\):H100 上,\(D=F=8192\) 时需要 \(B \ge 319\),\(D=F=4096\) 时 \(B \ge 345\),\(D=F=1024\) 时要 \(B\ge 698\),几乎翻倍。再如 int8 的 \([B, 4096]\times[4096, 16384]\) 在 v5e 上(int8 峰值 3.94e14),精确解出 \(B \gt 259\),略大于 240,因为把 \(D, F\) 的有限大小也算进去了。矩阵越小,越难吃饱。

分块(tiling)

大矩阵乘法在硬件里会被拆成小块,放进片上的高速存储(TPU 的 VMEM、GPU 的 SMEM/TMEM),块会被重复读取,所以并不严格是只读 \(O(N^2)\) 字节。设 \((m,k)\cdot(k,n)\) 的矩阵乘法用块大小 \(b_m, b_k, b_n\),块数 \(t_m = m/b_m\) 等,总 FLOPs 是 \(2\,t_m t_n t_k\, b_m b_n b_k\),总字节约 \(2\,t_m t_n\big(t_k(b_m b_k + b_k b_n) + b_m b_n\big)\)。忽略最后一项,强度约为 \(\frac{b_m b_n}{b_m + b_n}\),形式和整体的式子一样。这说明块要足够大,片上存储的容量因此很重要。

量化

强度永远很低的两种情况

MoE

MoE 有 \(E\) 个专家,每个 token 选 \(k\) 个。每个权重矩阵要读 \(E\cdot DF\) 个数,但 FLOPs 只有 \(2k\cdot BDF\)。以 INT8 权重、BF16 计算、v5e(临界 240)为例,计算受限需要 \(2kBDF/(EDF) > 240\),即 \(B > 120\,E/k\)。DeepSeek-V3(\(E=256, k=8\))得到 \(B > 3840\) 个 token——在生成阶段这是很大的 batch。换句话说:要让每个专家都吃饱,平均每个专家分到的 token 数要过临界值。

6. 交互:矩阵乘法耗时计算器

计算器把 \(T_\text{math}\)、\(T_\text{HBM}\) 和芯片间通信时间 \(T_\text{comm}\) 放在一起算,可以选择把矩阵乘法切到多块芯片上:

交互演示 · 矩阵乘法耗时:计算 vs HBM vs 通信
芯片
B(token)
D
F
精度
芯片数
切分方式

计算时间按每块芯片的 \(2BDF/n\) 除以峰值;HBM 时间按每块芯片要读的输入、权重和要写的输出字节数除以 HBM 带宽;沿 \(D\) 切时,部分和 \([B,F]\) 用环形 AllReduce 汇总,按每块芯片发送约 \(2\cdot\tfrac{n-1}{n}\cdot\) 部分和字节数估算(\(n=2\) 时就是把自己的一份发给对方,即第 7 节的两芯片例子;AllReduce 的推导见集合通信)。链路带宽:GPU 用每卡 NVLink 单向带宽(A100 300、H100/H200 450、B200 900 GB/s),TPU 用一条 ICI 链路的单向带宽(v5e 45、v5p/v6e 90 GB/s),都是理想值。部分和的加法(\(O(BF)\))忽略不计。

7. 网络的 roofline

前面的屋顶都是芯片内的:分母是 HBM 带宽。但同样的推理对芯片间的通信完全适用,而且分析并行策略时,关心的几乎都是这一种。

看一个最简单的例子:\(X\sim\text{bf16}[B,D]\)、\(Y\sim\text{bf16}[D,F]\),沿 \(D\) 均分到 2 块 TPU 上。每块芯片算一半:\(Z_0 = X[:, :D/2]\,Y[:D/2, :]\),\(Z_1\) 同理,然后把部分和发给对方相加。设每个方向能传 \(4.5\times10^{10}\) 字节/秒,每块芯片算力 \(1.97\times10^{14}\):

$$T_\text{math} = \frac{2BDF}{2\times 1.97\times10^{14}} = \frac{BDF}{1.97\times10^{14}},\qquad T_\text{comms} = \frac{2BF}{4.5\times10^{10}}$$

计算受限的条件是

$$\frac{BDF}{2BF} = \frac{D}{2} > \frac{1.97\times10^{14}}{4.5\times10^{10}} \approx 4377 \quad\Longrightarrow\quad D > 8755$$

和 HBM 的 roofline 不同,这里的临界条件只和 \(D\) 有关,和 \(B\) 无关。原因:FLOPs 和要传的字节(部分和 \([B, F]\))都和 \(B\)、\(F\) 成正比,二者相除只剩下 \(D\)——每块芯片在缩并维上做了多少工作,决定了它能「掩护」多少通信。推广到 \(n\) 块芯片做环形 AllReduce,条件大约是 \(D > 2(n-1)\cdot P/W\),切得越多越难。

每一层带宽都有自己的临界强度。拿 BF16 算力分别除以各层带宽,就能看到越往外走,需要的强度越高:

芯片链路带宽临界强度
H100HBM3.35 TB/s≈ 295
NVLink(每卡单向)450 GB/s≈ 2,200
InfiniBand(每节点 400 GB/s,摊到每卡)50 GB/s≈ 19,800
TPU v5pHBM2.77 TB/s≈ 166
ICI(每轴双向)180 GB/s≈ 2,550
PCIe(每芯片)16 GB/s≈ 28,700
DCN(每芯片)6.25 GB/s≈ 73,000

同一个道理也适用于 PCIe。假设把 \(\text{bf16}[D, F]\) 的权重和 \(\text{bf16}[B, D]\) 的激活都放在主机内存里,在一块 TPU v6e 上做矩阵乘法(\(F = 4D\),\(B\ll D\)),要不被 PCIe(1.6e10 B/s)卡住,需要 \(B > 9.18\times10^{14}/1.6\times10^{10}\approx 57{,}000\)。这就是为什么权重卸载(offload)到 CPU 内存只在 batch 极大时才划算。

8. TPU 是什么样的

TPU 的结构比 GPU 简单。

TensorCore MXU × 4 128×128 脉动阵列 bf16[8,128]@[128,128] 每 8 个周期一次 矩阵乘法,绝大部分算力 VPU (8, 128) 的二维 SIMD 逐元素运算、归约 VREG 向量寄存器 算力约为 MXU 的 1/30 VMEM:片上暂存,由编译器/程序员显式管理 v5e 上 128 MiB;到 MXU 的带宽约为 HBM 的 22 倍 标量单元 + SMEM 取指、发射指令、发起 DMA; 控制流只有一个线程 HBM v5e 16 GB / 8.2e11 B/s v5p 95 GiB / 2.77e12 B/s HBM 带宽
TPU 芯片的组成。数据流:HBM → VMEM → 寄存器 → MXU/VPU → VMEM → HBM。

概括地说,TPU 基本就是一个专门做矩阵乘法的计算核心(TensorCore),挂在一堆高速显存(HBM)上。TensorCore 里有三个关键单元:

此外还有标量单元(scalar core):它取指、发射所有指令、执行 HBM 到 VMEM 的传输。它是单线程的,所以每个 TensorCore 每周期只能发起一个 DMA 请求;一个标量核控制着 4,096 个 VPU ALU、4 个 MXU、2 个 XLU 和多个 DMA 引擎。「控制很少、计算很多」是 TPU 硬件效率的来源,也限制了它做依赖数据的灵活计算。

流水线与 VMEM 的强度

TPU 上所有操作都是流水线化、相互重叠的。做 \(X\cdot A\to Y\) 时,先把 \(A\) 和 \(X\) 的块从 HBM 拷到 VMEM,再送进 MXU 按 8×128(\(X\))和 128×128(\(A\))的块相乘,结果逐块写回 HBM;拷贝和 MXU 的工作是重叠的,所以 MXU 不必等数据——只要整体是计算受限的。如果 HBM→VMEM 比 MXU 慢,就是带宽受限:MXU 在「挨饿」。

VMEM 的带宽约为 HBM 的 22 倍,这意味着如果输入输出都能放在 VMEM 里,强度只要 10–20 就能吃满 MXU。还是上面那个 \(\text{int8}[B, 4096]\times\text{int8}[4096,16384]\) 的例子:在 v5e 上权重从 HBM 读时需要 \(B > 259\);如果从 VMEM 读(带宽按 HBM 的 22 倍算),只需 \(B > 11\)(实际上 VMEM 带宽不能全用来读权重,更接近 20)。这就是 VMEM 预取的价值:例如在算注意力的时候,把下一个 FFN 的权重提前搬进 VMEM,就能把权重加载的成本藏起来——前提是一层的权重切得足够小,放得进 VMEM。可惜 VMEM 很小,这往往是个挑战。

脉动阵列是怎么工作的

MXU 的核心是一个 128×128(=16,384 个)乘加单元组成的二维网格。权重(RHS,128×128)从上方预先装入,每个单元固定持有一个权重;输入(LHS,8×128)从左边斜着送进来,每个周期往右移一格;部分和从上往下流,每经过一个单元就加上「输入 × 该单元的权重」,从底部流出的就是结果。开始有一个装载权重和输入的流水线气泡,之后新输入可以连续送入,没有额外气泡。所以张量的维度至少要和 MXU 的边长一样大(128,v6e 上 256),否则要补零浪费算力;有多个 MXU 时,切块的维度要大于 128 × MXU 个数。

一个 3×3 的权重驻留型(weight-stationary)阵列,逐周期运行:

交互演示 · 脉动阵列计算 Y = X · W

每个格子是一个乘加单元:灰色小字是驻留的权重 \(W_{kj}\),紫色是这个周期流过的输入 \(x\),橙色是往下传的部分和。输入第 \(t\) 行的第 \(k\) 个元素在第 \(t+k\) 个周期进入第 \(k\) 行,所以左边的输入是斜着排的。真实的 MXU 是 128×128,每个周期送进一列 8 个数。

芯片、托盘与主机

一块 TPU 芯片通常有两个 TensorCore,共享显存,可以当成一个算力加倍的大加速器(叫 megacore),v4、v5p 都是这样(TPU v7 去掉了 megacore,改成两个核之间的高带宽连接)。v3 及更早的芯片两个核显存独立,被当作两个加速器;面向推理的 v5e 和 v6e 每芯片只有一个核(v6e 的核里是 2 个 256×256 的 MXU)。芯片 4 个一组装在一个托盘(tray)上,经 PCIe 连到 CPU 主机;v5e 每台主机挂 2 个托盘、8 块芯片。PCIe 带宽有限,例如 v4 是每个方向 16 GB/s,比 HBM 慢约 100 倍:可以把数据放到主机内存,但搬得不快。

9. TPU 的网络:ICI、torus、pod 与 DCN

同一个 pod 里的芯片通过 ICI(inter-chip interconnect)直连,不经过主机。v2/v3、推理芯片 v5e 和 v6e 的每块芯片连 4 个最近邻,构成二维 torus;v4 和 v5p 连 6 个最近邻,构成三维 torus。

要点 · 每块芯片只连最近邻(2D 4 条、3D 6 条) · 回绕链路(虚线)把最远距离从 N 降到 N/2 · 远处的芯片要经过中间的芯片逐跳转发 · 每芯片链路数固定 → 可以扩到很大的 pod · 只适合规整的通信模式(环、网格) v5p:4×4×4 的立方体之间用可重构的光交换机 连接,提供回绕;最大 16×20×28 = 8,960 芯片
一个 4×4 的二维 torus(v5e/v6e 的拓扑)。实线是相邻链路,虚线是回绕链路。

带宽的量级(v5p):HBM 每芯片 2.77e12 B/s;ICI 每轴 9e10 B/s(单向,每芯片 3 个轴);DCN 每芯片约 6.25e9 B/s(v5e 是 3.125e9,v6e 和 TPU7x 是 12.5e9);PCIe 每芯片约 1.6e10。ICI 每条链路的单向/双向带宽:v5e 4.5e10/9e10,v5p 和 v6e 9e10/1.8e11。双向带宽是指沿一个轴、两个方向加起来能发出的字节数,只有在有回绕的轴上(能构成环)才能用满,环形集合通信的公式里经常用它。

总结一下:TPU 很简单。大多数情况下可以把它看成一个矩阵乘法单元,连着显存(超快)、通过 ICI 连着邻居芯片(相当快)、通过 DCN 连着数据中心的其余部分(还算快)。让通信量和各条通道的速度成比例,就不会卡住 MXU。

10. GPU 是什么样的

现代 ML GPU(H100、B200)从高处看和 TPU 惊人地相似:一堆专做矩阵乘法的计算单元(叫 SM,streaming multiprocessor),连着一块高速显存(HBM)。区别是 TPU 有 1–2 个大 TensorCore,而 GPU 有一百多个小 SM(H100 132 个,B200 148 个)。每个 SM 比 TPU 的 TensorCore 弱得多,但彼此基本独立,所以 GPU 可以同时做几百件不同的事。

一个 SM(H100 共 132 个) SMEM / L1:256 KB,可编程的共享内存或硬件缓存 L2 缓存 ~50 MB 所有 SM 共享,实测约 5.5 TB/s HBM 80 GB 3.35 TB/s B200 每个 SM 另有 256 KB TMEM,专门给更大的 Tensor Core 喂数据;HBM 180 GB、约 8 TB/s,L2 126 MB。 寄存器:每个子分区 16,384 个 32 位字,每 SM 共 256 KB。
H100 一个 SM 的结构。

SM 内部

每个 SM 分成 4 个相同的子分区(SM subpartition),每个子分区有:

算下来,H100 共有 \(132\times4\times32 = 16{,}896\) 个 FP32 CUDA core,B200(148 个 SM)有 18,944 个;而 TPU v5p 一块芯片有 \(2\times4\times8\times128 = 8{,}192\) 个独立 ALU。

SIMT、warp 与调度

GPU 的线程以 32 个为一组,叫 warp,同一 warp 的线程同时执行同一条指令,处理不同的数据。warp 再组成更大的线程块(block,例如 512 或 1,024 个线程),每个块分配给一个 SM。一个 SM 可以同时驻留多个块和最多 64 个 warp,但每个 warp scheduler 每个周期只执行一个 warp 的指令;它在活跃的 warp 之间切换,以此掩盖访存延迟。相比之下 TPU 基本是单线程的。

CUDA core 比 TPU 的 VPU 灵活:它用 SIMT(single instruction, multiple threads)模型,每个线程有自己的指令指针,可以独立编程;而 VPU 是纯 SIMD。代价是:如果同一个 warp 里的线程走了不同的分支(warp divergence),硬件会把两条分支都执行一遍,把不该执行的线程屏蔽掉,性能悄悄下降。

在 GPU 上运行的程序叫内核(kernel),可以用 CUDA 或 Triton 写,编译成 PTX。写内核大致有四条路:纯 PyTorch(简单但慢)、torch.compile(简单、快、不够灵活)、Triton(更难、更快、更灵活)、CUDA(最难、最快、最灵活)。常见的优化有:访存合并(memory coalescing,让一个 warp 的 32 个线程访问连续地址,从而合并成一次 DRAM 突发读取)、分块(tiling,把数据搬进共享内存反复使用)、线程粗化和减少分支分歧。这些都是在给定内核里提高实际达到的带宽或算力,对应 roofline 上「离屋顶有多远」。内核细节见 GPU 内核与 FlashAttention。

存储层次

比较一下两种设计思路。GPU 起源于图形渲染,设计目标是通用,新任务上更容易「直接能跑」,对编译器的依赖小;但正因为硬件管得多,也更难推理和逼近 roofline——一个内核可能因为打爆 L2、访存没合并而性能很差,而你很难看出来。TPU 把更多负担交给编译器(单线程控制、只能做整个 VPU 宽度的指令,所有搬运要编译器排好流水线),反过来更容易接近峰值。另一个差异是 TPU 的快速片上存储(VMEM)多得多,如果能把权重放进或预取进 VMEM,LLM 推理可以更快。

11. GPU 的网络:NVLink、NVSwitch 与 InfiniBand

网络是 GPU 和 TPU 差别最大的地方。TPU 是只连邻居的 torus;GPU 用的是更传统的分层交换网络。

脊(spine)交换机:16 台,连接 4 个 SU → 1,024 块 GPU 叶交换机 × 8(一个 SU) 叶交换机 × 8(下一个 SU) InfiniBand 胖树(橙线):每节点 8×400 Gbps 网卡 = 400 GB/s 出口;节点内 NVLink(紫):每卡 450 GB/s 单向。
参考的 1,024 卡 DGX H100 SuperPod(简化):每个 SU 32 个节点 = 256 块 GPU。

节点内(scale-up):NVLink 与 NVSwitch

一个节点通常是 8 块 GPU(GB200 NVL72 可以到 72 块),彼此通过 NVLink 在一跳之内全互联,带宽高、延迟低。节点里有几个 NVSwitch 负责在本地 GPU 之间交换数据。H100 节点有 4 个 NVSwitch,每块 GPU 有 18 条 NVLink 4.0 链路,每条单向 25 GB/s(全双工,两个方向互不影响),于是每块 GPU 的出口带宽是 \(18\times25 = 450\) GB/s(NVIDIA 规格页写的 900 GB/s 是双向合计)。B200 的 NVLink 5 每条链路翻倍到 50 GB/s,每卡 900 GB/s 单向。

NVLink 代GPU每链路(单向)每卡链路数每卡带宽(单向)节点大小每节点 NVSwitch
3.0A10025 GB/s12300 GB/s86
4.0H10025 GB/s18450 GB/s84
5.0B20050 GB/s18900 GB/s8 / 722 / 18

一个 8×H100 节点:NVSwitch 一侧理论上能处理 \(4\times64\times25\) GB/s = 6.4 TB/s,但每块 GPU 最多 450 GB/s,所以节点的峰值是 \(8\times450\) GB/s = 3.6 TB/s,这也是它的对分带宽(把 8 块卡分成两半,两半之间能流过的总带宽)。

理论值和实测有差距:在 8×H100 节点内实测 AllReduce,总线带宽最多接近 370 GB/s,而且要 10 GB 级别的大消息才能达到;像 Llama 3 70B 一个 MLP 权重那么大的数组(8 路切分后约 58 MB),只能跑到约 150 GB/s。Hopper 开始的 NVSwitch 支持 SHARP(交换机内归约),理论上能让 AllReduce 的成本减半,实测提升约 30%。

节点间(scale-out):InfiniBand 胖树

节点之间用带宽低一些的 InfiniBand(IB)或以太网相连,每块 GPU 配一张网卡。NVIDIA 的参考设计 DGX SuperPod(1,024 块 H100)是这样搭的:

整个 IB 网络是一棵胖树(fat tree),线缆和交换机的带宽经过设计,保证节点之上有完整的对分带宽:任意把节点分成两半,每个节点都能以 400 GB/s 向另一半发送。这意味着跨节点的 AllGather/ReduceScatter 有大致恒定的带宽,无论跨多少节点。这只是参考设计,不同客户和云厂商会改;例如 Meta 训练 Llama 3 用的是以太网、三层交换和顶层超额订阅(oversubscribed)的网络。

做估算时可以记:节点内每卡 450 GB/s,跨节点每节点 400 GB/s,后者在 AllToAll 这种「每卡对每卡」的通信里会退化得很厉害(跨 2 个节点时相比节点内慢 4 倍以上)。GB200 NVL72 把 72 块 GPU 放进一个 NVLink 域,每卡 900 GB/s,节点出口带宽也同比提高到 3.6 TB/s,这显著改变了 GPU 上的通信 roofline。

Scale-up 与 scale-out

这两个词描述的正是上面两层:scale-up 网络(NVLink 域)是少量芯片之间的高带宽、低延迟全互联,不为大规模和容错设计(有人形容它是「加强版的 PCIe」);scale-out 网络(IB/以太网)可以连成千上万个节点,带宽每卡低一个数量级,更像传统的数据中心网络。TPU 的 ICI 介于两者之间:它在整个 pod(数千芯片)范围内提供每芯片恒定的带宽(v5p 每链路 90 GB/s、三个轴共 540 GB/s 出口),但只能用于规整的通信模式。并行策略的核心约束就在这里:通信量最大的并行方式(例如张量并行)要放在快的那一层里。

12. 对比与规格表

部件对照

GPUTPU是什么H100 数量TPU v5p 数量
SMTensorCore包含其他单元的核心「细胞」1322
Warp SchedulerVPU 槽位SIMD 向量运算单元5288
CUDA coreVPU ALUSIMD 的 ALU16,8968,192
SMEM(L1)VMEM快速片上存储32 MB128 MB
寄存器VREG寄存器32 MB256 KB
Tensor CoreMXU矩阵乘法单元5288
HBM(GMEM)HBM大容量高带宽显存80 GB95 GiB
NVLink / NVSwitchICI快速芯片互联8 卡全互联3D torus
InfiniBand / 以太网DCN慢速大范围网络胖树主机间

芯片规格

芯片BF16 稠密FP8/INT8HBMHBM 带宽临界强度快速互联(每芯片)
A1003.1e146.2e1480 GB2.0e12156NVLink 300 GB/s
H100 SXM9.9e142.0e1580 GB3.35e12295NVLink 450 GB/s
H2009.9e142.0e15141 GB4.8e12206NVLink 450 GB/s
B2002.25e154.5e15180 GB8.0e12281NVLink 900 GB/s
TPU v5e1.97e143.94e1416 GB8.2e11240ICI 4 链路 × 45 GB/s
TPU v5p4.59e149.18e1495 GiB2.77e12166ICI 6 链路 × 90 GB/s
TPU v6e9.18e141.84e1532 GB1.64e12560ICI 4 链路 × 90 GB/s

GPU 的互联是每卡单向;TPU 是每链路单向。B200 芯片上有 192 GB HBM3e,HGX/DGX B200 里每卡可用 180 GB(8 卡共 1,440 GB)。价格方面,Google Cloud 上 H200 的标价约每小时 10 美元、TPU v5p 约 4 美元,单块 H200 的算力接近 v5p 的 2 倍、显存 1.5 倍——GPU 单卡更强,TPU 更依赖把很多芯片连起来。

13. 小结

有了每条链路的带宽,就能算具体的通信操作(AllGather、ReduceScatter、AllReduce、AllToAll)各要多少时间,以及矩阵乘法被切分后需要哪些通信:见集合通信与分片矩阵乘法。

参考与延伸阅读

这一篇的 roofline 和硬件部分主要整理自 How to Scale Your Model 的第 1、2、12 章,GPU 内核优化的部分来自 Ultra-Scale Playbook。想动手练习,推荐把第 1 章和第 12 章末尾的习题做一遍;芯片规格以 NVIDIA 和 Google Cloud 的官方页面为准。