动态分块流水线并行(DeepSeek-V3.1)¶
快速开始¶
vLLM-Ascend 支持动态分块流水线并行(CPP),用于优化流水线并行场景下的预填充性能。该功能从版本 v0.19.1rc1 开始支持。
Important
CPP 专为在 Prefill-Decode(PD)分离部署中的 P(Prefiller)节点上使用而设计。 D(Decoder)节点无需配置 CPP。CPP 基于 profiling 数据动态计算最优分块大小,可显著降低 P 节点上长序列的首 Token 时间(TTFT)。
本指南演示在 3 台 Atlas 800T A3 服务器(64GB × 16)上使用 DeepSeek-V3.1 进行 PD 分离部署:一台服务器作为 Prefiller(P 节点,启用 CPP),两台服务器作为 Decoder(D 节点,不启用 CPP,采用 DP32 标准配置)。
有关配置详情,请参阅功能指南。
有关设计详情,请参阅设计文档。
有关完整的 PD 分离部署说明(环境验证、Mooncake 安装、代理部署),请参阅:
环境准备¶
模型权重¶
DeepSeek-V3.1-W8A8(量化版本):1 个 Atlas 800T A3(64GB × 16)节点
下载到共享目录,例如 /mnt/weight/
使用 Docker 运行¶
在每个节点上启动一个 Docker 容器。
export IMAGE=m.daocloud.io/quay.io/ascend/vllm-ascend:v0.23.0
export NAME=vllm-ascend
docker run --rm \
--name $NAME \
--net=host \
--shm-size=1g \
--device /dev/davinci0 \
--device /dev/davinci1 \
--device /dev/davinci2 \
--device /dev/davinci3 \
--device /dev/davinci4 \
--device /dev/davinci5 \
--device /dev/davinci6 \
--device /dev/davinci7 \
--device /dev/davinci8 \
--device /dev/davinci9 \
--device /dev/davinci10 \
--device /dev/davinci11 \
--device /dev/davinci12 \
--device /dev/davinci13 \
--device /dev/davinci14 \
--device /dev/davinci15 \
--device /dev/davinci_manager \
--device /dev/devmm_svm \
--device /dev/hisi_hdc \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \
-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \
-v /etc/ascend_install.info:/etc/ascend_install.info \
-v /etc/hccn.conf:/etc/hccn.conf \
-v /mnt/weight:/mnt/weight \
-it $IMAGE bash
安装 Mooncake¶
PD 分离的 KV 缓存传输需要 Mooncake。有关安装和编译步骤,请参阅PD 分离多节点 - 安装 Mooncake。
部署¶
Important
在 PD 分离部署中,仅在 P(Prefiller)节点上启用 CPP。D(Decoder)节点不使用流水线并行运行,专注于低延迟的逐 Token 解码。
- 建议使用
MooncakeConnectorV1作为kv_connector,因为它对 PP 提供更全面的支持。 - 建议不要在 PP 的 P 节点上启用
async-scheduling,因为这可能导致预填充阶段性能下降。
假设 P 服务器 IP 为 192.0.0.1,D 服务器 IP 为 192.0.0.3(解码器 1)和 192.0.0.4(解码器 2)。
#!/bin/sh
unset https_proxy
unset http_proxy
# 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="eth0"
local_ip="192.0.0.1"
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 OMP_PROC_BIND=false
export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True"
export OMP_NUM_THREADS=1
export HCCL_BUFFSIZE=2048
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
export HCCL_OP_EXPANSION_MODE="AIV"
export VLLM_USE_V1=1
export TASK_QUEUE_ENABLE=1
export ASCEND_LAUNCH_BLOCKING=0
export VLLM_ALLOW_LONG_MAX_MODEL_LEN=1
export VLLM_ASCEND_ENABLE_FLASHCOMM1=1
export VLLM_RPC_TIMEOUT=3600000
export VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=30000
export HCCL_EXEC_TIMEOUT=204
export HCCL_CONNECT_TIMEOUT=120
vllm serve /mnt/weight/DeepSeek-V3.1-w8a8 \
--host 0.0.0.0 \
--port 8003 \
--served-model-name model \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--enable-expert-parallel \
--max-num-seqs 32 \
--max-model-len 131072 \
--max-num-batched-tokens 32768 \
--gpu-memory-utilization 0.9 \
--enable-chunked-prefill \
--enable-prefix-caching \
--no-async-scheduling \
--trust-remote-code \
--quantization ascend \
--additional-config '{
"scheduler_config": {
"profiling_chunk_config": {"enabled":true, "smooth_factor":1.0, "min_chunk":4096}
}
}' \
--kv-transfer-config \
'{
"kv_connector": "MooncakeConnectorV1",
"kv_role": "kv_producer",
"kv_port": "36000",
"engine_id": "0",
"kv_connector_extra_config": {
"prefill": {
"pp_size": 2,
"dp_size": 1,
"tp_size": 8
},
"decode": {
"dp_size": 32,
"tp_size": 1
}
}
}'
如果日志输出 vLLM API server started on 0.0.0.0:8003,则表示服务器已成功启动。
D 节点使用 launch_online_dp.py 在每个节点上启动多个 DP 实例。从仓库中获取启动器和模板脚本:
-
在每个 D 节点上修改
run_dp_template.sh。启动器会向模板传递七个位置参数($1–$7)。有关每个参数的完整说明,请参阅 PD 分离多节点。#!/bin/sh unset https_proxy unset http_proxy nic_name="eth0" # network card name local_ip="192.0.0.3" 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 OMP_PROC_BIND=false export OMP_NUM_THREADS=10 export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True export HCCL_BUFFSIZE=600 export TASK_QUEUE_ENABLE=1 export HCCL_OP_EXPANSION_MODE="AIV" export VLLM_USE_V1=1 export ASCEND_RT_VISIBLE_DEVICES=$1 vllm serve /mnt/weight/DeepSeek-V3.1-w8a8 \ --host 0.0.0.0 \ --port $2 \ --data-parallel-size $3 \ --data-parallel-rank $4 \ --data-parallel-address $5 \ --data-parallel-rpc-port $6 \ --tensor-parallel-size $7 \ --enable-expert-parallel \ --seed 1024 \ --served-model-name model \ --max-model-len 131072 \ --max-num-batched-tokens 256 \ --max-num-seqs 40 \ --trust-remote-code \ --gpu-memory-utilization 0.94 \ --quantization ascend \ --no-enable-prefix-caching \ --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \ --additional-config '{ "scheduler_config": {"recompute_scheduler_enable": true}, "multistream_overlap_shared_expert": true, "finegrained_tp_config": {"lmhead_tensor_parallel_size": 16} }' \ --kv-transfer-config \ '{ "kv_connector": "MooncakeConnectorV1", "kv_role": "kv_consumer", "kv_port": "36000", "engine_id": "0", "kv_connector_extra_config": { "prefill": { "pp_size": 2, "dp_size": 1, "tp_size": 8 }, "decode": { "dp_size": 32, "tp_size": 1 } } }'#!/bin/sh unset https_proxy unset http_proxy nic_name="eth0" # network card name local_ip="192.0.0.4" 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 OMP_PROC_BIND=false export OMP_NUM_THREADS=10 export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True export HCCL_BUFFSIZE=600 export TASK_QUEUE_ENABLE=1 export HCCL_OP_EXPANSION_MODE="AIV" export VLLM_USE_V1=1 export ASCEND_RT_VISIBLE_DEVICES=$1 vllm serve /mnt/weight/DeepSeek-V3.1-w8a8 \ --host 0.0.0.0 \ --port $2 \ --data-parallel-size $3 \ --data-parallel-rank $4 \ --data-parallel-address $5 \ --data-parallel-rpc-port $6 \ --tensor-parallel-size $7 \ --enable-expert-parallel \ --seed 1024 \ --served-model-name model \ --max-model-len 131072 \ --max-num-batched-tokens 256 \ --max-num-seqs 40 \ --trust-remote-code \ --gpu-memory-utilization 0.94 \ --quantization ascend \ --no-enable-prefix-caching \ --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \ --additional-config '{ "scheduler_config": {"recompute_scheduler_enable": true}, "multistream_overlap_shared_expert": true, "finegrained_tp_config": {"lmhead_tensor_parallel_size": 16} }' \ --kv-transfer-config \ '{ "kv_connector": "MooncakeConnectorV1", "kv_role": "kv_consumer", "kv_port": "36000", "engine_id": "0", "kv_connector_extra_config": { "prefill": { "pp_size": 2, "dp_size": 1, "tp_size": 8 }, "decode": { "dp_size": 32, "tp_size": 1 } } }'在每个 D 节点上启动服务:
# on 192.0.0.3 (decoder 1, DP rank 0–15) python launch_online_dp.py --dp-size 32 --tp-size 1 --dp-size-local 16 --dp-rank-start 0 --dp-address 192.0.0.3 --dp-rpc-port 12321 --vllm-start-port 7100 # on 192.0.0.4 (decoder 2, DP rank 16–31) python launch_online_dp.py --dp-size 32 --tp-size 1 --dp-size-local 16 --dp-rank-start 16 --dp-address 192.0.0.3 --dp-rpc-port 12321 --vllm-start-port 7100
在与 prefiller 服务实例相同的节点上运行一个代理服务器。您可以在仓库的 examples 目录中获取代理程序:load_balance_proxy_server_example.py
python load_balance_proxy_server_example.py \
--host 192.0.0.1 \
--port 8080 \
--prefiller-hosts 192.0.0.1 \
--prefiller-port 8003 \
--decoder-hosts \
192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 \
192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 192.0.0.3 \
192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 \
192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 192.0.0.4 \
--decoder-ports \
7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 \
7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115
| 参数 | 含义 |
|---|---|
| --port | 代理端口 |
| --prefiller-port | 所有 prefill 端口 |
| --decoder-ports | 所有 decoder 端口 |
使用代理服务器端点检查服务健康状态。
curl http://192.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "model",
"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 是否在 P 节点上生效,请检查启动日志中与 profiling 相关的消息,例如 profiling_chunk_config 已启用以及块大小计算输出。
关键参数
--pipeline-parallel-size 2:启用流水线并行(必需,仅限 P 节点)--enable-chunked-prefill:启用分块预填充(必需,仅限 P 节点)--max-num-batched-tokens 32768:初始块大小(推荐用于 128k 序列)profiling_chunk_config.enabled:启用动态分块流水线并行profiling_chunk_config.smooth_factor:平滑因子(0 < x ≤ 1.0)。值越高,越信任动态预测profiling_chunk_config.min_chunk:动态计算的最小块大小。应小于max-num-batched-tokensprofiling_chunk_config.need_timing:启用/禁用在线校准profiling_chunk_config.max_fit_chunk:在线校准的块时间数据数量。profiling 失败时应增大使用 CPP 的 PD 分离关键点:
有关配置详情,参见功能指南。
在线校准¶
为获得最佳性能,请在生产环境前使用真实数据进行在线校准:
您可以使用 aisbench 生成长度固定的随机数据集。有关详情,参见使用 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。
| 数据集 | 准确率 |
|---|---|
| gsm8k | 95.83 |
性能基准测试¶
有关详情,参见使用 AISBench 进行性能评估。
为评估动态分块流水线并行在 PD 分离长序列 LLM 推理场景中的效果,我们使用 DeepSeek-V3.1-W8A8 和 Qwen3-235B,在 Ascend Atlas 800T A3 服务器(64GB × 16)上部署 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
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 vl/vllm-ascend