跳转至

动态分块流水线并行 (DeepSeek-V3.1)

快速开始

vLLM-Ascend 支持动态分块流水线并行(CPP),用于优化流水线并行场景下的预填充性能。此功能自版本 v0.19.1rc1 起支持。

Important

CPP 专为预填充-解码(PD)分离部署中的 P(预填充)节点设计。 D(解码)节点无需配置 CPP。通过基于性能分析数据动态计算最优分块大小,CPP 可显著降低 P 节点上长序列的首令牌时间(TTFT)。

本指南演示了在 3 台 Atlas 800T A3 服务器(64GB × 16)上使用 DeepSeek-V3.1 进行 PD 分离部署:一台服务器作为预填充节点(P 节点,启用 CPP),两台服务器作为解码节点(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(预填充)节点上启用 CPP。D(解码)节点不启用流水线并行,专注于低延迟的逐令牌解码。

  • 建议使用 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

在与预填充服务实例相同的节点上运行代理服务器。您可以在仓库的示例中获取代理程序: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 预填充的所有端口
--decoder-ports 解码器的所有端口

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

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
    }'

要验证 P 节点上的 CPP 是否已激活,请检查启动日志中与性能分析相关的消息,例如 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-tokens
  • profiling_chunk_config.need_timing:启用/禁用在线校准
  • profiling_chunk_config.max_fit_chunk:在线校准的分块时间数据数量。当性能分析失败时应增加

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

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

配置详情请参阅特性指南

在线校准

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

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

  1. 修改 <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}
            }
        },
    }
    
  2. 运行以进行在线校准:

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

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

精度评估

详情请参阅使用 AISBench

数据集 准确率
gsm8k 95.83

性能基准测试

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

为了评估动态分块流水线并行在 PD 分离长序列 LLM 推理场景中的有效性,我们使用 DeepSeek-V3.1-W8A8Qwen3-235B,在 Ascend Atlas 800T A3 服务器(64GB × 16)上部署预填充实例,配置和性能数据如下。

固定长度请求,并发数=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/卡