vLLM系统拆解-10-分布式部署:TP、PP、DP、EP各解决什么问题

前几篇讲的优化——scheduler、KV Cache、chunked prefill、spec decode——主要都在回答同一个问题:「一台机器 / 一组 GPU 上,如何把推理做得更高效?」

真实线上系统还会遇到另外几类问题,是单机优化无法解决的:

  • 模型太大,单张卡甚至单台机器装不下
  • 请求太多,单个推理实例扛不住总流量
  • MoE 模型的计算与通信模式,和普通 dense 模型不一样
  • 多节点部署下,网络通信本身也会成为瓶颈
  • prefill 想压 TTFT,decode 想提高 batch size,不同阶段目标不同

这时问题的性质变了:从「kernel 快不快」变成「这份权重怎么切到多张 GPU、这批请求怎么分给多个 engine、不同 GPU 之间什么时候通信」。

这篇文章讲 vLLM 如何用四种并行策略和分层架构来处理这些问题。读完你能解释 TP/PP/DP/EP 各自解决什么、为什么多节点常用「节点内 TP + 节点间 PP」、MoE 场景为什么需要额外的 EP + DBO,以及 vLLM 分层架构背后的设计逻辑。


四种并行的切分维度

先建立整体框架,再展开每一种。

并行策略 切分维度 主要解决的问题 典型代价
TP(张量并行) 层内算子 单层权重过大 / 单卡算力不足 层内通信(all-reduce/all-gather),高度依赖高速互联
PP(流水线并行) 网络深度(层) 多节点扩展 / GPU 数与模型切分不整齐 流水线气泡,单请求路径更长
DP(数据并行) 请求流量 总吞吐 / 服务扩展 权重复制占显存,需要负载均衡
EP(专家并行) MoE 专家集合 MoE 专家层的负载组织与局部性 稀疏路由的 all-to-all 通信

这四种策略不互斥,可以组合使用。vLLM 官方文档明确支持把数据并行 attention 与专家并行或张量并行的 MoE 层结合做大规模部署。


TP:同一层太大,横着或竖着拆

工作原理

TP 的本质是:同一层的参数和计算太大,一张 GPU 放不下或算不过来,于是把同一层的张量乘法拆到多张 GPU 上一起做。vLLM 实现了 Megatron-LM 的张量并行算法思路。

以大线性层为例,Y=XWY = XW,如果 WW 很大,可以把 WW 按列或按行拆到多张卡,每张卡算部分结果,最后聚合。

这是大模型推理里最常见的并行方式,代码层面很直接:

1
2
from vllm import LLM
llm = LLM("facebook/opt-13b", tensor_parallel_size=4)

或服务端:

1
vllm serve facebook/opt-13b --tensor-parallel-size 4

代价

TP 不是白送性能。把一层拆到多张 GPU 后,每层结束时通常需要 all-reduce / all-gather / reduce-scatter 等集合通信。计算被分散了,但通信变多了。

这里有一个关键约束:TP 特别依赖高速互联。同机 NVLink 通信比跨机网络快得多,官方文档明确指出高效张量并行需要快速节点间通信,最好是 InfiniBand 这类高速网络。

所以 TP 最适合单机多 GPU 且节点内互联良好的场景;如果 GPU 间互联一般,大 TP 未必是最优选择。


PP:不拆层内,而是按深度切段

工作原理

PP 把模型的不同层分配到不同 GPU 或节点,一个请求像流水线一样依次经过各个 stage:

1
2
3
4
5
6
7
80 层模型,4 张 GPU:
GPU 0: 1–20 层
GPU 1: 21–40 层
GPU 2: 41–60 层
GPU 3: 61–80 层

请求 → GPU 0 → GPU 1 → GPU 2 → GPU 3 → 输出

每张 GPU 只拿了模型的一段,stage 之间传递激活值,而不是频繁做层内同步。

TP vs PP 的选择逻辑

TP PP
切分位置 层内算子 层间(按深度)
通信模式 每层结束做 all-reduce,频率高 stage 间传递激活,频率低
适合硬件 节点内高速互联(NVLink) 节点间慢速网络,或无 NVLink 场景(如 L40S)
GPU 数整除 要求整除关系 支持 uneven splits
代价 层内通信开销 流水线气泡,单请求路径更长

官方文档给出了一个很实用的建议:单机但没有 NVLink 的场景(比如 L40S),可以考虑 tensor_parallel_size=1,直接用 pipeline_parallel_size 等于 GPU 数,避免层内高频同步的通信代价。

PP 的另一个优点是支持 uneven splits:当 GPU 数量与模型层数切分不那么整齐时,PP 更灵活。

PP 的代价不能忽视:不是每个 stage 时时刻刻都满载,存在流水线气泡;分段不均会导致木桶效应,某一段慢整条 pipeline 都被拖住。

多节点的典型组合:节点内 TP + 节点间 PP

这个组合几乎是多节点大模型部署的标准思路,原因是顺着硬件拓扑走:

