跳转至

动态分块流水线并行

Note

有关设计细节和数学模型,请参阅设计文档。有关部署教程,请参阅动态分块流水线并行教程。

概述

动态分块流水线并行(CPP)是一种基于性能剖析的动态分块策略,用于优化流水线并行(PP)场景中长序列的预填充性能。CPP 设计用于在预填充-解码(PD)分离部署的预填充器(P)节点上使用。 通过基于性能剖析数据动态计算最优分块大小,CPP 可显著降低 P 节点上长序列的首 Token 时间(TTFT)。

Important

在 PD 分离部署中,CPP 应配置在 P(预填充器)节点 上。D(解码器)节点无需配置 CPP。有关 PD 分离部署指南,请参阅以下教程:

何时使用

  • PD 分离 P 节点:在预填充器节点上启用 CPP,以使用流水线并行优化长序列预填充。解码器节点无需 CPP。
  • 变长序列服务:PP 不会在短序列上引入性能下降,并通过长序列上的动态分块获得收益。
  • 长序列推理:对于超出单机内存容量的序列,动态分块可显著减少流水线空闲时间。

支持场景

CPP 专注于 PD 分离场景中 P 节点 上预填充阶段的优化。CPP 推荐用于 PD(预填充/解码)分离场景。支持的功能如下:

Eager Graph 前缀
缓存
分块
预填充
Flashcomm1
CPP ✅ ✅ ✅ ✅ ✅

如何启用

PD 分离部署示例

在 PD 分离部署中,仅 在 P(预填充器)节点 上启用 CPP。下面是一个使用 MooncakeConnector 的 1P1D 架构完整示例。

注意:

  • 目前已知 async-scheduling 可能导致 PP 预填充阶段性能下降,并且 async-scheduling 对预填充收益很小。因此,目前建议不要在 PP 的 P 节点上启用异步调度。
  • 建议使用 MooncakeConnectorV1 作为 kv_connector,因为它对 PP 提供更全面的支持。
# For nic_name, run the `ifconfig` command to check the network adapter whose IP address is the same as that of the local host.
nic_name=<COMMAND_RESULT>
local_ip=<YOUR_MACHINE_IP>

export HCCL_IF_IP=$local_ip
export GLOO_SOCKET_IFNAME=$nic_name 
export TP_SOCKET_IFNAME=$nic_name
export HCCL_SOCKET_IFNAME=$nic_name
export VLLM_ALLOW_LONG_MAX_MODEL_LEN=1

vllm serve Qwen/Qwen3-30B-A3B \
    --host 0.0.0.0 \
    --port 13700 \
    --served-model-name "qwen" \
    --tensor-parallel-size 2 \
    --pipeline-parallel-size 2 \
    --enforce-eager \
    --max-model-len 131072 \
    --max-num-batched-tokens 32768 \
    --enable-prefix-caching \
    --no-async-scheduling \
    --hf-overrides '{"rope_theta": 1000000, "rope_scaling": {"rope_type": "yarn", "factor": 4.0, "original_max_position_embeddings": 40960}, "max_model_len": 262144}' \
    --additional-config '{"scheduler_config": {"profiling_chunk_config": {"enabled": true}}}' \
    --kv-transfer-config \
    '{
        "kv_connector": "MooncakeConnectorV1",
        "kv_role": "kv_producer",
        "kv_port": "30000",
        "engine_id": "0",
        "kv_connector_extra_config": {
            "prefill": {
                "pp_size": 2,
                "dp_size": 1,
                "tp_size": 2
            },
            "decode": {
                "dp_size": 2,
                "tp_size": 2
            }
        }
    }'
# For nic_name, run the `ifconfig` command to check the network adapter whose IP address is the same as that of the local host.
nic_name=<COMMAND_RESULT>
local_ip=<YOUR_MACHINE_IP>

export HCCL_IF_IP=$local_ip
export GLOO_SOCKET_IFNAME=$nic_name 
export TP_SOCKET_IFNAME=$nic_name
export HCCL_SOCKET_IFNAME=$nic_name
export VLLM_ALLOW_LONG_MAX_MODEL_LEN=1

vllm serve Qwen/Qwen3-30B-A3B \
    --host 0.0.0.0 \
    --port 13701 \
    --served-model-name "qwen" \
    --data-parallel-size 2 \
    --tensor-parallel-size 2 \
    --enable-prefix-caching \
    --max-model-len 131072 \
    --max-num-batched-tokens 256 \
    --gpu-memory-utilization 0.9 \
    --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
    --hf-overrides '{"rope_theta": 1000000, "rope_scaling": {"rope_type": "yarn", "factor": 4.0, "original_max_position_embeddings": 40960}, "max_model_len": 262144}' \
    --kv-transfer-config \
    '{
        "kv_connector": "MooncakeConnectorV1",
        "kv_role": "kv_consumer",
        "kv_port": "30000",
        "engine_id": "0",
        "kv_connector_extra_config": {
            "prefill": {
                "pp_size": 2,
                "dp_size": 1,
                "tp_size": 2
            },
            "decode": {
                "dp_size": 2,
                "tp_size": 2
            }
        }
    }'

在预填充器服务实例所在的同一节点上运行一个代理服务器。您可以从仓库的示例中获取代理程序:load_balance_proxy_server_example.py

shell python load_balance_proxy_server_example.py \ --host <PROXY_IP> \ --port 8080 \ --prefiller-hosts <PREFILL_MACHINE_IP> \ --prefiller-port 13700 \ --decoder-hosts <DECODE_MACHINE_IP> \ --decoder-ports 13701

| 参数 | 含义 |
| --- | --- |
| --port | 代理服务器的端口 |
| --prefiller-port | prefill 的所有端口 |
| --decoder-ports | decoder 的所有端口 |

