news 2026/9/20 11:37:23

昇腾910B部署Qwen3.5与vLLM Ascend实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
昇腾910B部署Qwen3.5与vLLM Ascend实战指南

1. 为什么要在昇腾910B上折腾Qwen3.5加vLLM Ascend

先把结论摆在前面:如果你手里有一台昇腾910B的机器,想跑Qwen3.5这个级别的模型,又希望推理吞吐能撑住多人并发,那vLLM Ascend基本是目前最省心的组合之一。我自己前前后后在三台不同配置的910B服务器上折腾过这套东西,踩的坑不算少,但跑通之后确实稳。

Qwen3.5是通义千问系列较新的一代模型,相比前代在长上下文理解、指令跟随和推理链稳定性上都有明显提升,参数量覆盖了从几B到几百B的多个档位。昇腾910B是国产AI加速卡里生态相对成熟的一款,单卡64GB HBM,算力在FP16下大约320 TFLOPS,卡间通过HCCS互联。vLLM Ascend则是vLLM社区针对昇腾硬件做的适配版本,把vLLM那套PagedAttention、连续批处理(continuous batching)的机制搬到了昇腾的CANN软件栈上。

这套组合解决的核心问题是:在国产算力上,用一套接近CUDA生态体验的推理框架,把大模型的显存利用率和并发吞吐拉起来。传统用transformers直接推理,显存浪费严重,batch一开大就OOM,并发上来延迟直接爆炸。vLLM的PagedAttention把KV Cache按块管理,显存碎片大幅减少,配合连续批处理,吞吐能翻好几倍。

适合谁看这篇?三类人:一是手里已经有910B机器、想跑通Qwen3.5推理的运维或算法工程师;二是正在做国产化替代、需要评估昇腾推理性能的技术选型负责人;三是对vLLM机制感兴趣、想了解它在非CUDA平台上怎么落地的人。下面我按实际部署顺序,把每个环节的细节和坑都摊开讲。

2. 部署前的环境盘点与方案选型

2.1 硬件与驱动的前置检查

动手之前,先把机器状态摸清楚,这一步偷懒后面会加倍还回来。昇腾910B的部署对驱动和固件版本极其敏感,版本不匹配是新手最容易卡住的地方。

登录机器后,第一件事是确认NPU设备能被识别:

npu-smi info

这条命令会列出所有NPU卡的状态、显存占用、温度、功耗。正常输出里应该能看到910B的型号标识和每张卡的HBM容量。如果这条命令报错或者看不到卡,别急着往下走,先解决驱动问题。

接着查驱动和固件版本:

npu-smi info -t board -i 0

输出里会包含驱动版本(driver version)和固件版本(firmware version)。这里有个经验:驱动、固件、CANN、torch_npu四者的版本必须严格对应,官方文档里有一张兼容性矩阵表,务必对着查。我遇到过驱动是较新版本、但CANN装的是旧版本,结果torch_npu加载时直接段错误,排查了大半天才发现是版本错配。

提示:昇腾的版本兼容性比CUDA生态严格得多,CUDA下驱动向下兼容基本没问题,但昇腾这边差一个小版本都可能出问题。建议把驱动、固件、CANN、torch_npu的版本号记在一个文档里,方便后续复现。

2.2 软件栈的版本搭配逻辑

整套软件栈从上到下是这样的:最底层是驱动和固件,往上是CANN(异构计算架构),再往上是torch_npu(PyTorch的昇腾适配层),最上面才是vLLM Ascend。每一层都要版本对齐。

我实测下来比较稳的一套组合是:CANN 8.0系列、torch_npu 2.1以上、vLLM Ascend对应版本。具体版本号建议直接查vLLM Ascend的官方仓库README,里面会写明推荐的CANN和torch_npu版本。不要凭感觉装最新版,最新版往往还没和vLLM Ascend对齐,装上去大概率跑不起来。

为什么这么强调版本?因为vLLM Ascend底层调用了CANN里的大量算子接口,CANN版本一变,算子签名可能就变了,vLLM Ascend如果没同步适配,编译或运行阶段就会报找不到符号之类的错误。这跟CUDA下cudnn版本不匹配导致的问题是一个道理,只是昇腾这边容错空间更小。

2.3 为什么选vLLM Ascend而不是其他方案

