欢迎光临

大模型推理中的流水线并行(Pipeline Parallelism)深度解析:从 PP 切分原理到 vLLM 多卡流水线部署与气泡优化实战

当大模型参数量突破单卡显存上限时,工程师首先想到的是张量并行(Tensor Parallelism, TP)——把每一层的权重切分到多张卡上,所有卡同时计算同一层的不同部分。但 TP 有一个致命瓶颈:每层都需要 All-Reduce 通信,卡数越多通信开销越大,通常超过 8 卡后扩展效率急剧下降。这时候,流水线并行(Pipeline Parallelism, PP)就成了关键武器——它把模型的不同层分配到不同卡上,卡之间只在层边界传递激活值,通信量与卡数无关。在百亿、千亿参数模型的推理部署中,PP 与 TP 的组合使用是工业界标准方案。

GPU流水线并行推理部署架构示意图

本文将从流水线并行的底层原理出发,深入解析 PP 的切分策略、气泡(Bubble)问题及优化方案,并通过 vLLM 的实际配置演示如何在多卡环境下部署流水线并行推理服务。

一、流水线并行的核心原理:把模型当作流水线

流水线并行的思想非常直观:想象一条工厂流水线,产品依次经过工位 1、工位 2、工位 3……每个工位只负责一道工序。在 LLM 推理中,我们把 Transformer 模型的 L 层按顺序切分到 N 张 GPU 上。例如一个 80 层的模型部署在 4 张卡上,GPU 0 负责第 0-19 层,GPU 1 负责第 20-39 层,GPU 2 负责第 40-59 层,GPU 3 负责第 60-79 层。

前向传播时,输入 token 经过 GPU 0 的 20 层计算后,将中间激活值(activation)通过点对点通信发送给 GPU 1,GPU 1 继续计算 20 层后传给 GPU 2,以此类推。与 TP 不同,PP 的通信只在相邻卡之间发生,且通信内容仅为激活值(而非梯度和权重碎片),通信量与卡数无关。

1.1 PP 与 TP 的本质区别

维度 张量并行 (TP) 流水线并行 (PP)
切分对象 每层的权重矩阵 不同的层(Layer 级别)
通信模式 每层 All-Reduce 层边界点对点 Send/Recv
通信量 随卡数线性增长 与卡数无关(固定为激活值大小)
显存节省 每层权重 ÷ N 总层数 ÷ N,每卡只存部分层
扩展上限 通常 ≤ 8 卡(受通信限制) 可达数十卡
延迟特性 低延迟(并行计算) 较高延迟(串行流水线)
带宽要求 高(需要 NVLink 级带宽) 低(PCIe 即可满足)

关键洞察:TP 适合在同一个节点的 NVLink 互联域内使用(通常 4-8 卡),而 PP 可以跨节点部署,因为 PP 的点对点通信只需要传输激活值,对带宽要求远低于 TP 的 All-Reduce。这就是为什么千亿参数模型的典型部署方案是 TP=8(节点内)× PP=N(跨节点)。

二、气泡问题:流水线并行的最大痛点

流水线并行的最大问题是流水线气泡(Pipeline Bubble)——在流水线填充和排空阶段,部分 GPU 处于空闲状态。理解气泡的成因是优化 PP 性能的前提。

2.1 简单流水线(GPipe 方式)的气泡分析

假设模型被切分到 4 张卡(PP=4),一个 batch 的前向传播需要依次经过 GPU 0→1→2→3。在朴素流水线中,必须等整个 batch 的前向传播完成后才能开始下一个 batch,这会产生大量空闲时间:


1
2
3
4
5
6
7
8
时间 →
GPU 0: [====前向====][          空闲          ][====下一个batch====]
GPU 1:    [空闲][====前向====][          空闲          ]
GPU 2:         [空闲][====前向====][          空闲      ]
GPU 3:              [空闲][====前向====][          空闲  ]

气泡比例 = (PP - 1) / (PP - 1 + num_micro_batches)
当 PP=4, num_micro_batches=1 时,气泡比例 = 3/4 = 75%

气泡比例公式为

1
(PP - 1) / (PP - 1 + M)

,其中 PP 是流水线深度,M 是 micro-batch 数量。当 M 远大于 PP 时,气泡比例趋近于 0。这就是为什么流水线并行需要微批次(Micro-batching)技术——把一个大 batch 切分成多个小 micro-batch,让它们像流水线上的产品一样依次注入,填满流水线。

2.2 微批次填充:用并发填满流水线


1
2
3
4
5
6
7
8
时间 →   t0   t1   t2   t3   t4   t5   t6   t7
GPU 0: [m1] [m2] [m3] [m4] [m1] [m2] [m3] [m4]  ← 前向+后向
GPU 1:      [m1] [m2] [m3] [m4] [m1] [m2] [m3] [m4]
GPU 2:           [m1] [m2] [m3] [m4] [m1] [m2] [m3] [m4]
GPU 3:                [m1] [m2] [m3] [m4] [m1] [m2] [m3] [m4]
         ↑ 填充阶段 ↑    ↑  稳态阶段  ↑    ↑ 排空阶段 ↑

