流水线并行¶
流水线并行(PP)将模型的 Transformer 隐藏层划分为连续的流水线阶段。每个阶段由一个张量并行(TP)组执行,当 TP 大小为 1 时,则由单个 worker 执行,并将中间激活传递给下一阶段。例如,--pipeline-parallel-size 2 将隐藏层划分为两个流水线阶段。
输入 token → PP 阶段 0(较早的隐藏层)→ 中间激活 → PP 阶段 1(较晚的隐藏层、最终归一化、LM 头及采样)→ 输出 token。
本指南使用以下术语:
| 术语 | 含义 |
|---|---|
| PP 阶段 | 包含连续模型层范围的一个逻辑分区。 |
| PP rank | worker 在其 PP 通信组内的阶段索引。具有相同阶段索引和不同 TP rank 的 worker 共同执行一个阶段。 |
| TP 组 | 在一个 PP 阶段内共同应用 TP 的 worker。 |
| 节点 | 一台物理服务器。一个节点可以包含多个 PP 阶段或 TP 组。 |
Note
并非所有模型实现都支持 PP。部署前,请查看支持的模型中的流水线并行列。
首次部署时,请从快速开始入手。生产环境部署前,请审阅配置层分区及兼容性限制。当部署需要额外功能或调优时,可使用高级场景和性能分析。
快速开始¶
以下示例展示了启动基本 PP 部署所需的最少选项。在生产环境中使用该配置前,请审阅拓扑、兼容性和性能部分。
开始之前¶
启动服务前,请确认以下事项:
- 可见的 NPU 数量满足所选 TP/PP/DP 拓扑的要求。
- 所有节点使用相同的 vLLM、vLLM Ascend、模型权重和 Python 环境。
- 所有节点可以访问相同的模型路径或等效的本地副本。
- 对于 MP 部署,每个节点都能访问头节点的 master 地址和端口。
- HCCL 以及(对于 Ray 部署)Ray 资源视图均处于健康状态。
单节点部署¶
以下示例演示了在两个 NPU 上的最小 PP 语法。仅在需要层分区时使用此拓扑:
export ASCEND_RT_VISIBLE_DEVICES=0,1
vllm serve /path/to/model \
--tensor-parallel-size 1 \
--pipeline-parallel-size 2 \
--trust-remote-code
当每个阶段应使用 TP 组时,请增大 --tensor-parallel-size。例如,TP4 PP2 需要八个可见的 NPU。
使用多进程的两节点部署¶
多进程(MP)后端可以在没有 Ray 集群的情况下跨两个节点启动 PP 部署。以下示例使用两个 8-NPU 节点,将 TP 保持在每个节点内部,并在每个节点上放置一个 PP 阶段。将 <HEAD_NODE_IP> 替换为 worker 节点可以访问的地址,将 <MASTER_PORT> 替换为未使用的 TCP 端口。两个节点上使用相同的值。
在头节点上,使用节点 rank 0:
vllm serve /path/to/model \
--distributed-executor-backend mp \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--nnodes 2 \
--node-rank 0 \
--master-addr <HEAD_NODE_IP> \
--master-port <MASTER_PORT> \
--trust-remote-code
在 worker 节点上,使用节点 rank 1 并添加 --headless:
vllm serve /path/to/model \
--distributed-executor-backend mp \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--nnodes 2 \
--node-rank 1 \
--master-addr <HEAD_NODE_IP> \
--master-port <MASTER_PORT> \
--headless \
--trust-remote-code
只有头节点启动 API 服务器。两个节点必须使用相同的模型、TP、PP、--nnodes、--master-addr 和 --master-port 值。为每个节点分配从 0 到 --nnodes - 1 的唯一 --node-rank,并在运行任一命令前设置所需的 HCCL 和通信环境变量。
使用 Ray 的多节点部署¶
启动 Ray 集群后,在头节点上运行 vllm serve。对于两个 8-NPU 节点,将 TP 保持在每个节点内部,并在节点之间使用 PP:
vllm serve /path/to/model \
--distributed-executor-backend ray \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--trust-remote-code
仅在每个节点上设置所需的通信环境变量后,再启动 Ray。完整的集群设置文档请参阅Ray 分布式。
何时使用 PP¶
PP 在以下情况下很有用:
- 模型无法放入一个 TP 组,必须按层进一步划分。
- 多节点部署应将高频 TP 集合通信保持在每个节点内部,同时通过 PP 在节点之间发送激活。
- P/D 分离部署中的 Prefill 节点需要更多设备来满足模型容量或长上下文 prefill 的需求。
PP 还会引入阶段间的激活传输和流水线气泡。当模型可以放入一个节点时,以仅 TP 部署为基线,然后在模型容量或网络拓扑需要时添加 PP。
| 部署目标 | 推荐起点 |
|---|---|
| 模型可放入一个节点 | 先仅使用 TP 并进行基准测试,然后再添加 PP。 |
| 模型跨多个节点 | 在节点内使用 TP,在节点间使用 PP。 |
| 需要多个服务副本 | 先构建一个 TP/PP 副本,然后仅在支持的模型、算子和网络拓扑上添加 DP。 |
| 长上下文 Prefill 需要 PP 调优 | 从 PP 开始,然后评估动态分块流水线并行。 |
配置层分区¶
层分区决定每个 PP 阶段执行目标模型隐藏层的哪个范围。如果目标模型有 N 个隐藏层且 PP 大小为 P,则 PP rank i 选择的阶段使用半开区间:
只有目标模型的 Transformer 隐藏层参与此分区。
自动分区¶
默认情况下,vLLM 会尽可能均匀地分配隐藏层。当层数不能被 PP 大小整除时,vLLM 会从倒数第二个阶段开始,向较低的 PP 排名分配剩余的层。这样可以避免给最后一个阶段增加更多隐藏层,因为最后一个阶段通常还运行最终的归一化、LM 头、logits 和采样。当阶段数超过两个时,较小的余数会放置在中间阶段,以减少输入和输出阶段的压力。
对于具有 61 个隐藏层的目标模型:
PP2: [31, 30]
PP rank 0: [0, 31)
PP rank 1: [31, 61)
PP4: [15, 15, 16, 15]
PP rank 0: [0, 15)
PP rank 1: [15, 30)
PP rank 2: [30, 46)
PP rank 3: [46, 61)
首先使用自动分区。仅在测量显示阶段级内存或延迟不平衡时,才进行自定义。
自定义分区¶
将 VLLM_PP_LAYER_PARTITION 设置为每个 PP 阶段执行的目标隐藏层数,按 PP 排名顺序排列:
export VLLM_PP_LAYER_PARTITION="32,29"
vllm serve /path/to/61-layer-model \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--trust-remote-code
在此示例中:
分区必须满足以下所有规则:
- 每个 PP 排名使用一个正整数,用逗号分隔。
- 条目数必须等于
--pipeline-parallel-size。 - 总和必须等于目标模型的隐藏层数。
- 不要包含嵌入、最终归一化、LM 头或草稿模型层。
- 在一个服务实例中,所有 worker 必须使用相同的值。
- 在 Ray 集群上,在启动 Ray 之前在每个节点上设置该值。
- 更改值后重启服务。
无效的条目数或层数总和会导致启动失败。
调整阶段平衡¶
阶段平衡取决于工作负载。最后一个阶段通常需要额外的内存和计算资源,用于最终的归一化、LM 头、logits、采样,以及可能的本地投机解码草稿模型。
使用以下调优循环:
- 从自动分区开始。
- 在代表性工作负载下,记录每个 PP 阶段中 worker 的峰值内存和步进延迟。
- 如果最后一个阶段是瓶颈,则每次将一个目标隐藏层从最后一个阶段移动到 PP 排名较低的前一个阶段,例如从
[31,30]改为[32,29]。 - 每次更改后重复准确性和性能测试。
移动过多层可能只是将瓶颈转移到另一个阶段,因此不要仅根据内存容量来选择分区。
高级场景¶
带投机解码的 PP¶
VLLM_PP_LAYER_PARTITION 仅对目标模型进行分区。草稿模型层不包含在其层数总和中,其权重命名和层布局取决于具体的投机解码实现。
在当前 Model Runner V1 本地 MTP 和 EAGLE 提议者路径中,草稿模型加载在最后一个 PP 阶段上,而不是跨 PP 阶段分区。因此,最后一个阶段可以包含:
- 其目标模型的隐藏层。
- 目标模型的输出侧模块。
- 本地草稿模型及其支持模块。
如果最后一个排名受到内存或延迟的限制,请使用自定义分区减少其目标隐藏层。draft_tensor_parallel_size 仅控制草稿模型的 TP 大小;它不定义草稿模型的 PP 分区。
投机解码的支持因模型、方法和模型运行器而异。在将其与 PP 一起启用之前,请参阅投机解码和相关模型教程。
P/D 分离服务中的 PP¶
PP 通常在 Prefill 节点上启用,而 Decode 节点保持 PP1。使用 MooncakeConnectorV1 时,Prefill 和 Decode 配置都必须描述实际的 Prefill 拓扑。
| 设置 | Prefill 节点 | Decode 节点 |
|---|---|---|
--pipeline-parallel-size |
实际的 Prefill PP 大小,例如 2。 |
当前为 1。 |
prefill.pp_size |
实际的 Prefill PP 大小。 | 相同的 Prefill PP 大小。 |
prefill.pp_layer_partition |
自定义 Prefill 分区(如果使用)。 | 相同的自定义 Prefill 分区。 |
decode.pp_size |
1 |
1 |
VLLM_PP_LAYER_PARTITION |
当 Prefill 节点使用自定义分区时设置。 | 不要将 Prefill 值复制到 PP1 Decode 进程。 |
对于使用 TP8 PP2 和自定义分区 [32,29] 的 61 层 Prefill 模型,请在两侧的 kv_connector_extra_config 中包含以下拓扑:
{
"prefill": {
"dp_size": 1,
"tp_size": 8,
"pp_size": 2,
"pp_layer_partition": "32,29"
},
"decode": {
"dp_size": 1,
"tp_size": 1,
"pp_size": 1
}
}
Prefill 进程还必须导出:
如果使用自动分区,请在两侧省略 pp_layer_partition。当前的 Mooncake 连接器要求 Prefill TP 大小大于或等于 Decode TP 大小,并且是其整数倍。
有关完整的 P/D 启动命令,请参阅使用 Mooncake 进行 PD 分离。有关长上下文 Prefill 优化,请参阅动态分块流水线并行。
性能特征:PP 与 TP 的比较¶
PP 和 TP 以不同的方式分布模型执行,因此两种策略都不是普遍更快的。TP 在 Transformer 层内对支持的操作进行分区。TP worker 参与每一层,并使用集合通信来合并部分结果,而未分区的组件可以保持复制。PP 将连续的层分配给不同的阶段,并在相邻阶段之间传输中间张量。在混合拓扑中,TP 通信保持在每个 PP 阶段内部,而 PP 通信连接各个阶段。
PP 相对于大型跨节点 TP 组的主要性能优势是通信局部性,而不是单个层内更快的计算。
| 方面 | 张量并行 | 流水线并行 | 性能影响 |
|---|---|---|---|
| 模型划分 | 每个TP rank参与每一层,并持有每个组件的分片或副本。 | 每个PP阶段仅执行其分配到的层;TP还可以对这些层进行分片。 | PP可以在不创建非常大的TP组的情况下扩展模型容量。 |
| 通信 | 集体通信操作可能发生在许多Transformer层内部。 | 中间张量在相邻的PP阶段之间传输。 | 将TP保持在每个节点内通常可以减少跨节点通信的频率。 |
| Single-request latency | All TP workers contribute to the same layer concurrently. | A request traverses the PP stages in sequence. | TP usually favors latency-sensitive, low-concurrency workloads when the TP interconnect is fast. |
| 稳态吞吐量 | 大型TP组可能因集体通信延迟和更小的每rank矩阵运算而损失效率。 | 由独立请求或Prefill块形成的并发调度器批次可以使不同阶段保持忙碌。 | 当流水线有足够的工作且各阶段均衡时,PP可以提高吞吐量。 |
| 内存 | 权重在每一层内进行分片;某些张量可以保持复制。 | 权重和KV缓存按层划分,而边缘阶段还持有输入侧或输出侧的模块。 | 两者都减少了每rank的内存,但相同的并行大小并不能保证相同的峰值内存。 |
| 扩展限制 | 可用的TP大小可能受到注意力头、KV头、隐藏维度、专家路由或支持的kernel的限制。 | 模型必须支持PP并且具有可划分的层。 | 当增加TP无效或不再高效时,PP非常有用。 |
| 主要瓶颈 | 缓慢的集体通信会延迟TP组中的每个rank。 | 最慢的阶段及其边界传输限制了流水线。 | TP依赖于集体通信效率;PP依赖于阶段均衡和激活传输效率。 |
流水线气泡与负载影响¶
PP会在工作进入和离开流水线时,以及某个阶段比其他阶段耗时更长时引入空闲时间。这种空闲时间通常被称为流水线气泡。相等的层数并不能保证相等的阶段延迟:第一个和最后一个阶段还可能执行嵌入、最终归一化、LM头、logits处理、采样或投机解码草稿模型。
| 负载特征 | 预期行为 |
|---|---|
| 单个请求或低并发Decode | 几乎没有独立的工作来占用多个阶段。与高效的节点本地TP基线相比,阶段遍历和通信通常会增加延迟。对于跨节点的TP组,请测量两种配置,因为跨节点集体通信延迟可能会反转结果。 |
| 高并发与连续批处理 | 并发调度器批次可以占用不同的阶段,减少相对流水线气泡并提高吞吐量。 |
| 长上下文Prefill | 每个块更多的计算可以摊销PP通信,并且PP可以提供长上下文所需的内存容量。 |
| 可变的提示长度 | 批次和块持续时间各不相同,因此固定长度的基准测试可能掩盖阶段不平衡。 |
| 不平衡的层划分 | 最慢的阶段使其他阶段等待,并限制了稳态吞吐量。 |
因此,当TP可以保持在每个节点内、阶段可以通过测量的延迟和内存进行均衡,并且负载提供足够的并发请求或Prefill块来保持流水线忙碌时,PP更有可能优于跨节点TP。当模型适合单个节点、低并发延迟是主要目标或阶段无法均衡时,TP仍然是首选基线。
对于长上下文Prefill负载,请考虑动态分块流水线并行,它使用测量的执行时间来调整块大小,以减少阶段空闲时间。
兼容性与限制¶
- 当前版本中,PP不能与Prefill上下文并行(PCP)结合使用。请参阅上下文并行。
- Xlite图模式与PP不兼容。
- 稀疏KV缓存卸载不支持PP。
MooncakeConnectorV1目前要求Decode侧PP大小为1。- 对于通过RoCE的跨节点MoE部署,PP和DP目前不能同时启用,因为
MoeDistributeDispatch通信路径不支持这种组合拓扑。 - 功能组合仍然是模型特定的。在生产部署之前,请查阅相关的功能指南。
故障排除¶
| 症状 | 检查与操作 |
|---|---|
| 启动时报告资源不足 | 确认可见的NPU满足所选的DP/PP/TP拓扑,并且Ray能看到每个预期的NPU。 |
启动时拒绝VLLM_PP_LAYER_PARTITION |
检查条目数是否等于PP大小,并且总和是否等于目标隐藏层数。 |
| 某个PP rank内存不足 | 检查它是否是最后一个阶段或托管了本地草稿模型。测量所有rank,然后一次移动一个目标层。 |
| 第一个请求挂起 | 使用较小的序列和批次限制重试。使用--enforce-eager来确定是否涉及图捕获,并验证rank间通信。 |
| MP工作节点无法加入头节点 | 验证两个节点使用相同的主地址、主端口、节点数量、模型和并行大小;每个节点排名唯一;主端口可访问。工作节点命令必须包含 --headless。 |
| Ray部署挂起或缺少排名 | 验证每个节点上的环境、模型路径、通信变量和Ray资源一致。 |
| 启用PP和DP后,跨节点RoCE MoE部署失败或挂起 | 将PP大小或DP大小保持为1;当前的MoeDistributeDispatch路径不支持这种组合拓扑。 |
| P/D握手超时 | 比较P/D连接器拓扑、prefill.pp_size、可选的prefill.pp_layer_partition以及可用的kv_port范围。 |