昇腾上跑大模型,可选方案其实不少:直接用transformers加torch_npu、用MindIE、用vLLM Ascend、或者自己写推理服务。我选vLLM Ascend主要看中三点。

第一是PagedAttention带来的显存效率。传统推理里KV Cache要预分配一大块连续显存,按最大序列长度预留,实际用不到那么多就浪费了。PagedAttention把KV Cache切成固定大小的块,按需分配,显存利用率能提升到90%以上。对于Qwen3.5这种长上下文模型,KV Cache占用很可观,这个优化直接决定了你能开多大的batch。

第二是连续批处理。传统静态batch要等一个batch里所有请求都生成完才能处理下一批,短请求被长请求拖死。连续批处理是每生成一个token就检查有没有新请求可以插进来,把GPU/NPU利用率拉满。实测在混合长短请求的场景下,吞吐能比静态batch高2到3倍。

第三是生态兼容性。vLLM的接口和OpenAI API兼容,上层应用几乎不用改代码就能从CUDA切到昇腾。这对做国产化替代的团队来说,迁移成本极低。

MindIE是华为自家的推理框架,性能也不错,但生态和社区活跃度不如vLLM,遇到问题查资料相对费劲。transformers加torch_npu最简单,但性能和显存效率差一大截,只适合单请求调试,不适合生产。

3. 核心细节解析与实操要点

3.1 CANN与torch_npu的安装细节

CANN的安装包从昇腾社区下载,注意要选对架构(x86还是arm)和操作系统版本。安装时用--install参数,装完要source环境变量:

source /usr/local/Ascend/ascend-toolkit/set_env.sh

这一步很多人会忘,结果后面import torch_npu时报找不到libascend相关的库。建议把这条source命令写进~/.bashrc,省得每次开新终端都要手动执行。

torch_npu的安装要和PyTorch版本对应。比如PyTorch 2.1对应torch_npu 2.1,不能混装。安装命令大致是:

pip install torch==2.1.0 pip install torch-npu==2.1.0.post8

装完后验证:

import torch import torch_npu print(torch.npu.is_available()) print(torch.npu.device_count())

如果输出True和正确的卡数,说明torch_npu这层通了。如果报错,八成是CANN环境变量没source,或者版本不匹配。

注意:torch_npu和PyTorch的版本对应关系不是简单的数字相等,有些post版本号有特定要求。装之前一定查官方release note,别想当然。

3.2 vLLM Ascend的编译与安装

vLLM Ascend有两种安装方式:pip直接装预编译包,或者从源码编译。强烈建议先试pip预编译包,能省掉大量编译时间。如果预编译包和你的CANN版本不匹配,再考虑源码编译。

pip安装大致是:

pip install vllm-ascend

但这里有个坑:vllm-ascend依赖特定版本的vllm,pip可能会自动装一个不兼容的vllm版本。稳妥做法是先手动装对应版本的vllm,再装vllm-ascend,用--no-deps避免它乱改依赖:

pip install vllm==<指定版本> pip install vllm-ascend --no-deps

源码编译的话,需要先clone仓库,然后按README里的步骤设置CANN路径、编译算子。编译过程比较吃CPU和内存,建议在配置好一点的机器上做,编译一次大概十几分钟到半小时。

编译时常见的报错是找不到CANN的头文件或库文件,这时候要检查ASCEND_HOME_PATH环境变量是否指向正确的CANN安装目录。另一个常见问题是gcc版本,昇腾的算子编译对gcc版本有要求,太新或太旧都可能失败,一般gcc 9到11比较稳。

3.3 模型权重的准备与格式转换

Qwen3.5的权重从官方渠道下载,通常是safetensors格式。vLLM Ascend能直接加载safetensors,不需要额外转换,这点比早期版本方便很多。

下载权重时注意几点:一是确认下载的是完整权重,不是分片缺失的;二是检查模型配置文件config.json里的参数,比如num_hidden_layershidden_sizenum_attention_heads,这些要和实际权重对应;三是如果模型有tokenizer.jsontokenizer_config.json,确保都在。

权重存放路径建议单独放一个目录,比如/data/models/Qwen3.5-xxB,不要和代码混在一起。加载时用绝对路径,避免相对路径带来的困惑。