1
2
3
4
5
6
2 节点 × 8 GPU = 16 GPU
配置:tensor_parallel_size=8, pipeline_parallel_size=2

节点 0: [GPU0-GPU7] ← TP=8,一起处理同一层(节点内 NVLink 快)
↕ PP 跨节点传激活(网络慢,但只有 stage 切换时才通信)
节点 1: [GPU0-GPU7] ← TP=8,处理模型另一段

节点内 GPU 互联通常更快,适合做层内高频同步的 TP;节点间网络更慢,用 PP 按层分段,减少跨节点的高频通信。并行策略要尊重硬件通信层级,而不是在抽象层面看哪些维度都能并行就随便组合。


DP:服务扩展,不是模型内并行

TP 和 PP 解决的是「模型怎么切」,DP 解决的是另一个问题:同一个模型副本太忙,需要多份副本一起接流量。

DP 的本质不是让单个请求更快,而是让整体服务能力更强。多个副本各自有完整的模型权重,不同副本处理不同请求。这是服务层的扩展,不是算子层的扩展。

线上流量高并发、波动性强、长短请求混合,单个 engine 即使 continuous batching 做得很好,也总会到达极限。DP 是横向扩展服务副本的主要手段。

vLLM V1 里的 DP 进程结构

vLLM 的 DP 不只是「多几个模型副本」,而是形成了一套完整的多进程结构。官方架构文档说明:每个 DP rank 有一个 engine core,每个 GPU 有一个 worker 进程,当 data_parallel_size > 1 时还会有一个 DP coordinator 进程。

具体进程数对比:

1
2
3
4
5
单节点 vllm serve -tp=4:
1 API server + 1 engine core + 4 GPU workers = 6 个进程

单节点 vllm serve -tp=2 -dp=4:
4 API servers + 4 engine cores + 8 GPU workers + 1 DP coordinator = 17 个进程

每个 DP rank 有自己的 engine core,意味着局部调度相对自治,横向扩展更自然,不需要一个超级大总控进程统管所有 GPU。

DP Coordinator 的作用

DP coordinator 的存在不是为了「代替 scheduler」,而是做 rank 间协调:

  • 管理不同 DP rank 之间的 load balancing,避免某个 rank 过载
  • 在 MoE 场景下协调 synchronized forward passes(各 rank 前向执行必须同步)

这是一个很典型的系统分层设计:engine core 管自己 rank 内部怎么 batch,coordinator 管 rank 之间怎么配合。比一个大总控进程包办一切更容易扩展,也更清晰。


EP 与 DBO:MoE 为什么是特殊问题

MoE 的计算路径是稀疏动态的

Dense 模型里每个 token 都经过同样的 FFN。MoE 不同:每个 token 先经过 router,router 决定它去哪些 expert,不同 token 可能路由到不同专家。

这个特性带来两个后果:

  1. 如果所有 expert 都堆在同一张卡,会非常拥挤
  2. Token 会在不同 GPU 之间按 expert 路由,产生 all-to-all 通信

MoE 的主要通信开销不是来自层内张量切分,而是来自 token 在 expert 间的 dispatch 和 combine。这是 EP 存在的根本原因。

EP:按专家维度切分

EP 的核心:把不同 expert 分散部署到不同 GPU,使 expert 层沿「专家维度」切分。 这样同一个 expert 固定在某些 GPU 上,locality 更好,专家层不必硬套 dense 模型的切法,通信和计算可以更贴近 MoE 的结构特点来组织。

官方文档给出了 EP 的 GPU 数计算关系:

EP_SIZE=TP_SIZE×DP_SIZEEP\_SIZE = TP\_SIZE \times DP\_SIZE

EP 不是独立存在的一套小并行,而是嵌在整个分布式拓扑里的。官方也明确指出 EP 更高效的前提是与 DP 结合使用。

MoE 模型里不是每层都要 EP:attention 层仍按 TP/DP 组织,expert 层按 EP 切。这形成了「分层异构并行」结构:

1
2
3
4
5
6
7
8
9
MoE 模型的并行结构:

Transformer 层
├── Attention 层 → TP / DP 组织
└── MoE FFN 层
├── Router(轻量)
└── Expert 0, 1, ..., N → EP 组织(不同 expert 在不同 GPU)

token 按路由做 all-to-all dispatch

这正是 MoE 服务比 dense 模型更复杂的根源。

DBO:把通信藏到计算后面

MoE 的 expert 层通常伴随 sparse all-to-all 通信(token dispatch → expert 计算 → combine 结果)。如果通信和计算串行排队,即使 GPU 算力没问题,系统也在大量等待通信。

DBO(Dual Batch Overlap)的目标:把 MoE 层中的 sparse all-to-all 通信与周围计算重叠起来,只面向 DP+EP 部署场景。

实现思路是跨层协同优化:

1
2
3
4
5
6
7
8
9
DBO 执行流程(简化):

原始 batch
↓ 切成两个 microbatch(ubatch0, ubatch1)

