Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

9.4 流水线并行:把网络切成段

9.3 的张量并行把层内切开,通信频繁;换一种更粗的粒度——按层分段,把模型的第 1–8 层放在卡 1、第 9–16 层放在卡 2……每张卡只负责一段,段与段之间只传边界上的激活。这就是流水线并行(pipeline parallelism, PP)。

它的通信最少(只传边界张量)、对链路要求最低(段间点对点即可,可跨机),却引入一个全新的问题:同一时刻只有一段在算。为了让每一段都能持续工作,必须把一个小批切成多个 micro-batch 依次送入——像工厂流水线一样「错峰」。错峰带来的开头的填充(fill)与结尾的排空(drain)就是气泡(bubble)。

本节回答:

  1. 朴素调度下气泡占多大比例?公式怎么推出来?
  2. 三种减小气泡的手段各自改变公式里的哪个量?
  3. 1F1B 为什么能大幅降低峰值显存?
  4. micro-batch 数和批大小、梯度累积怎么联动?

9.4.1 流水线并行与 micro-batch 切分

定义 9.4.1 · 流水线并行与气泡

把模型的 LL 层按顺序切成 PP 段(stage),第 jj 段放在第 jj 张卡上。为填满流水线,把一个小批拆成 MM 个 micro-batch 依次送入:

stage 1→stage 2→⋯→stage P.\text{stage }1\to\text{stage }2\to\cdots\to\text{stage }P .

micro-batch 1 在第 1 段算完就传给第 2 段,同时 micro-batch 2 进入第 1 段——各段并行工作。

代价是首尾的空转:开头需要 P−1P-1 个时间段把流水线「填充」起来(后面的段还没数据),结尾需要 P−1P-1 个时间段「排空」(前面的段已经没数据)。这些空档称为气泡(bubble):

气泡占用的时间段数=2(P−1),理想(无气泡)时=M.\text{气泡占用的时间段数}=2(P-1),\qquad\text{理想(无气泡)时}=M .

工厂流水线的类比,以及它把问题搬到了哪里

把每个 stage 想成一条产线上的工位:工位 1 做完传给工位 2,同时工位 1 立刻开始做下一个工件。稳态时所有工位都忙;但开工那一刻只有工位 1 有活干,收工那一刻只有最后一个工位有活干。

流水线并行的取舍因此很清晰:

  • 换来:每卡只存 1/P1/P 份模型(第 PP 段只存自己那几层)→ 解决「整体放不下」;
  • 付出:① 开工与收工的空转(气泡);② 卡与卡之间传激活的点对点通信;③ 每段的层数不均衡时,最慢的段拖住所有人。

注意它不解决「单层放不下」(层没有被切开),也不解决激活总量——它只是把激活分散到各段,让每段只需保留自己那部分的中间结果。

9.4.2 气泡公式的推导

结论 9.4.1 · 朴素调度下的气泡占比

设流水线有 PP 段,每段处理一个 micro-batch 的耗时相同,共 MM 个 micro-batch,调度方式为最朴素的 GPipe 风格(先把 MM 个 micro-batch 的前向全部做完,再做全部反向)。则:

  • 总时间段数(墙钟时间的最小单位)为 Ttotal=M+P−1T_{\text{total}}=M+P-1;
  • 其中被气泡占用的部分对应 P−1P-1 个「本可工作但没工作」的时间段;
  • 气泡占比 bubble=P−1M+P−1.\text{bubble}=\frac{P-1}{M+P-1}.
推导(三个步骤,每步都可复核)

(i) 每张卡在稳态下必须完成的「工作量」。 每个 stage 都要处理全部 MM 个 micro-batch(前向一次、反向一次)。把「一个 micro-batch 走完一段」记为一个时间片,则每个 stage 的有效工作量为 MM 个时间片(这里把一次前向或一次反向都折算成 1 个单位;真实比例是前向约占 1/3、反向约占 2/3,见下面的注意)。

