跳到主要内容
推理优化

6.3 流水线并行推理:按层切段、Bubble 与跨节点扩展

PP 的层切分与段间通信、推理场景的 Bubble 与 Micro-batch 组织、PP 与 TP 的组合、vLLM 中 PP 的适用场景(跨节点与不均匀切分)

流水线并行Pipeline ParallelBubbleMicro-batch跨节点

6.2 结尾留下一个结论:跨节点的事交给 PP。这一节讲清楚 PP 是什么、为什么它适合跨节点、以及它的两个固有代价——Bubble(气泡)单请求延迟上升。推理场景里 PP 不是主角(TP 才是),但它是”模型大到单节点装不下”时的必经之路,也是理解 vLLM 启动参数 --pipeline-parallel-size 的钥匙。

📑 目录


1. 直觉:把模型按深度切成几段

TP 是”每一层内部切”,PP 是”层与层之间切”——把 N 层 Transformer 按深度分成 P 段,每段放在一张(或一组)GPU 上:

PP=4 的 32 层模型:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ GPU 0    │ │ GPU 1    │ │ GPU 2    │ │ GPU 3    │
│ 层 0-7   │→│ 层 8-15  │→│ 层 16-23 │→│ 层 24-31 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
    Embedding                LM Head + Softmax

每个 GPU 只存自己那段的权重(约 1/P 的模型参数量),段与段之间传递的是激活(hidden state),不是梯度。前向像接力棒一样从第 0 段传到第 P-1 段。

💡 提示:PP 和 TP 的切分维度完全不同——TP 切”宽度”(矩阵的列/行),PP 切”深度”(层)。直观记忆:TP 是横着切蛋糕,PP 是竖着切蛋糕。两者可以叠加(6.1 的 TP×PP),先横切再竖切。


2. 前向过程:接力棒式的层间传输

单个请求的完整前向:段 0 算完 → 把中间激活发给段 1 → 段 1 算 → …… → 段 P-1 输出 logits。

2.1 通信成本

段间通信发生在每一段边界,每次传的数据量是激活 [B,H][B, H](和 TP 的 AllReduce 数据量同量级),但频率远低于 TP

  • TP:每层 2 次 AllReduce(80 层模型 = 160 次)
  • PP:每段边界 1 次 P2P(PP=4 的 80 层模型 = 3 次)

这解释了 6.1 的结论:PP 的通信稀疏,配得上慢一个数量级的节点间网络。400Gb/s IB 下,单次激活传输 2×B×H2 \times B \times H 字节(Decode 场景 B=1B=1:32KB)耗时微秒级,几乎可以忽略。

2.2 显存分布

  • 权重显存:÷P(每段只存自己的层)
  • 激活显存:只存自己段的激活(但注意 PP 的激活是”整段”的,段内层数多时激活不小)
  • Embedding 和 LM Head:通常放在第 0 段和第 P-1 段,两段显存会比其他段多一块

📌 关键点:PP 的显存收益是”按段”的——段内的层共享一份激活空间,但权重严格 ÷P。和 TP 一样,权重显存线性降;和 TP 不同的是,PP 不要求节点内高速互联,因为它天然为跨节点设计。


3. Bubble:流水线的固有浪费

3.1 单个请求的悲剧:串行接力

如果一次只有一个请求,PP 的延迟是所有段延迟之和——单请求场景下 PP 比单卡还慢(多了段间通信),完全没有收益。PP 的收益来自多个请求(或 micro-batch)交错填充流水线

                时间 →
段 0: [MB1][MB2][MB3][MB4]░░░░░░░░░░░░░░░░░░░
段 1: ░░░░[MB1][MB2][MB3][MB4]░░░░░░░░░░░░░░░
段 2: ░░░░░░░░[MB1][MB2][MB3][MB4]░░░░░░░░░░░
段 3: ░░░░░░░░░░░░░░[MB1][MB2][MB3][MB4]░░░░░
       ├─填充期─┤              ├─排空期─┤
  • 填充期(Warm-up):前 P-1 个时间片,后面的段在空等(bubble)
  • 稳态:所有段都在忙
  • 排空期(Drain):最后 P-1 个时间片,前面的段空等

