想象你经营一家跑车厂。以前只有一位全能老师傅,从冲压钢板到喷漆总装一个人包圆,一天出不了几台车,还累得够呛——这就是单张 GPU 训练大模型的窘境:模型太大放不下,计算串行跑不快。后来你决定把产线拆成 4 个工位,每位师傅只负责一道工序,半成品车在传送带上依次流过,这就是“流水线并行”(Pipeline Parallelism)。大模型训练也是一条产线:神经网络的一层层就是一道道工序,前向传播是造车,反向传播是质检返修。本期主角是两种排班方案:GPipe 讲究“全厂同步、干完一起歇”,PipeDream 1F1B 讲究“造一辆检一辆、错峰出击”。前者简单稳定但气泡(等活干的时间)扎堆,后者把激活显存大幅降低。选对方案,你的“造车厂”吞吐可能差出几倍。

岗位关联

做大模型分布式训练的基础设施工程师,看懂 GPipe 与 1F1B 的取舍,才能给千亿模型选对流水线方案,把每张卡的算力榨干。

目录
  1. 可视化演示
  2. 1. 流水线基础:从“全能老师傅”到“接力产线”
  3. 2. GPipe 调度:全厂统一大轮班
  4. 3. Bubble 开销:师傅们喝咖啡的时间
  5. 4. 1F1B 调度:造一辆、检一辆
  6. 5. 方案对比:两位老师傅,两种脾气
  7. 6. Stage 划分:工位怎么分才不窝工
  8. 7. 混合并行:从一条产线到工厂集团
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

对比GPipe同步微批次的全前向-全反向调度与PipeDream异步1F1B交错调度,动态展示Stage流水线时间线、Bubble开销、显存占用和吞吐性能对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

GPipe 同步 反向 流水线并行

1. 流水线基础:从“全能老师傅”到“接力产线”

想象一家跑车厂只有一位全能老师傅。他要会冲压、焊接、喷漆、总装全套手艺,工具得备一整仓库,一天还造不出几台车。单卡训练大模型就是这个状态:一张 GPU 装下全部参数,显存吃紧,计算只能一串到底。

流水线并行(Pipeline Parallelism)的办法是把产线切开:模型按层切成 P 段,每段放一张 GPU,成为一个工位(Stage)。数据像半成品车,从第一个工位流入,逐段加工,最后流出。每张卡不再需要装下整个模型,显存压力立刻分散。

每个 Stage 只保存自己那几层的参数;不同工位还能同时加工不同微批次,形成接力。但代价是工位之间要搬运激活和梯度,通信成为新瓶颈。流水线并行不是白拿的收益,总时间是计算与通信的叠加:

一句话总结:把“全能师傅”换成“接力产线”,每位师傅只精通一道工序,工具不用备全套;但传送带必须够快,否则车堵在中转站,师傅照样喝咖啡。

T总=T计算+T通信\displaystyle T_{\text{总}} = T_{\text{计算}} + T_{\text{通信}}
公式说明:流水线并行把大计算拆成小计算接力,但每棒交接都要花通信时间;只有通信远小于省下的计算时间才划算。
  1. 总时间 T_总一个 batch 走完整条流水线(前向加后向)的墙钟时间
    T总\displaystyle T_{\text{总}}
  2. 计算时间 T_计算所有工位真正干活的总时长
    T计算\displaystyle T_{\text{计算}}
  3. 通信时间 T_通信工位之间搬运激活值和梯度的总时长
    T通信\displaystyle T_{\text{通信}}
  4. 工程含义Stage 切得越碎,通信项占比越高,省下的计算时间可能被通信吃掉
    T通信T总\displaystyle \frac{T_{\text{通信}}}{T_{\text{总}}}

2. GPipe 调度:全厂统一大轮班

GPipe 上任第一天定的规矩很简单:全厂同步,统一大轮班。它先把一个大 batch 切成 M 个更小的微批次(micro-batch),再让微批次一辆接一辆开上产线跑前向。

这套调度叫 fill-drain(填满-排空)。前向阶段,微批次灌满整条产线,所有工位都在造车;等最后一个微批次的前向跑完,全厂才齐刷刷切换成后向,从最后一个工位开始把梯度倒推回来。梯度到各工位后统一做参数更新,这就是“同步”的含义。