Thread 0 Thread 1
执行 ubatch0 compute ⇄ 等待 ubatch1 all-to-all
等待 ubatch0 all-to-all ⇄ 执行 ubatch1 compute
(在 FusedMoEModularKernel 的 yield 点做 ping-pong)

关键组件包括 GPUModelRunnerUBatchWrapperUBatchContext。这不是「调一个 NCCL 参数」,而是涉及 batch 组织方式、CPU 线程调度、kernel 内部 yield 点、CUDA graph 执行包装的协同改动。

这类「让多个阶段彼此重叠」的系统级 overlap 思维,是 MoE infra 优化里最能体现深度的部分。


运行时选择:单机用 multiprocessing,多机用 Ray

官方文档的建议很直接:

  • 单机推理:默认使用 Python multiprocessing
  • 多机推理:默认使用 Ray

单机时 GPU 资源就在本机,进程创建和共享环境更简单,multiprocessing 足够直接且轻量。

到了多机,需要解决跨节点任务分配、各节点环境一致性、执行位置管理、容错与观测等问题。vLLM 使用 Ray 管理分布式任务执行,同时利用 Ray 提供生产级的 fault tolerance、scaling 和 distributed observability。

这不是 vLLM「不会做分布式」,而是多机编排本来就是系统工程问题,直接借助成熟分布式运行时更合理。


为什么 vLLM 架构要分层

理解了四种并行后,可以回过头看 vLLM V1 的大规模服务架构为什么要分成 API server / engine core / GPU worker / DP coordinator 四层。

flowchart TD
    subgraph 服务层
        A1[API Server 0]
        A2[API Server 1]
        A3[API Server 2]
        A4[API Server 3]
    end

    subgraph 协调层
        C[DP Coordinator]
    end

    subgraph 调度层
        E1[Engine Core\nDP rank 0]
        E2[Engine Core\nDP rank 1]
        E3[Engine Core\nDP rank 2]
        E4[Engine Core\nDP rank 3]
    end

    subgraph 执行层
        W1[GPU Worker 0]
        W2[GPU Worker 1]
        W3[GPU Worker 2]
        W4[GPU Worker 3]
        W5[GPU Worker 4]
        W6[GPU Worker 5]
        W7[GPU Worker 6]
        W8[GPU Worker 7]
    end

    A1 --> E1
    A2 --> E2
    A3 --> E3
    A4 --> E4
    C --- E1 & E2 & E3 & E4
    E1 --> W1 & W2
    E2 --> W3 & W4
    E3 --> W5 & W6
    E4 --> W7 & W8

示例:tp=2, dp=4 配置下的进程结构(共 17 个进程)

分层设计的三个目的:

目的一:把「对外服务」和「内部执行」解耦。 API server 负责 HTTP 接口、输入预处理、tokenizer、流式返回;engine core 负责 scheduler、KV Cache 管理、请求生命周期;GPU worker 负责装模型、执行 forward、管理 GPU 内存。职责清楚,服务层变化不必强耦合到底层执行。

目的二:让 DP 成为「多个局部自治引擎」的扩展,而不是「单点大总控」的扩展。 每个 DP rank 各自有 engine core,局部调度相对自治。如果所有请求、KV Cache、worker 都被一个超级中心进程统管,CPU 压力极大,可扩展性更差,中心点出问题影响更广。

目的三:针对不同并行维度做不同层次的协调。 TP/PP 影响 worker 侧执行拓扑;DP 影响 engine core 数量与请求分流;EP/DBO 影响 MoE 层内执行与 rank 间同步;DP coordinator 处理 rank 间全局协同。不同问题交给最合适的角色,而不是平铺到同一层。


场景决策参考

场景 推荐并行策略 关键原因
单卡放不下,单机有高速互联 TP 最直接,节点内 NVLink 代价可控
单机但无 NVLink(如 L40S)或 GPU 数切分不整齐 PP(或 TP=1 + PP=GPU数) 避免层内高频通信,PP 支持 uneven splits
模型能跑起来,服务扛不住流量 DP 吞吐扩展和服务副本扩展的主要手段
MoE 模型,专家层通信重 EP + DP,考虑 DBO MoE 稀疏路由有专属通信模式,dense 模型切法不适合
多节点大模型 节点内 TP + 节点间 PP(+ DP 扩流量) 顺硬件拓扑:节点内互联快→TP,节点间网络慢→PP

核心结论

大规模 LLM 推理服务的瓶颈不只是计算,还有通信、进程组织和请求分流。TP/PP 解决模型怎么切,DP 解决流量怎么扩,EP 解决 MoE 专家怎么组织,DBO 解决 MoE 通信和计算怎么 overlap——这四个问题是不同维度的,用一种并行解决所有问题往往会在某一维很差。

多节点常用「节点内 TP + 节点间 PP」不是习惯,而是顺着硬件通信层级走的结果。

vLLM 的分层架构——API server / engine core / GPU worker / DP coordinator——是为了把不同层次的问题分别交给最合适的角色,既保持职责清晰,又让 DP 扩展变成「多个局部自治单元的水平扩展」,而不是「单点总控的垂直扩展」。



系列导航