(ii) 总时间由「最后一个 stage 的完工时刻」决定。 采用 GPipe 的「先全前向、再全反向」:

  • 第 1 个 micro-batch 到达最后一段需要 PP 个时间片(依次流过 PP 段);此后每隔 1 个时间片就有下一个 micro-batch 进入最后一段;
  • 因此前向阶段结束时,最后一段才刚处理完 MM 个 micro-batch 的前向,耗时 M+P−1M+P-1 个时间片;
  • 反向按相反顺序进行,同样需要 M+P−1M+P-1 个时间片(可以从最后一段往前推,也可以用对称性论证);
  • 但前向与反向可以部分重叠(最后一段做反向时,第 1 段仍在做前向),因此整条流水线从第一个 micro-batch 进入第 1 段,到最后一个 micro-batch 的反向完成,总共恰好需要 Ttotal=M+P−1 (以「一次前向/反向」为单位的稳态时长口径).T_{\text{total}}=M+P-1\ \text{(以「一次前向/反向」为单位的稳态时长口径)}. (更严格的证明:所有 stage 的完工时刻 ≥M+(该 stage 的启动延迟)\ge M + (\text{该 stage 的启动延迟}),其中第 jj 段的启动延迟为 j−1j-1,故最慢的末段决定了 Ttotal≥M+P−1T_{\text{total}}\ge M+P-1;GPipe 调度把每段的空闲时间压到最小,故取等号。)

(iii) 气泡 = 总时间 − 有效工作量。 每张卡的有效时间是 MM,而墙钟时间是 M+P−1M+P-1,因此每张卡空闲(气泡)P−1P-1 个时间片: bubble=空闲墙钟=P−1M+P−1.■\text{bubble}=\frac{\text{空闲}}{\text{墙钟}}=\frac{P-1}{M+P-1}.\qquad\blacksquare

两个极限的读法:M→∞M\to\infty 时气泡 →0\to 0(足够多的 micro-batch 能把首尾空转摊薄到零);M≪PM\ll P 时气泡 →P−1P≈1\to\dfrac{P-1}{P}\approx1(几乎全程空转,等于白买卡)。

注意(口径说明):本推导把前向与反向折算成等长的单位时间片,这是教学口径。实际反向约为前向的两倍耗时,因此更精确的公式会出现前向 / 反向的时间比因子(不同文献的系数略有差异,具体系数需按你的实现实测校准);但结论方向完全一致:气泡随 PP 增大、随 MM 减小,且形式为 P−1常数×M+P−1\dfrac{P-1}{\text{常数}\times M+P-1}。

∎
例 9.4.1 · 代入具体数字

P=8P=8 段、M=16M=16 个 micro-batch。求气泡占比与理论利用率;若把 MM 提到 64,结果如何?

解

(1)P=8P=8、M=16M=16。 bubble=8−116+8−1=723=30.4%,理论利用率=1−30.4%=69.6%.\text{bubble}=\frac{8-1}{16+8-1}=\frac{7}{23}=30.4\%,\qquad\text{理论利用率}=1-30.4\%=69.6\%. 也就是说:即使通信为零、负载完美均衡,每张卡也有 30.4% 的时间在等。

(2)M=64M=64。 bubble=764+7=771=9.9%,利用率=90.1%.\text{bubble}=\frac{7}{64+7}=\frac{7}{71}=9.9\%,\qquad\text{利用率}=90.1\%.

(3)反过来问:MM 要多大才能把气泡压到 10% 以下? 解 7M+7≤0.1⇒M+7≥70⇒M≥63\dfrac{7}{M+7}\le0.1\Rightarrow M+7\ge70\Rightarrow M\ge63。即 M≳9PM\gtrsim 9P 才把气泡压到 10% 以内。这条经验关系(MM 取 PP 的若干倍)在配配置时非常常用。

(4)若 P=16P=16(再多一倍段):M=64M=64 时气泡为 15/79=19.0%15/79=19.0\%,翻了一倍多。加段必加 micro-batch。■\blacksquare

