跳转至

Qwen3.5-397B-A17B

1 简介

Qwen3.5-397B-A17B 是一个大规模 Qwen3.5 MoE 模型,结合多模态能力、长上下文推理、MTP 推测解码以及 W8A8 量化部署,用于在 Ascend 硬件上进行生产级服务。

本文档介绍该模型的主要验证步骤,包括支持的功能、前提条件、安装、单节点在线部署、多节点部署、Prefill-Decode(PD)分离、功能验证、精度与性能评估、性能调优以及常见问题。

Qwen3.5-397B-A17B 模型首次支持于 vllm-ascend:v0.17.0rc1。使用该模型时请使用 v0.17.0rc1 或更高版本。对于 Ascend95DT,该模型从 vllm-ascend:v0.23.0rc1 开始支持。以下示例使用文档构建系统配置的版本占位符。

2 支持的功能

参见支持的功能以获取该模型支持的功能矩阵,包括 BF16、W8A8 量化、分块预填充、自动前缀缓存、推测解码、异步调度、张量并行、专家并行、数据并行、PD 分离和 ACLGraph 支持。

参见功能指南以获取功能配置详情。

:::{note} 支持矩阵记录的是该模型已验证的最大能力。本文档中的启动示例采用面向在线服务和性能测试的实用验证配置。请根据服务负载和可用 KV 缓存调整 --max-model-len、--max-num-seqs 和 --max-num-batched-tokens。 :::

3 前提条件

3.1 模型权重

权重版本 硬件要求 下载链接
Qwen3.5-397B-A17B(BF16 版本) 2 个 950DT Products(96GB x 8)节点或 2 个 Atlas 800 A3(64GB x 16)节点或 4 个 Atlas 800 A2(64GB x 8)节点 ModelScope
Qwen3.5-397B-A17B-w8a8(量化版本) 1 个 Atlas 800 A3(64GB x 16)节点或 2 个 Atlas 800 A2(64GB x 8)节点 ModelScope
Qwen3.5-397B-A17B-w4a8(量化版本) 1 个 Atlas 800 A3(64GB x 16)节点或 2 个 Atlas 800 A2(64GB x 8)节点 ModelScope
Qwen3.5-397B-A17B-w8a8-mxfp8(量化版本) 1 个 950DT Products(96GB x 8)节点 ModelScope
Qwen3.5-397B-A17B-w4a4-mxfp4(量化版本) 1 个 950DT Products(96GB x 8)节点 ModelScope

建议将模型权重下载到多节点共享目录中,例如 /root/.cache/,以便所有服务节点加载相同路径。

路径说明:将模型权重下载到您选择的目录并记录该目录。确保后续部署命令中的模型路径与该目录一致。

3.2 验证多节点通信(可选)

如果要在多节点环境中部署该模型,请按照验证多节点通信环境验证通信环境。

4 安装

4.1 Docker 镜像安装

请根据机器类型选择镜像,并在节点上启动 Docker 镜像,参见使用 Docker。

Qwen3.5-397B-A17B 模型首次支持于 vllm-ascend:v0.17.0rc1。使用该模型时请使用 v0.17.0rc1 或更高版本。对于 Ascend95DT,该模型从 vllm-ascend:v0.23.0rc1 开始支持。

在每个节点上启动 Docker 镜像。

export IMAGE=quay.io/ascend/vllm-ascend:v0.23.0-a5
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/davinci_manager \
  --device /dev/hisi_hdc \
  --device /dev/ummu \
  --device /dev/uburma \
  -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
  -v /etc/ascend_install.info:/etc/ascend_install.info \
  -v /etc/hccl_rootinfo.json:/etc/hccl_rootinfo.json \
  -v /etc/hixlep/:/etc/hixlep/ \
  -v /root/.cache:/root/.cache \
  -v /usr/local/sbin:/usr/local/sbin \
  -v /usr/local/dcmi:/usr/local/dcmi \
  -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
  -v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi \
  -v /usr/lib64:/usr/lib64 \
  -itd $IMAGE bash

在每个节点上启动 Docker 镜像。

export IMAGE=quay.io/ascend/vllm-ascend:v0.23.0-a3
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 /root/.cache:/root/.cache \
  -it $IMAGE bash

请在每个节点上启动 Docker 镜像。

export IMAGE=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/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

进入容器后,验证 vLLM 和 vLLM-Ascend 是否可以导入:

python -c "import vllm, vllm_ascend; print('vllm and vllm_ascend are ready')"

如果要部署多节点服务,请在每个节点上配置相同的环境。

4.2 源代码安装

您也可以从源代码构建并安装 vllm-ascend。请参考 使用 Python 设置。

如果要部署多节点服务,请在每个节点上安装相同版本的 vLLM 和 vLLM-Ascend。

5 在线服务部署

5.1 单节点在线部署

单节点部署在同一节点上同时运行 Prefill 和 Decode。它适用于功能验证和长上下文单集群服务。

