从0训练LLM · 现代大模型体系 阅读 + 实验 ≈ 40 分钟 页内实验 ×3
C10 并行:一张卡不够怎么办 第 4 课

按层切到不同卡上,为什么有卡在等?

流水线并行与气泡
把 64 层切成 8 段放在 8 张卡上,跨机只传段与段之间的激活——可整批依次流过时,任何时刻只有一张卡在算。切成 micro-batch 让空等降到 (p−1)/m;1F1B 把在途激活从 m 份压到 p 份;interleaved、Zero Bubble、DualPipe 继续把空等填掉。所有调度都由真实算法生成、按依赖关系排时间。
❓

本课一问:张量并行只能留在一台机里。卡再多,最自然的切法是按层切:64 层分成几段,每段放一张(或一组)卡,跨机只传段与段之间的激活。可这样一来,为什么总有卡在空等?

这一课先不碰:长序列(岔路 1)与 MoE 专家(岔路 2);DualPipe 的完整调度只给公式;段与段之间的通信时间(模拟里记为 0)。

从已有出发

从已有出发 · 训练一步摊到多张卡 已学本课还没学
取一批
前向 ZeROTPPP
loss
反向
梯度同步 DP
优化器更新
每课多切一维:
数字口径:构建时由锚点 Qwen3.8-27B 与 DeepSeek-V3 的官方 config 按公式现算;卡数按 8 张计。本章没有我们自己的多卡实测。

前三课切的是数据、模型状态、层内矩阵;这一课切「层」本身。它的吸引力在通信上:按层切时,段与段之间只传一份激活。锚点一个 micro-batch(4,096 个 token)在一个段边界上传 4,096 × 5,120 × 2 字节 = 41.9 MB,前向一次、反向一次;对比上一课张量并行同样 4,096 个 token 每卡要发 18.8 GB。所以跨机的那一维通常交给流水线。

这一课讨论的是 micro-batch 在层之间怎么流动,序列的内容不进入调度,所以贯穿例句不出场:下面的每一格都是「某一段处理某一个 micro-batch」。

先自己设计:切成 8 段,整批依次流过

锚点 64 层切成 p = 8 段,每段 8 层。最朴素的跑法:整个 batch 先在第 0 段做前向,交给第 1 段……到第 7 段算出 loss,再从第 7 段一路反向回来。

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案整批不切,依次流过 8 段
前向 F 反向 B 权重梯度 W 空等(气泡)

改进一:micro-batch,气泡降到 (p−1)/m

GPipe(Huang 等,2018)把一批切成 m 个 micro-batch。设一段处理一个 micro-batch 的前向用 F、反向用 B,整条流水线的时间是 (m + p − 1)(F + B),其中每张卡真正在干活的只有 m(F + B)。多出来的 (p − 1)(F + B) 就是气泡:

1F1B / GPipe 的气泡(Narayanan 等 2021 §2.2 的写法)
= (F + B) ÷ (F + B) =
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
下面的组件对每种调度都同时给出两种口径,并用闭式公式核对模拟结果。

这个公式是由调度直接推出来的 。可 m 不能无限加:全局 batch 固定时,m 越大,每个 micro-batch 越小,矩阵乘越吃不满卡;GPipe 还有一个更直接的代价——所有前向做完才开始反向,每段要同时存 m 份激活。

改进二:1F1B,激活从 m 份压到 p 份

PipeDream(Harlap 等,2018)的调度是「一前一后」:第 d 段先做 p − d − 1 个前向热身,之后做一个前向、做一个反向,交替进行,反向做完就释放那一份激活。气泡和 GPipe 一样多,但 0 号段最多同时存 p 份激活,而不是 m 份 。

改进三、四:interleaved 与 Zero Bubble

下面这个组件把五种调度放在一起。每一种的执行顺序都由它的算法生成——GPipe、1F1B、Megatron 的 interleaved 1F1B、Zero Bubble 的 ZB-H1——再按「F 等前一段、B 等后一段」的依赖关系逐个排时间:

前向 F 反向 B 权重梯度 W 空等(气泡)
🎯

试三件事:① 先在曲线上方做预测,再看:m 从 32 翻到 64,相对理想时间的气泡大约减半,但永远到不了 0;② 在 GPipe 与 1F1B 之间来回切,总时长与气泡一模一样,读「0 号卡最多同时存几份」——32 份对 8 份;③ 切到 interleaved(v = 4),气泡从 21.9% 降到 5.5%;再切到 Zero Bubble,看紫色的 W 块是怎么填进原来的斜纹空等里的。

现代:DualPipe,把通信也藏起来