∎
「气泡」的三个常见误读
  1. 「1F1B 能消除气泡」。不能。1F1B 改变的是调度顺序与峰值显存,气泡的总量在朴素口径下不变(它让前向与反向交错,缩短的是激活驻留时间)。消除气泡要靠增大 MM、交错调度或 zero-bubble 拆分。
  2. 「气泡率就是吞吐损失率」。气泡率是下界之外的空转;实际吞吐还要再乘上通信、负载不均衡、算子效率等因子。反过来也不能说「利用率 = 1 − 气泡率」就等于实测 MFU。
  3. 「段数 PP 越大越省显存所以越多越好」。PP 增大确实降低每卡模型状态(16N/P16N/P),但气泡按 P−1M+P−1\frac{P-1}{M+P-1} 变差,且段间的激活传输次数增加。PP 的正确取法是「刚好让每段放得下」,而不是尽量大。

9.4.3 三种减小气泡的手段

三种手段各自改变公式里的哪个量
手段 机制 对公式的影响 代价
① 增大 MM(micro-batch 数) 把同一批数据切得更细,让流水线被更多小批填满 分母 M+P−1M+P-1 变大 → 气泡下降 每个 micro-batch 的矩阵乘更小 → 算子效率下降;激活数量增多;梯度累积步数上升
② 交错调度(interleaved / virtual pipeline) 每张卡持有多个不连续的层块(如卡 1 持有第 1 块与第 5 块),于是任意时刻总有一块处于「可算」状态 等效段数变为 P/VP/V(VV 为每卡块数),气泡约按 P−1VM+P−1\frac{P-1}{V M+P-1} 下降 通信次数增加(每块边界都要传);显存略增
③ zero-bubble(拆分反向) 把反向拆成「对输入的梯度」与「对权重的梯度」两部分,其中一部分可被推迟到气泡时段执行 把本会空转的时段填上有用的计算 实现复杂;峰值显存与调度约束增加;效果随层性质不同

特别说明 zero-bubble:它的核心观察是「反向中计算权重梯度(WW)的那部分不依赖其他 micro-batch 的结果,因此可以推迟到流水线的空隙里做」;而计算输入梯度(BB)的部分必须立刻做(因为要传给前一段)。把反向拆成 BB 与 WW 两步后,气泡能被大量有效的 WW 计算填掉。这是一项 2024 年提出的调度改进(见本节延伸阅读),属于拓展内容:真实实现的效果与配置强相关,具体收益需按你的框架与模型实测。

交错调度为什么有效,以及它为什么不是免费的

朴素调度里,每张卡只负责一段连续层。气泡的根源是「第 1 段在等反向、其余段在等前向」。交错调度给每张卡分配多个不连续的块:卡 1 拿第 1 块和第 5 块。当流水线填到后半程时,卡 1 手上的第 5 块还能继续做前向,于是原本的空档被填上了。

代价是通信次数:原来只传 P−1P-1 个边界,现在有 V(P−1)V(P-1) 个边界要传(VV 为每卡的块数)。所以它适合「通信便宜、气泡贵」的场景(例如段间通信量小、机器负载均衡差)。反过来,若段间通信已经很吃紧,交错调度会先恶化通信。没有免费的调度——这一点与张量并行里「能靠更大批解决就不要加 TP」的权衡完全同型。

9.4.4 micro-batch 数与显存、批大小的联动

从「等效批」倒推 micro-batch 数

设数据并行度为 kk、每卡的单次 micro-batch 为 BmicroB_{\text{micro}}、每卡每个 micro-batch 的层块数为 VV、梯度累积步数为 KK。则一次优化器步骤处理的样本数为

Bglobal=K×M×Mdp×Bmicro.B_{\text{global}}=K\times M\times M_{\text{dp}}\times B_{\text{micro}} .

关键点:流水线的 MM 个 micro-batch 是对已经分到本卡的那个小批的再切分——也就是说,切 MM 份不改变本卡拿到的样本总数,只改变「一次同时推进多少份」。所以在「每卡一批」的常见口径下,上式中的 MdpM_{\text{dp}} 就是本卡数据分区内的批大小,而 MM 只影响显存与气泡;只有当实现把「PP 段各过一遍」的采样算作 MPMP 个样本时(GPipe 论文的记账方式),才会有额外的 PP 因子。配置前必须核对你的框架如何定义「全局批」,否则会算错学习率缩放(此项标注为需核实)。