运行以下脚本,在 1 台 950DT 产品(96G x 8)上执行在线推理。量化版本(Qwen3.5-397B-A17B-w8a8-mxfp8 和 Qwen3.5-397B-A17B-w4a4-mxfp4)可以部署在单个 950DT 产品节点上,需要 --quantization ascend。

#!/bin/sh

# Load model from ModelScope to speed up download.
export VLLM_USE_MODELSCOPE=True
export HCCL_BUFFSIZE=400
export HCCL_INTRA_ROCE_ENABLE=0
export HCCL_OP_EXPANSION_MODE="AIV"
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export VLLM_ASCEND_ENABLE_PREFETCH_MLP=1

# Reduce memory fragmentation and avoid out-of-memory errors.

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w4a4-mxfp4 \
  --host 0.0.0.0 \
  --port 8000 \
  --distributed-executor-backend mp \
  --data-parallel-size 1 \
  --tensor-parallel-size 8 \
  --enable-expert-parallel \
  --seed 1024 \
  --quantization ascend \
  --served-model-name qwen3.5 \
  --max-num-seqs 128 \
  --max-model-len 133000 \
  --max-num-batched-tokens 8192 \
  --trust-remote-code \
  --enable-prefix-caching \
  --gpu-memory-utilization 0.95 \
  --async-scheduling \
  --speculative-config '{"method": "qwen3_5_mtp", "num_speculative_tokens": 3}' \
  --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' \
  --additional-config '{"enable_cpu_binding":true}'

运行以下脚本,在 1 台 Atlas 800 A3(64GB x 16)上执行 128k 在线推理,并在 1 台 Atlas 800 A3(64GB x 16)节点上进行 W8A8 部署。W8A8 版本需要 --quantization ascend。

#!/bin/sh

# Load model from ModelScope to speed up download.
export VLLM_USE_MODELSCOPE=True
export HCCL_BUFFSIZE=1024
export HCCL_OP_EXPANSION_MODE="AIV"
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

# Reduce memory fragmentation and avoid out-of-memory errors.

echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
sysctl -w vm.swappiness=0
sysctl -w kernel.numa_balancing=0
sysctl kernel.sched_migration_cost_ns=50000

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w8a8-mtp \
  --host 0.0.0.0 \
  --port 8000 \
  --data-parallel-size 1 \
  --tensor-parallel-size 16 \
  --enable-expert-parallel \
  --seed 1024 \
  --quantization ascend \
  --served-model-name qwen3.5 \
  --max-num-seqs 128 \
  --max-model-len 133000 \
  --max-num-batched-tokens 16384 \
  --trust-remote-code \
  --gpu-memory-utilization 0.90 \
  --enable-prefix-caching \
  --speculative-config '{"method": "qwen3_5_mtp", "num_speculative_tokens": 3, "enforce_eager": true}' \
  --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' \
  --additional-config '{"enable_cpu_binding":true, "enable_fused_mc2":1}'

对于 W8A8 部署,需要 2 台 Atlas 800 A2(64G x 8)节点。多节点 MP 部署请参考 第 5.2 节。

常见问题提示:如果服务启动失败、HBM 不足,或请求未按预期调度,请先参考 公共常见问题,然后查看第 10 节中的模型特定常见问题。

关键参数:

  • --data-parallel-size 1 和 --tensor-parallel-size 16 为一台 16-NPU A3 节点设置 DP 和 TP。
  • --enable-expert-parallel 为 MoE 层启用专家并行。请勿在同一 MoE 层中混用 MoE 张量并行和专家并行。
  • --max-model-len 是单个请求的最大输入加输出长度。仅在 KV 缓存充足时才增大该值。
  • --max-num-seqs 是每个 DP 组调度的最大活动请求数。进行性能测试时,请将 --max-num-seqs * --data-parallel-size 设置为大于或等于测试并发数。
  • --max-num-batched-tokens 是单个调度步骤中处理的最大 token 数。较大的值可以提高 prefill 效率,但会消耗更多激活内存。
  • --gpu-memory-utilization 控制 vLLM 可用于计算 KV 缓存容量的 HBM 比例。较高的值会增大 KV 缓存大小,但如果运行时内存高于 profiling 阶段测得的内存,可能触发 OOM。
  • --enable-prefix-caching 启用前缀缓存。对于 Qwen3.5,当混合 KV 缓存管理器将块大小调整为较大值时,短前缀可能不会被缓存。
  • --quantization ascend 为 W8A8 模型启用 Ascend 量化。部署 BF16 模型时请移除此选项。
  • --speculative-config 启用 Qwen3.5 MTP 推测解码。如果工作负载对首 token 延迟敏感,或 MTP 在你的环境中不稳定,请减少 num_speculative_tokens 或移除此选项。
  • --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' 启用完整解码 ACLGraph 重放,以减少调度开销。
  • --additional-config 启用 Ascend 特定优化。enable_fused_mc2 启用 MoE 融合算子,enable_cpu_binding 启用 Ascend 原生 CPU 绑定。

多节点 MP 部署在节点之间使用 vLLM 数据并行,在每个节点内部使用张量并行。对于在 2 个 Atlas 800 A2(64GB x 8)节点上部署 W8A8 模型,推荐使用该方式。

