Sealessland logo Sealessland
LLM Inference

大模型分布式并行的实际配合

DP、TP、PP 单独看都明白,叠加在一起就容易晕。整理一下实际怎么用。

大模型分布式并行的实际配合

DP、TP、PP 这三个东西,单独拎出来讲都能讲清楚,但真到看代码或者配集群的时候,经常搞不清哪一层切了什么、通信发生在哪。我之前看 Megatron-LM 的配置文件,三个参数一乘等于总卡数,但背后的数据流其实没完全想明白。

这篇逐个讲清楚,再讲组合方式。

三个维度分别解决什么问题

维度切什么解决什么问题通信方式
DP数据 (batch)单卡算力不够,需要更多 GPU 一起算All-Reduce (梯度)
TP单层参数单层模型太大,单卡装不下All-Reduce (激活)
PP模型层模型层数太多,单卡装不下P2P Send/Recv

DP 最直观:4 张卡,每张卡算 1/4 的数据,最后同步梯度。和传统的数据并行训练没有本质区别。

TP 稍微复杂一点:单层网络(比如一个 MLP 或 Attention)的参数量超过了单卡显存,需要把矩阵切开。常见的是列切分接行切分:

Input

  ├──► [GPU0] W_0 ──┐
  └──► [GPU1] W_1   │──► 聚合

  ...               ...

切完之后每张卡只存一部分权重,forward 完需要 all-reduce 把结果拼回来。

PP 是按层切:0-19 层放 GPU0,20-39 层放 GPU1,数据依次经过每个 stage 计算。问题是 GPU1 在等 GPU0 的输出时会空转,产生 pipeline bubble

3D 怎么叠加

实际部署时,三个维度是正交的,三个维度正交组合,可以唯一确定每张卡负责的数据和参数。

假设有 64 张 GPU:

DP = 4      (4 个数据并行副本)
PP = 4      (每个副本内 4 个流水线 stage)
TP = 4      (每个 stage 内 4 卡张量并行)

4 × 4 × 4 = 64

数据流是这样的:

  1. Batch 被 DP 切成 4 份,每份给一组 PP 流水线
  2. 每组 PP 内部,数据流经 4 个 stage
  3. 每个 stage 内部,单层参数被 TP 切到 4 张卡上
graph TD
    subgraph DP["DP = 4"]
        direction TB
        G0[DP Group 0]
        G1[DP Group 1]
        G2[DP Group 2]
        G3[DP Group 3]
    end
    
    subgraph PP["PP = 4"]
        direction LR
        S0[Stage 0]
        S1[Stage 1]
        S2[Stage 2]
        S3[Stage 3]
        S0 --> S1 --> S2 --> S3
    end
    
    subgraph TP["TP = 4"]
        direction TB
        T0[GPU0]
        T1[GPU1]
        T2[GPU2]
        T3[GPU3]
    end
    
    G0 --> S0
    S0 --> T0
    S0 --> T1
    S0 --> T2
    S0 --> T3
请求 A ──► [DP Group 0]
              ├── PP Stage 0 (TP: GPU0,1,2,3)
              ├── PP Stage 1 (TP: GPU4,5,6,7)
              ├── PP Stage 2 (TP: GPU8,9,10,11)
              └── PP Stage 3 (TP: GPU12,13,14,15)

请求 B ──► [DP Group 1]
              ├── PP Stage 0 (TP: GPU16,17,18,19)
              ...

通信放在哪很重要

三个维度的通信特征完全不同,放错了位置性能直接崩。

并行维度通信类型通信量应该放在哪
DPAll-Reduce (梯度)2 × 模型大小跨节点 (IB/RoCE)
TPAll-Reduce (激活)每层的激活值单节点内 (NVLink)
PPP2P Send/Recv相邻 stage 的激活同节点或相邻节点

TP 必须放在单节点内。因为 TP 每层的 forward/backward 都有 all-reduce,通信最频繁,需要最高带宽。如果 TP 跨节点走 IB,通信延迟会把计算收益吃掉。

DP 通信量其实最小:一轮 forward+backward 只需要同步一次梯度,而且是异步的(和下一轮 forward overlap)。所以 DP 可以跨节点甚至跨机架。

PP 介于中间:通信量是相邻 stage 的激活值,比 TP 小,比 DP 频繁。尽量同机架部署。

训练 vs 推理的区别

场景DPTPPP
训练大模型
单节点推理 (8卡)可选
多节点推理
高并发服务可选可选

训练时 3D 并行是标配,因为既要装下模型,又要并行算数据。

推理时更灵活。单节点 8 卡推理一个 70B 模型,通常 TP=8 就够了,DP=1。如果是服务化场景(vLLM/SGLang),可能 TP=4 做推理,DP=2 做两个副本,增加并发。

我之前搞混的地方

PP 的 bubble 问题。一开始以为只要增加 micro-batch 数量就能完全消除 bubble,但其实:

gantt
    title Pipeline Bubble 示意(4 Stages,4 Micro-batches)
    dateFormat X
    axisFormat %s
    
    section Stage 0
    F0 : 0, 1
    F1 : 1, 2
    F2 : 2, 3
    F3 : 3, 4
    
    section Stage 1
    F0 : 1, 2
    F1 : 2, 3
    F2 : 3, 4
    F3 : 4, 5
    
    section Stage 2
    F0 : 2, 3
    F1 : 3, 4
    F2 : 4, 5
    F3 : 5, 6
    
    section Stage 3
    F0 : 3, 4
    F1 : 4, 5
    F2 : 5, 6
    F3 : 6, 7

Stage 0 算完 F0 后,Stage 1 才开始。Stage 1 开始前,Stage 2 和 Stage 3 都在空等,这就是 bubble。

  • GPipe:micro-batch 很多,但内存爆炸(需要同时存很多中间激活)
  • 1F1B (One-Forward-One-Backward):内存好了,但 bubble 只是减少,不是消除

没有免费的午餐,PP 的收益始终受限于 bubble 比例。

TP 的切分方向。列并行和行并行的顺序不能乱,MLP 的标准做法是”列切 → 行切”,这样中间的激活不需要 all-reduce,只在最后做一次。如果切反了,通信量翻倍。

总结

3D 并行本质上是在解决”模型太大、算力不够、显存不够”这三个问题,分别用 TP、PP、DP 来应对。实际配置时,最优先保证 TP 在机内,PP 在机架内,DP 可以跨机柜。训练必须 3D 全上,推理根据模型大小和并发需求灵活组合。

写到这感觉比之前看 Megatron 配置文件时清楚多了,至少能一眼看出 tensor_model_parallel_size × pipeline_model_parallel_size × data_parallel_size = world_size 背后,每张卡具体在算什么、和谁通信。