工程上的取法通常是「先定等效批与 BmicroB_{\text{micro}},再让 MM 吃掉剩下的预算」,因为 MM 是唯一既影响气泡、又能通过梯度累积灵活调节的量:

你想做的事 调哪个 会影响什么
压气泡 增大 MM 算子变小、激活总量上升
降激活显存 减小 BmicroB_{\text{micro}} 或 MM 气泡变大;等效批用 KK 补偿
减少激活驻留的时间 用 1F1B 调度 峰值显存下降,不影响气泡
段数太多导致气泡大 适当减少 PP,把层放不下的部分交给 TP / ZeRO 改变整体策略配平(9.6.5)
1F1B 到底改了什么

GPipe 调度是「MM 个前向全做完,再做 MM 个反向」。这意味着所有 micro-batch 的中间激活都要一直留着,直到反向轮到它——峰值激活显存约等于 MM 份。

1F1B(One-Forward-One-Backward) 在预热(前 P−1P-1 个时段)之后,让每段交替执行「一个前向、一个反向」:

  • 一旦某段完成一个 micro-batch 的反向,它的激活就可以立刻释放;
  • 于是同时驻留的激活只有约 PP 份(而不是 MM 份);
  • 前向与反向交错还让通信更容易与计算重叠(前向的发送可与反向的计算并行)。

所以:1F1B 不改变气泡公式,但显著改善「同样的卡数能塞下多大的 micro-batch / 多少个 micro-batch」。在大模型训练里它基本是默认调度。

例 9.4.2 · 为一个 8 段流水线配参数

目标:每卡一次更新处理的样本数(局部批)Blocal=512B_{\text{local}}=512;硬件 8 张卡;模型切 P=4P=4 段、数据并行 k=2k=2(每卡分到 512512 条样本);每卡 BmicroB_{\text{micro}} 最多取 2(受显存限制,T=1024T=1024)。要求气泡降到 15% 以内。求 MM 与 KK。

解

(1)气泡约束。 解 P−1M+P−1≤0.15\dfrac{P-1}{M+P-1}\le0.15: 3M+3≤0.15⟹M+3≥20⟹M≥17⟹M=20 (取整并用便于划分的值).\frac{3}{M+3}\le0.15\Longrightarrow M+3\ge20\Longrightarrow M\ge17\Longrightarrow M=20\ (\text{取整并用便于划分的值}). 此时气泡 =323=13.0%=\dfrac{3}{23}=13.0\% ✔(若取 M=16M=16,气泡 =3/19=15.8%=3/19=15.8\%,略超)。

(2)等效批约束。 由 Blocal=K×M×BmicroB_{\text{local}}=K\times M\times B_{\text{micro}}: 512=K×20×2=40K⟹K=25.6.512=K\times20\times2=40K\Longrightarrow K=25.6 . 不是整数。这是真实配平行程里最常见的摩擦:把某个约束放松一格。取 M=16M=16、K=16K=16: 16×16×2=512 ✓16\times16\times2=512\ ✓ 此时气泡 =3/19=15.8%=3/19=15.8\%(略超 15%,工程上通常可接受;若要严格达标可把 MM 提到 20 并让 K=12.8K=12.8 不可行,故改为把 BmicroB_{\text{micro}} 调到 2.52.5 或调整局部批为 640640)。