假设你有 2 个 Atlas 800 A2 节点,并希望跨这些节点部署 Qwen3.5-397B-A17B-w8a8-mtp。请将 nic_name、local_ip 和 node0_ip 替换为你环境中实际的网络接口和 IP 地址。

在节点 0 上运行以下脚本。

#!/bin/sh

# Get these values through ifconfig.
# nic_name is the network interface name corresponding to local_ip.
nic_name="xxxx"
local_ip="xxxx"

export VLLM_USE_MODELSCOPE=True
export HCCL_BUFFSIZE=1024
export HCCL_IF_IP=$local_ip
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w8a8-mtp \
  --host 0.0.0.0 \
  --port 8000 \
  --data-parallel-size 2 \
  --api-server-count 2 \
  --data-parallel-size-local 1 \
  --data-parallel-address $local_ip \
  --data-parallel-rpc-port 13389 \
  --seed 1024 \
  --served-model-name qwen3.5 \
  --tensor-parallel-size 8 \
  --enable-expert-parallel \
  --max-num-seqs 16 \
  --max-model-len 32768 \
  --max-num-batched-tokens 4096 \
  --trust-remote-code \
  --gpu-memory-utilization 0.9 \
  --no-enable-prefix-caching \
  --quantization ascend \
  --speculative-config '{"method": "qwen3_5_mtp", "num_speculative_tokens": 3}' \
  --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' \
  --additional-config '{"enable_cpu_binding":true, "multistream_overlap_shared_expert": true}'

常见问题提示:如果节点 1 无法加入服务或 HCCL 初始化超时,请参考 验证多节点通信环境 和 常见问题。请确保各节点上的网络接口名称、IP 地址和 RPC 端口保持一致。

在节点 1 上运行以下脚本。

#!/bin/sh

# Get these values through ifconfig.
# nic_name is the network interface name corresponding to local_ip.
nic_name="xxxx"
local_ip="xxxx"

export VLLM_USE_MODELSCOPE=True
export HCCL_BUFFSIZE=1024
export HCCL_IF_IP=$local_ip
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

# The value of node0_ip must be consistent with local_ip on node 0.
node0_ip="xxxx"

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w8a8-mtp \
  --host 0.0.0.0 \
  --port 8000 \
  --headless \
  --data-parallel-size 2 \
  --data-parallel-size-local 1 \
  --data-parallel-start-rank 1 \
  --data-parallel-address $node0_ip \
  --data-parallel-rpc-port 13389 \
  --seed 1024 \
  --tensor-parallel-size 8 \
  --served-model-name qwen3.5 \
  --max-num-seqs 16 \
  --max-model-len 32768 \
  --max-num-batched-tokens 4096 \
  --enable-expert-parallel \
  --trust-remote-code \
  --gpu-memory-utilization 0.9 \
  --no-enable-prefix-caching \
  --quantization ascend \
  --speculative-config '{"method": "qwen3_5_mtp", "num_speculative_tokens": 3}' \
  --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' \
  --additional-config '{"enable_cpu_binding":true, "multistream_overlap_shared_expert": true}'

常见问题提示:如果无头节点立即退出,请检查节点 0 是否已在运行、--data-parallel-address 是否指向节点 0,以及 --data-parallel-start-rank 在每个节点上是否唯一。

如果服务启动成功,节点 0 上会显示以下信息:

INFO:     Started server process [44610]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Started server process [44611]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

MP 部署的关键参数:

  • --data-parallel-size 是所有节点间的全局 DP 大小。在示例中,使用了 2 个 DP rank。
  • --data-parallel-size-local 是当前节点上的 DP rank 数量。在示例中,每个 A2 节点有 1 个本地 DP rank。
  • --data-parallel-start-rank 是当前节点上的第一个 DP rank。节点 0 默认从 0 开始,节点 1 从 1 开始。
  • --data-parallel-address 必须指向主 DP 节点。在节点 0 上使用节点 0 的 local_ip,在其他节点上使用 node0_ip。
  • --data-parallel-rpc-port 是 DP RPC 端口。在所有节点上使用相同的值,并确保该端口可用。
  • --api-server-count 控制在主节点上启动多少个 API 服务器进程。
  • --headless 启动一个不暴露 API 服务器的工作节点。在非主节点上使用它。
  • --tensor-parallel-size 8 将一个 TP 组映射到每个 A2 节点上的 8 个 NPU。
  • HCCL_IF_IP、GLOO_SOCKET_IFNAME 和 HCCL_SOCKET_IFNAME 将 HCCL 和 Gloo 通信绑定到所选网络。
  • multistream_overlap_shared_expert 重叠共享专家计算,以提升 MoE 工作负载的吞吐量。

5.3 多节点 Ray 部署

对于基于 Ray 的分布式部署,请参考 Ray 分布式(Qwen/Qwen3-235B-A22B)。相同的模型权重、通信验证和参数调优原则同样适用于 Qwen3.5-397B-A17B。

常见问题提示:如果 Ray 工作节点无法互相发现,请先检查 Ray 集群状态,然后验证与 MP 部署中使用的相同 HCCL 和网络接口设置。