如果显存有限,可以考虑量化版本。Qwen3.5有AWQ、GPTQ等量化权重,vLLM Ascend对部分量化格式有支持,但支持程度不如CUDA版vLLM全面。用之前先查vLLM Ascend的文档,确认你要用的量化格式被支持。我实测AWQ在昇腾上的支持相对成熟,GPTQ偶尔会有算子缺失的问题。

3.4 关键参数的取值逻辑

启动vLLM服务时,几个参数直接决定性能和稳定性,这里逐个说清楚。

--tensor-parallel-size是张量并行度,等于你用几张卡。910B单卡64GB,跑Qwen3.5的某个中等规模版本,如果单卡放不下,就要用TP。TP=2表示两张卡分摊模型权重。注意TP度要和卡数匹配,且最好是2的幂次,跨卡通信走HCCS,效率比走PCIe高。

--gpu-memory-utilization是显存利用率上限,默认0.9。这个值决定vLLM能用多少显存来放KV Cache。设太高容易OOM,设太低浪费显存。我的经验是0.85到0.92之间比较稳,具体看模型大小和序列长度。如果经常OOM,往下调到0.85;如果显存还有富余,可以往上试到0.92。

--max-model-len是最大序列长度,要和模型本身支持的长度匹配。Qwen3.5支持长上下文,但设太长会吃掉大量KV Cache显存。如果实际业务用不到那么长,就设小一点,把省下的显存留给batch。

--max-num-seqs是最大并发序列数,控制同时处理多少请求。这个值越大吞吐越高,但显存占用也越大。建议从较小值开始压测,逐步往上调,找到显存和吞吐的平衡点。

--block-size是PagedAttention的块大小,默认16。这个值影响显存碎片和调度效率,一般不用改,除非有特殊需求。

4. 实操过程与核心环节实现

4.1 从零到跑通的第一条命令

假设环境都装好了,权重也下载了,第一条启动命令可以这样写:

python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen3.5-xxB \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192 \ --max-num-seqs 64 \ --port 8000 \ --trust-remote-code

--trust-remote-code在加载Qwen系列模型时通常需要,因为它的模型定义里有自定义代码。不加这个参数可能会报找不到模型类的错误。

启动过程会经历几个阶段:加载权重、初始化KV Cache、编译算子(首次启动会慢一些)、启动HTTP服务。看到类似Uvicorn running on http://0.0.0.0:8000的输出,说明服务起来了。

首次启动时,vLLM Ascend会做一些算子编译和缓存,这个过程可能持续几分钟。第二次启动会快很多,因为缓存已经建好了。如果每次启动都很慢,检查缓存目录是否可写,或者是不是每次都在重新编译。

4.2 用curl验证服务是否正常

服务起来后,先用最简单的请求验证:

curl http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "/data/models/Qwen3.5-xxB", "prompt": "你好,请介绍一下你自己", "max_tokens": 128, "temperature": 0.7 }'

如果返回正常的生成结果,说明整条链路通了。如果报错,看服务端的日志,通常错误信息会指出问题所在。

也可以用OpenAI的Python SDK来测:

from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy") resp = client.chat.completions.create( model="/data/models/Qwen3.5-xxB", messages=[{"role": "user", "content": "你好"}], max_tokens=128 ) print(resp.choices[0].message.content)

用chat接口时注意,Qwen3.5有对话模板,vLLM会自动应用。如果发现输出格式不对,检查tokenizer配置里的chat_template是否正确。

4.3 压测与性能调优的实操记录

跑通之后,下一步是压测,搞清楚这套配置的实际吞吐和延迟。我用的是一个简单的并发压测脚本,模拟多个用户同时请求:

import concurrent.futures import time from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy") def send_request(i): start = time.time() resp = client.chat.completions.create( model="/data/models/Qwen3.5-xxB", messages=[{"role": "user", "content": f"请写一段关于数字{i}的短文"}], max_tokens=256 ) return time.time() - start with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor: futures = [executor.submit(send_request, i) for i in range(100)] latencies = [f.result() for f in futures] print(f"平均延迟: {sum(latencies)/len(latencies):.2f}s") print(f"P99延迟: {sorted(latencies)[int(len(latencies)*0.99)]:.2f}s")

压测时观察npu-smi info里的显存占用和利用率。如果显存接近打满但利用率不高,说明batch开小了,可以调大--max-num-seqs。如果显存打满且频繁OOM,说明开太大了,要往下调。