(3)显存检查(1F1B 口径)。 同时驻留的激活约 PP 份 micro-batch:同时在场 token 数 ≈P×M段内×Bmicro×T\approx P\times M_{\text{段内}}\times B_{\text{micro}}\times T。这里每段同时推进的份数取 M段内≈2M_{\text{段内}}\approx2(1F1B 下每段交错持有的 micro-batch 数),故 token 数 ≈4×2×2×1024=16384\approx4\times2\times2\times1024=16384。按 9.1.3 的口径(每层每 token 每 hidden 元素约 34 字节),本段 8 层(L=32L=32、P=4P=4)、h=4096h=4096: Mact≈34×16384×4096×8≈1.8×1010 字节 ⇒ ≈17 GB.M_{\text{act}}\approx34\times16384\times4096\times8\approx1.8\times10^{10}\ \text{字节}\ \Rightarrow\ \approx17\ \text{GB}. 模型状态每卡 =16N/(P⋅k)=16N/(P\cdot k):若 N=7×109N=7\times10^{9},则为 112/8=14112/8=14 GB。合计约 31 GB + 通信缓冲 → 80 GB 卡上仍然舒适,但已不是「可以忽略」的量;若不重算、或把 TT 提到 8192,这一项就会成为配平的主要约束(这就是为什么长序列训练几乎总要开激活重计算)。口径说明:同时在场 token 数随调度实现而异,此处取 1F1B 的常见量级,精确值需实测。

(4)结论:P=4,k=2,M=16,K=16,Bmicro=2P=4,k=2,M=16,K=16,B_{\text{micro}}=2,气泡 15.8%,每卡模型状态 14 GB、激活数 GB 量级。■\blacksquare

∎
例 9.4.3 · GPipe 与 1F1B 的甘特图,以及 PP 的通信量

(1)画出 P=4P=4、M=4M=4 的 GPipe 调度,标出气泡; (2)估算 P=4P=4 段、L=32L=32 层、Bmicro=2B_{\text{micro}}=2、T=2048T=2048、h=4096h=4096、bf16 时,流水线并行的每卡每步通信量,并与数据并行对比。

解

(1)甘特图(▉ 前向、▨ 反向、░ 气泡)。 行是段(stage),列是时间片:

        t →  1    2    3    4    5    6    7    8    9   10   11   12   13
段1          ▉F1  ▉F2  ▉F3  ▉F4  ░    ░    ░   ▨B4  ▨B3  ▨B2  ▨B1  ░    ░
段2          ░    ▉F1  ▉F2  ▉F3  ▉F4  ░    ░   ░   ▨B4  ▨B3  ▨B2  ▨B1  ░
段3          ░    ░    ▉F1  ▉F2  ▉F3  ▉F4  ░   ░   ░   ▨B4  ▨B3  ▨B2  ▨B1
段4          ░    ░    ░    ▉F1  ▉F2  ▉F3  ▉F4  ░   ░   ░   ▨B4  ▨B3  ▨B2
气泡总数:段1 有 3 个(第 5–7)、段2 有 4 个、段3 有 4 个、段4 有 4 个;
用公式核验:墙钟 M+P-1 = 4+3 = 7 个阶段 × 2(前向段与反向段)… 教学口径下
气泡占比 = (P-1)/(M+P-1) = 3/7 = 42.9%。

(严格地说,GPipe 的总时间片数为 2(M+P−1)2(M+P-1)(前向与反向各一轮),每段有效工作 2M2M,空闲 2(P−1)2(P-1),比值仍是 P−1M+P−1\frac{P-1}{M+P-1},与 结论 9.4.1 一致。)

(2)PP 的通信量。 段边界传的是该 stage 输出的激活,形状 Bmicro×T×hB_{\text{micro}}\times T\times h: Spp=2×2048×4096×2 字节=3.4×107 字节≈33.5 MB.S_{\text{pp}}=2\times2048\times4096\times2\ \text{字节}=3.4\times10^{7}\ \text{字节}\approx33.5\ \text{MB}. 每步每个边界要传前向 1 次 + 反向 1 次(反向传的是梯度激活),故每卡涉及的量级约为 2×33.5 MB×M累计2\times33.5\ \text{MB}\times M_{\text{累计}};以 M=16M=16 计,约 1.1 GB/卡/步\mathbf{1.1\ GB/卡/步} 量级。

与数据并行对比:DDP 的梯度 All-Reduce 是 24.524.5 GB/卡/步(例 9.2.2)。所以流水线并行的通信量比数据并行小一个数量级——这才是它有资格跨机的原因。代价换到了气泡与调度复杂度上。

判据:当「段间链路带宽 / 单步时间」不足以承载 GB 级通信时,PP 仍可运行;而 TP 的通信量(例 9.3.1 的 22 GB/卡/步)与 DP 同量级却更频繁、延迟更敏感,所以必须留在节点内。按「对链路的要求由高到低」排序是 TP > DP > PP(TP 要求最苛刻)。■\blacksquare