5.4 Prefill-Decode 分离(A3)

PD 分离将 Prefill 和 Decode 分离到不同的服务组中。Prefill 节点处理大型 Prompt 块,Decode 节点负责 Token 生成,代理在它们之间转发请求。该模式适用于需要分别调整 Prefill 和 Decode 资源比例的生产服务场景。

我们建议使用 Mooncake 进行部署。有关通用 PD 分离工作流,请参考 Mooncake。

对于 Atlas 800 A3(64G x 16),我们建议为 Qwen3.5-397B-A17B-w8a8-mtp 使用 2 个节点部署 1P1D:

  • 1 个 Prefill 节点:1 台 Atlas 800 A3(64G x 16)。
  • 1 个 Decode 节点:1 台 Atlas 800 A3(64G x 16)。

在对应节点上部署 run_p.sh 和 run_d.sh,并在 Prefill 主节点上部署代理脚本以转发请求。

5.4.1 Prefill 节点

在 Prefill 节点上创建 run_p.sh。

#!/bin/bash

unset ftp_proxy
unset https_proxy
unset http_proxy

# Get these values through ifconfig.
# nic_name is the network interface name corresponding to local_ip.
nic_name="xxxx"
local_ip="xxxx"

export IP_ADDRESS=$local_ip
export NETWORK_CARD_NAME=$nic_name
export HCCL_BUFFSIZE=1536
export HCCL_IF_IP=$IP_ADDRESS
export HCCL_OP_EXPANSION_MODE="AIV"
export HCCL_SOCKET_IFNAME=$NETWORK_CARD_NAME
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages:$LD_LIBRARY_PATH
export GLOO_SOCKET_IFNAME=$NETWORK_CARD_NAME
export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True"

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w8a8-mtp \
  --host ${IP_ADDRESS} \
  --port 30060 \
  --no-enable-prefix-caching \
  --enable-expert-parallel \
  --data-parallel-size 1 \
  --data-parallel-size-local 1 \
  --api-server-count 1 \
  --data-parallel-address ${IP_ADDRESS} \
  --max-num-seqs 64 \
  --data-parallel-rpc-port 6884 \
  --tensor-parallel-size 16 \
  --seed 1024 \
  --distributed-executor-backend mp \
  --served-model-name qwen3.5 \
  --max-model-len 16384 \
  --max-num-batched-tokens 4096 \
  --trust-remote-code \
  --quantization ascend \
  --no-disable-hybrid-kv-cache-manager \
  --speculative-config '{"method": "qwen3_5_mtp", "num_speculative_tokens": 3, "enforce_eager": true}' \
  --additional-config '{"enable_cpu_binding": true}' \
  --gpu-memory-utilization 0.9 \
  --enforce-eager \
  --kv-transfer-config \
  '{"kv_connector": "MooncakeLayerwiseConnector",
    "kv_role": "kv_producer",
    "kv_port": "23010",
    "kv_connector_extra_config": {
      "prefill": {
        "dp_size": 1,
        "tp_size": 16
      },
      "decode": {
        "dp_size": 1,
        "tp_size": 16
      }
    }
  }'

常见问题提示:如果 Prefill 服务长时间未就绪,请检查模型路径是否已共享、ASCEND_RT_VISIBLE_DEVICES 是否包含全部 16 个 NPU,以及 Mooncake 的 kv_port 是否可用。

5.4.2 Decode 节点

在 Decode 节点上创建 run_d.sh。

#!/bin/bash

unset ftp_proxy
unset https_proxy
unset http_proxy

# Get these values through ifconfig.
# nic_name is the network interface name corresponding to local_ip.
nic_name="xxxx"
local_ip="xxxx"

export IP_ADDRESS=$local_ip
export NETWORK_CARD_NAME=$nic_name
export HCCL_BUFFSIZE=1536
export HCCL_IF_IP=$IP_ADDRESS
export HCCL_OP_EXPANSION_MODE="AIV"
export HCCL_SOCKET_IFNAME=$NETWORK_CARD_NAME
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages:$LD_LIBRARY_PATH
export GLOO_SOCKET_IFNAME=$NETWORK_CARD_NAME
export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True"

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w8a8-mtp \
  --host ${IP_ADDRESS} \
  --port 30050 \
  --no-enable-prefix-caching \
  --enable-expert-parallel \
  --data-parallel-size 1 \
  --data-parallel-size-local 1 \
  --api-server-count 1 \
  --data-parallel-address ${IP_ADDRESS} \
  --max-num-seqs 32 \
  --data-parallel-rpc-port 6884 \
  --tensor-parallel-size 16 \
  --seed 1024 \
  --distributed-executor-backend mp \
  --served-model-name qwen3.5 \
  --max-model-len 16384 \
  --max-num-batched-tokens 128 \
  --trust-remote-code \
  --quantization ascend \
  --no-disable-hybrid-kv-cache-manager \
  --speculative-config '{"method": "qwen3_5_mtp", "num_speculative_tokens": 3}' \
  --additional-config '{"recompute_scheduler_enable": true, "enable_cpu_binding": true}' \
  --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
  --gpu-memory-utilization 0.96 \
  --kv-transfer-config \
  '{"kv_connector": "MooncakeLayerwiseConnector",
    "kv_buffer_device": "npu",
    "kv_role": "kv_consumer",
    "kv_port": "36010",
    "kv_connector_extra_config": {
      "prefill": {
        "dp_size": 1,
        "tp_size": 16
      },
      "decode": {
        "dp_size": 1,
        "tp_size": 16
      }
    }
  }'