微批次是关键:如果不切,产线里一次只有一辆车,其他工位全部晒太阳;切成 M 份,流水线才能同时容纳多个微批次。但 GPipe 的后向必须等全部前向结束,启动和排空时大量工位闲着。一个完整周期的墙钟时间是:

GPipe 像一家纪律严明的工厂:先把一整车队的车全部造出来,再统一返修。管理简单,但排队等候的时间藏不住——这就是下一节的主角:气泡。

总时间=2(M+P−1)×t单步\displaystyle \text{总时间} = 2(M + P - 1) \times t_{\text{单步}}
公式说明:M+P-1 是前向阶段需要的步数,后向对称再来一遍所以翻倍;t_单步 是微批次在一个工位上前向或后向一步的耗时。
  1. 前向步数第一个微批次穿过 P 个工位要 P 步,之后每多一个微批次只多 1 步,共 M+P-1 步
    M+P−1\displaystyle M + P - 1
  2. 翻倍后向阶段与前向阶段对称,总步数乘以 2
    2(M+P−1)\displaystyle 2(M+P-1)
  3. 单步耗时每个微批次每经过一个工位,耗费的一步时间
    t单步\displaystyle t_{\text{单步}}
  4. 理想对照P 个工位全程满负荷的总活量是 2MP 步,实际墙钟时间更长,差额就是气泡
    2MP\displaystyle 2MP

3. Bubble 开销:师傅们喝咖啡的时间

气泡(bubble)是流水线工位等活干的空闲时间。GPipe 有两个明显的“咖啡时段”:开工时,最后一个工位的师傅要眼巴巴等前面把车传过来(启动气泡);收工时,第一个工位的活早干完了,却要等全厂质检倒推完(排空气泡)。

气泡比例可以精确算出来。设 P 为工位数、M 为微批次数,一个周期总步数 2(M+P-1),每个工位只干 2M 步,其余都在等。数学上:

气泡就是师傅们喝咖啡的时间。GPipe 的气泡集中成前后“两大段空闲”,一眼可见;要压气泡,要么加餐(增大 M),要么换更灵活的排班——PipeDream 的 1F1B 就是后者。

Bubble=1−MM+P−1=P−1M+P−1\displaystyle \text{Bubble} = 1 - \frac{M}{M+P-1} = \frac{P-1}{M+P-1}
公式说明:P 决定等多久,M 决定干多久。工位越多气泡越大,微批次越多气泡越小。
  1. 启动等待第一个微批次传遍 P 个工位需要 P 步,前 P-1 步里有工位在空等
    P−1\displaystyle P-1
  2. 周期步数一个周期共 M+P-1 个前向步,后向步数与之相同
    M+P−1\displaystyle M+P-1
  3. 增大 M微批次数越大,气泡比例越小,极限趋近于 0
    lim⁡M→∞P−1M+P−1=0\displaystyle \lim_{M\to\infty}\frac{P-1}{M+P-1}=0
  4. 数值手感P=4 时,M=8 气泡约 27%,M=4 时约 43%
    311≈27%,37≈43%\displaystyle \frac{3}{11}\approx27\%,\quad \frac{3}{7}\approx43\%

4. 1F1B 调度:造一辆、检一辆

PipeDream 提出另一种排班哲学:每个工位不必等全厂前向完工,而是“造一辆、检一辆”交错进行。最后一个工位拿到第一个微批次,立刻就能开始它的后向,不用等整条产线的车全部造完。这就是 1F1B(one-forward-one-backward,一前一后)。

这个节奏最大的收益在内存。GPipe 前向阶段要把 M 个微批次的激活全部存着,等后向慢慢用;1F1B 里每个微批次的后向紧随其后,激活算完即释放,在途微批次数量从 O(M) 降到约 O(P)。代价是节奏错位可能引入权重版本(weight versioning),权重需要多存几份:

1F1B 像小作坊的错峰排班:车到手就开工,质检完立刻腾空仓库。相同配置下它的气泡占比与 GPipe 一样,但省下的显存能支撑更大的模型和批次——这才是它的杀手锏。