气泡比例 = (PP - 1) / (PP - 1 + M) = 3 / (3 + 4) ≈ 43%

当 M=4、PP=4 时,气泡从 75% 降到 43%。继续增大 M 到 16 时,气泡降到 3/(3+16) ≈ 16%。但增大 M 也意味着更大的显存占用(需要缓存更多 micro-batch 的激活值),需要在气泡和显存之间做权衡。

三、1F1B 调度:推理场景下的气泡优化

在训练场景中,1F1B(One Forward One Backward)调度是主流方案,它通过交错前向和后向来减少激活值缓存需求。但在推理场景中,没有后向传播,气泡优化的策略有所不同。

流水线并行微批次调度时序图

3.1 推理场景的连续流水线填充

在推理服务中,请求是持续到达的。关键优化是让请求以 micro-batch 形式持续注入流水线,保持所有 GPU 尽可能忙碌。vLLM 的 PP 实现采用了以下策略:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# vLLM 流水线并行的核心调度逻辑(简化版)
class PipelineParallelScheduler:
    def __init__(self, num_stages, num_micro_batches):
        self.num_stages = num_stages  # PP 深度
        self.num_micro_batches = num_micro_batches
        self.stage_id = None  # 当前 GPU 的 stage 编号

    def schedule_forward(self, requests):
        """将请求切分为 micro-batch 并注入流水线"""
        micro_batches = self.split_into_micro_batches(requests)
       
        for i, mb in enumerate(micro_batches):
            # 非最后一个 stage:计算后发送激活值给下一个 stage
            if self.stage_id < self.num_stages - 1:
                output = self.forward_layers(mb)
                self.send_activation(output, dest=self.stage_id + 1)
            # 非第一个 stage:先接收前一个 stage 的激活值
            if self.stage_id > 0:
                mb.input = self.recv_activation(src=self.stage_id - 1)
                output = self.forward_layers(mb)
           
            # 最后一个 stage:输出结果
            if self.stage_id == self.num_stages - 1:
                yield output

3.2 交错流水线(Interleaved PP)

Megatron-LM 提出了交错流水线(Interleaved Pipeline)方案,将模型的 L 层分成 V 个 chunk,每个 GPU 负责不连续的多个 chunk。例如 16 层模型、4 卡、V=2:GPU 0 负责 [0-1, 8-9],GPU 1 负责 [2-3, 10-11],GPU 2 负责 [4-5, 12-13],GPU 3 负责 [6-7, 14-15]。


1
2
3
4
5
6
7
8
9
交错 PP 调度(V=2, PP=4):

GPU 0: [c0-m1][c0-m2][c4-m1][c4-m2]  ← chunk 0 和 chunk 4 交替
GPU 1:        [c1-m1][c1-m2][c5-m1][c5-m2]
GPU 2:               [c2-m1][c2-m2][c6-m1][c6-m2]
GPU 3:                      [c3-m1][c3-m2][c7-m1][c7-m2]

气泡比例 = (PP - 1) / (V × (PP - 1) + M)
当 V=2, PP=4, M=4 时: 气泡 = 3 / (2×3 + 4) = 3/10 = 30%

交错 PP 将气泡从 (PP-1)/(PP-1+M) 降低到 (PP-1)/(V×(PP-1)+M),代价是通信次数增加 V 倍。在推理场景中,由于没有后向传播,交错 PP 的收益相对训练场景较小,但对于长序列推理(Prefill 阶段计算量大)仍有价值。

四、vLLM 流水线并行部署实战

vLLM 从 0.4 版本开始支持流水线并行,通过

1
--pipeline-parallel-size

参数配置。下面是一个完整的多卡 PP 部署示例。

4.1 基本部署:纯 PP 模式


1
2
3
4
5
6
7
8
# 部署 Llama-3-70B 模型,使用 4 卡流水线并行
# 每卡负责约 20 层(70B 模型共 80 层)
vllm serve meta-llama/Meta-Llama-3-70B \
  --pipeline-parallel-size 4 \
  --tensor-parallel-size 1 \
  --gpu-memory-utilization 0.90 \
  --max-model-len 8192 \
  --port 8000

这个配置将 70B 模型均匀切分到 4 张 GPU 上,每卡约 17.5GB 权重(70B × 2 bytes / 4 ≈ 35GB ÷ 2 = 17.5GB per FP16),加上 KV Cache 和激活值,单卡显存占用约 30-40GB,适合 A100-40G 或 A100-80G。

4.2 TP + PP 混合并行:千亿参数部署