∎
# ── 流水线并行的接口骨架(概念正确、字段随版本变化,需核实)──────────
import torch.nn as nn
from torch.distributed.pipelining import SplitPoint, pipeline, ScheduleGPipe, Schedule1F1B

# ① 切分:按层边界把模型拆成 P 段(这里是概念写法)
def build_stages(model, num_stages):
    # 真实实现里用 SplitPoint / 逐段包装把子模块分配到各 stage
    ...

# ② 描述 micro-batch 的形状(不是数据本身,只给形状与 dtype)
example_args = (torch.zeros(4, 1024, dtype=torch.long, device="cuda"),)  # (B_micro, T)

# ③ 组装流水线
pipe = pipeline(module=model, mb_args=example_args, split_spec=None, num_stages=8)

# ④ 选择调度:GPipe(先全前向再全反向,峰值显存高)
#              1F1B(交错,峰值显存低,大模型默认)
schedule = Schedule1F1B(pipe, n_microbatches=16)     # M = 16,气泡 ≈ (8-1)/(16+8-1) = 30%
# 调参方向:增大 n_microbatches 压气泡;减小它换算子效率与显存

for step, batch in enumerate(loader):
    losses = []                                  # 收集各 micro-batch 的损失
    schedule.step(batch)                         # 内部完成各段的前向/反向与 p2p 通信
    optimizer.step()
    optimizer.zero_grad(set_to_none=True)

# ── 传统写法:Megatron 系的等价开关(字段名以你的版本为准,需核实)──
#   --pipeline-model-parallel-size 4      # P = 4
#   --num-micro-batches 16                # M = 16
#   --virtual-pipeline-model-parallel-size 2   # V = 2(交错调度)
流水线并行的「负载均衡」检查

切分段时只在层边界切会带来段间不均衡:Transformer 的层是同构的,所以按层数均分通常均衡;但首尾段带有 embedding 与 输出头(lm head),它们参数量大(词表 ×h\times h)、计算轻,会把首尾段变成显存瓶颈或计算瓶颈。

检查清单:

  1. 参数量均衡:把 embedding / lm head 的参数量单独列出,看首尾段是否超出其他段。
  2. 计算量均衡:按「每段的前向 FLOPs」而不是「层数」比较(首尾段多了 embedding 查表与输出投影)。
  3. 激活均衡:首段的输入激活是 embedding 输出(B×T×hB\times T\times h),末段的激活要支撑 softmax over 词表(B×T×∣V∣B\times T\times |V|,可能极大 —— 这项常被忽略,是末段 OOM 的常见原因)。
  4. 不均衡的处方:把 embedding / lm head 与相邻段共享放置、或在首尾段多放/少放若干层来抵消;框架里常有对应的「首尾段特殊配置」开关(具体字段需核实)。
流水线并行的六个高频误解
  1. 「流水线并行能解决单层放不下」。不能。它切的是层,一个层本身仍然必须放进一张卡(除非叠加张量并行)。
  2. 「PP 越大越省显存,所以越大越好」。省的是每段的模型状态(16N/P16N/P),但气泡按 P−1M+P−1\frac{P-1}{M+P-1} 变差;PP 的正确取法是「刚好让每段放得下」。
  3. 「micro-batch 就是数据并行的批」。两者不同:micro-batch 是同一批数据在流水线内部的细分,用于填空档;数据并行的批是分给不同卡的独立数据。二者可同时存在,等效批是它们的乘积(从「等效批」倒推 micro-batch 数)。
  4. 「1F1B 就是 GPipe 换个名字」。GPipe 需要保留所有 micro-batch 的激活(峰值 ∝M\propto M);1F1B 只需约 PP 份,且更容易与通信重叠。在 M=16M=16、P=4P=4 时两者峰值激活差距可达数倍。
  5. 「气泡是通信造成的」。气泡是调度造成的(首尾填充/排空),与通信无关;通信是另一项独立的开销。把两者混为一谈会调错方向。
  6. 「MM 越大越好」。MM 增大压气泡,但每个 micro-batch 的矩阵尺寸变小,GPU 越难跑满;同时梯度累积次数增加、单步墙钟变长。MM 的甜点通常在「气泡降到 10%–20%」附近,而不是无限增大。