Mem1F1BMemGPipe≈PM\displaystyle \frac{\text{Mem}_{1F1B}}{\text{Mem}_{GPipe}} \approx \frac{P}{M}
公式说明:激活内存峰值从“微批次数量”降到“工位数量”,M 比 P 大越多,省得越多。
  1. GPipe 峰值前向阶段同时保存 M 份激活,内存 O(M)
    MemGPipe=O(M)\displaystyle \text{Mem}_{GPipe}=O(M)
  2. 1F1B 峰值只保留流水线深度内的在途激活,内存 O(P)
    Mem1F1B=O(P)\displaystyle \text{Mem}_{1F1B}=O(P)
  3. 内存比值峰值比约等于 P 除以 M,省显存幅度一目了然
    PM\displaystyle \frac{P}{M}
  4. 权重版本代价异步节奏下保证梯度一致性,需要保存多份权重
    Mem权重版本=O(P×W)\displaystyle \text{Mem}_{\text{权重版本}}=O(P\times W)

5. 方案对比:两位老师傅,两种脾气

把两种方案摆在一起,像极了左撇子和右撇子的两位老师傅:干活总量差不多,脾气和家当完全不同。

相同 P、M 下,两者吞吐(墙钟时间)几乎打平,理论气泡占比都是 (P-1)/(M+P-1);分水岭在显存:GPipe 激活 O(M),1F1B 约 O(P)。实现上 GPipe 简单直接、天然适配同步梯度;1F1B 调度器更精细、对工程师更挑剔。算力利用率公式为:

所以选方案不看出力,看出库房和工期:显存吃紧、模型巨大选 1F1B;求稳、代码要快速上线选 GPipe。

利用率=2MP2(M+P−1)=MM+P−1\displaystyle \text{利用率} = \frac{2MP}{2(M+P-1)} = \frac{M}{M+P-1}
公式说明:理想满负荷是 2MP 步活,实际周期 2(M+P-1) 步,两者相除就是算力利用率,与调度方案无关。
  1. 理想工作量P 个工位各干 M 次前向和 M 次后向,共 2MP 步
    2MP\displaystyle 2MP
  2. 实际周期填满-排空的总步数,两种方案在相同配置下相同
    2(M+P−1)\displaystyle 2(M+P-1)
  3. 利用率两种方案数值相同,吞吐打平
    MM+P−1\displaystyle \frac{M}{M+P-1}

6. Stage 划分:工位怎么分才不窝工

排班再好,工位分得歪七扭八也白搭。Stage 划分就是决定把哪些层放到哪张卡,相当于给产线画工位边界:每道工序耗时接近,产线才平滑。

核心原则是负载均衡:让每个工位的计算时间尽量相等。产线的总节拍由最慢工位决定,谁最慢谁就是瓶颈。不能简单按层数分,因为 attention、MLP、embedding 的计算量差异很大;还要考虑通信边界,把跨机通信放在高带宽链路(如机内 NVLink)上。实践中用 profiling 测出每层耗时,再做切分:

工位分得好,每个师傅手头都有活;分得差,有人忙死有人闲死,闲人的咖啡时间全厂买单——Stage 划分就是替老板省这杯咖啡钱。

节拍=max⁡i  ti+c\displaystyle \text{节拍} = \max_{i} \; t_i + c
公式说明:整条产线的速度等于最慢工位的时间加上通信开销,其余工位再快也得跟着等。
  1. 工位耗时第 i 个工位的耗时等于它的计算量除以卡算力
    ti=计算量i算力i\displaystyle t_i = \frac{\text{计算量}_i}{\text{算力}_i}
  2. 瓶颈总节拍取所有 t_i 的最大值,优化先打最慢工位
    max⁡iti\displaystyle \max_i t_i
  3. 通信项跨工位传递激活与梯度的时间 c 也要计入
    +  c\displaystyle + \; c

7. 混合并行:从一条产线到工厂集团

模型到了千亿参数,一条流水线已经塞不下,得像跨国集团一样开分厂:数据并行、张量并行、流水线并行三个维度叠加,这就是常说的 3D 并行。

数据并行复制整条产线,各分厂吃不同数据、定期同步梯度;张量并行把单层内的矩阵乘法拆给多张卡,相当于把一台巨型设备换成一组联动机器;流水线并行把模型按层切成工位。三者相乘就是总卡数,全局 batch 则由数据并行度放大:

3D 并行就是从“一条产线”升级成“全国连锁工厂”:每家分厂有一条流水线(P),产线上每台大设备由多台机器协同(T),所有分厂接不同订单(D)。GPipe 与 1F1B 的调度博弈,在这里被放大了几百倍。

S=D×P×T,global_bs=D×M×m\displaystyle S = D \times P \times T, \qquad \text{global\_bs} = D \times M \times m
公式说明:总卡数 = 数据并行度 × 流水线深度 × 张量并行度;全局 batch 由数据并行度放大,m 是单个微批次的样本数。
  1. 总卡数三个并行度相乘得到集群总卡数
    S=D×P×T\displaystyle S = D \times P \times T
  2. 全局批次每路数据流有 M 个微批次、每个 m 个样本,乘上 D 路复制
    global_bs=D×M×m\displaystyle \text{global\_bs} = D \times M \times m
  3. 典型规模T=8、P=32、D=16 时共 4096 卡
    8×32×16=4096\displaystyle 8 \times 32 \times 16 = 4096
  4. 配置顺序先定 T(受单机卡数限制),再按显存定 P,最后用 D 扩规模
    T→P→D\displaystyle T \to P \to D

AI 应用场景

场景1:千亿参数 LLM 的三维并行训练

阶段:模型训练(分布式框架选型与集群配置)

解决的问题:单卡显存放不下千亿模型,纯数据并行梯度通信爆炸。用 T×P×D 组合把模型拆开,再用 1F1B 压低激活显存,产线才能跑得动。

具体案例:某团队训练 175B 参数模型,采用 Megatron-DeepSpeed 框架,配置 T=8(机内张量并行)、P=32(跨机流水线)、D=16(数据并行)。流水线内部用 1F1B 调度,把激活峰值从 O(16) 压到 O(32) 的量级,模型算力利用率 MFU 冲到 40% 以上。

场景2:长序列微调的内存急救

阶段:模型微调(序列长度与显存权衡)

解决的问题:长序列的激活值巨大,GPipe 的 O(M) 激活在 8 卡小集群上直接 OOM;换 1F1B 后激活峰值降到 O(P),序列长度可以从 4K 拉到 8K。

具体案例:某算法团队在 8×A100(80G)上微调 7B 模型,采用 P=4 流水线加 1F1B 调度,关闭激活重计算,sequence length 4096 训练成功,batch size 比 GPipe 方案放大一倍,收敛步数明显减少。

Python 演示

python46 行
# -*- coding: utf-8 -*-"""流水线并行调度对比:GPipe 气泡模拟 vs 1F1B 内存估算"""  def build_gpipe(P, M):    """构造 GPipe 的 fill-drain 调度表,返回 (调度表, 总时间步)"""    total = 2 * (M + P - 1)    table = [['.'] * total for _ in range(P)]    for s in range(P):        for j in range(M):            table[s][s + j] = 'F'            table[s][M + P - 1 + (P - 1 - s) + j] = 'B'    return table, total  def bubble_ratio(table, total):    """统计调度表中气泡(.)占比,即空闲时间步 / 总时间步"""    work = sum(1 for row in table for c in row if c in 'FB')    return 1.0 - work / (len(table) * total)  def bubble_formula(P, M):    """理论气泡比例 (P-1)/(M+P-1)"""    return (P - 1) / (M + P - 1)  def memory_ratio(P, M):    """1F1B 相对 GPipe 的激活峰值比约 P/M"""    return P / M  def main():    P, M = 4, 8    table, total = build_gpipe(P, M)    print('GPipe 调度表 (P=4, M=8, .表示气泡)')    for row in table:        print(''.join(row))    sim = bubble_ratio(table, total)    theory = bubble_formula(P, M)    print('实际气泡比例: %.3f' % sim)    print('理论气泡比例: %.3f' % theory)    print('1F1B 激活峰值约为 GPipe 的 %.1f%%' % (memory_ratio(P, M) * 100))  if __name__ == '__main__':    main()
目的:构造 GPipe 的调度表并算出气泡比例,与理论公式对照验证;同时估算 1F1B 相对 GPipe 的激活内存优势。
  1. table[s][s + j] = 'F'前向阶段,第 j 个微批次在第 s 个工位的时间步就是 s+j,构成调度表上的对角线。
  2. table[s][M + P - 1 + (P - 1 - s) + j] = 'B'后向从周期中点开始,工位越靠前后向越晚,形成反方向对角线。
  3. bubble_ratio(table, total)统计非气泡格子的占比后取补集,直接验证理论气泡公式。
  4. bubble_formula(P, M)理论公式 (P-1)/(M+P-1),代码输出应与模拟结果吻合。
  5. memory_ratio(P, M)1F1B 相对 GPipe 的激活峰值比 P/M,M 越大省得越多。

