长序列上下文并行(Deepseek)¶
快速开始¶
Note
上下文并行功能目前仅在 Atlas A3 设备上支持,未来将支持 Atlas A2。
vLLM-Ascend 现已支持长序列的上下文并行功能。本指南将逐步介绍如何在有限资源下验证这些特性。
以 Deepseek-V3.1-w8a8 模型为例,使用 3 台 Atlas 800T A3 服务器部署“1P1D”架构。P 节点跨多机部署,D 节点部署在单机上。假设预填充服务器 IP 为 192.0.0.1(预填充 1)和 192.0.0.2(预填充 2),解码服务器 IP 为 192.0.0.3(解码 1)。每台服务器使用 8 个 NPU(16 个芯片)部署一个服务实例。在本示例中,我们将在 P 节点上启用上下文并行功能以改善 TTFT。尽管在 D 节点上启用 DCP 功能可以减少内存使用,但会引入额外的通信和小算子开销。因此,我们不在 D 节点上启用 DCP 功能。
环境准备¶
模型权重¶
DeepSeek-V3.1_w8a8mix_mtp(包含 mix mtp 的量化版本):下载模型权重。请在torch_dtype中将float16从bfloat16修改为config.json。
建议将模型权重下载到多节点共享目录,例如 /root/.cache/
验证多节点通信¶
请参考验证多节点通信环境来验证多节点通信。
安装¶
您可以使用我们官方的 Docker 镜像直接运行 DeepSeek-V3.1。
根据您的机器类型选择镜像,并在节点上启动 Docker 镜像,请参考使用 Docker 安装。
# Update the vllm-ascend image according to your environment.
export IMAGE=m.daocloud.io/quay.io/ascend/vllm-ascend:v0.22.1rc1
export NAME=vllm-ascend
# Run the container using the defined variables
# Note: If you are running bridge network with Docker, please expose available ports for multiple nodes communication in advance
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/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 /root/.cache:/root/.cache \
-it $IMAGE bash
您需要在每个节点上设置环境。
预填充/解码节点部署¶
我们可以运行以下脚本分别在预填充/解码节点上启动服务。请注意,每个 P/D 节点会占用从 kv_port 到 kv_port + num_chips 的端口来初始化 socket 监听。为避免出现问题,应防止端口冲突。此外,请确保每个节点的 engine_id 唯一分配,以避免冲突。
-
运行以下脚本,分别在三个节点上执行 128k 长度的在线推理。
```shell nic_name="eth0" # network card name local_ip="192.0.0.1" master_addr="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 HCCL_BUFFSIZE=768 export OMP_PROC_BIND=false export VLLM_ASCEND_ENABLE_FLASHCOMM1=1 export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True" export OMP_NUM_THREADS=1 export HCCL_OP_EXPANSION_MODE="AIV" export VLLM_USE_V1=1 export TASK_QUEUE_ENABLE=1 vllm serve /path_to_weight/DeepSeek-V3.1_w8a8mix_mtp \ --host 0.0.0.0 \ --port 8004 \ --decode-context-parallel-size 8 \ --prefill-context-parallel-size 2 \ --cp-kv-cache-interleave-size 128 \ --tensor-parallel-size 16 \ --enable-expert-parallel \ --quantization ascend \ --enforce-eager \ --served-model-name deepseek_v3 \ --seed 1024 \ --no-enable-chunked-prefill \ --no-enable-prefix-caching \ --max-num-seqs 1 \ --max-model-len 136000 \ --max-num-batched-tokens 136000 \ --block-size 128 \ --trust-remote-code \ --gpu-memory-utilization 0.8 \ --nnodes 2 \ --node-rank 0 \ --master-addr $master_addr \ --master-port 7001 \ --speculative-config '{"num_speculative_tokens": 3, "method":"deepseek_mtp"}' \ --kv-transfer-config \ '{"kv_connector": "MooncakeConnectorV1", "kv_role": "kv_producer", "kv_port": "30000", "kv_connector_extra_config": { "use_ascend_direct": true, "prefill": { "dp_size": 1, "tp_size": 16 }, "decode": { "dp_size": 1, "tp_size": 16 } } }' ``````shell nic_name="eth0" # network card name local_ip="192.0.0.2" master_addr="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 HCCL_BUFFSIZE=768 export OMP_PROC_BIND=false export VLLM_ASCEND_ENABLE_FLASHCOMM1=1 export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True" export OMP_NUM_THREADS=1 export HCCL_OP_EXPANSION_MODE="AIV" export VLLM_USE_V1=1 export TASK_QUEUE_ENABLE=1 vllm serve /path_to_weight/DeepSeek-V3.1_w8a8mix_mtp \ --host 0.0.0.0 \ --port 8004 \ --decode-context-parallel-size 8 \ --prefill-context-parallel-size 2 \ --cp-kv-cache-interleave-size 128 \ --tensor-parallel-size 16 \ --enable-expert-parallel \ --quantization ascend \ --enforce-eager \ --served-model-name deepseek_v3 \ --seed 1024 \ --no-enable-chunked-prefill \ --no-enable-prefix-caching \ --max-num-seqs 1 \ --max-model-len 136000 \ --max-num-batched-tokens 136000 \ --block-size 128 \ --trust-remote-code \ --gpu-memory-utilization 0.8 \ --nnodes 2 \ --node-rank 1 \ --headless \ --master-addr $master_addr \ --master-port 7001 \ --speculative-config '{"num_speculative_tokens": 3, "method":"deepseek_mtp"}' \ --kv-transfer-config \ '{"kv_connector": "MooncakeConnectorV1", "kv_role": "kv_producer", "kv_port": "30000", "kv_connector_extra_config": { "use_ascend_direct": true, "prefill": { "dp_size": 1, "tp_size": 16 }, "decode": { "dp_size": 1, "tp_size": 16 } } }' ``````shell 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 HCCL_BUFFSIZE=768 export OMP_PROC_BIND=false export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True" export OMP_NUM_THREADS=1 export HCCL_OP_EXPANSION_MODE="AIV" export VLLM_USE_V1=1 export TASK_QUEUE_ENABLE=1 vllm serve /path_to_weight/DeepSeek-V3.1_w8a8mix_mtp \ --host 0.0.0.0 \ --port 8004 \ --api-server-count 1 \ --data-parallel-size 1 \ --data-parallel-size-local 1 \ --data-parallel-start-rank 0 \ --data-parallel-address $local_ip \ --data-parallel-rpc-port 5980 \ --decode-context-parallel-size 1 \ --tensor-parallel-size 16 \ --enable-expert-parallel \ --quantization ascend \ --no-enable-prefix-caching \ --distributed-executor-backend mp \ --served-model-name deepseek_v3 \ --seed 1024 \ --max-model-len 136000 \ --max-num-batched-tokens 128 \ --enable-chunked-prefill \ --max-num-seqs 4 \ --trust-remote-code \ --gpu-memory-utilization 0.96 \ --additional-config '{"recompute_scheduler_enable": true}' \ --speculative-config '{"num_speculative_tokens": 3, "method":"deepseek_mtp"}' \ --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY", "cudagraph_capture_sizes":[1,2,4]}' \ --kv-transfer-config \ '{"kv_connector": "MooncakeConnectorV1", "kv_role": "kv_consumer", "kv_port": "30200", "kv_connector_extra_config": { "prefill": { "dp_size": 1, "tp_size": 16 }, "decode": { "dp_size": 1, "tp_size": 16 } } }' ``` -
Prefill master node
proxy.shscript -
运行代理
在与预填充服务实例相同的节点上运行代理服务器。您可以在仓库的示例中获取代理程序:load_balance_proxy_server_example.py
注意: The parameters are explained as follows:
--tensor-parallel-size16 是张量并行(TP)大小的常见设置。--prefill-context-parallel-size2 是预填充上下文并行(PCP)大小的常见设置。--decode-context-parallel-size8 是解码上下文并行(DCP)大小的常见设置。--max-model-len表示上下文长度,即单个请求的输入加输出的最大值。--max-num-seqs表示每个 DP 组允许处理的最大请求数。如果发送到服务的请求数超过此限制,超出部分将处于等待状态而不会被调度。请注意,等待状态所花费的时间也会计入 TTFT 和 TPOT 等指标中。因此,在测试性能时,通常建议--max-num-seqs*--data-parallel-size>= 实际总并发数。--max-num-batched-tokens表示模型单步能处理的最大 token 数量。目前,vLLM v1 调度默认启用了 ChunkPrefill/SplitFuse,这意味着:- (1)如果请求的输入长度大于
--max-num-batched-tokens,将按照--max-num-batched-tokens拆分为多轮计算; - (2)解码请求优先调度,仅在有剩余容量时才调度预填充请求。
- 通常,
--max-num-batched-tokens设置得越大,整体延迟越低,但对 NPU 内存(激活值占用)的压力越大。
- (1)如果请求的输入长度大于
--gpu-memory-utilization表示 vLLM 将用于实际推理的 HBM 占比。其核心作用是计算可用的 kv_cache 大小。在预热阶段(vLLM 中称为 profile run),vLLM 会记录输入大小为--max-num-batched-tokens的推理过程中的峰值 NPU 内存使用量。然后按如下方式计算可用 kv_cache 大小:--gpu-memory-utilization* HBM 大小 - 峰值 NPU 内存使用量。因此,--gpu-memory-utilization的值越大,可用的 kv_cache 就越多。然而,由于预热阶段的 NPU 内存使用量可能与实际推理时不同(例如由于 EP 负载不均),将--gpu-memory-utilization设置过高可能会导致实际推理时出现 OOM(内存不足)问题。默认值为0.9。--enable-expert-parallel表示启用 EP。请注意,vLLM 不支持 ETP 和 EP 混合的方式;即 MoE 只能使用纯 EP 或纯 TP。--no-enable-prefix-caching表示禁用前缀缓存。要启用前缀缓存,请移除该选项。--quantization"ascend" 表示使用量化。要禁用量化,请移除该选项。--additional-config '{"recompute_scheduler_enable": true}': enables the recomputation scheduler. When the Key-Value Cache (KV Cache) of the decode node is insufficient, requests will be sent to the prefill node to recompute the KV Cache. In the PD separation scenario, enable this configuration only on decode nodes.--compilation-configcontains configurations related to the aclgraph graph 模式. The most significant configurations are "cudagraph_模式" and "cudagraph_capture_sizes", which have the following meanings: "cudagraph_模式": represents the specific graph 模式. Currently, "PIECEWISE" and "FULL_DECODE_ONLY" are supported. The graph 模式 is mainly used to reduce the cost of operator dispatch. Currently, "FULL_DECODE_ONLY" is recommended.- "cudagraph_capture_sizes":表示图模式的不同级别。默认值为 [1, 2, 4, 8, 16, 24, 32, 40, ...,
--max-num-seqs]。在图模式下,不同级别的图输入是固定的,级别之间的输入会自动填充到下一级别。目前推荐使用默认设置。仅在少数场景下需要单独设置以获得最佳性能。 export VLLM_ASCEND_ENABLE_FLASHCOMM1=1表示启用 Flashcomm1 优化。目前,该优化仅支持在 tensor-parallel-size > 1 的场景下对 MoE 生效。
注意:
- tensor-parallel-size 需要能被 decode-context-parallel-size 整除。
- decode-context-parallel-size 必须小于或等于 tensor-parallel-size。
精度评估¶
使用 AISBench¶
-
详细信息请参考使用 AISBench 进行精度评估。
-
执行后即可获得结果,以下是
DeepSeek-V3.1-w8a8的结果,仅供参考。
| 数据集 | 版本 | 指标 | 模式 | vllm-api-生成eral-chat |
|---|---|---|---|---|
| aime2024 | - | 准确率 | 生成 | 86.67 |
性能¶
使用 AISBench¶
详细信息请参考使用 AISBench 进行性能评估。
使用 vLLM 基准测试¶
以 DeepSeek-V3.1-w8a8 为例运行性能评估。
更多详细信息请参考 vLLM 基准测试。
有三个 vllm bench 子命令:
latency:基准测试单批次请求的延迟。serve:基准测试在线服务的吞吐量。throughput:基准测试离线推理的吞吐量。
以 serve 为例,运行如下代码。
export VLLM_USE_MODELSCOPE=True
vllm bench serve --model /path_to_weight/DeepSeek-V3.1_w8a8mix_mtp --dataset-name random --random-input 131072 --num-prompts 20 --request-rate 0 --save-result --result-dir ./
大约几分钟后,即可获得性能评估结果。
| 数据集 | 版本 | 指标 | 模式 | ttft |
|---|---|---|---|---|
| 随机 | - | 性能 | perf | 20.7秒 |