想象你经营一家跑车厂。以前只有一位全能老师傅,从冲压钢板到喷漆总装一个人包圆,一天出不了几台车,还累得够呛——这就是单张 GPU 训练大模型的窘境:模型太大放不下,计算串行跑不快。后来你决定把产线拆成 4 个工位,每位师傅只负责一道工序,半成品车在传送带上依次流过,这就是“流水线并行”(Pipeline Parallelism)。大模型训练也是一条产线:神经网络的一层层就是一道道工序,前向传播是造车,反向传播是质检返修。本期主角是两种排班方案:GPipe 讲究“全厂同步、干完一起歇”,PipeDream 1F1B 讲究“造一辆检一辆、错峰出击”。前者简单稳定但气泡(等活干的时间)扎堆,后者把激活显存大幅降低。选对方案,你的“造车厂”吞吐可能差出几倍。
做大模型分布式训练的基础设施工程师,看懂 GPipe 与 1F1B 的取舍,才能给千亿模型选对流水线方案,把每张卡的算力榨干。
目录
可视化演示
对比GPipe同步微批次的全前向-全反向调度与PipeDream异步1F1B交错调度,动态展示Stage流水线时间线、Bubble开销、显存占用和吞吐性能对比。
本节配有交互动效(进入视口后自动加载,约 3KB)。
GPipe 同步 反向 流水线并行
1. 流水线基础:从“全能老师傅”到“接力产线”
想象一家跑车厂只有一位全能老师傅。他要会冲压、焊接、喷漆、总装全套手艺,工具得备一整仓库,一天还造不出几台车。单卡训练大模型就是这个状态:一张 GPU 装下全部参数,显存吃紧,计算只能一串到底。
流水线并行(Pipeline Parallelism)的办法是把产线切开:模型按层切成 P 段,每段放一张 GPU,成为一个工位(Stage)。数据像半成品车,从第一个工位流入,逐段加工,最后流出。每张卡不再需要装下整个模型,显存压力立刻分散。
每个 Stage 只保存自己那几层的参数;不同工位还能同时加工不同微批次,形成接力。但代价是工位之间要搬运激活和梯度,通信成为新瓶颈。流水线并行不是白拿的收益,总时间是计算与通信的叠加:
一句话总结:把“全能师傅”换成“接力产线”,每位师傅只精通一道工序,工具不用备全套;但传送带必须够快,否则车堵在中转站,师傅照样喝咖啡。
- 总时间 T_总一个 batch 走完整条流水线(前向加后向)的墙钟时间
- 计算时间 T_计算所有工位真正干活的总时长
- 通信时间 T_通信工位之间搬运激活值和梯度的总时长
- 工程含义Stage 切得越碎,通信项占比越高,省下的计算时间可能被通信吃掉
2. GPipe 调度:全厂统一大轮班
GPipe 上任第一天定的规矩很简单:全厂同步,统一大轮班。它先把一个大 batch 切成 M 个更小的微批次(micro-batch),再让微批次一辆接一辆开上产线跑前向。
这套调度叫 fill-drain(填满-排空)。前向阶段,微批次灌满整条产线,所有工位都在造车;等最后一个微批次的前向跑完,全厂才齐刷刷切换成后向,从最后一个工位开始把梯度倒推回来。梯度到各工位后统一做参数更新,这就是“同步”的含义。
微批次是关键:如果不切,产线里一次只有一辆车,其他工位全部晒太阳;切成 M 份,流水线才能同时容纳多个微批次。但 GPipe 的后向必须等全部前向结束,启动和排空时大量工位闲着。一个完整周期的墙钟时间是:
GPipe 像一家纪律严明的工厂:先把一整车队的车全部造出来,再统一返修。管理简单,但排队等候的时间藏不住——这就是下一节的主角:气泡。
- 前向步数第一个微批次穿过 P 个工位要 P 步,之后每多一个微批次只多 1 步,共 M+P-1 步
- 翻倍后向阶段与前向阶段对称,总步数乘以 2
- 单步耗时每个微批次每经过一个工位,耗费的一步时间
- 理想对照P 个工位全程满负荷的总活量是 2MP 步,实际墙钟时间更长,差额就是气泡
3. Bubble 开销:师傅们喝咖啡的时间
气泡(bubble)是流水线工位等活干的空闲时间。GPipe 有两个明显的“咖啡时段”:开工时,最后一个工位的师傅要眼巴巴等前面把车传过来(启动气泡);收工时,第一个工位的活早干完了,却要等全厂质检倒推完(排空气泡)。
气泡比例可以精确算出来。设 P 为工位数、M 为微批次数,一个周期总步数 2(M+P-1),每个工位只干 2M 步,其余都在等。数学上:
气泡就是师傅们喝咖啡的时间。GPipe 的气泡集中成前后“两大段空闲”,一眼可见;要压气泡,要么加餐(增大 M),要么换更灵活的排班——PipeDream 的 1F1B 就是后者。
- 启动等待第一个微批次传遍 P 个工位需要 P 步,前 P-1 步里有工位在空等
- 周期步数一个周期共 M+P-1 个前向步,后向步数与之相同
- 增大 M微批次数越大,气泡比例越小,极限趋近于 0
- 数值手感P=4 时,M=8 气泡约 27%,M=4 时约 43%
4. 1F1B 调度:造一辆、检一辆
PipeDream 提出另一种排班哲学:每个工位不必等全厂前向完工,而是“造一辆、检一辆”交错进行。最后一个工位拿到第一个微批次,立刻就能开始它的后向,不用等整条产线的车全部造完。这就是 1F1B(one-forward-one-backward,一前一后)。
这个节奏最大的收益在内存。GPipe 前向阶段要把 M 个微批次的激活全部存着,等后向慢慢用;1F1B 里每个微批次的后向紧随其后,激活算完即释放,在途微批次数量从 O(M) 降到约 O(P)。代价是节奏错位可能引入权重版本(weight versioning),权重需要多存几份:
1F1B 像小作坊的错峰排班:车到手就开工,质检完立刻腾空仓库。相同配置下它的气泡占比与 GPipe 一样,但省下的显存能支撑更大的模型和批次——这才是它的杀手锏。
- GPipe 峰值前向阶段同时保存 M 份激活,内存 O(M)
- 1F1B 峰值只保留流水线深度内的在途激活,内存 O(P)
- 内存比值峰值比约等于 P 除以 M,省显存幅度一目了然
- 权重版本代价异步节奏下保证梯度一致性,需要保存多份权重
5. 方案对比:两位老师傅,两种脾气
把两种方案摆在一起,像极了左撇子和右撇子的两位老师傅:干活总量差不多,脾气和家当完全不同。
相同 P、M 下,两者吞吐(墙钟时间)几乎打平,理论气泡占比都是 (P-1)/(M+P-1);分水岭在显存:GPipe 激活 O(M),1F1B 约 O(P)。实现上 GPipe 简单直接、天然适配同步梯度;1F1B 调度器更精细、对工程师更挑剔。算力利用率公式为:
所以选方案不看出力,看出库房和工期:显存吃紧、模型巨大选 1F1B;求稳、代码要快速上线选 GPipe。
- 理想工作量P 个工位各干 M 次前向和 M 次后向,共 2MP 步
- 实际周期填满-排空的总步数,两种方案在相同配置下相同
- 利用率两种方案数值相同,吞吐打平
6. Stage 划分:工位怎么分才不窝工
排班再好,工位分得歪七扭八也白搭。Stage 划分就是决定把哪些层放到哪张卡,相当于给产线画工位边界:每道工序耗时接近,产线才平滑。
核心原则是负载均衡:让每个工位的计算时间尽量相等。产线的总节拍由最慢工位决定,谁最慢谁就是瓶颈。不能简单按层数分,因为 attention、MLP、embedding 的计算量差异很大;还要考虑通信边界,把跨机通信放在高带宽链路(如机内 NVLink)上。实践中用 profiling 测出每层耗时,再做切分:
工位分得好,每个师傅手头都有活;分得差,有人忙死有人闲死,闲人的咖啡时间全厂买单——Stage 划分就是替老板省这杯咖啡钱。
- 工位耗时第 i 个工位的耗时等于它的计算量除以卡算力
- 瓶颈总节拍取所有 t_i 的最大值,优化先打最慢工位
- 通信项跨工位传递激活与梯度的时间 c 也要计入
7. 混合并行:从一条产线到工厂集团
模型到了千亿参数,一条流水线已经塞不下,得像跨国集团一样开分厂:数据并行、张量并行、流水线并行三个维度叠加,这就是常说的 3D 并行。
数据并行复制整条产线,各分厂吃不同数据、定期同步梯度;张量并行把单层内的矩阵乘法拆给多张卡,相当于把一台巨型设备换成一组联动机器;流水线并行把模型按层切成工位。三者相乘就是总卡数,全局 batch 则由数据并行度放大:
3D 并行就是从“一条产线”升级成“全国连锁工厂”:每家分厂有一条流水线(P),产线上每台大设备由多台机器协同(T),所有分厂接不同订单(D)。GPipe 与 1F1B 的调度博弈,在这里被放大了几百倍。
- 总卡数三个并行度相乘得到集群总卡数
- 全局批次每路数据流有 M 个微批次、每个 m 个样本,乘上 D 路复制
- 典型规模T=8、P=32、D=16 时共 4096 卡
- 配置顺序先定 T(受单机卡数限制),再按显存定 P,最后用 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 演示
# -*- 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()
table[s][s + j] = 'F'前向阶段,第 j 个微批次在第 s 个工位的时间步就是 s+j,构成调度表上的对角线。table[s][M + P - 1 + (P - 1 - s) + j] = 'B'后向从周期中点开始,工位越靠前后向越晚,形成反方向对角线。bubble_ratio(table, total)统计非气泡格子的占比后取补集,直接验证理论气泡公式。bubble_formula(P, M)理论公式 (P-1)/(M+P-1),代码输出应与模拟结果吻合。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 上?
- A.按训练数据的批次切分
- B.按神经网络的不同层切分← 正确答案
- C.按梯度的大小切分
- D.按参数的随机种子切分
答案:B。流水线并行是把模型按层切成 P 段,每张卡负责一段工序,数据像车过流水线一样依次流经各 Stage。按数据切分是数据并行做的事,按梯度或随机种子切分没有意义。类比工厂里按工序分工位,而不是按订单分工位。
Q2. 在 GPipe 中引入“微批次”(micro-batch)的主要作用是什么?
- A.减少模型的参数量
- B.让各工位手里有活← 正确答案
- C.增大通信带宽
- D.减少显存总量
答案:B。如果不切微批次,产线里一次只有一辆车,其他工位全部空闲。切成 M 个微批次后,流水线能同时容纳多个微批次,各 Stage 可以并行干活。微批次不会减少参数量,也不改变通信带宽,只是把大 batch 拆小依次注入。
Q3. 当流水线深度 P=4、微批次数 M=8 时,GPipe 的理论气泡比例约为多少?
- 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 的影响是?
- A.气泡下降且内存上升← 正确答案
- B.气泡上升且内存下降
- C.气泡与内存都下降
- D.气泡与内存都上升
答案:A。气泡比例 (P-1)/(M+P-1) 随 M 增大而下降;但 GPipe 前向阶段要同时保存 M 份激活,激活显存随 M 线性上升。这就是“微批次越大越好”误区背后的真实权衡:省了时间,费了显存。
Q5. 关于 PipeDream 1F1B 调度,下列说法正确的有?
- A.每个工位一前一后交替计算← 正确答案
- B.前向保存全部微批次激活
- C.内存峰值约 O(P) 级别← 正确答案
- D.全部前向结束才开后向
答案:AC。1F1B 的核心是“造一辆检一辆”:每个工位在稳态下交替执行前向和后向,激活随用随释放,内存峰值约 O(P)。B 和 D 是 GPipe fill-drain 的特征:前向阶段保存所有 M 份激活,全部前向完成才统一后向。
Q6. 与 GPipe 相比,1F1B 最核心的优势是什么?
- A.吞吐大幅提升
- B.激活内存更低← 正确答案
- C.通信量更小
- D.无需权重同步
答案:B。相同 P、M 下两者气泡占比相同,吞吐几乎打平,1F1B 的真正优势是激活内存从 O(M) 降到 O(P)。省下的显存可以支撑更大的模型和 batch。注意它并非通信更小,也不省权重同步,甚至可能要多存权重版本。
Q7. 划分流水线 Stage 时首要原则是什么?
- A.各 Stage 计算量均衡← 正确答案
- B.每个 Stage 层数最多
- C.首 Stage 层数最多
- D.末 Stage 层数最少
答案:A。产线总节拍由最慢工位决定,所以各 Stage 计算时间要尽量均衡。层数多不等于计算量大(attention 和 MLP 差异很大),所以不能光数层数。类比:工位分得不均,有人忙死有人闲死,全厂跟着最慢的师傅走。
Q8. 关于三维混合并行 S = D×P×T,下列说法正确的有?
- A.总卡数等于三者的乘积← 正确答案
- B.全局批次由数据并行决定← 正确答案
- C.张量并行受单机卡数限制← 正确答案
- D.三者之间互不约束
答案:ABC。总卡数 = 数据并行度 × 流水线深度 × 张量并行度;global_bs = D×M×m,由数据并行度放大;张量并行度受单机卡数和机内互联限制。D 错误:三者相互约束,比如 T 不能超过单机卡数,P 受显存和通信影响。
Q9. 当微批次数 M 远大于流水线深度 P 时,GPipe 气泡比例的近似值是?
- 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 的理论气泡比例关系是?
- A.GPipe 的气泡更小
- B.1F1B 的气泡更小
- C.两者气泡比例相同← 正确答案
- D.气泡与调度方式无关
答案:C。相同 P、M 下两者的气泡占比都是 (P-1)/(M+P-1),吞吐几乎持平;区别在内存:1F1B 激活峰值 O(P),GPipe 激活峰值 O(M)。这是最反直觉也最常考的点——1F1B 赢在显存,不是赢在气泡。