对于 405B 级别的模型,单靠 TP 或 PP 都不够。标准方案是节点内 TP=8、跨节点 PP=N:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 部署 Llama-3-405B-FP8,2 个节点,每节点 8×H100
# 节点内 TP=8(NVLink 互联),跨节点 PP=2
# 总 GPU = 8 × 2 = 16

# 节点 0(PP rank 0)
vllm serve meta-llama/Meta-Llama-3-405B-FP8 \
  --tensor-parallel-size 8 \
  --pipeline-parallel-size 2 \
  --pipeline-parallel-rank 0 \
  --pipeline-parallel-size-total 2 \
  --gpu-memory-utilization 0.92 \
  --max-model-len 4096 \
  --quantization fp8 \
  --port 8000 \
  --distributed-executor-backend ray

# 节点 1(PP rank 1)
vllm serve meta-llama/Meta-Llama-3-405B-FP8 \
  --tensor-parallel-size 8 \
  --pipeline-parallel-size 2 \
  --pipeline-parallel-rank 1 \
  --pipeline-parallel-size-total 2 \
  --gpu-memory-utilization 0.92 \
  --max-model-len 4096 \
  --quantization fp8 \
  --port 8000 \
  --distributed-executor-backend ray

4.3 层切分策略:均匀 vs 非均匀

vLLM 默认采用均匀切分,但不同层的计算量并不相同(例如 Embedding 层和 LM Head 层的计算量远小于 Transformer 层)。对于非均匀模型,可以手动指定切分点:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 自定义层切分策略
from vllm.distributed.pipeline_parallel import get_pipeline_parallel_layer_split

# 假设模型有 80 层,切分到 4 卡
# 方案 A:均匀切分(默认)
# GPU 0: layers 0-19, GPU 1: layers 20-39,
# GPU 2: layers 40-59, GPU 3: layers 60-79

# 方案 B:非均匀切分(考虑 Embedding 和 LM Head)
# GPU 0: layers 0-22 (含 Embedding)
# GPU 1: layers 23-41
# GPU 2: layers 42-60
# GPU 3: layers 61-79 (含 LM Head)

split_indices = [23, 42, 61]  # 3 个切分点 → 4 段
# 在 vLLM 启动参数中通过环境变量指定
# export VLLM_PP_LAYER_SPLIT="23,42,61"

非均匀切分的目标是让每张卡的前向计算时间尽可能相等,因为流水线的吞吐量受限于最慢的 stage(木桶效应)。实践中,可以通过 profiling 每层的 FLOPs 和显存占用来确定最优切分点。

五、PP 推理性能调优与常见问题

5.1 通信优化:NCCL 点对点操作

PP 的核心通信是相邻 stage 之间的 Send/Recv。vLLM 使用 NCCL 的

1
ncclSend

和

1
ncclRecv

原语实现点对点通信:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# vLLM PP 通信核心代码(简化)
import torch.distributed as dist

class PipelineParallelCommunicator:
    def send_activation(self, tensor, dest_rank):
        """发送激活值到下一个 stage"""
        # 使用 NCCL Send,非阻塞
        dist.send(tensor, dst=dest_rank)
       
    def recv_activation(self, src_rank, shape, dtype):
        """从前一个 stage 接收激活值"""
        tensor = torch.empty(shape, dtype=dtype, device='cuda')
        dist.recv(tensor, src=src_rank)
        return tensor

    def send_recv_overlap(self, send_tensor, recv_shape, recv_dtype,
                          dest_rank, src_rank):
        """通信与计算重叠:用 CUDA Stream 实现"""
        send_stream = torch.cuda.Stream()
        recv_stream = torch.cuda.Stream()
       
        with torch.cuda.stream(send_stream):
            dist.send(send_tensor, dst=dest_rank)
       
        with torch.cuda.stream(recv_stream):
            recv_tensor = torch.empty(recv_shape, dtype=recv_dtype,
                                       device='cuda')
            dist.recv(recv_tensor, src=src_rank)
       
        # 等待通信完成
        send_stream.synchronize()
        recv_stream.synchronize()
        return recv_tensor

通信与计算重叠(Overlap)是 PP 性能调优的关键。通过将 Send/Recv 操作放在独立的 CUDA Stream 上,可以让通信和前向计算并行执行,隐藏通信延迟。在跨节点 PP 部署中,这一点尤为重要——InfiniBand 的延迟约 1-2μs,如果通信和计算不重叠,每个 micro-batch 都要付出额外的通信等待。

5.2 KV Cache 在 PP 中的分布

在 PP 模式下,KV Cache 也分布在不同的 GPU 上。每个 stage 只存储自己负责层的 KV Cache。这意味着:


1
2
3
4
5
6
7
8
9
模型: 80 层, PP=4, 每卡 20 层
序列长度: 4096, KV Cache per layer ≈ 32MB (FP16)