常见误区

误区流水线并行和数据并行差不多,把模型复制到每张卡上,各卡算不同数据,最后同步梯度就行。

正解流水线并行是把模型按层切开,每张卡只负责一部分层,数据像车过流水线一样依次经过各 Stage;数据并行才是复制模型、切分数据。类比:数据并行是 4 条产线同时造 4 辆车,流水线并行是 1 条产线拆成 4 个工位接力造一辆车。

为何常见:都是“多卡训练”,初学者容易只从“卡变多了”去理解,忽略了切分维度(数据 vs 模型)的本质差异。

误区把 batch 切得越碎,流水线填得越满,bubble 越小,所以 M 越大越好,反正只是多切几刀。

正解M 增大会降低气泡比例,但 GPipe 前向阶段要同时保存 M 份激活,激活显存随 M 线性增长,显存可能爆炸;微批次过小还会降低单卡计算效率和梯度质量。类比:把一道菜切成一万颗米粒也能让每口都“在嚼”,但厨师光切菜就累死了。

为何常见:只看气泡公式随 M 单调递减,忽略了内存和计算效率的另一面。

误区1F1B 前向后向交错执行,工位看起来一直在干活,吞吐肯定比 GPipe 高很多。

正解在相同 P 和 M 下,1F1B 的气泡占比与 GPipe 相同(都是 (P-1)/(M+P-1)),它的真正优势是激活内存更低(峰值约 O(P) 而非 O(M)),省下的显存可以支撑更大的模型和 batch。类比:两条产线节拍一样,1F1B 赢在仓库小、周转快,而不是工位跑得快。

为何常见:把“看起来没有大片空闲”误当成“没有气泡”,没有按时间步数精确计算。

误区模型有 48 层、4 张卡,每张卡分 12 层就完事了。

正解不同层的计算量差异很大(attention 和 MLP 的 FLOPs 不同),跨卡通信还要考虑带宽。正确做法是用 profiling 测出每层耗时,再做负载均衡,让最慢工位的耗时最小化。类比:流水线上拧螺丝和喷漆的耗时不同,光按零件个数分工位,会有人闲死有人忙死。

为何常见:默认“层数=计算量”是线性关系,忽略了层类型和通信开销的差异。

面试题

Q1. 流水线并行主要把大模型按什么维度切分到多张 GPU 上?

单选题 · easy

  • A.按训练数据的批次切分
  • B.按神经网络的不同层切分← 正确答案
  • C.按梯度的大小切分
  • D.按参数的随机种子切分

答案:B。流水线并行是把模型按层切成 P 段,每张卡负责一段工序,数据像车过流水线一样依次流经各 Stage。按数据切分是数据并行做的事,按梯度或随机种子切分没有意义。类比工厂里按工序分工位,而不是按订单分工位。

Q2. 在 GPipe 中引入“微批次”(micro-batch)的主要作用是什么?

单选题 · easy

  • A.减少模型的参数量
  • B.让各工位手里有活← 正确答案
  • C.增大通信带宽
  • D.减少显存总量

答案:B。如果不切微批次,产线里一次只有一辆车,其他工位全部空闲。切成 M 个微批次后,流水线能同时容纳多个微批次,各 Stage 可以并行干活。微批次不会减少参数量,也不改变通信带宽,只是把大 batch 拆小依次注入。

Q3. 当流水线深度 P=4、微批次数 M=8 时,GPipe 的理论气泡比例约为多少?

单选题 · mid

  • A.约 25%
  • B.约 27%← 正确答案
  • C.约 33%
  • D.约 50%