常见问题提示:如果 Decode 节点初始化失败,请检查 --tensor-parallel-size 是否为 16、--data-parallel-size-local 是否与全局 Decode DP 大小(1)一致,以及 kv_connector_extra_config.decode.dp_size 是否与全局 Decode DP 大小一致。

PD 分离的关键参数:

  • --distributed-executor-backend mp 在每个节点上使用多进程来运行本地 worker。
  • Prefill 使用 --data-parallel-size 1、--data-parallel-size-local 1 和 --tensor-parallel-size 16。这会创建 1 个 DP 组,TP 为 16。
  • Decode 使用 --data-parallel-size 1、--data-parallel-size-local 1 和 --tensor-parallel-size 16。这会创建 1 个本地 DP 组,每个组的 TP 为 16。
  • --data-parallel-address 和 --data-parallel-rpc-port 定义 DP 控制平面。
  • --max-num-batched-tokens 在 Prefill 节点上较大,在 Decode 节点上较小,因为 Prefill 阶段 prompt token 密集,而 Decode 阶段对延迟敏感。
  • 当 Decode 的 KV cache 不足时,recompute_scheduler_enable 会将请求发回 Prefill 端重新计算 KV cache。在 PD 模式下,仅在 Decode 节点上启用该参数。
  • --kv-transfer-config 设置 Mooncake 连接器。Prefill 上 kv_role 为 kv_producer,Decode 上为 kv_consumer。
  • kv_connector_extra_config.prefill.dp_size/tp_size 和 decode.dp_size/tp_size 必须与实际的全局 DP 和 TP 布局一致。
  • --no-enable-prefix-caching 禁用前缀缓存。对于 PD 分离,D 节点前缀缓存的已知问题跟踪在 #7944。
  • 建议在 Decode 节点上使用 --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}',以减少 Decode 调度开销。

5.5 预填充-解码分离(950DT 产品)

对于 950DT 产品(96G x 8),我们建议为 Qwen3.5-397B-A17B-w4a4-mxfp4 部署 2 个节点的 1P1D:

  • 1 个预填充节点:1 台 950DT 产品(96G x 8)。运行独立服务,DP=1,TP=8。
  • 1 个解码节点:1 台 950DT 产品(96G x 8)。组成全局 DP=1 组,包含 1 个本地 DP rank(TP=8)。

预填充服务通过 Mooncake p2p 连接器将 KV 缓存推送到解码节点。

在相应节点上部署 run_p.sh 和 run_d.sh,并在预填充主节点上部署代理脚本以转发请求。

5.5.1 预填充节点

在预填充节点上创建 run_p.sh。

#!/bin/bash

unset ftp_proxy
unset https_proxy
unset http_proxy

source /root/.bashrc
export PROMETHEUS_MULTIPROC_DIR=/dev/shm/vllm_metrics && mkdir -p $PROMETHEUS_MULTIPROC_DIR
# Get these values through ifconfig.
# nic_name is the network interface name corresponding to local_ip.
nic_name="xxx"
local_ip="xxx"

export VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=30000
export HCCL_BUFFSIZE=300
export HCCL_IF_IP=$local_ip
export HCCL_SOCKET_IFNAME=$nic_name
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
export DYNAMIC_EPLB="true"
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export HCCL_DFS_CONFIG="task_exception:off,inconsistent_check:off"
export HCCL_ALGO=level0:fullmesh

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w4a4-mxfp4 \
  --host 0.0.0.0 \
  --port 30060 \
  --no-enable-prefix-caching \
  --enable-expert-parallel \
  --data-parallel-size 1 \
  --data-parallel-size-local 1 \
  --data-parallel-address $local_ip \
  --data-parallel-rpc-port 6884 \
  --tensor-parallel-size 8 \
  --seed 1024 \
  --distributed-executor-backend mp \
  --served-model-name qwen3.5 \
  --max-model-len 133000 \
  --max-num-batched-tokens 8192 \
  --max-num-seqs 64 \
  --trust-remote-code \
  --gpu-memory-utilization 0.95 \
  --quantization ascend \
  --async-scheduling \
  --enforce-eager \
  --speculative-config '{"num_speculative_tokens": 1, "method": "qwen3_5_mtp", "enforce_eager": true}' \
  --additional-config '{"enable_cpu_binding": true, "multistream_overlap_shared_expert": true, "recompute_scheduler_enable": true}' \
  --kv-transfer-config \
  '{"kv_connector": "MooncakeConnector",
    "kv_role": "kv_producer",
    "kv_port": "30100",
    "engine_id": "1",
    "kv_connector_module_path": "vllm_ascend.distributed.kv_transfer.kv_p2p.mooncake_connector",
    "kv_connector_extra_config": {
      "prefill": {
        "dp_size": 1,
        "tp_size": 8
      },
      "decode": {
        "dp_size": 1,
        "tp_size": 8
      },
      "ascend_local_comm_res_path": "/etc/hixlep"
    }
  }'