GPU 0: KV Cache = 20 × 32MB = 640MB
GPU 1: KV Cache = 20 × 32MB = 640MB  
GPU 2: KV Cache = 20 × 32MB = 640MB
GPU 3: KV Cache = 20 × 32MB = 640MB

总 KV Cache = 2560MB(与单卡部署相同,只是分散存储)

PP 的一个优势是 KV Cache 的增长不会集中在单卡上。但需要注意的是,最后一个 stage(GPU 3)通常还需要存储 LM Head 的权重和输出 logits,显存压力可能比其他 stage 更大。在调整

1
gpu_memory_utilization

时要考虑这一点。

5.3 常见问题排查

问题 原因 解决方案
PP stage 间通信超时 NCCL 跨节点初始化失败 检查 IB 驱动、设置 NCCL_SOCKET_IFNAME
某些 GPU 利用率明显偏低 层切分不均匀导致木桶效应 用 Nsight Systems profiling 各 stage 耗时,调整切分点
吞吐量不如纯 TP 气泡比例过高(M 太小) 增大 max_num_seqs 以提高 micro-batch 数量
首 token 延迟(TTFT)过高 PP 串行特性导致 Prefill 延迟叠加 对延迟敏感场景优先用 TP,PP 适合吞吐优先
OOM 在最后一个 stage LM Head + logits 占用额外显存 非均匀切分,给最后 stage 分配更少层

六、PP vs TP vs DP:推理场景选型决策

在实际部署中,如何选择并行策略?以下是基于实际经验的决策框架:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────────────────────────────┐
│                    并行策略选择决策树                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  模型能放进单卡?                                            │
│  ├─ 是 → 不需要并行,单卡部署                                 │
│  └─ 否 → 模型能放进单节点(8卡 NVLink)?                     │
│     ├─ 是 → TP=8,节点内张量并行                              │
│     └─ 否 → 需要跨节点                                       │
│        ├─ 延迟敏感(< 500ms TTFT)?                          │
│        │  └─ TP=8 × PP=2,尽量减少 PP 深度                    │
│        └─ 吞吐优先?                                         │
│           └─ TP=8 × PP=N,N 根据模型大小确定                  │
│              └─ 请求量大?叠加 DP(数据并行)多副本             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

核心原则:TP 优先于 PP,因为 TP 的延迟更低(并行计算 vs 串行流水线)。只有当 TP 达到扩展上限(通常 8 卡)仍无法容纳模型时,才引入 PP。对于推理服务,PP 更适合吞吐优先的场景(如批量处理、离线推理),对延迟优先的场景(如实时对话)要谨慎使用 PP,因为流水线的串行特性会叠加每卡的延迟。

6.1 三种并行策略的适用场景对比

策略 适用模型规模 延迟 吞吐 扩展性 典型配置
TP only ≤ 70B (FP16) / ≤ 180B (FP8) 低 中 ≤ 8 卡 TP=4 或 TP=8
PP only 70B-400B 高 高 数十卡 PP=4 到 PP=16
TP + PP 400B+ 中高 高 数百卡 TP=8 × PP=2-16
TP + PP + DP 任意规模 中高 极高 无上限 多副本 TP+PP

总结

流水线并行是大模型推理部署中不可或缺的并行策略。与张量并行互补,PP 通过层间切分突破了 TP 的扩展上限,使得千亿参数模型的推理成为可能。关键要点回顾:

  1. PP 的核心优势是通信量与卡数无关,适合跨节点扩展;核心劣势是流水线气泡和串行延迟。
  2. 气泡优化的关键是增大 micro-batch 数量 M,使
    1
    (PP-1)/(PP-1+M)

    趋近于 0;交错 PP 可进一步降低气泡。

  3. vLLM PP 部署通过
    1
    --pipeline-parallel-size

    参数配置,支持 TP+PP 混合并行,是当前最易用的 PP 推理框架。

  4. 选型原则:TP 优先于 PP,PP 用于突破 TP 扩展上限;延迟敏感场景慎用 PP,吞吐优先场景适合 PP。
  5. 通信优化:利用 CUDA Stream 实现 Send/Recv 与计算的 Overlap,隐藏跨节点通信延迟。

在百亿到千亿参数模型的推理部署中,TP+PP 的混合并行是工业界标准方案。理解 PP 的原理和调优方法,是 AI Infra 工程师必备的核心能力。随着 vLLM、SGLang 等框架对 PP 支持的不断完善,流水线并行推理部署的门槛正在降低,但背后的原理和调优经验仍然决定着生产环境的最终性能表现。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » 大模型推理中的流水线并行(Pipeline Parallelism)深度解析:从 PP 切分原理到 vLLM 多卡流水线部署与气泡优化实战
分享到: 更多 (0)