3.2 Bubble 的定量代价

设 micro-batch 数为 MM,段数为 PP,流水线吞吐效率:

Efficiency=M×PM×P+(P1)MM+1(P 较大时)\text{Efficiency} = \frac{M \times P}{M \times P + (P-1)} \approx \frac{M}{M+1} \quad (P \text{ 较大时})
  • M=4,P=4M=4, P=4:效率 =16/1984%= 16/19 \approx 84\%(16% 的 bubble)
  • M=16,P=4M=16, P=4:效率 =64/6795%= 64/67 \approx 95\%
  • MM \to \infty:效率 100%\to 100\%

💡 提示:结论——Bubble 靠”多请求”来填。训练里靠增大 micro-batch 数,推理里靠 Continuous Batching 的并发请求天然充当 micro-batch。这就是为什么低并发(空闲)场景下 PP 的 bubble 不可忽视,高并发场景下 PP 的效率反而不错

3.3 单请求延迟:PP 的第二个代价

一个请求从头到尾要走完 P 段:单请求延迟 ≈ P × 单段延迟(填充期 + 自己的计算)。所以:

  • 延迟视角:PP 让单请求变慢(×P 量级),与 TP 完全相反
  • 吞吐视角:高并发下 PP 的吞吐效率可以接近 100%

📌 关键点:PP 是”用延迟换容量”的策略——它解决的是”装不下”(容量),代价是单请求延迟上升。如果模型单节点能装下,永远优先 TP;PP 只在”单节点装不下”或”无 NVLink 节点”(6.1 的边缘情况)时才出场。


4. 推理场景的 PP:和训练哪里不一样

训练里的 PP 有复杂的调度(1F1B、interleaved 等,模块三讲过),推理场景简单得多,因为:

维度训练 PP推理 PP
反向传播有(需要对称的梯度流水)——只有前向,调度简化一半
Micro-batch 来源显式切分训练 batchContinuous Batching 的并发请求天然填充
调度目标吞吐 + 收敛吞吐 + 延迟均衡
显存要存反向用的激活只需前向激活(可以边算边丢)

💡 提示:推理 PP 没有反向传播,因此不需要 1F1B 那样的双向流水调度,实现和调优都比训练简单。这也是 vLLM 等推理引擎敢把 PP 做进 v1 引擎的原因之一。


5. 不均匀切分:PP 的隐藏能力

TP 要求权重矩阵能整除切分(hidden size 能被 TP 整除,通常没问题),但总层数不一定能被 GPU 数整除。PP 天然支持不均匀切分——比如 33 层模型放 4 张卡,可以切成 [9, 8, 8, 8] 或 [9, 9, 8, 7]。

vLLM 官方文档明确提到的边缘场景:

如果模型在单节点能放下,但 GPU 数量不能整除模型(或 GPU 间没有 NVLink 互联,如 L40S),可以设置 tensor_parallel_size=1pipeline_parallel_size=GPU 数,用 PP 替代 TP。

两个典型用例:

  1. 整除不了:TP 需要矩阵维度整除,PP 只要”层数能分”——几乎所有模型都能分
  2. 无 NVLink:L40S 这类 PCIe 互联的卡,TP 的每层 AllReduce 太贵,PP 的稀疏段间通信反而更优

📌 关键点:**“没有 NVLink 就用 PP”**是一条很实用的工程经验——它把 TP 的通信税(6.2 第 5 节)换成 PP 的 bubble 税,在低并发/高吞吐场景里往往更划算。判断标准:PCIe 互联(L40S、RTX 系列)→ PP;NVLink(A100/H100/B200 节点)→ TP。