我实测下来,双卡910B跑Qwen3.5中等规模版本,--max-num-seqs设64、--max-model-len设8192时,32并发下平均延迟在可接受范围,吞吐比单请求串行高了将近一个数量级。具体数字因模型规模和序列长度而异,这里给的是量级参考。

调优的核心逻辑是:在显存不OOM的前提下,尽量把batch和并发拉满,让NPU利用率维持在较高水平。NPU利用率和吞吐正相关,利用率上不去,说明有资源闲置。

4.4 多卡并行的通信优化

用TP大于1时,卡间通信是性能瓶颈之一。910B的HCCS互联带宽比PCIe高不少,所以尽量让TP的卡在同一台机器内,且走HCCS。如果跨机器做TP,通信走网络,延迟会明显上升。

检查卡间互联拓扑可以用:

npu-smi info -t topo

输出会显示卡与卡之间的连接方式。如果显示HCCS,说明是高速互联;如果显示SYS或PCIe,说明走的是较慢的通道,TP效率会打折。

另一个优化点是--enable-prefix-caching,开启前缀缓存后,多个请求如果共享相同的前缀(比如相同的system prompt),KV Cache可以复用,省掉重复计算。对于有固定system prompt的场景,这个优化效果很明显。

5. 常见问题与排查技巧实录

5.1 启动阶段的典型报错

部署过程中遇到的报错,我整理成了一张速查表,方便对照排查。

报错现象可能原因排查方向
ImportError: libascend...CANN环境变量未source执行set_env.sh,检查LD_LIBRARY_PATH
torch.npu.is_available()返回False驱动或torch_npu版本不匹配查兼容性矩阵,核对版本
加载权重时OOM模型太大或显存利用率设太高降低gpu-memory-utilization,或增加TP
算子编译失败gcc版本或CANN版本问题换gcc 9-11,核对CANN版本
服务启动后请求超时首次编译未完成或卡死看日志,等待编译完成
找不到模型类未加trust-remote-code启动命令加--trust-remote-code

这张表里的每一条我基本都踩过。印象最深的是算子编译失败那次,报错信息很模糊,只说是某个算子编译不过。后来发现是gcc版本太新(gcc 13),换成gcc 11就过了。昇腾的算子编译对gcc版本比较挑,建议用系统自带的或者官方推荐的版本。

5.2 运行阶段的性能问题

服务跑起来后,性能不达预期是另一个常见问题。表现是吞吐低、延迟高、NPU利用率上不去。

先查NPU利用率:

npu-smi info -t usages -i 0

如果利用率长期低于50%,说明有资源闲置。可能的原因:batch太小、请求串行、或者有CPU瓶颈。先调大--max-num-seqs,看利用率是否上升。如果调大后OOM,说明显存是瓶颈,考虑用量化权重或减少max-model-len。

如果利用率高但吞吐还是低,可能是卡间通信瓶颈。检查TP配置,确认卡间走的是HCCS。另外,如果请求的输入长度差异很大,连续批处理的调度开销会上升,可以适当调整调度策略。

还有一个容易被忽略的点是CPU预处理。tokenization和请求解析在CPU上做,如果CPU性能不足,会成为瓶颈。压测时用top看CPU占用,如果某个核跑满,考虑优化tokenizer或增加CPU资源。

5.3 显存管理的避坑经验

显存管理是昇腾部署里最容易出问题的地方。几个经验点:

第一,--gpu-memory-utilization不要设太满。留一点余量给系统和其他进程,设0.9比设0.95稳。我见过设0.95后跑一段时间因为显存碎片导致OOM的案例。

第二,KV Cache的显存占用和序列长度、并发数成正比。如果业务里长序列请求多,max-model-len要设够,但设太大又浪费。建议根据实际业务的P99序列长度来设,不要盲目设成模型支持的最大值。

第三,多卡TP时,每张卡的显存占用不完全一样,因为有些层可能集中在某张卡上。用npu-smi info观察每张卡的显存,如果某张卡明显偏高,可能是负载不均,考虑调整TP策略。

第四,长时间运行后如果出现显存缓慢增长,可能是内存泄漏。vLLM Ascend的某些版本有过这类问题,升级到修复版本可以解决。如果无法升级,定期重启服务是个笨但有效的办法。

5.4 模型加载的疑难杂症

模型加载失败的原因五花八门,这里列几个我遇到过的。