常见问题提示:如果预填充服务长时间未就绪,请检查 ASCEND_RT_VISIBLE_DEVICES 是否包含全部 8 个 NPU、Mooncake kv_port 是否可用,以及 ascend_local_comm_res_path 是否指向可写的共享路径。

5.5.2 解码节点

在解码节点上创建 run_d.sh。

#!/bin/bash

unset ftp_proxy
unset https_proxy
unset http_proxy

source /root/.bashrc
export PROMETHEUS_MULTIPROC_DIR=/dev/shm/vllm_metrics && mkdir -p $PROMETHEUS_MULTIPROC_DIR
# Get these values through ifconfig.
# nic_name is the network interface name corresponding to local_ip.
nic_name="xxx"
local_ip="xxx"

export VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=30000
export HCCL_BUFFSIZE=1200
export HCCL_IF_IP=$local_ip
export HCCL_SOCKET_IFNAME=$nic_name
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
export DYNAMIC_EPLB="true"
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export HCCL_DFS_CONFIG="task_exception:off,inconsistent_check:off"
export HCCL_ALGO=level0:fullmesh

# Ensure the model path matches the directory recorded during download
vllm serve Eco-Tech/Qwen3.5-397B-A17B-w4a4-mxfp4 \
  --host 0.0.0.0 \
  --port 30050 \
  --no-enable-prefix-caching \
  --enable-expert-parallel \
  --data-parallel-size 1 \
  --data-parallel-size-local 1 \
  --api-server-count 1 \
  --data-parallel-address $local_ip \
  --data-parallel-rpc-port 6884 \
  --tensor-parallel-size 8 \
  --seed 1024 \
  --distributed-executor-backend mp \
  --served-model-name qwen3.5 \
  --max-model-len 133000 \
  --max-num-batched-tokens 240 \
  --max-num-seqs 64 \
  --trust-remote-code \
  --gpu-memory-utilization 0.95 \
  --quantization ascend \
  --async-scheduling \
  --speculative-config '{"num_speculative_tokens": 3, "method": "qwen3_5_mtp"}' \
  --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
  --additional-config '{"enable_cpu_binding": true, "multistream_overlap_shared_expert": true, "recompute_scheduler_enable": true, "ascend_compilation_config": {"enable_npugraph_ex": false}}' \
  --kv-transfer-config \
  '{"kv_connector": "MooncakeConnector",
    "kv_role": "kv_consumer",
    "kv_port": "30300",
    "engine_id": "3",
    "kv_connector_module_path": "vllm_ascend.distributed.kv_transfer.kv_p2p.mooncake_connector",
    "kv_connector_extra_config": {
      "prefill": {
        "dp_size": 1,
        "tp_size": 8
      },
      "decode": {
        "dp_size": 1,
        "tp_size": 8
      },
      "ascend_local_comm_res_path": "/etc/hixlep"
    }
  }'

常见问题提示:如果解码节点 0 初始化失败,请检查 --data-parallel-start-rank 是否为 0、--tensor-parallel-size 是否为 8,以及 kv_connector_extra_config.decode.dp_size 是否与全局解码 DP 大小(1)匹配。

5.6 请求转发

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

对于 A3 PD 分离(1P1D),代理将请求转发到 1 个预填充节点和 1 个解码节点。使用逐层代理脚本。

    unset ftp_proxy
    unset https_proxy
    unset http_proxy
    python3 load_balance_proxy_server_example.py \
      --prefiller-hosts 192.xx.xx.1 \
      --prefiller-ports 30060 \
      --decoder-hosts 192.xx.xx.2 \
      --decoder-ports 30050 \
      --host 192.xx.xx.1 \
      --port 8000
    ```

    例如:

    ```shell
    cd vllm-ascend/examples/disaggregated_prefill_v1/
    bash proxy.sh
    ```

=== "950DT Products"

    对于 950DT Products PD 分离(1P1D),代理将请求转发到 1 个 prefill 节点和 1 个 decode 节点。请使用 layerwise 代理脚本。

    ```shell
    unset ftp_proxy
    unset https_proxy
    unset http_proxy
    python3 load_balance_proxy_layerwise_server_example.py \
      --prefiller-hosts 192.xx.xx.1 \
      --prefiller-ports 30060 \
      --decoder-hosts 192.xx.xx.2 \
      --decoder-ports 30050 \
      --host 192.xx.xx.1 \
      --port 8000
    ```

常见问题提示:如果请求到达代理但未返回输出,请检查代理主机列表是否包含所有健康的 prefill 和 decode 端点,并验证第 6 节中的服务验证请求能否通过代理端口成功执行。

## 6 功能验证 {#6-functional-verification}

服务器启动后,发送一个请求以验证基本模型功能。对于单节点和 MP 部署,请使用节点 0 上的 API 端点。对于 PD 分离,请使用代理端点。

```shell
curl http://<server_ip>:<port>/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.5",
    "prompt": "The future of AI is",
    "max_tokens": 50,
    "temperature": 0
  }'

