Skip to content

一张卡装不下之后:把模型切开

glossary §8 那张显存预算表算到 185 GB 就停了,结论是「至少 3~4 张卡」。

这篇文档从那一行往下接。 训练的账比推理狠得多——同一个 70B,推理要 185 GB,训练要 1.12 TB。而一旦跨过一张卡,问题就从「装不装得下」变成了「卡和卡之间怎么说话」。

每个条目固定三行:是什么 / 一个可验证的数字 / 没有它会坏在哪

🔗 前置:LLM 基础:跟着一个 token 走完全程


0. 骨架

先把账算清楚。训练比推理贵在哪,是这篇文章的全部起点。

python
# 推理(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,可能比这还大

三件事看完这段就该记住:

  1. 训练的显存不是 3~4 倍推理,是 8 倍。 16 字节/参数,这是整篇文章第一个要背下来的数。
  2. 前四行里有三行(fp32 主权重 + m + v,共 12 字节/参数)在整个前向反向里根本没被用到——它们只在优化器那一步被读写一次。这个观察就是 ZeRO 的全部思想。
  3. 激活值是另一笔账,而且在长序列下会超过模型状态。§1

四种切法(本文主线)

切什么名字通信原语在哪
切优化器状态 / 梯度 / 参数ZeRO / FSDPreduce-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§1Ring Attention / Ulysses§6
ZeRO 1/2/3 / FSDP§2全对全 / DeepEP§7
Ring all-reduce§3NVLink vs InfiniBand§8
列并行 / 行并行§4FlashAttention§9
重计算 / 选择性重计算§10MFU / HFU§11

1. 账本:训练到底占多少显存

模型状态:16 字节/参数

  • 是什么:混合精度 + Adam 的标准配置下,每个参数要存五份东西。ZeRO 论文的记法是 16Ψ 字节,其中 Ψ 是参数量,拆成 2Ψ(权重)+ 2Ψ(梯度)+ KΨ(优化器状态,Adam 的 K=12)

  • 数字

    精度字节/参数
    计算权重bf162
    梯度bf162
    fp32 主权重fp324
    Adam 一阶动量 mfp324
    Adam 二阶动量 vfp324
    合计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²·b

    s=序列长度,b=微批大小,h=隐藏维,a=注意力头数。

    那个 34 是拆出来的:注意力块 11sbh + 5as²b、MLP 块 19sbh、层归一化 4sbh——11+19+4 = 34

  • ⭐ 关键的是第二项 5as²b——它是序列长度的平方。这就是那个 T×T 注意力矩阵被显式存下来的代价。

    两项谁大?解 5as/h > 34s > 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
    ZeRO-1优化器状态4Ψ + 12Ψ/N31.4 GB
    ZeRO-2+ 梯度2Ψ + 14Ψ/N16.6 GB
    ZeRO-3+ 参数16Ψ/N1.9 GB64×(=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-scatterN−1M/NM(N−1)/N
    all-gatherN−1M/NM(N−1)/N
    合计2(N−1)2M(N−1)/N

    N → ∞ 时趋于 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 亿参数以内的模型效果不错,再大就崩了」。两个原因:

  1. all-reduce 得走比机内 NVLink 慢得多的机间链路
  2. 并行度太高导致 GEMM 太小,GPU 利用率下降

具体的带宽差距见 §8——约 18 倍

所以 §12 那两个前沿配置里,TP 全都等于 8。不是巧合,是机内 NVLink 域的大小。


5. PP:切层

  • 是什么:把 L 层切成 p 段,每张卡拿一段,数据像流水线一样穿过。通信量最小的一种——只在段边界传激活值,点对点,不需要集合通信。
  • 数字气泡率 = (p−1)/mm 是微批数。推导很直白:
    气泡时间 t_pb = (p−1)·(t_f + t_b)
    理想时间 t_id = m·(t_f + t_b)
    比值 = (p−1)/m
    m ≫ 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 份而不是 mm ≫ 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)PP
    ZB1P(PP−1)(F+B−2W)PP
    DualPipe(PP/2−1)(F&B+B−3W)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 万亿参数、预训练比等效稠密模型快约
  • 没有它会坏在哪: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 NDR400 Gb/s = 50 GB/s 每网卡
InfiniBand XDR800 Gb/s = 100 GB/s
PCIe Gen5 x1664 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 序列),整体框架是「比优化过的基线快 2~4×」。
    • FA2:比 FA1 快约 ;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,多一次前向变成 4F4/3 = +33%实测 Chen 等人是 30%,HuggingFace 文档写约 20%。别把 33% 算到论文头上。

选择性重计算(现代做法,好得多)

Megatron 那篇的关键洞察:别全重算,只重算那些「显存占得多但重算很便宜」的部分——也就是 softmax / dropout / QKᵀ 这块,正好是 5as²b 那个平方项。

策略每层显存FLOPs 开销实际时间开销
不重计算sbh(34 + 5as/h)00
选择性重计算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-321.3%
    MT-NLG 530B29.7%
    Gopher32.5%
    Llama 3.1 405B38% / 41% / 43%
    Megatron 175B51.4%
    Megatron 1T(3072×A100)52%
    MegaScale 175B(12,288 卡)55.2%
    Megatron 530B56.0%
    PaLM 540B46.2% MFU / 57.8% HFU

    PaLM 那一行最有教学价值:MFU 46.2%,HFU 57.8%,中间 11.6 个点就是重计算。 实践共识区间:35~45% 算好,50%+ 算优秀

  • 为什么到不了 100%:非矩阵乘的必要开销(数据加载、梯度通信、优化器 step)、输入流水停顿、混合精度里仍需高精度的算子、roofline 上的算术强度限制、大规模下的互联延迟。万卡以上跨楼宇的规模,MFU 掉到 30% 以下是物理问题,不是水平问题。

⚠️ 两个必须警惕的陷阱

  1. 分母作弊。H100 的 FP8 峰值约 2000 TFLOPS,BF16 约 989。一个跑在 FP8 峰值 75% 的负载,如果拿 BF16 峰值当分母,会算出 152% 这种不可能的数。 看到高 70% 甚至 90% 的端到端 MFU,第一件事是问分母是什么。顺带一提,NVIDIA 官方规格表默认给的是开启稀疏的数字(2:4 结构化稀疏),也要当心。
  2. 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 并行

卡数TPCPPPDP序列长TFLOPs/卡MFU
8,1928116648,19243043%
16,38481161288,19240041%
16,384816168131,07238038%

核对:8×1×16×64 = 8,1928×16×16×8 = 16,384

三件事一眼可见

  1. TP 恒等于 8——就是 §8 那个机内 NVLink 域。三个配置一次都没变过。
  2. 第三行是长上下文配置:序列从 8k 拉到 128k,CP 从 1 开到 16,DP 从 128 缩到 8。总卡数不变,切法完全重排。这就是 §6 存在的理由,写在配置表里。
  3. 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
PP16(DualPipe)
EP64(跨 8 节点)
DPZeRO-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 静态批 ,比 FasterTransformer 比已经在做连续批处理的 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 通用规格。

Released under the MIT License.