一是权重文件不完整。下载过程中断导致某个分片缺失,加载时报找不到文件或形状不匹配。解决办法是校验文件完整性,对比官方提供的文件列表和MD5。

二是config.json配置和权重不匹配。比如config里写的层数和实际权重层数不一致,加载时会报形状错误。这种情况通常是下载了错误的权重版本,或者手动改过config。

三是tokenizer问题。Qwen3.5的tokenizer有特殊配置,如果tokenizer文件缺失或版本不对,会导致tokenization结果异常,表现为生成的文本乱码或不符合预期。确保tokenizer相关文件齐全,且和模型版本对应。

四是量化权重和vLLM Ascend版本不兼容。某些量化格式需要特定版本的vLLM Ascend支持,版本不对会报算子缺失。用之前查文档确认支持情况。

6. 生产环境部署的额外考量

6.1 服务化与高可用

单机跑通只是第一步,生产环境要考虑服务化和高可用。vLLM Ascend的OpenAI兼容接口可以直接对接上层网关,用Nginx做负载均衡,后面挂多个vLLM实例。

多实例部署时,每个实例占用的NPU卡要隔离,避免互相抢资源。可以用ASCEND_RT_VISIBLE_DEVICES环境变量指定每个实例可见的卡:

ASCEND_RT_VISIBLE_DEVICES=0,1 python -m vllm.entrypoints.openai.api_server ...

这样实例A用0、1卡,实例B用2、3卡,互不干扰。

高可用方面,网关层做健康检查,某个实例挂了自动摘除。vLLM Ascend有健康检查接口,可以配置到网关里。另外,模型加载时间长,实例重启慢,建议保持一定冗余实例,避免单点故障导致服务不可用。

6.2 监控指标的搭建

生产环境必须要有监控。关键指标包括:NPU利用率、显存占用、请求延迟、吞吐、错误率。

NPU相关指标通过npu-smi采集,可以写个脚本定时抓取,推到Prometheus。vLLM自身也暴露了一些指标,比如运行中的请求数、等待队列长度、KV Cache使用率,这些可以通过它的metrics接口获取。

监控告警的阈值设置:NPU利用率持续低于30%说明资源浪费,持续高于95%说明接近瓶颈;显存占用超过90%要警惕OOM;P99延迟超过业务容忍阈值要告警。

6.3 版本升级的注意事项

昇腾生态迭代快,版本升级频繁。升级时要注意:先在小规模环境验证,确认新版本和现有模型、上层应用兼容,再灰度到生产。

升级CANN或驱动时,最好停机操作,因为升级过程中NPU不可用。升级后要重新验证torch_npu和vLLM Ascend是否正常,有时候升级CANN后需要重新编译vLLM Ascend的算子。

保留旧版本的安装包和环境快照,万一新版本有问题可以快速回滚。昇腾的回滚比CUDA麻烦,因为涉及驱动、固件、CANN多层,提前准备好回滚方案能省很多事。

我在实际部署中最大的体会是:昇腾这套东西,版本管理比技术本身更考验人。技术原理搞懂了,剩下的就是和版本兼容性作斗争。把每个组件的版本号记录清楚,每次变更都做验证,能避开大部分坑。另外,社区和官方文档是重要参考,遇到问题先搜有没有人踩过同样的坑,往往能省下大量排查时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 11:37:18

夸克资源社实测:解决链接失效与网盘转存痛点的资源搜索工具

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 11:36:41

流式响应半路截断?TaoToken + Cline 这样核对模型 ID 与上下文长度

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 11:34:12

2026年Agent学习路线:从零到一掌握7个开源项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 11:34:00

开源前端商城模板选型与改造实战:从跑通到上线的完整指南

简介&#xff1a;这是一款基于HTML、CSS、JavaScript与jQuery构建的开源前端商城模板&#xff0c;面向需要快速搭建电商网站的前端及全栈开发者。模板提供完整的页面布局与交互功能&#xff0c;省去从零搭建项目的繁琐流程&#xff0c;尤其适合中小型电商项目、个人创业者或新手…

作者头像 李华
网站建设 2026/9/20 11:33:23

BrewUI:基于Node.js打造Homebrew图形化包管理工具的完整实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 11:32:30

CC-switch 搭配 Gemini CLI 完整指南:一键切换 AI 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华