一张卡装不下之后:把模型切开
glossary §8 那张显存预算表算到 185 GB 就停了,结论是「至少 3~4 张卡」。
这篇文档从那一行往下接。 训练的账比推理狠得多——同一个 70B,推理要 185 GB,训练要 1.12 TB。而一旦跨过一张卡,问题就从「装不装得下」变成了「卡和卡之间怎么说话」。
每个条目固定三行:是什么 / 一个可验证的数字 / 没有它会坏在哪。
0. 骨架
先把账算清楚。训练比推理贵在哪,是这篇文章的全部起点。
# 推理(glossary §8):只需要权重 + KV Cache
weights = 70e9 * 2 # bf16 = 140 GB
kv_cache = ... # @128k = 43 GB
# 合计 ≈ 185 GB
# 训练:每一个参数要存五份东西
w_bf16 = 70e9 * 2 # 计算用的权重 = 140 GB
grad = 70e9 * 2 # 梯度 = 140 GB
w_fp32 = 70e9 * 4 # fp32 主权重 = 280 GB
adam_m = 70e9 * 4 # 一阶动量 = 280 GB
adam_v = 70e9 * 4 # 二阶动量 = 280 GB
# ────────────────────────────
# 模型状态合计 ≈ 1,120 GB
activations = ... # 激活值,见 §1,可能比这还大三件事看完这段就该记住:
- 训练的显存不是 3~4 倍推理,是 8 倍。
16 字节/参数,这是整篇文章第一个要背下来的数。 - 前四行里有三行(fp32 主权重 + m + v,共 12 字节/参数)在整个前向反向里根本没被用到——它们只在优化器那一步被读写一次。这个观察就是 ZeRO 的全部思想。
- 激活值是另一笔账,而且在长序列下会超过模型状态。 见 §1。
四种切法(本文主线)
| 切什么 | 名字 | 通信原语 | 在哪 |
|---|---|---|---|
| 切优化器状态 / 梯度 / 参数 | ZeRO / FSDP | reduce-scatter + all-gather | §2 |
| 切数据 | DP 数据并行 | all-reduce | §3 |
| 切矩阵 | TP 张量并行 | all-reduce(每层 4 次) | §4 |
| 切层 | PP 流水并行 | 点对点 | §5 |
| 切序列 | CP/SP 上下文并行 | all-gather / all-to-all | §6 |
| 切专家 | EP 专家并行 | all-to-all | §7 |
速查表
| 名词 | 在哪 | 名词 | 在哪 |
|---|---|---|---|
| 16 字节/参数 | §1 | 流水气泡 / 1F1B / DualPipe | §5 |
| 激活值公式 34sbh | §1 | Ring Attention / Ulysses | §6 |
| ZeRO 1/2/3 / FSDP | §2 | 全对全 / DeepEP | §7 |
| Ring all-reduce | §3 | NVLink vs InfiniBand | §8 |
| 列并行 / 行并行 | §4 | FlashAttention | §9 |
| 重计算 / 选择性重计算 | §10 | MFU / HFU | §11 |
1. 账本:训练到底占多少显存
模型状态:16 字节/参数
是什么:混合精度 + Adam 的标准配置下,每个参数要存五份东西。ZeRO 论文的记法是
16Ψ字节,其中 Ψ 是参数量,拆成2Ψ(权重)+ 2Ψ(梯度)+ KΨ(优化器状态,Adam 的 K=12)。数字:
项 精度 字节/参数 计算权重 bf16 2 梯度 bf16 2 fp32 主权重 fp32 4 Adam 一阶动量 mfp32 4 Adam 二阶动量 vfp32 4 合计 16 没有它会坏在哪:fp32 主权重不是冗余,是数值必需。bf16 只有 8 位尾数,学习率 3e-4 量级的更新量在 bf16 下会被直接舍入掉——梯度更新根本累加不进去。主权重存 fp32,是让「小的更新」不至于消失。
⚠️ 别写「20 字节/参数」:这个数在网上流传但我没找到一手来源。16 是 ZeRO 论文的正典(bf16 梯度);18 是 fp32 梯度累加的变体(Megatron-LM 常用)。要写就写这两个之一。
70B 的训练账
70e9 × 16 字节 = 1.12e12 字节 = 1,120 GB ≈ 1.12 TB除以 80 GB/卡 = 14 张卡,这还只是放下模型状态,一个激活值都没存。 对比 glossary §8 的推理账 185 GB:训练是推理的 6 倍。
激活值:另一笔账,而且可能更大
是什么:反向传播需要前向时的中间结果。glossary §7 说反向传播要对每个权重求偏导——求导的链式法则需要前向的激活值,所以它们必须留着。
数字:Megatron 那篇《Reducing Activation Recomputation》给了精确公式(单位是字节,2 字节/元素已算进去):
每层激活值 = s·b·h·(34 + 5·a·s/h) 字节 = 34·s·b·h + 5·a·s²·bs=序列长度,b=微批大小,h=隐藏维,a=注意力头数。那个 34 是拆出来的:注意力块
11sbh + 5as²b、MLP 块19sbh、层归一化4sbh——11+19+4 = 34。⭐ 关键的是第二项
5as²b——它是序列长度的平方。这就是那个T×T注意力矩阵被显式存下来的代价。两项谁大?解
5as/h > 34:s > 34h/(5a)。代进 Llama-3-70B(h=8192, a=64):s > 870。 超过 870 个 token,平方项就开始主导。 而现在没人训 870 以下的序列。70B 在 s=8192 下的实算:
34·s·b·h = 34 × 8192 × 1 × 8192 = 2.28 GB / 层 5·a·s²·b = 5 × 64 × 8192² = 21.47 GB / 层 ← 平方项是它的 9.4 倍 单层合计 ≈ 23.75 GB × 80 层 ≈ 1,900 GB激活值 1,900 GB,比模型状态的 1,120 GB 还大。
没有它会坏在哪:不存激活值就没法反向传播。而全存下来,光激活值就要 24 张卡。这就是 §9 FlashAttention 和 §10 重计算 存在的全部理由——它们干的都是消灭那个
5as²b。
2. ZeRO:把那 12 字节切掉
§0 说过:fp32 主权重 + m + v 那 12 字节,在整个前向反向里没被用到过。那为什么每张卡都要存一份完整的?
这就是 ZeRO 的全部洞察。它不是压缩,不是近似——它只是不让每张卡冗余地存同一份东西。
三个 stage
是什么:数据并行下,
N张卡各存一份完整的 16Ψ。ZeRO 逐级把这份东西切开,每张卡只留 1/N。数字(ZeRO 论文,7.5B 模型,64 卡):
Stage 切什么 每卡显存公式 7.5B@64卡 降幅 通信量 基线 DP 什么都不切 16Ψ120 GB 1× 1× ZeRO-1 优化器状态 4Ψ + 12Ψ/N31.4 GB 4× 1× ZeRO-2 + 梯度 2Ψ + 14Ψ/N16.6 GB 8× 1× ZeRO-3 + 参数 16Ψ/N1.9 GB 64×(=N) 1.5× ⭐ 最反直觉的一行:ZeRO-1 和 ZeRO-2 的通信量和基线 DP 完全一样。 因为 all-reduce 本来就等于
reduce-scatter + all-gather(见 §3)——ZeRO 只是不再重复计算,通信一个字节都没多。省 8 倍显存,零通信代价。这在系统设计里非常罕见。ZeRO-3 才开始付钱:参数要在前向和反向前各 all-gather 一次,通信量涨到 1.5×。换来的是显存线性随卡数下降(渐进趋于 0)。
没有它会坏在哪:不切的话,70B 要 1.12 TB / 80 GB = 14 张卡才放得下模型状态——而这 14 张卡存的是14 份一模一样的东西。
⚠️ ZeRO 只管模型状态,不管激活值。 上面那 1,900 GB 的激活值,ZeRO 一个字节都不省。那是 §6 和 §10 的活。
FSDP 就是 ZeRO-3
PyTorch 的 FULL_SHARD 和 ZeRO-3 功能等价,SHARD_GRAD_OP 对应 ZeRO-2。核心机制一模一样:把 DDP 的 all-reduce 拆成 reduce-scatter + all-gather。通信量同样是 DDP 的约 1.5×。 一个工程细节值得记:单次 all-gather 低于 3300 万个元素时,总通信时间开始劣化——这是 FlatParameter 分桶策略的经验阈值。
极限:offload
- ZeRO-Offload:把 12Ψ+2Ψ(优化器状态、梯度、优化器 step)挪到 CPU,参数和前向反向留在 GPU。单张 V100-32GB 训 13B,比 PyTorch 基线大 10 倍;10B 模型上单卡 40 TFLOPS(PyTorch 基线只能训 1.4B,30 TFLOPS)。
- ZeRO-Infinity:再往 NVMe 挪。单台 DGX-2(16×V100)放下 1 万亿参数;512 张 V100 上放下 32 万亿,持续 25+ PFLOPS = 峰值的 40%。
- ⚠️ 那个 32T 是「装得下」,不是「训到收敛了」。 这类数字要按能力上限读,不是按成果读。
3. DP:切数据
是什么:每张卡一份完整权重,各喂不同的数据,反向后把梯度 all-reduce 求平均。最简单的一种,也是唯一一种不需要改模型代码的。
数字:Ring all-reduce 的通信量是
2M(N−1)/N字节每卡(M 是张量字节数,N 是卡数)。推导:阶段 步数 每步块大小 每卡通信量 reduce-scatter N−1 M/N M(N−1)/Nall-gather N−1 M/N M(N−1)/N合计 2(N−1) 2M(N−1)/NN → ∞时趋于2M——和卡数无关。 这就是「带宽最优」的含义:再多卡,每卡的通信量也不会超过 2M。没有它会坏在哪:不做梯度同步,每张卡就在训自己的模型,得到 N 个不同的模型。
⚠️ 但延迟是
O(N):ring 要走2(N−1)跳。所以 NCCL 2.4 之后默认用双二叉树,把延迟降到2·log(N),带宽仍然打满。带宽最优和延迟最优是两件事,ring 只赢了前者。
4. TP:切矩阵
DP 解决不了「一张卡放不下一层」。TP 是唯一一种把单个矩阵乘切开的办法。
列并行 + 行并行的配对
- 是什么:Megatron 的核心技巧。FFN 的第一个 GEMM 按输出维切(列并行,
A = [A₁, A₂]),第二个 GEMM 按输入维切(行并行,B = [B₁; B₂])。 - ⭐ 为什么必须这样配对:列并行之后,每张卡拿到的是完整的一部分输出通道,所以 glossary §4 里那个逐元素的非线性(SwiGLU / GeLU)可以在本地独立算,不需要同步。如果反过来切,非线性前就得先通信一次。两个 GEMM 之间的那次通信被这个配对省掉了。
- 注意力同理:QKV 投影按头切(列并行),输出投影
W_o行并行。天然按头切,和多头的结构完全吻合。 - 数字:每个 transformer 层,前向 2 次 all-reduce,反向 2 次,合计 4 次。 论文里是一对共轭算子
f(前向恒等、反向 all-reduce)和g(前向 all-reduce、反向恒等),注意力块和 MLP 块各一对。 每次 all-reduce 的量是一整个激活张量:M = b·s·h × 精度字节数。 - 没有它会坏在哪:Llama 3 405B 的 FFN 中间维是 53,248——单个权重矩阵
16384 × 53248 × 2 字节 = 1.7 GB,还只是一层里的一个矩阵。不切,单卡放不下。
⚠️ TP 有一条硬边界:不能出机器
Megatron-LM 2 的原话:TP「在 DGX A100(8 卡)上对 200 亿参数以内的模型效果不错,再大就崩了」。两个原因:
- all-reduce 得走比机内 NVLink 慢得多的机间链路;
- 并行度太高导致 GEMM 太小,GPU 利用率下降。
具体的带宽差距见 §8——约 18 倍。
所以 §12 那两个前沿配置里,TP 全都等于 8。不是巧合,是机内 NVLink 域的大小。
5. PP:切层
- 是什么:把
L层切成p段,每张卡拿一段,数据像流水线一样穿过。通信量最小的一种——只在段边界传激活值,点对点,不需要集合通信。 - 数字:气泡率 =
(p−1)/m,m是微批数。推导很直白:气泡时间 t_pb = (p−1)·(t_f + t_b) 理想时间 t_id = m·(t_f + t_b) 比值 = (p−1)/mm ≫ p是唯一的解法。 GPipe 的经验:K=8 段、M=64 微批 → 约 10% 气泡;M=256 → 约 3%。 - 没有它会坏在哪:不切层,模型层数受限于单卡显存。而且 PP 的通信量比 TP 小几个数量级,它是唯一适合跨机器用的并行方式。
三代调度
GPipe:最朴素,所有前向做完再做反向。激活值峰值
O(N + (L/K)·(N/M))。重计算让单卡能训的模型大 2.7 倍,4 倍卡数下 Transformer 提速约 3.5 倍(接近线性)。 ⚠️ 网上流传的「4× 省内存 / 1.7× 提速」不是这篇论文的数字,别引用。1F1B(PipeDream-Flush):气泡率和 GPipe 一样,但把在途的前向限制在
p个以内——激活值只需存p份而不是m份。m ≫ p时这是巨大的差别。同样的气泡,少得多的显存。交错式(虚拟流水线):每卡拿
v段不连续的层,气泡率降到(p−1)/(m·v),吞吐提升 >10%,显存基本不变。代价是通信次数变成v倍。Zero Bubble PP(ICLR 2024):把反向拆成 B(激活梯度)和 W(权重梯度)——只有 B 是前一段的串行依赖,W 可以推迟到任何空隙里做。这是第一个在同步训练语义下真正做到零气泡的调度。ZB-2p 在相近显存下吞吐 +23%,放宽显存约束后 +31%。 ⚠️ ICLR 提交版摘要写的是 15%,arXiv 修订版是 23%/31%。引用时说清是哪版。
DualPipe(DeepSeek-V3):双向调度,气泡
(PP/2−1)(F&B+B−3W)。方法 气泡 参数显存 激活显存 1F1B (PP−1)(F+B)1× PP ZB1P (PP−1)(F+B−2W)1× PP DualPipe (PP/2−1)(F&B+B−3W)2× PP+1 ⚠️ 那个 2× 是参数显存(双向调度要两份模型副本),不是激活显存——激活是 PP+1 那一列,论文原话是「峰值激活显存只增加 1/PP」。很多二手资料把这两列搞混了。
6. CP:切序列
§1 算出激活值里的 5as²b 是平方项。当序列长到 128k,切数据、切矩阵、切层都不管用了——得切序列本身。
- Ring Attention:把 KV 块在环上传,通信和分块注意力计算重叠。上下文长度随卡数线性增长,无近似、无额外通信/计算开销。论文里训到了超过 1 亿 token 的序列。
- DeepSpeed-Ulysses:按序列维切分,注意力前后各一次 all-to-all(先 序列→头 重分区,算完再 头→序列)。关键性质:序列长度和卡数按比例增长时,通信量保持恒定——而 Megatron 的 all-gather 方案是线性增长的。通信量降 >10×,4 倍序列长度下训练快 2.5×。 ⚠️ 硬约束:序列并行度不能超过注意力头数。 Ring Attention 没有这个限制。
- Llama 3 的 CP(用 all-gather,不是 ring):Meta 选了 all-gather 而非环形,理由写得很实在——
cp大而序列短时,ring attention 会导致计算 kernel 碎片化、效率反而下降,还有部分注意力的合并开销;集合通信方案也更好支持输入打包所需的复杂掩码。 一个漂亮的细节:因果掩码下的负载均衡——把序列切成2×CP份,第i号卡拿第i份和第2·CP−1−i份(首尾配对)。因为因果掩码下靠前的 token 要算的注意力更少,不配对就会有卡闲着。
7. EP:切专家
glossary §4 说 MoE 把容量和算力解耦,但显存不省——671B 全部得驻留在 HBM 里。EP 就是解决这个的:专家分散到不同卡上。
- 是什么:每张卡持有一部分专家。token 经路由后,all-to-all 发到对应专家所在的卡,算完再 all-to-all 送回来。每个 MoE 层两次全对全。
- ⭐ 为什么 all-to-all 特别难:DP 的 all-reduce、TP 的 all-gather,通信量和目的地都是提前确定的。而 EP 的传输大小和去向由路由器每一步动态决定——为固定尺寸设计的标准集合通信原语在这里效率很低。这是稀疏的、数据依赖的交换。
- DeepSeek 的两个应对:
- 节点受限路由:每个 token 的专家最多分布在 4 个节点上,从源头掐住跨机流量。
- DeepEP:非对称带宽转发——token 先走 IB 到「节点内序号匹配」的那张卡,再走 NVLink 转发到目标专家,两条路径完全重叠。结果是只用 20 个 SM 就能把 IB 和 NVLink 同时打满。
- 数字:GShard 用 EP 在 2048 块 TPU v3 上 4 天训出 600B 的翻译模型;Switch Transformer 用 top-1 路由做到 1.6 万亿参数、预训练比等效稠密模型快约 4×。
- 没有它会坏在哪:DeepSeek-V3 的 671B 在 bf16 下是 1.3 TB,单节点 8 卡(640 GB)放不下。MoE 的容量优势必须靠 EP 才能兑现。
8. 带宽:为什么 TP 只能待在机内
这一节只有一个数,但它解释了 §4 和 §12 的全部设计。
| 链路 | 带宽 |
|---|---|
| NVLink 4.0(H100/H200 SXM) | 900 GB/s 每卡 |
| NVLink 5.0(B200) | 1,800 GB/s |
| NVLink(H800,中国特供) | 400 GB/s |
| NVSwitch 4.0(Blackwell) | 14.4 TB/s 聚合 |
| GB200 NVL72 整机柜 | 130 TB/s 截面带宽,72 卡同一 NVLink 域 |
| InfiniBand NDR | 400 Gb/s = 50 GB/s 每网卡 |
| InfiniBand XDR | 800 Gb/s = 100 GB/s |
| PCIe Gen5 x16 | 64 GB/s 单向 |
⭐ 那个比值
H100:NVLink 900 GB/s ÷ IB 50 GB/s = 18×
H800:NVLink 400 GB/s(实测 160)÷ 50 GB/s ≈ 3.2×(设计目标约 4:1)一个 8 卡 H100 节点内部是 900 GB/s 的全连接;集合通信一旦跨出节点边界,掉到每网卡 50 GB/s。这个 18 倍的悬崖,就是所有并行策略布局的物理约束。
由此推出的布局规律(§12 会验证):
- TP 通信最重(每层 4 次 all-reduce)→ 必须机内 → 所以 TP ≤ 8
- PP 通信最轻(点对点,段边界)→ 可以跨机
- DP 通信可重叠 → 放最外层
H800 那一行还有个额外信息:NVLink 从 900 砍到 400 GB/s,是为了压在 600 GB/s 的出口管制线以下。DeepSeek-V3 训练时完全不用 TP,论文原话是「因 NVLink 带宽受限而效率不足」——一条硬件限制直接改写了并行策略的选择。
9. FlashAttention:省的不是算力,是搬运
glossary §8 说过 decode 是带宽受限的。FlashAttention 是同一个洞察在训练侧的应用。
- 是什么:三件事——分块(Q/K/V 切成能放进 SRAM 的块,
N×N矩阵从不在 HBM 里成形)、在线 softmax(用滚动的最大值和求和统计量增量地算,结果精确相等)、重计算(反向时在 SRAM 里重算注意力分数,而不是存下来)。 - ⭐ 它是精确的,不是近似。FLOPs 甚至比标准实现略多(反向要重算)。它优化的是 HBM 访问次数,不是浮点运算次数:
FlashAttention: O(N²d²M⁻¹) 次 HBM 访问 标准注意力: O(Nd + N²) 次M是 SRAM 大小。因为通常M ≫ d²,所以渐进更少。 - 数字:
- 显存:注意力从
O(N²)降到O(N),比精确注意力基线省最多 20 倍。这直接消灭了 §1 那个5as²b项。 - FA1 提速:BERT-large 端到端 +15%,GPT-2(1k 序列)3×,整体框架是「比优化过的基线快 2~4×」。
- FA2:比 FA1 快约 2×;A100 上 230 TFLOPs/s = 理论峰值的 73%(FA1 只有 25~40%)。端到端 GPT 训练 72% MFU。
- FA3(H100):前向比 FA2 快 1.5~2.0×,反向 1.5~1.75×。 ⚠️ 版本差异必须说清:arXiv v1 是 740 TFLOPs/s(FP16)、75% 利用率、FP8 约 1.2 PFLOPs/s;NeurIPS 正式版是 840 TFLOPs/s(BF16)、85%、1.3 PFLOPs/s。引用哪版就说哪版。 有个很说明问题的消融阶梯:FA2 = 350 TFLOPS → 用上 Hopper 的 WGMMA + TMA = 540~570 → 再加异步和 GEMM-softmax 重叠 = 661。
- 显存:注意力从
- 没有它会坏在哪:这是它和线性/稀疏注意力最根本的区别。那些方法确实减少了 FLOPs,但论文里的评价是「相对标准注意力没有表现出实际时钟速度的提升,也没有被广泛采用」。 **减少 FLOPs 不等于跑得快。真正的瓶颈是搬运。**这和 glossary §8 那个「decode 卡在带宽不是算力」是同一件事,只是换了个位置发生。
10. 重计算:拿算力换显存
- 是什么:前向时不存激活值,反向需要时重新算一遍。用时间换空间。
- 数字(经典结果):n 层网络,
O(√n)显存,代价是每个 minibatch 多一次前向。极端情况可以做到O(log n)显存 /O(n log n)额外计算。实测:1000 层 ResNet 从 48 GB 降到 7 GB,运行时开销 约 30%。 - ⚠️ 关于那个「33%」:它是理论上界,不是论文的实测值。推导是:反向约等于 2 倍前向,基线一步
1F + 2F = 3F,多一次前向变成4F,4/3 = +33%。实测 Chen 等人是 30%,HuggingFace 文档写约 20%。别把 33% 算到论文头上。
选择性重计算(现代做法,好得多)
Megatron 那篇的关键洞察:别全重算,只重算那些「显存占得多但重算很便宜」的部分——也就是 softmax / dropout / QKᵀ 这块,正好是 5as²b 那个平方项。
| 策略 | 每层显存 | FLOPs 开销 | 实际时间开销 |
|---|---|---|---|
| 不重计算 | sbh(34 + 5as/h) | 0 | 0 |
| 选择性重计算 | sbh(10 + 24/t) | GPT-3: 2.7% | 很小 |
| 全量重计算 | 2sbh | ~33% | 30~40% |
GPT-3 上:激活显存降约 70%,只付 2.7% 的算力。 论文的总结是「激活显存降约 5 倍,同时收回了全量重计算 90% 以上的计算开销」。
配合序列并行后的端到端收益:
| 模型 | 吞吐提升 | 最终 MFU |
|---|---|---|
| 22B | +29.0% | 41.5% |
| 175B | +31.8% | 51.4% |
| 530B | +29.7% | 56.0% |
| 1T | +32.1% | 56.3% |
⚠️ 一个时效性提醒:选择性重计算最初瞄准的是 5as²b,而 §9 的 FlashAttention 已经从根上让这一项不再成形。所以在现代技术栈里,这个动机大部分蒸发了——现在更该重算的是 MLP、layernorm 这些,每层有效开销趋向 34sbh/t。两个技术解决同一个问题时,先出现的那个会被后来的削弱。
11. MFU:这堆卡到底跑出了多少
是什么:实测吞吐 ÷ 峰值算力下的理论吞吐,只算一次前向+反向必需的 FLOPs——所以它和实现无关,不把重计算算进去。HFU 则用硬件实际执行的 FLOPs,重计算会把它抬高。HFU ≥ MFU 恒成立,两者的差就是重计算。
数字(全部一手来源):
训练 MFU GPT-3 21.3% MT-NLG 530B 29.7% Gopher 32.5% Llama 3.1 405B 38% / 41% / 43% Megatron 175B 51.4% Megatron 1T(3072×A100) 52% MegaScale 175B(12,288 卡) 55.2% Megatron 530B 56.0% PaLM 540B 46.2% MFU / 57.8% HFU PaLM 那一行最有教学价值:MFU 46.2%,HFU 57.8%,中间 11.6 个点就是重计算。 实践共识区间:35~45% 算好,50%+ 算优秀。
为什么到不了 100%:非矩阵乘的必要开销(数据加载、梯度通信、优化器 step)、输入流水停顿、混合精度里仍需高精度的算子、roofline 上的算术强度限制、大规模下的互联延迟。万卡以上跨楼宇的规模,MFU 掉到 30% 以下是物理问题,不是水平问题。
⚠️ 两个必须警惕的陷阱
- 分母作弊。H100 的 FP8 峰值约 2000 TFLOPS,BF16 约 989。一个跑在 FP8 峰值 75% 的负载,如果拿 BF16 峰值当分母,会算出 152% 这种不可能的数。 看到高 70% 甚至 90% 的端到端 MFU,第一件事是问分母是什么。顺带一提,NVIDIA 官方规格表默认给的是开启稀疏的数字(2:4 结构化稀疏),也要当心。
- MFU ≠ GPU 利用率。
nvidia-smi的利用率只表示「采样窗口内有没有 kernel 在跑」。一个显示 90% 利用率的卡,在带宽受限的推理负载上算力利用率可能只有 15%。 这两个数经常被当成一回事。
还有一个 MFU 根本没捕捉到的东西:失败重跑。 Llama 3 那 54 天里有 419 次意外中断(见 §12),有效训练时间 >90%——所以真实的「端到端 MFU」大约是 41% × 0.90 ≈ 37%。
12. 两个真实的前沿配置
前面十一节的所有取舍,在这两张表里同时出现。
Llama 3.1 405B — 4D 并行
| 卡数 | TP | CP | PP | DP | 序列长 | TFLOPs/卡 | MFU |
|---|---|---|---|---|---|---|---|
| 8,192 | 8 | 1 | 16 | 64 | 8,192 | 430 | 43% |
| 16,384 | 8 | 1 | 16 | 128 | 8,192 | 400 | 41% |
| 16,384 | 8 | 16 | 16 | 8 | 131,072 | 380 | 38% |
核对:8×1×16×64 = 8,192 ✓ 8×16×16×8 = 16,384 ✓
三件事一眼可见:
- TP 恒等于 8——就是 §8 那个机内 NVLink 域。三个配置一次都没变过。
- 第三行是长上下文配置:序列从 8k 拉到 128k,CP 从 1 开到 16,DP 从 128 缩到 8。总卡数不变,切法完全重排。这就是 §6 存在的理由,写在配置表里。
- MFU 随之从 41% 掉到 38%——长上下文是要付效率代价的。
规模:16,384 张 H100,15.6T token,3.8×10²⁵ FLOPs,405B 单独耗 3,084 万 H100 卡时。网络是 RoCE 400 Gbps,不是 IB。
⚠️ 可靠性——这是最少被讲但最该讲的一段:54 天里 466 次中断(47 次计划内 + 419 次意外),有效训练时间 >90%。意外中断的构成:GPU 相关 78%(其中 GPU 故障 30.1%、HBM3 显存故障 17.2%)、软件 bug 12.9%、网络 8.4%。
419 次除以 54 天 ≈ 每天 7.8 次,平均每 3 小时挂一次。 万卡规模的训练,「不出故障」不是一个可选项——checkpoint 和自动恢复是基础设施,不是优化。
DeepSeek-V3 — 另一条路
| 项 | 值 |
|---|---|
| 卡 | 2,048 张 H800 |
| PP | 16(DualPipe) |
| EP | 64(跨 8 节点) |
| DP | ZeRO-1 |
| TP | 训练时完全不用 |
| 数据 | 14.8T token |
| 总卡时 | 278.8 万 H800 卡时 |
| 成本 | $5.576 M(按 $2/卡时) |
⭐ 「TP = 0」那一行是全文最有信息量的一个配置选择。 论文的理由是「NVLink 带宽受限下效率不足」——H800 的 NVLink 被砍到 400 GB/s(实测 160),和 IB 的 50 GB/s 只差 3.2 倍,而不是 H100 的 18 倍。机内优势不够大,TP 就不划算了。一条出口管制条款,改写了并行策略。
取而代之的是把预算全押在 EP 上(64 路),配合节点受限路由(每 token 最多 4 节点)和 DeepEP 的 20 SM 通信内核。
FP8 训练:全程 E4M3,激活 1×128 分块量化、权重 128×128 分块量化,每累加 128 个元素提升到 FP32(绕过 H800 Tensor Core 约 14 位的累加精度)。相对 BF16 误差 < 0.25%。
⚠️ 关于那个 $5.576M:论文自己说清楚了,这是最后一次训练运行的边际成本,不含前期研究、消融实验、数据和硬件采购。引用时必须带上这句。
还有一个对比极强的事实:DeepSeek-V3 在 14.8T token 上零不可恢复的 loss 尖峰、零回滚。而 Llama 3 是 419 次意外中断。两者的规模、硬件、软件栈都不同,不能直接归因——但这两个数字放在一起,本身就值得看很久。
(顺带,DeepSeek 那篇硬件论文给的 KV Cache 对比,正好印证 glossary §3 的表:DeepSeek-V3 的 MLA 是 70.272 KB/token,Qwen-2.5 72B 的 GQA 是 327.680 KB,Llama-3.1 405B 是 516.096 KB——分别是 4.66× 和 7.28× 的差距。用 glossary §6 那个公式验算 405B:2 × 126 × 8 × 128 × 2 = 516,096 字节 ✓ 公式对得上一手数据。)
13. 推理侧:同一套逻辑,不同的约束
训练切的是显存和算力,推理切的是吞吐和延迟。但底层机制是同一批。
- PagedAttention / vLLM:KV Cache 按页管理。现有系统只用掉分配显存的 20.4%~38.2%——也就是浪费 61.8%~79.6%;vLLM 降到 4% 以下。 吞吐提升 2~4×。
- 连续批处理:那个著名的 23× 数字,基线是朴素的 HuggingFace 静态批处理。同一份 benchmark 里诚实的对比是:比 Ray Serve / TGI 静态批 8×,比 FasterTransformer 4×,比已经在做连续批处理的 TGI 只有约 2×。 提升的来源是输出长度方差:静态批处理的吞吐会随生成长度方差从 200 tok/s 掉到 81 tok/s。长 prompt 短输出的场景只有 3~5×。
- PD 分离:glossary §6 说 prefill 算力受限、decode 带宽受限——既然两个阶段的瓶颈不同,就该跑在不同的卡上。DistServe 报告 7.4× 有效吞吐或同等速率下 12.6× 更紧的 SLO,KV 传输开销在机内 NCCL 下 <0.1%。 ⚠️ 这些基线都是同代的共置系统,和现代分块 prefill 调度器比差距要小得多。
- 投机解码:EAGLE 在 LLaMA2-Chat 13B 上 temperature=0 时 3.01~3.76×,temperature=1 时降到 2.66~2.89×。 ⚠️ 但独立评测(Spec-Bench)给出的是更保守的约 2.4×,而且接受率通常在 batch 1~4 下测得——投机解码的收益在连续批处理擅长的大 batch 下会缩水。这两个加速不是相乘的关系。
14. 一句话收尾
glossary §8 停在 185 GB。这篇文章从那里往下,最后落在两张配置表上。
中间的所有内容可以压成一条链:训练要 16 字节/参数,激活值在长序列下比模型状态还大;ZeRO 把冗余切掉,前两级零通信代价;再往下切就得选切矩阵、切层还是切序列,而怎么选由 NVLink 和 InfiniBand 之间那 18 倍的带宽差决定。
Llama 3 的三个配置里 TP 永远是 8,DeepSeek-V3 干脆一次 TP 都不用。这两个决定的理由是同一个数——机内和机外差多少倍。
硬件不是软件的背景,是它的形状。
附:本文的证据分级与已知争议
写系统类文档最容易翻车的地方是把「某个版本的数字」当成「这个技术的数字」。下面这些在正文里都做了标注:
- 「20 字节/参数」:没有一手来源,本文未采用。用 16(ZeRO 正典)或 18(fp32 梯度累加)。
- DeepSeek-V3 的 MFU:论文没有给,本文不写这个数。
- GPipe 的「4× 省内存 / 1.7× 提速」:不是论文数字。正确的是 2.7×(重计算,单卡)和约 3.5×(4 倍卡数)。
- FlashAttention-3:740 TFLOPS / 75% / 1.2 PF 是 arXiv v1;840 / 85% / 1.3 PF 是 NeurIPS 正式版。本文两个都列了。
- Zero Bubble PP:15% 是 ICLR 提交版,23%/31% 是 arXiv 修订版。
- Mooncake:525% 是模拟场景,59~498% 是真实 trace,75% 是对比竞品——三个数出自不同版本,本文未在正文引用。
- 连续批处理 23×:基线是朴素静态批处理,正文已注明诚实对比是约 2× ~ 8×。
- 重计算的 33%:是理论上界不是实测,实测 30%。
- DualPipe 的 2×:是参数显存不是激活显存。
- Llama 3 的机内 NVSwitch 具体带宽:论文里没找到,本文用的是 NVIDIA 官方的 H100 NVLink 900 GB/s 通用规格。