预期结果:HTTP 状态码为 200,且 JSON 响应中包含带有生成文本的 choices 字段。

7 精度评估

以下是两种精度评估方法。

7.1 使用 AISBench

  1. 有关详细信息,请参阅 使用 AISBench。
  2. 执行后,您可以获取结果。以下 Qwen3.5-397B-A17B-w8a8 在 vllm-ascend:v0.17.0rc1 上的结果仅供参考。
数据集 版本 指标 模式 vllm-api-general-chat
gsm8k - accuracy gen 96.74

7.2 使用 Language Model Evaluation Harness

有关安装和使用详情,请参阅 使用 lm_eval。使用在线服务时,请将 base_url 设置为第 5 节中启动的端点。

lm_eval \
  --model local-completions \
  --model_args model=qwen3.5,base_url=http://127.0.0.1:8000/v1/completions,tokenized_requests=False,trust_remote_code=True \
  --tasks gsm8k \
  --output_path ./

8 性能评估

8.1 使用 AISBench

有关详细信息,请参阅 使用 AISBench 进行性能评估。

8.2 使用 vLLM Benchmark

以运行 Qwen3.5-397B-A17B-w8a8 的性能评估为例。有关更多详情,请参阅 vLLM 基准测试。

vllm bench 有三个子命令:

  • latency:基准测试单个请求批次的延迟。
  • serve:基准测试在线服务吞吐量。
  • throughput:基准测试离线推理吞吐量。

以 serve 为例:

export VLLM_USE_MODELSCOPE=True

vllm bench serve \
  --model Eco-Tech/Qwen3.5-397B-A17B-w8a8-mtp \
  --served-model-name qwen3.5 \
  --dataset-name random \
  --random-input 200 \
  --num-prompts 200 \
  --request-rate 1 \
  --save-result \
  --result-dir ./

几分钟后,您可以获取性能评估结果。

9 性能调优

以下配置已在特定测试环境中验证,仅供参考。最佳配置取决于硬件类型、最大输入/输出长度、请求并发数、前缀缓存命中率、量化以及 prefill/decode 比例。请根据实际工作负载调整第 9.2 节中的参数。

场景 部署模式 NPU 总数 权重版本 关键考虑因素
长上下文 单节点在线服务 16 个 A3 NPU W8A8 MTP 使用更大的 --max-model-len 并预留足够的 KV 缓存。如果发生 OOM,请降低 --max-num-seqs。
长上下文 单节点在线服务 8 个 950DT Products NPU W4A4 MXFP4 MTP 使用 TP=8,并为 133k 上下文预留足够的 KV 缓存。如果发生 OOM,请降低 --max-num-seqs。
高吞吐 多节点 MP 16 个 A2 NPU W8A8 MTP 通过 DP 提高并发,并调整 --max-num-batched-tokens 以优化 prefill 吞吐量。
低延迟 1P1D PD 分离 48 个 A3 NPU W8A8 MTP 使用独立的 prefill 和 decode DP/TP 布局,并在 decode 节点上启用完整 decode ACLGraph。
低延迟 1P1D PD 分离 16 个 950DT Products NPU W4A4 MXFP4 MTP 使用一个 8 NPU prefill 节点和一个 8 NPU decode 节点。在 decode 节点上启用完整 decode ACLGraph。
场景 节点角色 NPU TP DP 最大序列数 最大模型长度 最大批处理令牌数 MTP 令牌数 前缀缓存 主要优化
长上下文 单节点 16 16 1 128 133000 16384 3 开启 FullGraph、Fused MC2、CPU 绑定
高吞吐 MP 节点 每节点 8 8 每节点 1,全局 2 每 DP 16 32768 4096 3 关闭 FullGraph、共享专家重叠、CPU 绑定
低延迟 Prefill 节点 16 2 8 64 16384 4096 3 关闭 重计算调度器、Fused MC2、CPU 绑定
低时延 Decode节点 每节点16 2 每节点8,全局16 32 16384 128 3 Off FullGraph、recompute scheduler、Fused MC2、CPU绑定

9.2 调优指南

通用调优方法请参考公共性能调优文档,功能说明请参考特性矩阵。

推荐的调优顺序:

  1. 首先设置部署拓扑。验证场景使用单节点部署,简单的多节点服务使用MP部署,当prefill和decode需要不同资源配比时使用PD分离部署。
  2. 使用 --max-model-len 选择最大上下文长度。长上下文会增加KV cache占用,因此发生OOM时请减小 --max-num-seqs 或 --gpu-memory-utilization。
  3. 调优 --max-num-batched-tokens。较大的值通常能提升prefill吞吐,但会增加激活内存占用。以decode为主的负载通常需要较小的值。
  4. 根据服务并发度调优 --max-num-seqs。高于该值的请求将在队列中等待,等待时间会计入TTFT和TPOT。
  5. 调优 --gpu-memory-utilization。增大该值可提供更多KV cache,但需为运行时内存波动和专家负载不均衡预留余量。
  6. 调优 --speculative-config。MTP可以提升decode吞吐,但最佳 num_speculative_tokens 取决于接受率和工作负载。
  7. 调优ACLGraph捕获。decode场景推荐使用 FULL_DECODE_ONLY。如果手动设置 cudagraph_capture_sizes,请包含常见的decode批处理大小。使用序列并行时,捕获大小应为TP大小的倍数。

