动态分块流水线并行¶
Note
设计细节和数学模型请参见设计文档。部署教程请参见动态分块流水线并行教程。
概述¶
动态分块流水线并行(CPP)是一种基于性能分析的动态分块策略,用于优化流水线并行(PP)场景下长序列的预填充性能。CPP 专为预填充-解码(PD)分离部署中的预填充器(P)节点设计。 通过基于性能分析数据动态计算最优分块大小,CPP 显著降低了 P 节点上长序列的首令牌时间(TTFT)。
Important
在PD分离部署中,CPP应配置在P(预填充)节点上。D(解码)节点不需要配置CPP。有关PD分离部署的指导,请参考以下教程:
使用场景¶
- PD分离P节点:在预填充节点上启用CPP,通过流水线并行优化长序列预填充。解码节点不需要CPP。
- 变长序列服务:PP不会对短序列引入性能下降,并通过动态分块在长序列上获得收益。
- 长序列推理:对于超出单机内存容量的序列,动态分块显著减少流水线空闲时间。
支持场景¶
CPP专注于在PD分离场景中的P节点上的预填充阶段进行优化。CPP推荐用于PD(预填充/解码)分离场景。支持的功能如下:
| Eager | Graph | Prefix Cache |
Chunked Prefill |
Flashcomm1 | |
|---|---|---|---|---|---|
| CPP | ✅ | ✅ | ✅ | ✅ | ✅ |
启用方法¶
PD 分离部署示例¶
在 PD 分离部署中,仅在 P(预填充器)节点 上启用 CPP。以下是使用 MooncakeConnector 实现 1P1D 架构的完整示例。
注意:
- CPP supports asynchronous scheduling (
--async-scheduling) only with Model Runner V2 (VLLM_USE_V2_MODEL_RUNNER=1). Enabling asynchronous scheduling together with CPP on Model Runner V1 fails startup with a configuration error. Short Request First (SRF) can be combined with CPP asynchronous scheduling; the CPP async scheduler installs the SRF waiting queue. - It is recommended to use
MooncakeConnectorV1as thekv_connector, as it provides more comprehensive support for 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
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 | 预填充的所有端口 |
| --decoder-ports | 解码器的所有端口 |
使用代理服务器端点检查服务健康状态。
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
}'
PD 分离与 CPP 的关键点:
配置参数调优¶
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
enabled |
bool | False | 启用/禁用动态分块流水线并行 |
smooth_factor |
float | 1.0 | 平滑因子(0 < x ≤ 1.0)。值越大,越信任动态预测结果 |
min_chunk |
int | 4096 | Minimum chunk size for dynamic calculation |
need_timing |
bool | True | 启用/禁用在线校准 |
max_fit_chunk |
int | 30 | Number of chunk-time data for Online Calibration |
需要调整--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进行性能评估。
-
修改
<YOUR_AISBENCH_PATH>/benchmark/ais_bench/datasets/synthetic/synthetic_config.py: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} } }, } -
运行在线校准:
配置在线校准数据长度以匹配max-model-len。使用batch_size=1,并确保数据不同以避免启用前缀缓存时的缓存命中。
性能¶
详情请参见使用AISBench进行性能评估。
为了评估动态分块流水线并行在长序列LLM推理场景中的效果,我们使用DeepSeek-V3.1-W8A8和Qwen3-235B,在昇腾Atlas A3推理产品(64 GB,A3)上部署了一个P(预填充)实例,配置和性能数据如下。
固定长度请求,并发数=1:
-
DeepSeek-V3.1-W8A8:
配置 CPP
(动态分块,
分块大小=32k)PP
(静态分块,
分块大小=32k)输入长度128k TTFT: 22.5s TTFT: 27.0s -
Qwen3-235B:
配置 CPP
(动态分块,
分块大小=32k)PP
(静态分块,
分块大小=32k)输入长度256k TTFT: 53.5s TTFT: 61.4s
变长请求,并发数=4:
-
DeepSeek-V3.1-W8A8:
配置 4k~64k 输入, 均值=32k, 标准差=32k
前缀命中率=99%CPP2TP8 输入吞吐量:22424 tps/卡 DP2TP8 输入吞吐量:16150 tps/卡 TP16 输入吞吐量:18875 tps/卡
约束条件¶
- 需要流水线并行:
--pipeline-parallel-size > 1 - 需要分块预填充:
--enable-chunked-prefill - 与均衡调度不兼容:无法启用
additional_config.scheduler_config.enable_balance_scheduling - 启动开销:性能分析会增加约64次前向传播(数十秒)