DeepSeek-V3 用 16 路流水线,并设计了 DualPipe:micro-batch 从流水线的两头同时喂进来,一个前向块和一个反向块成对执行,把专家并行的 all-to-all 和流水线的通信藏到对方的计算后面(岔路 2 会回到这一点)。报告表 2 给出三种调度的对比 ,把 F = 1、B = 2(整段反向)、W = 1 代进去,p = 8:

调度气泡(报告表 2)代入 F = 1、B = 2、W = 1参数激活
1F1B(PP − 1)(F + B)211 份PP 份
ZB1P(PP − 1)(F + B − 2W)71 份PP 份
DualPipe(PP/2 − 1)(F&B + B − 3W)3 × (F&B − 1):F&B 是一对前向、反向块重叠执行的时间,介于 2 与 3 之间,所以是 3 到 62 份PP + 1 份

DualPipe 的代价写在最后两列:要存两份参数(报告说因为专家并行切得很大,这不太增加显存),激活多一份。它还要求段数和 micro-batch 数都是偶数,但不要求 micro-batch 数是段数的整数倍。

⚠

常见误解之一:「8 段流水线 = 8 张卡同时在算,所以快 8 倍。」

不切 micro-batch 时,8 张卡里任何时刻只有 1 张在算。流水线并行首先解决的是「放不下」;要快,得靠 micro-batch 把流水线填满,而灌满、排空的 p − 1 格永远在。

常见误解之二:「micro-batch 越多越好。」

m 越大气泡越小,但:GPipe 的激活随 m 涨;全局 batch 固定时 m 越大,每个 micro-batch 越小、矩阵乘越吃不满卡;全局 batch 也受收敛性约束(C5 岔路)。

常见误解之三:「气泡率就是 (p−1)/m。」

那是「空等 ÷ 理想时间」。空等占总时间的比例是 (p−1)/(m+p−1):p = 8、m = 32 时一个是 21.9%,一个是 17.9%。读论文和博客时先看它用的是哪一个。

🔍 锚点切 8 段,最后一段为什么总是最慢?

上面的模拟假设每段一样快。锚点真切起来并不均匀:最后一段除了 8 层,还有 lm_head——每个 token 要和 248,320 个词的输出向量各做一次点积,算量 2 × 5,120 × 248,320 ≈ 2.54 GFLOP/token;而一层平均 3.81 亿个参数,约 0.76 GFLOP/token。lm_head 一个就顶约 3.3 层,最后一段比别的段慢四成,整条流水线都要等它。实际配置会少给首尾两段分几层;DeepSeek-V3 的 DualPipe 则把最浅的层(含 embedding)和最深的层(含输出头)放在同一张卡上,让 MTP 与主模型共享这两块参数和梯度(报告 §3.2.3)。

证据三问

问本课的回答
① 它解决什么问题?模型大到一台机放不下、张量并行又不能跨机时,按层切开,跨机只传段间激活
② 数学上为什么可能有效?由调度推出的气泡公式:GPipe / 1F1B 为 (p−1)/m(相对理想时间),interleaved 再除以 v,ZB-H1 每段空等 (p−1)(F+B−W)
③ 实验上为什么最后选了它?Megatron 2021 用 PP 与 TP、DP 组合,在 3,072 张 GPU 上训 1T 参数的模型,达到峰值的 52%;DeepSeek-V3 与 Llama 3 都用 16 路流水线

演进地图:你在这里

