Qwen3-Dense(Qwen3-0.6B/8B/32B)#

简介#

Qwen3是Qwen系列最新的大型语言模型,提供了一套全面的密集模型和混合专家(MoE)模型。Qwen3基于大量的训练,在推理、指令遵循、代理能力和多语言支持方面取得了突破性进展。

欢迎来到vLLM-Ascend环境中优化Qwen Dense模型的教程。本指南将帮助您为您的用例配置最有效的设置,并提供突出关键优化点的实际示例。我们还将探讨如何调整服务参数以在各种场景下最大化吞吐量性能。

本文档将展示模型的主要验证步骤,包括支持的功能、功能配置、环境准备、准确性和性能评估。

Qwen3 Dense模型首次在 v0.8.4rc2 中支持。

节点#

此示例需要版本v0.11.0rc2。早期版本可能缺少某些功能。

支持的特性#

请参阅 支持的特性 获取模型的特性支持矩阵。

请参阅 特性指南 获取特性的配置方法。

环境准备#

模型权重#

  • Qwen3-0.6B (BF16版本):需要1个Atlas 800 A3 (64G × 2) 卡或1个Atlas 800I A2 (64G × 1) 卡。 下载模型权重

  • Qwen3-1.7B (BF16版本):需要1个Atlas 800 A3 (64G × 2) 卡或1个Atlas 800I A2 (64G × 1) 卡。 下载模型权重

  • Qwen3-4B (BF16版本):需要1个Atlas 800 A3 (64G × 2) 卡或1个Atlas 800I A2 (64G × 1) 卡。 下载模型权重

  • Qwen3-8B (BF16版本):需要1个Atlas 800 A3 (64G × 2) 卡或1个Atlas 800I A2 (64G × 1) 卡。 下载模型权重

  • Qwen3-14B (BF16版本):需要1个Atlas 800 A3 (64G × 2) 卡或2个Atlas 800I A2 (64G × 1) 卡。 下载模型权重

  • Qwen3-32B (BF16版本):需要2个Atlas 800 A3 (64G × 4) 卡或4个Atlas 800I A2 (64G × 4) 卡。 下载模型权重

  • Qwen3-32B-W8A8 (量化版本):需要2个Atlas 800 A3 (64G × 4) 卡或4个Atlas 800I A2 (64G × 4) 卡。 下载模型权重

以上是推荐的卡数量,可根据实际情况进行调整。

建议将模型权重下载到多节点的共享目录中,例如 /root/.cache/

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

如果您想部署多节点环境,需要按照验证多节点通信环境来验证多节点通信。

安装#

您可以使用我们官方的Docker镜像来支持Qwen3 Dense模型。目前,我们提供了一体化镜像。 下载镜像

Docker拉取(按标签)#

docker pull quay.io/ascend/vllm-ascend:v0.12.0rc1

Docker运行#

# Update --device according to your device (Atlas A2: /dev/davinci[0-7] Atlas A3:/dev/davinci[0-15]).
# Update the vllm-ascend image according to your environment.
# Note you should download the weight to /root/.cache in advance.
export IMAGE=quay.io/ascend/vllm-ascend:v0.12.0rc1
docker run --rm \
    --name vllm-ascend-env \
    --shm-size=1g \
    --net=host \
    --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

默认工作目录为 /workspace,vLLM和vLLM Ascend代码位于 /vllm-workspace 并以 开发模式pip install -e)安装,以帮助开发人员立即进行更改,而无需重新安装。

运行Docker容器 中,通过具体示例提供了详细说明。

此外,如果您不想使用上述Docker镜像,也可以从源码构建。

  • 从源码安装 vllm-ascend,请参考 安装

如果您想部署多节点环境,需要在每个节点上设置环境。

部署#

在本节中,我们将演示在vLLM-Ascend中调整超参数以最大化推理吞吐量性能的最佳实践。通过针对不同用例定制服务级配置,您可以确保您的系统在各种场景下都能达到最佳性能。我们将指导您如何根据观察到的现象(如max_model_len、max_num_batched_tokens和cudagraph_capture_sizes)微调超参数以获得最佳性能。

具体的示例场景如下:

  • 机器环境为Atlas 800 A3 (64G*16)

  • LLM为Qwen3-32B-W8A8

  • 数据场景为固定长度输入3.5K,输出1.5K。

  • 并行配置要求为DP=1&TP=4

  • 如果机器环境为Atlas 800I A2(64G*8),部署方法保持不变。

运行Docker容器:#