答案:B。气泡比例 = (P-1)/(M+P-1) = 3/11 ≈ 27%。A 是把 3/12 当结果的感觉值,C 是把 (P-1)/M 误算成 3/8=37.5% 但选项不匹配,D 是 M 很小时的极端值。记住公式:分母是周期步数,分子是启动等待步数。

Q4. 增大微批次数 M 对 GPipe 的影响是?

单选题 · mid

  • A.气泡下降且内存上升← 正确答案
  • B.气泡上升且内存下降
  • C.气泡与内存都下降
  • D.气泡与内存都上升

答案:A。气泡比例 (P-1)/(M+P-1) 随 M 增大而下降;但 GPipe 前向阶段要同时保存 M 份激活,激活显存随 M 线性上升。这就是“微批次越大越好”误区背后的真实权衡:省了时间,费了显存。

Q5. 关于 PipeDream 1F1B 调度,下列说法正确的有?

多选题 · mid

  • A.每个工位一前一后交替计算← 正确答案
  • B.前向保存全部微批次激活
  • C.内存峰值约 O(P) 级别← 正确答案
  • D.全部前向结束才开后向

答案:AC。1F1B 的核心是“造一辆检一辆”:每个工位在稳态下交替执行前向和后向,激活随用随释放,内存峰值约 O(P)。B 和 D 是 GPipe fill-drain 的特征:前向阶段保存所有 M 份激活,全部前向完成才统一后向。

Q6. 与 GPipe 相比,1F1B 最核心的优势是什么?

单选题 · mid

  • A.吞吐大幅提升
  • B.激活内存更低← 正确答案
  • C.通信量更小
  • D.无需权重同步

答案:B。相同 P、M 下两者气泡占比相同,吞吐几乎打平,1F1B 的真正优势是激活内存从 O(M) 降到 O(P)。省下的显存可以支撑更大的模型和 batch。注意它并非通信更小,也不省权重同步,甚至可能要多存权重版本。

Q7. 划分流水线 Stage 时首要原则是什么?

单选题 · easy

  • A.各 Stage 计算量均衡← 正确答案
  • B.每个 Stage 层数最多
  • C.首 Stage 层数最多
  • D.末 Stage 层数最少

答案:A。产线总节拍由最慢工位决定,所以各 Stage 计算时间要尽量均衡。层数多不等于计算量大(attention 和 MLP 差异很大),所以不能光数层数。类比:工位分得不均,有人忙死有人闲死,全厂跟着最慢的师傅走。

Q8. 关于三维混合并行 S = D×P×T,下列说法正确的有?

多选题 · hard

  • A.总卡数等于三者的乘积← 正确答案
  • B.全局批次由数据并行决定← 正确答案
  • C.张量并行受单机卡数限制← 正确答案
  • D.三者之间互不约束

答案:ABC。总卡数 = 数据并行度 × 流水线深度 × 张量并行度;global_bs = D×M×m,由数据并行度放大;张量并行度受单机卡数和机内互联限制。D 错误:三者相互约束,比如 T 不能超过单机卡数,P 受显存和通信影响。

Q9. 当微批次数 M 远大于流水线深度 P 时,GPipe 气泡比例的近似值是?

单选题 · hard

  • A.(P-1)/M← 正确答案
  • B.M/(P-1)
  • C.(P-1)/(M+P)
  • D.恒等于 50%

答案:A。当 M 远大于 P 时,分母 M+P-1 约等于 M,气泡比例 ≈ (P-1)/M。这也是实践中“增大微批次数压气泡”的理论依据。B 在 M≫P 时远大于 1,显然不可能;C 的近似不准确;D 只在特定 M 下成立。

Q10. 在相同 P 和 M 下,GPipe 与 1F1B 的理论气泡比例关系是?

单选题 · extreme

  • A.GPipe 的气泡更小
  • B.1F1B 的气泡更小
  • C.两者气泡比例相同← 正确答案
  • D.气泡与调度方式无关

答案:C。相同 P、M 下两者的气泡占比都是 (P-1)/(M+P-1),吞吐几乎持平;区别在内存:1F1B 激活峰值 O(P),GPipe 激活峰值 O(M)。这是最反直觉也最常考的点——1F1B 赢在显存,不是赢在气泡。