9.3 模型专属优化

优化项 启用方式 收益 说明
RoPE优化 默认启用 跨层复用位置编码计算,降低decode开销。 无需额外配置。
AddRMSNormQuant融合 默认启用 融合归一化和量化操作,减少内存访问。 适用于量化路径。
Zero-like消除 默认启用 移除attention中不必要的zero-like张量操作。 无需额外配置。
Qwen3.5 MTP投机解码 --speculative-config '{"method": "qwen3_5_mtp", "num_speculative_tokens": 3, "enforce_eager": true}' 在接受率良好时提升decode吞吐。 如果时延或稳定性回退,请减少投机token数。
Full decode ACLGraph --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' 减少算子分发开销,稳定decode性能。 推荐用于以decode为主的服务。
Fused MC2 --additional-config '{"enable_fused_mc2": 1}' 启用MoE融合算子,提升MoE prefill/decode效率。 如果在多DP大token场景下精度或性能回退,请禁用该功能并对比。
Shared expert overlap --additional-config '{"multistream_overlap_shared_expert": true}' 在MoE工作负载中重叠共享专家计算。 推荐用于MP吞吐场景。
Recompute调度器 --additional-config '{"recompute_scheduler_enable": true}' 在PD模式下decode KV cache不足时,通过prefill重计算KV。 仅在 kv_role 为 kv_consumer 的decode节点上有效。
CPU绑定 --additional-config '{"enable_cpu_binding": true}' 提升CPU亲和性,减少ARM服务器上的调度抖动。 许多配置中默认启用,但显式设置可使配置方案更清晰。

10 常见问题

常见环境、安装和通用参数问题请参考公共FAQ。本节仅介绍Qwen3.5-397B-A17B的模型专属问题。

Q1:为什么服务在启动时或接收请求后不久会报OOM?

现象: 服务在profile运行阶段失败,或者启动成功但在真实流量到达时报告OOM。

原因: Qwen3.5-397B-A17B对权重和KV cache的内存需求较高。较大的 --max-model-len、--max-num-seqs、--max-num-batched-tokens 或较高的 --gpu-memory-utilization 可能导致HBM预留空间不足。运行时的专家负载不均衡也可能导致实际推理比profile运行阶段占用更多内存。

解决方案: 尽可能使用带 --quantization ascend 的W8A8模型,降低 --max-model-len、--max-num-seqs、--max-num-batched-tokens,或减小 --gpu-memory-utilization。保持 PYTORCH_NPU_ALLOC_CONF=expandable_segments:True。对于BF16或更大上下文,请使用所需数量的A2/A3节点。

Q2:为什么多节点MP部署在初始化时会卡住?

现象: 某个节点等待其他rank,HCCL初始化超时,或headless节点退出。

原因: 各节点的网络接口名称、IP地址、DP rank或RPC端口不一致。

解决方案: 首先验证多节点通信。确保 HCCL_IF_IP、GLOO_SOCKET_IFNAME 和 HCCL_SOCKET_IFNAME 与所选NIC匹配。确保所有节点使用相同的 --data-parallel-rpc-port,非主节点使用 --headless,且 --data-parallel-start-rank 不重叠。

Q3:为什么PD分离示例中禁用了prefix caching?

现象: 在decode节点上启用prefix caching时,PD分离部署可能出现异常行为。

原因: D节点的prefix-cache问题是已知限制,跟踪见#7944。

解决方案: 在该限制解决之前,PD分离部署请使用 --no-enable-prefix-caching。对于非PD单节点服务,仅当工作负载存在重复前缀且缓存命中率具有实际意义时,才启用prefix caching。

Q4: 为什么启用序列并行或 Fused MC2 后性能会回退?

现象: 启用通信或 MoE 融合优化后,吞吐量下降、延迟增加,或 MoE 负载变得不稳定。

原因: 这些优化依赖于工作负载。序列并行在高并发 TP 场景中最有用。对于某些多 DP 大 token 场景,填充 token 会使某些专家过载,Fused MC2 可能并不适合。

Q5: 应如何为该模型调整 MTP 推测解码?

现象: MTP 在某些工作负载中可提高吞吐量,但在其他情况下可能增加首 token 延迟或收益有限。

原因: 收益取决于推测 token 接受率、请求长度和解码并发度。

解决方案: 如本文档所示,先从将 num_speculative_tokens 设置为 3 开始。如果服务对延迟敏感或接受率较低,请降低该值或移除 --speculative-config,并比较 TTFT、TPOT 和吞吐量。

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