节点#

  • /model/Qwen3-32B-W8A8是模型路径,请替换为您的实际路径。

  • v0.11.0rc2-a3是镜像标签,请替换为您的实际标签。

  • 请替换为您的实际端口:‘-p 8113:8113’。

  • 请替换为您的实际卡:‘–device /dev/davinci0’。

# Update the vllm-ascend image
export IMAGE=quay.io/ascend/vllm-ascend:v0.12.0rc1
docker run --rm \
--name vllm-ascend \
--shm-size=1g \
--privileged=true \
--device /dev/davinci0 \
--device /dev/davinci1 \
--device /dev/davinci2 \
--device /dev/davinci3 \
--device /dev/davinci_manager \
--device /dev/devmm_svm \
--device /dev/hisi_hdc \
-v /usr/local/dcmi:/usr/local/dcmi \
-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 /model/Qwen3-32B-W8A8:/model/Qwen3-32B-W8A8 \
-p 8113:8113 \
-it $IMAGE bash

在多 NPU 上进行在线推理#

运行以下脚本在多NPU上启动vLLM服务器。

此脚本配置为在上述特定示例场景下达到最佳性能,在两张A3卡上batchsize=72。

# set the NPU device number
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3

# Set the operator dispatch pipeline level to 1 and disable manual memory control in ACLGraph
export TASK_QUEUE_ENABLE=1

# [Optional] jemalloc
# jemalloc is for better performance, if `libjemalloc.so` is install on your machine, you can turn it on.
# if os is Ubuntu
# export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
# if os is openEuler
# export LD_PRELOAD=/usr/lib64/libjemalloc.so.2:$LD_PRELOAD


# Enable the AIVector core to directly schedule ROCE communication
export HCCL_OP_EXPANSION_MODE="AIV"

# Enable dense model and general optimizations for better performance.
export VLLM_ASCEND_ENABLE_DENSE_OPTIMIZE=1

# Enable FlashComm_v1 optimization when tensor parallel is enabled.
export VLLM_ASCEND_ENABLE_FLASHCOMM1=1

vllm serve /model/Qwen3-32B-W8A8 \
  --served-model-name qwen3 \
  --trust-remote-code \
  --async-scheduling \
  --quantization ascend \
  --distributed-executor-backend mp \
  --tensor-parallel-size 4 \
  --max-model-len 5500 \
  --max-num-batched-tokens 40960 \
  --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
  --port 8113 \
  --block-size 128 \
  --gpu-memory-utilization 0.9

节点#

  • /model/Qwen3-32B-W8A8是模型路径,请替换为您的实际路径。

  • 如果模型不是量化模型,请移除 --quantization ascend 参数。

  • 如果追求极致性能,可以启用cudagraph_capture_sizes参数,参考:关键优化点优化亮点。这是batchsize为72的示例:--compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY", "cudagraph_capture_sizes":[1,8,24,48,60,64,72,76]}'

服务器启动后,您可以用输入提示查询模型

curl https://:8113/v1/chat/completions -H "Content-Type: application/json" -d '{
  "model": "qwen3",
  "messages": [
    {"role": "user", "content": "Give me a short introduction to large language models."}
  ],
  "temperature": 0.6,
  "top_p": 0.95,
  "top_k": 20,
  "max_tokens": 4096
}'

在多 NPU 上进行离线推理#

运行以下脚本在多NPU上执行离线推理。

节点#

  • /model/Qwen3-32B-W8A8是模型路径,请替换为您的实际路径。

  • 如果模型不是量化模型,请移除 quantization="ascend" 参数。

import gc
import torch

from vllm import LLM, SamplingParams
from vllm.distributed.parallel_state import (destroy_distributed_environment,
                                             destroy_model_parallel)

def clean_up():
    destroy_model_parallel()
    destroy_distributed_environment()
    gc.collect()
    torch.npu.empty_cache()

prompts = [
    "Hello, my name is",
    "The future of AI is",
]
sampling_params = SamplingParams(temperature=0.6, top_p=0.95, top_k=40)
llm = LLM(model="/model/Qwen3-32B-W8A8",
          tensor_parallel_size=4,
          trust_remote_code=True,
          distributed_executor_backend="mp",
          max_model_len=5500,
          max_num_batched_tokens=5500,
          quantization="ascend",
          compilation_config={"cudagraph_mode": "FULL_DECODE_ONLY"})

outputs = llm.generate(prompts, sampling_params)
for output in outputs:
    prompt = output.prompt
    generated_text = output.outputs[0].text
    print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

