16. 分布式推理:TP、PP、DP 与 EP
前面的讨论都假设一张 GPU 就够了:8B 模型的 16 GB 权重放进 80 GB 显存,还剩大量空间给 KV。资料助手升级到 70B 模型以后,情况变了:权重本身就有 140 GB,一张卡放不下。全公司同时使用时,一套实例也扛不住流量。这一篇讲怎样把推理工作分给多张 GPU:张量并行、流水线并行、数据并行、专家并行分别拆了什么,各自要付出什么通信代价。读完你应该能为一个给定的模型和流量,判断该用哪种并行、需要几张卡,并解释为什么“加一张卡”不一定更快。
先问清楚:要解决什么问题
多卡要解决的问题有三类,对应不同的拆法:
- 放不下:权重或 KV 超过了单卡显存。需要把模型本身拆开。
- 扛不住:单个实例的吞吐不够。需要复制更多实例分担请求。
- 不够快:单个请求的延迟太高。需要让多张卡同时处理同一个请求。
| 方式 | 拆分对象 | 解决什么 | 主要代价 |
|---|---|---|---|
| TP,张量并行 | 每一层内部的矩阵 | 放不下,也能降低单请求延迟 | 每层都要通信,依赖高速互联 |
| PP,流水线并行 | 不同的层放到不同的卡 | 放不下,可跨机器 | 流水线气泡,单请求延迟不降 |
| DP,数据并行 | 不同的请求给不同的副本 | 扛不住 | 每个副本一份完整权重 |
| EP,专家并行 | MoE 模型的专家 | 超大 MoE 放不下、算不动 | 每层两次全交换通信,专家负载不均 |
这些方式可以组合使用,比如每台机器内用 TP,机器之间用 PP,再复制多个副本做 DP。
先算一笔账:70B 需要几张卡
Llama-3.1-70B 有 80 层、64 个注意力头、8 个 KV 头,每头 128 维。
text
权重:约 706 亿参数 × 2 字节 ≈ 141 GB
每 token 的 KV:2 × 80 × 8 × 128 × 2 = 327,680 字节 = 320 KiB两张 H100 合计 160 GB,放下权重以后只剩十几 GB,扣掉运行时开销,KV 只够几万个 token,并发能力很差。实际部署中常用 4 张卡:每张卡分到约 35 GB 权重,剩下的空间留给 KV。如果换成 FP8 权重(约 71 GB),2 张卡就能有比较宽裕的 KV 空间。
这个例子说明,选几张卡不只看“权重放不放得下”,还要看留给 KV 的空间够不够第 4 篇算出的并发需求。
张量并行:把每一层切开
张量并行(Tensor Parallelism)把每一层的矩阵切成几份,每张卡算一份,然后汇总结果。
以 MLP 为例,它由两个矩阵乘法组成。第一个矩阵按列切:每张卡拿到一部分列,用完整的输入算出输出的一部分,各卡之间不需要通信。第二个矩阵按行切:每张卡用上一步自己算出的那部分,乘以对应的行,得到一份“部分和”。最后把各卡的部分和加起来,才是完整的输出,这一步叫 all-reduce,需要卡间通信。
注意力层也是同样的结构:多个注意力头天然可以分开,每张卡负责几个头(包括对应的 KV 头),注意力输出前的最后一个矩阵(输出投影)按行切,再做一次 all-reduce。所以标准做法下,每一层有两次 all-reduce,注意力一次,MLP 一次。nano-vllm 的 linear.py 就是这样实现的(第 13 篇)。
张量并行的好处:
- 权重和 KV 都平均分到各张卡上。4 卡 TP 时,每张卡只存 1/4 的权重和 1/4 的 KV 头。
- 每张卡每一步要搬的权重变成 1/4,单个请求的 Decode 也变快了。这是 TP 和其他方式最大的不同。
代价是通信。70B 模型 80 层,全部按 TP 切分时,每生成一个 token 要做 160 次 all-reduce。每次的数据量不大(Decode 时大约是批大小 × 每个 token 的激活向量长度 × 2 字节,70B 模型的向量长度是 8192),但次数多,对延迟非常敏感。所以 TP 几乎只在同一台机器内、有 NVLink 高速互联的卡之间使用。H100 的 NVLink 带宽远高于 PCIe,跨机器的网络更慢,TP 跨机器通常得不偿失。
还有一个约束:注意力头要能平均分给各张卡,所以 TP 的卡数要能整除注意力头数(Llama-3.1-70B 是 64)。KV 头也一样:TP 不超过 8 时,8 个 KV 头平均分给各张卡;超过 8 时,KV 头不够分,只能在卡之间复制,KV 的显存就省不下来了。
流水线并行:按层分段
流水线并行(Pipeline Parallelism)把模型按层切成几段:第 1—40 层放在机器 A,第 41—80 层放在机器 B。一个请求先在 A 上算完前半段,把中间结果传给 B,B 算完后半段。
它的通信只发生在段与段的交界处,每一步只传一次激活,数据量小、次数少,所以适合跨机器。
问题在于单个请求的路径仍然是串行的:A 算的时候 B 闲着,B 算的时候 A 闲着,这叫流水线气泡。要让两段都忙起来,需要同时有多批请求在流水线上流动:A 处理第 2 批的同时,B 处理第 1 批。所以 PP 能提升吞吐、解决放不下的问题,但不会降低单个请求的延迟,甚至因为多了一次传输而略微增加。
常见的组合是:一台 8 卡机器内部用 TP=8,放不下时再用 PP 跨两台机器。
数据并行:复制多个副本
数据并行(Data Parallelism)最简单:每个副本都有完整的模型,各自独立处理不同的请求。8B 模型在一张卡上就能跑,那么 8 张卡就部署 8 个副本,前面放一个负载均衡。
吞吐几乎随副本数线性增长,单个请求的延迟不变。代价是每个副本都要一份完整的权重,而且每个副本的前缀缓存是独立的:同一份制度,在 8 个副本上要各算一次。所以负载均衡最好按前缀把请求分到固定的副本上(第 10 篇说的缓存感知路由,第 21 篇再展开)。
模型能放进一张卡(或一台机器)时,扩容的首选就是数据并行:简单、没有卡间通信、故障互不影响。
专家并行:MoE 模型的拆法
MoE(混合专家)模型的每一层 MLP 换成了很多个“专家”,每个 token 只经过其中少数几个。比如 DeepSeek-V3 总共约 6710 亿参数,每个 token 只激活约 370 亿。计算量看激活参数,显存却要放下全部参数。
专家并行(Expert Parallelism)把不同的专家放在不同的卡上。每一层的计算变成三步:
- 分发(dispatch):路由器为每个 token 选出专家,把 token 发到这些专家所在的卡上。
- 各卡上的专家处理分到自己这里的 token。
- 汇合(combine):把结果送回 token 原来所在的卡。
分发和汇合都是 all-to-all 通信:每张卡都要和其他所有卡交换数据。这是 EP 最大的成本,网络拓扑和带宽直接决定性能。
EP 还有一个特有的问题:专家负载不均。路由是由模型决定的,某些专家可能特别“热门”,分到大量 token,其所在的卡成了瓶颈,其他卡却在等它。引擎通常会复制热门专家、或者动态调整专家的放置位置来缓解。
大型 MoE 的常见部署方式是:注意力部分用数据并行(每张卡处理不同请求的注意力),专家部分用专家并行(所有卡共同承担专家)。各引擎对这种组合的命名不同,比如“DP attention + EP”。
长上下文:上下文并行
还有一种情况:单个请求的上下文长到几十万、上百万 token,这个请求的 KV 本身就放不下,或者 Prefill 太慢。上下文并行(Context Parallelism)把一个请求的序列切成几段,分给几张卡,注意力计算时卡之间交换 KV 片段。它主要用于超长上下文的 Prefill,实现复杂度较高。
怎样选
按顺序问几个问题:
- 模型能放进一张卡吗? 能,就用一张卡跑,靠数据并行扩容。
- 放不进一张卡,能放进一台机器吗? 能,就在机器内用 TP,卡数取能放下权重并留足 KV 空间的最小值。
- 一台机器也放不下? 机器内 TP,机器间 PP;或者先考虑 FP8、INT4 量化能否让它放进一台机器。
- 是大型 MoE? 考虑 EP,并特别关注网络带宽和专家负载。
- 单请求延迟不达标? 加大 TP 可以降低 Decode 延迟,但收益随卡数递减,通信占比越来越高。
在 vLLM 中,对应的参数是 --tensor-parallel-size、--pipeline-parallel-size、--data-parallel-size 和 --enable-expert-parallel。跨机器部署可以用 Ray,也可以用 vLLM 自带的多进程方式。
最后要记住:多卡不自动等于更快。两张卡做 TP,如果通过 PCIe 而不是 NVLink 连接,通信开销可能吃掉大部分收益;8B 模型硬上 4 卡 TP,每张卡上的计算太少,通信反而成了主要耗时。任何并行方案都要在真实的互联和负载下测过。
分布式推理先要分清问题:放不下就拆模型,扛不住就复制副本,不够快就让多卡共同处理一个请求。TP 切开每一层,每层两次 all-reduce,适合机器内高速互联;PP 按层分段,适合跨机器,但不降低单请求延迟;DP 复制副本,最简单,缓存各自独立;EP 把 MoE 的专家分散到各卡,代价是 all-to-all 通信和负载不均。
还有一种拆法没讲:不按模型拆,也不按请求拆,而是按阶段拆,让 Prefill 和 Decode 在不同的机器上运行。下一篇讲 P/D 分离。