并行的演进:每切一维,都是被上一代的某个真数字逼出来的
  1. 起点 一张卡 C9 的结论
    解决了训练一步的全部东西都在一张卡上,不需要任何通信。
    卡在锚点的模型状态 16 字节 × 27,781,427,952 = 444.5 GB,是 80 GB 的 5.6 倍;就算放得下也太慢:DeepSeek-V3 预训练用了 2,664K H800 GPU 小时,一张卡约 304 年。
  2. 第一代 数据并行 + ring all-reduce ring 来自 HPC(2009),2017–2018 进入深度学习
    解决了每卡一份完整模型、算 1/N 的数据,每步把梯度加起来。ring 让每卡只收发 2(N−1)/N 倍梯度:锚点 8 卡 97.2 GB、64 卡 109.4 GB(汇总到 0 号卡的朴素做法:388.9 GB → 3,500 GB)。
    卡在只解决「慢」:每张卡仍要放下完整的 444.5 GB。
  3. 第一代 · 工程 分桶,边反向边同步 PyTorch DDP(Li et al. 2020)
    解决了反向逐层产出梯度,攒满一桶(默认 25 MiB)就先发,通信藏到后面几层的反向里。
    卡在DP 的通信量每步固定:锚点 8 卡每卡 97.2 GB,按 IB 50 GB/s 要 1.94 s;每卡 4,096 个 token 的反向只有约 1.05 s(推演)——token 太少就藏不住。
  4. 第二代 ZeRO-1 / 2 / 3 · FSDP Rajbhandari 2019;PyTorch FSDP 2023
    解决了不再每卡存一份:8 卡上 444.5 → 152.8 → 104.2 → 55.6 GB(依次切优化器状态、梯度、参数)。
    卡在切参数后每步多一次 all-gather,通信是 DP 的 1.5 倍;激活不切:s = 4,096 的选择性重计算要 45.6 GB,放不进剩下的 24.4 GB。
  5. 第三代 张量并行(层内切矩阵) Megatron-LM(Shoeybi 2019)
    解决了第一个矩阵按列切、第二个按行切,一个 MLP 只要一次 all-reduce;t = 8 配序列并行,激活 45.6 → 5.7 GB。
    卡在每层前向 2 次、反向 2 次同步:每 4,096 个 token 每卡要发 18.8 GB,每层都要等——只能放在机内。ZeRO 原文实测:跨两台 DGX-2,每卡约 5 TFLOPS,不到峰值 5%。
  6. 第四代 流水线并行:GPipe → 1F1B → interleaved 2018–2021 你在这里
    解决了按层切,跨机只传段与段之间的激活;切 micro-batch 填满流水线;1F1B 把在途激活从 m 份压到 p 份;interleaved 把气泡除以 v。
    卡在首尾必然空等:p = 8、m = 32 时气泡 (p−1)/m = 21.9%(占总时间 17.9%),interleaved v = 4 也还有 5.5%。
  7. 2023–2024 Zero Bubble · DualPipe Qi 2023;DeepSeek-V3 2024
    解决了反向拆成「对输入」B 与「对权重」W,W 拿去填空泡(F = B = W 时 ZB-H1 的气泡是 1F1B 的 1/3);DualPipe 从两头喂 micro-batch,并把 all-to-all 藏到另一批计算后面。
    卡在DualPipe 要存两份参数;调度更复杂;通信只是被藏住了,量一点没少。
  8. 岔路 · 长序列 序列并行 / 上下文并行 2021–2023:SP、Ring Attention、Ulysses
    解决了128K 的序列按序列切给多张卡;zig-zag 头尾配对,把因果负载 10 : 26 : 42 : 58 配平成每卡一样多。
    卡在每个全注意力层都要在卡之间传 K、V:锚点 s = 131,072、8 卡时每层 0.5 GB;Ulysses 按头切,KV 只有 4 个头。
  9. 岔路 · MoE 专家并行 + all-to-all GShard 2020 → DeepSeek-V3 2024
    解决了DeepSeek-V3 的 654B 路由专家参数分到 64 张卡,每卡每层 4 个;每个 token 最多去 4 个节点。
    卡在跨节点 IB 50 GB/s 只有 NVLink 160 GB/s 的约 1/3,跨节点 EP 的计算与通信约 1 : 1;热门专家所在的卡成为掉队者(→ C11)。
  10. 2021 起 组合:3D / 4D / 5D 并行 Narayanan 2021;Llama 3;DeepSeek-V3
    解决了Llama 3 405B:[TP, CP, PP, DP] = 8 × 1 × 16 × 128,16,384 张 H100,BF16 MFU 41%;DeepSeek-V3:PP 16 × EP 64 × ZeRO-1 DP,不用 TP。
    卡在没有万能配置:每种组合都要先算清每卡显存、每一维的通信走哪条链路、气泡多大(实验课)。
数字口径:锚点与 DeepSeek-V3 的数字由 anchor-model.json / frontier-models.json 的官方 config 在构建时按公式现算(GB = 10⁹ 字节,卡按 80 GB 计);「分桶」一行的时间是推演(有效算力 400 TFLOPS、IB 50 GB/s);5 TFLOPS 出自 ZeRO 原文 §1;GPU 小时、并行配置、带宽出自 DeepSeek-V3 报告 §1、§3.1–3.2(本地 PDF);Llama 3 的配置出自其报告表 4。本章没有我们自己的多卡实测。

一句话

📌

流水线并行按层切,但下一段要等上一段的输出、反向又要等后面的梯度,首尾必然空等;切成 m 个 micro-batch 让空等占比降到 (p−1)/m,1F1B 把在途激活压到 p 份,interleaved、Zero Bubble、DualPipe 继续填空。

🚦

先停在这里:记住「灌满与排空的 p − 1 格永远在」「1F1B 省的是激活,不是气泡」「两种气泡口径」三句,就可以去实验课。
再深一层:展开 lm_head 的 Dig,再去 PP 调度可视化器里看 DualPipe 的双向调度。

下一个缺口

你可能会问