del llm
clean_up()

精度评估#

这是准确性评估方法之一。

使用 AISBench#

  1. 详情请参阅使用 AISBench

  2. 执行后,您可以获得结果,以下是 Qwen3-32B-W8A8vllm-ascend:0.11.0rc2 中的结果,仅供参考。

数据集

version

指标

模式

任务名称

vllm-api-general-chat

gsm8k

-

accuracy

gen

gsm8k_gen_0_shot_noncot_chat_prompt

96.44

math500

-

accuracy

gen

math500_gen_0_shot_cot_chat_prompt

97.60

aime

-

accuracy

gen

aime2024_gen_0_shot_chat_prompt

76.67

性能#

使用AISBench#

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

使用 vLLM Benchmark#

Qwen3-32B-W8A8 的性能评估为例。

更多详情请参阅 vllm benchmark

有三个 vllm bench 子命令

  • latency:对单批请求的延迟进行基准测试。

  • serve:对在线服务吞吐量进行基准测试。

  • throughput:对离线推理吞吐量进行基准测试。

serve 为例。运行代码如下。

节点#

  • /model/Qwen3-32B-W8A8是模型路径,请替换为您的实际路径。

vllm bench serve --model /model/Qwen3-32B-W8A8 --served-model-name qwen3 --port 8113 --dataset-name random --random-input 200 --num-prompt 200 --request-rate 1 --save-result --result-dir ./

大约几分钟后,您就可以得到性能评估结果。

关键优化点#

在本节中,我们将介绍可以显著提高Qwen Dense模型性能的关键优化点。这些技术旨在提高各种场景下的吞吐量和效率。

1. Rope优化#

Rope优化通过修改位置编码过程来提高模型的效率。具体来说,它确保cos_sin_cache和相关的索引选择操作仅在正向传播的第一层执行。对于后续层,位置编码直接重用,消除了冗余计算,并显著加速了解码阶段的推理。

此优化默认启用,无需设置任何额外的环境变量。

2. AddRMSNormQuant 融合#

AddRMSNormQuant融合将逐地址多尺度归一化和量化操作合并,以实现更高效的内存访问和计算,从而提高吞吐量。

此优化默认启用,无需设置任何额外的环境变量。

3. FlashComm_v1#

FlashComm_v1通过将传统的allreduce集体通信分解为reduce-scatter和all-gather,显著提高了在大批量场景下的性能。这种分解有助于减少RMSNorm token维度的计算,从而实现更高效的处理。在量化场景下,FlashComm_v1还通过减少比特级数据传输来降低通信开销,进一步减小了预填充阶段的端到端延迟。

需要注意的是,将allreduce通信分解为reduce-scatter和all-gather操作仅在高并发场景下有益,而不会出现明显的通信退化。在其他情况下,这种分解可能会导致性能下降。为缓解此问题,当前实现使用基于阈值的方法,仅当每次推理调度的实际token数超过阈值时才启用FlashComm_v1。这确保了该功能仅在能够提高性能的场景下激活,避免了在低并发情况下的潜在性能下降。

此优化需要设置环境变量 VLLM_ASCEND_ENABLE_FLASHCOMM1 = 1 来启用。

4. Matmul 和 ReduceScatter 融合#

一旦启用了FlashComm_v1,就可以应用额外的优化。此优化将矩阵乘法和ReduceScatter操作与分块优化相结合。Matmul计算被视为一个流水线,而ReduceScatter和dequant操作在单独的流水线中处理。这种方法显著减少了通信步骤,提高了计算效率,并实现了更好的资源利用率,从而在大型分布式环境中提高了吞吐量。

此优化在FlashComm_v1激活后自动启用。然而,由于在低并发场景下此融合可能导致性能下降,目前使用基于阈值的方法来缓解此问题。仅当token数超过阈值时才应用优化,以确保在可能对性能产生负面影响的情况下不启用它。

5. 权重预取#

权重预取通过在权重需要时将其预加载到缓存中来优化内存使用,从而最大限度地减少模型执行期间由内存访问引起的延迟。

在密集模型场景中,MLP的gate_up_proj和down_proj线性层通常表现出相对较高的MTE利用率。为了解决这个问题,我们为权重预取创建了一个单独的流水线,该流水线与MLP之前的原始向量计算流水线(如RMSNorm和SiLU)并行运行。这种方法允许提前将权重预加载到L2缓存中,减少MLP计算期间的MTE利用率,并通过最小化资源争用和优化数据流间接提高Cube计算效率。