即时自测 · 9.4

Q1 写出气泡占比公式,并说明 PP、MM 各自变大时它怎么变。

Q2 P=6P=6、M=12M=12:算出气泡占比。要把气泡压到 10% 以下,MM 至少取多少?

Q3 GPipe 与 1F1B 的核心差别是什么?为什么 1F1B 的峰值激活显存更低?

Q4 某任务 P=8P=8、M=64M=64、实测扩展效率只有 0.45。已确认显存充足。给出两个最可能的瓶颈与对应的检查动作。

参考要点

Q1 bubble=P−1M+P−1\text{bubble}=\dfrac{P-1}{M+P-1}。PP 增大 → 分子变大 → 气泡变差;MM 增大 → 分母变大 → 气泡变好(趋近 0)。

Q2 P=6,M=12P=6,M=12:517=29.4%\dfrac{5}{17}=29.4\%。要 ≤10%\le10\%:5M+5≤0.1⇒M+5≥50⇒M≥45\dfrac{5}{M+5}\le0.1\Rightarrow M+5\ge50\Rightarrow M\ge45。

Q3 GPipe 把所有 micro-batch 的前向全做完再统一做反向,因此每个 micro-batch 的中间激活必须一直保留到它的反向开始 → 峰值激活约 ∝M\propto M;1F1B 在预热后交替执行一前一后,反向一旦完成就立刻释放激活 → 同时驻留约 PP 份。1F1B 还让前向通信与反向计算更容易重叠。

Q4 最可能:① 段间负载不均衡——某一段(常见是带 embedding 或 lm head 的首尾段)计算量/显存明显更大,拖住整条线;检查各段的前向 FLOPs、参数量与激活规模是否一致,必要时调整切分点。② micro-batch 太小导致算子效率低——M=64M=64 时每个 micro-batch 的矩阵乘很小,GPU 利用率下降;检查 profiler 中的 kernel 时长分布与 GPU 利用率曲线,试着减少 MM 或增大 BmicroB_{\text{micro}} 再比较。也可答「段间点对点通信未重叠」「PP 过大导致气泡与通信双增」。

延伸阅读
  • Huang et al., GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism(2019,arXiv:1811.06965)——micro-batch 填充流水线与气泡的原始分析(本节公式的来源)。
  • Narayanan et al., PipeDream: Generalized Pipeline Parallelism for DNN Training(2019,arXiv:1806.03377)——异步流水线与权重的多版本管理(另一种思路:用异步换气泡)。
  • Narayanan et al., Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM(2021,arXiv:2104.04473)——1F1B、交错调度与 TP×PP×DP 的组合分析,气泡公式在其中的表达与本节一致。
  • Qi et al., Zero Bubble Pipeline Parallelism(2024,arXiv:2401.10241)——把反向拆成输入梯度与权重梯度两步、用后者填气泡的调度;本节的 zero-bubble 小结即出自此文。
  • PyTorch 官方 torch.distributed.pipelining 文档(截至 2025 年)——ScheduleGPipe / Schedule1F1B 等接口语义(无对应论文,以官方文档为准)。

本节小结:流水线并行按层分段,用 micro-batch 错峰填满流水线;朴素调度下气泡占比 P−1M+P−1\dfrac{P-1}{M+P-1}(推导把它拆成「有效工作量 MM」与「墙钟 M+P−1M+P-1」两个量),MM 越大、PP 越小越好。三种压气泡手段分别是增大 MM、交错调度(等效段数变 P/VP/V)、zero-bubble(拆分反向填补空档),各自有明确代价。1F1B 不消灭气泡,但把峰值激活从 ∝M\propto M 降到 ∝P\propto P,是大模型的默认调度。下一节转向另一条线索:不切分、只把每一项做小——混合精度、激活重计算与量化。