使用代理服务器的端点检查服务健康状态。

shell curl http://<PROXY_IP>:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen", "messages": [ { "role": "system", "content": "You are a useful AI assistant." }, { "role": "user", "content": "Question: Janet'\''s ducks lay 16 eggs per day. She eats three for breakfast and bakes muffins with four. She sells the remainder for $2 each. How much does she make?\nAnswer:" } ], "max_completion_tokens": 100, "temperature": 0 }'

使用 CPP 进行 PD 分离的关键点:

  • CPP(profiling_chunk_config.enabled、--pipeline-parallel-size > 1)仅在 P 节点上配置。
  • D 节点不运行流水线并行——它专注于低延迟的逐 token 解码。
  • 有关完整的 PD 分离设置说明(环境验证、Mooncake 安装、代理部署),请参阅:

配置参数调优

参数 类型 默认值 描述
enabled bool False 启用/禁用动态分块流水线并行
smooth_factor float 1.0 平滑因子(0 < x ≤ 1.0)。值越高,越信任动态预测
min_chunk int 4096 动态计算的最小分块大小
need_timing bool True 启用/禁用在线校准
max_fit_chunk int 30 在线校准所需的分块时间数据数量

需要调整 --max-num-batched-tokens 和 smooth_factor 参数。建议先调整 --max-num-batched-tokens 参数,再调整 smooth_factor。

1. --max-num-batched-tokens

值得注意的是,CPP 的 TTFT 对 --max-num-batched-tokens(被视为动态分块计算的初始分块大小)非常敏感。 因为如果该值过大,会引入大量计算浪费;如果过小,则会导致算子效率下降。

我们建议先在不启用动态分块的情况下,使用固定分块大小进行优化。典型的优化搜索空间为 [8192, 16384, 24576, 32768]。启用动态分块时的最佳 --max-num-batched-tokens 通常是固定分块的 2 到 4 倍。

同时,为了给动态调整留出足够空间,我们建议处理的序列越长,--max-num-batched-tokens 应设置得越大。推荐值:

序列长度 --max-num-batched-tokens
64k 20480
128k 32768

2. smooth_factor

控制对动态预测的信任程度

  • 1.0:严格遵循模型预测
  • 0.6~0.85:在动态调整和调度开销之间取得平衡
  • 0.0:不进行动态调整(退化为固定分块)

启用 need_timing 以进行正确的在线校准,通常可以获得更准确的分块时延拟合结果。因此,建议在启用 need_timing 时不要调整 smooth_factor。如果禁用了 need_timing,可以将 smooth_factor 调整为 0.5 到 0.8 之间的值,以达到启用 need_timing 的效果。

3. min_chunk

通常不需要调整。应小于 max-num-batched-tokens。

4. max_fit_chunk

通常不需要调整。

在线校准

Important

目前,need_timing 默认启用,即在线校准默认启用。因此,务必以标准方式完成在线校准过程,否则会造成严重的性能恶化。

或者,您可以将 need_timing 设置为 False 以跳过在线校准过程,但性能结果可能不是最优的。

为获得最佳性能,请在投入生产前使用真实数据进行在线校准:

您可以使用 ais_bench 生成固定长度的随机数据集。详情请参阅 使用 AISBench 进行性能评估。

  1. 修改 <YOUR_AISBENCH_PATH>/benchmark/ais_bench/datasets/synthetic/synthetic_config.py:

    python synthetic_config = { "Type": "string", "RequestCount": 5, "TrustRemoteCode": False, "StringConfig": { "Input": { "Method": "uniform", "Params": {"MinValue": 131072, "MaxValue": 131072} # Your max sequence length, max-model-len }, "Output": { "Method": "uniform", "Params": {"MinValue": 1, "MaxValue": 1} } }, }

  2. 运行以进行在线校准:

    bash ais_bench --models vllm_api_stream_chat --datasets synthetic_gen --mode perf --debug

配置在线校准数据长度,使其与您的 max-model-len 匹配。使用 batch_size=1,并确保数据互不相同,以避免在启用前缀缓存时发生缓存命中。

性能

详情请参阅 使用 AISBench 进行性能评估。

为了评估动态分块流水线并行(Dynamic Chunked Pipeline Parallel)在长序列 LLM 推理场景中的有效性,我们使用 DeepSeek-V3.1-W8A8 和 Qwen3-235B,在 Ascend Atlas A3 推理产品(64 GB,A3)上部署了一个 P(Prefill)实例,配置和性能数据如下。

固定长度请求,并发数=1:

  • DeepSeek-V3.1-W8A8:

    配置 CPP
    (动态分块,
    chunksize=32k)
    PP
    (静态分块,
    chunksize=32k)
    输入长度 128k TTFT: 22.5s TTFT: 27.0s
  • Qwen3-235B:

    配置 CPP
    (动态分块,
    chunksize=32k)
    PP
    (静态分块,
    chunksize=32k)
    输入长度 256k TTFT: 53.5s TTFT: 61.4s

变长请求,并发数=4:

  • DeepSeek-V3.1-W8A8:

    配置 输入 4k~64k, mean=32k, std=32k
    前缀命中率=99%
    CPP2TP8 输入吞吐率: 22424 tps/card
    DP2TP8 输入吞吐率: 16150 tps/card
    TP16 输入吞吐率: 18875 tps/card

约束

  • 需要启用流水线并行(Pipeline Parallelism):--pipeline-parallel-size > 1
  • 需要启用分块预填充(Chunked Prefill):--enable-chunked-prefill
  • 与 Balance Scheduling 不兼容:不能启用 additional_config.scheduler_config.enable_balance_scheduling
  • 启动开销:Profiling 会增加约 64 次前向传播(耗时数十秒)

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 vl/vllm-ascend