需要强调的是,由于我们使用向量计算来隐藏权重预取流水线,因此预取缓冲区大小的设置至关重要。如果缓冲区大小太小,优化收益将无法完全实现;而较大的缓冲区大小可能导致资源争用,从而导致性能下降。为了适应不同的场景,我们公开了两个环境变量 VLLM_ASCEND_MLP_GATE_UP_PREFETCH_SIZEVLLM_ASCEND_MLP_DOWN_PREFETCH_SIZE,允许根据特定工作负载灵活配置缓冲区大小。

此优化需要设置环境变量 VLLM_ASCEND_ENABLE_PREFETCH_MLP = 1VLLM_ASCEND_ENABLE_DENSE_OPTIMIZE = 1 来启用。

6. Zerolike 消除#

此消除移除了Attention正向传播中与零类张量相关的非必要操作,提高了矩阵运算的效率并减少了内存使用。

此优化默认启用,无需设置任何额外的环境变量。

7. FullGraph 优化#

ACLGraph提供了几个关键优化来提高模型执行效率。通过一次性回放整个模型执行图,我们显著减少了与多次小规模回放相比的调度延迟。这种方法还稳定了多设备性能,因为将模型捕获为单个静态图可以减轻跨设备的调度波动。此外,整合图捕获会释放流,允许捕获更多图并优化资源使用,最终提高系统效率并降低开销。

在启动服务时使用配置compilation_config = { “cudagraph_mode”: “FULL_DECODE_ONLY”}。此设置对于启用aclgraph的full decode-only模式是必需的。

8. 异步调度#

异步调度是一种用于优化推理效率的技术。它允许非阻塞任务调度,以提高并发性和吞吐量,尤其是在处理大型模型时。

此优化通过设置 --async-scheduling 来启用。

优化亮点#

基于前面概述的特定示例场景,本节重点介绍了在实现最佳性能方面起关键作用的调优点。通过关注对超参数和优化的最有效调整,我们将强调可以利用的策略,以最大限度地提高吞吐量、最小化延迟,并确保在各种环境中高效地利用资源。这些见解将帮助您为最佳结果进行自己的配置微调。

1. 预取缓冲区大小#

设置正确的预取缓冲区大小对于优化权重加载至关重要,此预取缓冲区的大小直接关系到可以通过向量计算隐藏的时间。为了实现预取流和计算流的近乎完美的重叠,您可以通过分析和观察不同缓冲区大小下的重叠程度来灵活调整缓冲区大小。

例如,在上面提到的实际场景中,我将MLP中gate_up_proj和down_proj的预取缓冲区大小设置为18MB。原因是,在该值下,RMSNorm和SiLU的向量计算可以有效地隐藏预取流,从而加速两个线性层的Matmul计算。

2. Max-num-batched-tokens#

max-num-batched-tokens参数决定了单个批次可以处理的最大token数。调整此值有助于平衡吞吐量和内存使用。设置此值过小会对端到端性能产生负面影响,因为每个批次处理的token数较少,可能导致效率低下。反之,设置过大则会因内存消耗过大而增加内存溢出(OOM)错误的风险。

在上述实际场景中,我们不仅进行了广泛的测试以确定最经济有效的值,还考虑了启用分块预填充时decode token的累积。如果该值设置得太小,单个请求可能会被分块多次,并且在推理的早期阶段,一个批次可能只包含少量decode token。这可能导致端到端吞吐量未达到预期。

3. Cudagraph_capture_sizes#

cudagraph_capture_sizes参数控制推理过程中图捕获的粒度。调整此值决定一次捕获多少计算图,这会显著影响性能和内存使用。

如果未手动指定此列表,它将填充一系列均匀分布的值,这通常可以确保良好的性能。然而,如果您想进一步微调,手动指定值将获得更好的结果。这是因为如果批次大小介于两个大小之间,框架会自动将token数填充到较大的大小。这通常会导致实际性能偏离预期甚至下降。

因此,就像上述实际场景一样,在调整基准请求并发时,我们始终确保并发数实际包含在cudagraph_capture_sizes列表中。这样,在解码阶段,填充操作基本上被避免,确保了实验数据的可靠性。

需要注意的是,如果您启用了FlashComm_v1,此列表中的值必须是TP大小的整数倍。任何不满足此条件的都会被自动过滤掉。因此,我建议在启用FlashComm_v1后,根据TP大小逐步增加并发量。