6. TP×PP 组合:节点内张量并行、节点间流水线

超大规模模型的标配组合(6.1 的黄金配置):TP 锁节点内、PP 跨节点

以 2 节点 × 8 卡跑 700B 模型为例:

节点 A(8 卡,NVLink)          节点 B(8 卡,NVLink)
┌──────────────────────┐      ┌──────────────────────┐
│ TP=8 组:层 0-39      │  ←→  │ TP=8 组:层 40-79     │
│ 每层内部 2 次 AllReduce│ IB  │ 每层内部 2 次 AllReduce│
└──────────────────────┘      └──────────────────────┘
         PP 段间通信(每段边界 1 次,走 IB)
  • 节点内:TP=8 的 AllReduce 走 NVLink(900GB/s 级)
  • 节点间:PP 段间激活走 IB(400Gb/s ≈ 50GB/s),但每 token 只有 1 次传输

vLLM 中对应:--tensor-parallel-size 8 --pipeline-parallel-size 2(16 进程,world size = 16)。

💡 提示:组合时的 rank 排列遵循 6.1 的原则——TP 最内层(同节点内连续编号)、PP 外层(跨节点)。vLLM 启动时会按此自动分配进程到节点,用户需要保证 --nnodes 与节点数一致。


7. vLLM 中的 PP:配置与适用场景

7.1 配置

# 2 节点 × 8 卡,TP=8 + PP=2
vllm serve <model> \
    --tensor-parallel-size 8 \
    --pipeline-parallel-size 2 \
    --nnodes 2 \
    --distributed-executor-backend ray

Python 侧:LLM(model=..., tensor_parallel_size=8, pipeline_parallel_size=2)

7.2 适用场景清单

场景PP 是否合适说明
模型单节点装不下✅ 合适TP + PP 组合,PP 承载跨节点部分
GPU 数不能整除模型✅ 合适TP=1, PP=GPU 数(不均匀切分)
节点无 NVLink✅ 合适PP 替代 TP,通信税更低
单节点能装下、追求最低延迟❌ 不合适PP 增加单请求延迟,纯 TP 更好
低并发、延迟敏感❌ 不合适bubble 在低并发下占比高

7.3 与投机解码的兼容性

第 5.5 节提过:vLLM ≤0.15 不支持 PP + 投机解码组合,新版逐步放开,但PP 下的投机收益需要单独验证——PP 的段间传输给验证前向增加了额外延迟,接受率收益会被摊薄。

📌 关键点:vLLM 中 PP 的定位是”容量方案”而非”性能方案”——用它是因为装不下或互联受限,而不是因为它快。评估 PP 部署时盯两个数:显存是否装下(容量目标)高并发吞吐是否达标(bubble 是否被并发填满)


📝 总结

  • PP 的本质:按层深度切段,段间传激活(P2P),通信稀疏——为跨节点而生
  • 两个固有代价:Bubble(吞吐效率随 micro-batch 数提升)和单请求延迟上升(×P)
  • 推理 PP 更简单:没有反向传播,Continuous Batching 的并发请求天然填流水线
  • 隐藏能力:不均匀切分(层数不整除)、无 NVLink 节点(PP 替代 TP)
  • 黄金组合:TP=节点内 GPU 数、PP=节点数,TP 内层 PP 外层
  • vLLM--pipeline-parallel-size + --nnodes,定位是容量方案

🎯 自我检验清单

  • 用”横切/竖切”解释 TP 与 PP 的区别?
  • 为什么 PP 的通信适合跨节点而 TP 不适合?频率差多少倍?
  • Bubble 是怎么产生的?吞吐效率公式是什么?什么参数能填满它?
  • 为什么推理 PP 比训练 PP 简单?micro-batch 从哪来?
  • “GPU 数不能整除模型层数”和”无 NVLink”两个场景为什么用 PP?
  • TP×PP 组合时 rank 如何排列?为什么 TP 必须在内层?

📚 参考资料