1. 为什么要在昇腾910B上折腾Qwen3.5
先把结论摆在前面:如果你手里有一台昇腾910B的机器,想跑Qwen3.5这个级别的模型,并且希望推理吞吐能撑住真实业务,那vLLM Ascend基本是目前最省心的路线。我自己前前后后在三台不同配置的910B机器上折腾了将近两周,从最开始用transformers硬扛、到尝试MindIE、最后落到vLLM Ascend,中间踩的坑足够写一篇长文了。
Qwen3.5是通义千问系列较新的一代,相比Qwen2.5在长上下文、推理能力和多语言上都有明显提升,模型参数量覆盖从0.5B到72B甚至更大的MoE版本。而昇腾910B是国产AI加速卡里生态相对成熟的一款,64GB HBM的版本在跑72B级别的模型时,配合量化是能塞得下的。问题在于,昇腾的软件栈和英伟达那套CUDA生态完全是两回事,很多人第一次上手会懵——CANN、torch_npu、MindIE、vLLM Ascend这几个东西到底什么关系,谁依赖谁,装哪个版本,这些在官方文档里往往是分散的,需要自己拼起来。
这篇内容适合三类人看:一是手里已经有昇腾910B机器、想跑Qwen3.5做推理服务的;二是正在评估国产算力方案、想了解实际部署难度的;三是对vLLM比较熟、想迁移到昇腾平台的技术同学。我会把整个部署链路拆开讲,包括环境准备、模型准备、vLLM Ascend的安装配置、启动参数调优、性能实测,以及我踩过的那些坑。所有命令和配置都是我在真机上验证过的,你可以直接抄。
需要提前说明的是,昇腾的软件版本迭代很快,CANN从7.x到8.x变化不小,vLLM Ascend也在快速更新。我下面写的是基于CANN 8.0、torch_npu 2.4、vLLM Ascend 0.7.x这一套组合,如果你用的是别的版本,部分细节可能需要微调,但整体思路是通的。
2. 部署前的整体思路与方案选型
2.1 为什么不用transformers直接推理
最开始我图省事,直接用transformers加torch_npu跑Qwen3.5-72B。能跑通,但性能惨不忍睹。单条请求的生成速度大概只有个位数token每秒,batch稍微大一点显存就爆。原因很简单:transformers是逐token生成,没有做KV Cache的PagedAttention优化,也没有continuous batching,显存利用率极低。对于72B这种大模型,光是权重加载就要占掉大量显存,留给KV Cache的空间本来就不多,再不做优化根本撑不住并发。
所以如果你的目标是"能跑起来就行",transformers够用;但只要涉及多用户并发或者要求吞吐,就必须上推理框架。昇腾平台上可选的主要有MindIE和vLLM Ascend两条路。
2.2 MindIE和vLLM Ascend怎么选
MindIE是昇腾原生的推理引擎,华为自家维护,对昇腾硬件的适配是最深的,性能调优也最充分。但它的接口和生态相对封闭,配置方式是JSON文件那一套,和主流开源社区的习惯不太一样。如果你是从英伟达平台迁移过来的,用MindIE会有一段适应期。
vLLM Ascend则是把vLLM这套开源推理框架移植到昇腾上,底层通过torch_npu调用昇腾算力。它的最大好处是接口和vLLM完全一致——OpenAI兼容的API、同样的启动参数、同样的PagedAttention和continuous batching机制。如果你之前用过vLLM,迁移成本几乎为零。而且vLLM的社区活跃,新模型的支持往往更快。
我最终选vLLM Ascend,核心原因有三个:第一,我的业务代码本来就是基于vLLM的OpenAI API写的,换框架意味着改代码;第二,vLLM Ascend对Qwen系列的支持比较及时,Qwen3.5发布后没多久就有适配;第三,它的配置方式我熟悉,出问题好排查。当然,如果你追求极致性能且不介意学习成本,MindIE值得一试,这个我不否认。
2.3 硬件和显存的基本账
在动手之前,先算一笔显存账,这决定了你能跑多大的模型、用什么精度。
昇腾910B常见的有两个版本:32GB和64GB HBM。跑Qwen3.5的话,模型权重的显存占用大致可以这样估算:
| 模型规模 | FP16权重占用 | INT8量化 | INT4量化 | 推荐卡型 |
|---|---|---|---|---|
| Qwen3.5-7B | 约14GB | 约7GB | 约4GB | 32GB单卡 |
| Qwen3.5-14B | 约28GB | 约14GB | 约8GB | 32GB单卡 |
| Qwen3.5-32B | 约64GB | 约32GB | 约18GB | 64GB单卡 |
| Qwen3.5-72B | 约144GB | 约72GB | 约40GB | 64GB双卡/四卡 |
注意这只是权重占用,实际还要留出KV Cache和中间激活的空间。经验值是权重占用不要超过总显存的60%,剩下的留给KV Cache。所以64GB单卡跑72B的INT4量化是可行的,但并发数上不去;要撑并发,得上多卡张量并行。
我这次实测用的是64GB单卡的910B,跑Qwen3.5-32B的INT8量化版本,这个组合在显存和性能之间比较平衡,也是我认为大多数中小团队最可能落地的配置。
3. 环境准备:CANN、驱动和torch_npu
3.1 驱动和固件的安装顺序
昇腾的环境安装有个铁律:先装驱动和固件,再装CANN,最后装Python侧的torch_npu。顺序错了会出现各种莫名其妙的报错,比如设备识别不到、算子找不到。
驱动和固件的版本必须和CANN匹配。我用的组合是驱动版本24.1.rc3、固件版本7.5.0.3、CANN 8.0.RC2。这几个版本是官方文档里明确互相兼容的。如果你拿到的机器驱动版本比较老,建议先升级,否则CANN 8.0可能装不上。
安装驱动前先确认机器上有没有旧版本,有的话要卸载干净:
# 查看当前驱动版本 npu-smi info # 卸载旧驱动(如果有) ./Ascend-hdk-910b-npu-driver_xxx.run --uninstall安装驱动时用--full参数,它会同时装驱动和固件:
chmod +x Ascend-hdk-910b-npu-driver_24.1.rc3_linux-aarch64.run ./Ascend-hdk-910b-npu-driver_24.1.rc3_linux-aarch64.run --full装完重启机器,然后用npu-smi info确认能看到卡。这一步如果看不到卡,后面全都白搭,一定要先解决。
注意:驱动安装会重启相关服务,如果机器上有其他业务在跑,挑个维护窗口操作。另外驱动和固件版本不匹配是新手最常见的坑,装之前务必对照官方兼容性矩阵确认。
3.2 CANN的安装与验证
CANN是昇腾的异构计算架构,相当于英伟达的CUDA。它包含了算子库、通信库、编译器等一系列组件。安装包一般叫Ascend-cann-toolkit_8.0.RC2_linux-aarch64.run。
安装前先装依赖:
# 以Ubuntu为例 apt-get install -y gcc g++ make cmake zlib1g-dev libsqlite3-dev openssl \ libssl-dev libffi-dev libbz2-dev libreadline-dev liblzma-dev然后执行安装:
chmod +x Ascend-cann-toolkit_8.0.RC2_linux-aarch64.run ./Ascend-cann-toolkit_8.0.RC2_linux-aarch64.run --install安装脚本会提示你设置环境变量,装完后需要source一下:
source /usr/local/Ascend/ascend-toolkit/set_env.sh建议把这行加到~/.bashrc里,省得每次手动source。验证CANN是否正常:
# 查看CANN版本 cat /usr/local/Ascend/ascend-toolkit/latest/version.cfg # 运行一个简单的算子测试 python3 -c "import acl; print(acl.get_soc_name())"如果get_soc_name()能返回Ascend910B之类的字符串,说明CANN基本正常。
3.3 torch_npu和PyTorch的版本匹配
这是最容易出问题的一环。torch_npu是PyTorch的昇腾适配层,它的版本必须和PyTorch版本严格对应。比如torch_npu 2.4.0对应PyTorch 2.4.0,不能混用。
我的建议是用conda建一个独立环境,避免污染系统Python:
conda create -n qwen_ascend python=3.10 -y conda activate qwen_ascend然后安装PyTorch和torch_npu。注意PyTorch要装CPU版本,因为昇腾的算力是通过torch_npu提供的,不需要CUDA版PyTorch:
pip install torch==2.4.0 --index-url https://download.pytorch.org/whl/cpu pip install torch-npu==2.4.0装完后验证:
python3 -c "import torch; import torch_npu; print(torch.npu.is_available()); print(torch.npu.device_count())"如果输出True和卡的数量,说明torch_npu装好了。这一步如果报错,八成是CANN环境变量没source,或者版本不匹配。
实操心得:torch_npu的安装包在pip源上不一定有最新版,有时候需要从昇腾社区下载whl文件手动安装。另外,如果你在aarch64架构的机器上装,注意选对架构的包,x86的包在ARM机器上装不了。
4. vLLM Ascend的安装与模型准备
4.1 vLLM Ascend的安装方式
vLLM Ascend的安装有两种方式:pip直接装和源码编译。pip装简单,但版本可能不是最新的;源码编译麻烦,但能拿到最新特性和修复。
我建议先用pip装,跑通了再说:
pip install vllm-ascend==0.7.3注意vllm-ascend会依赖特定版本的vllm,pip会自动处理。装完后验证:
python3 -c "import vllm_ascend; print(vllm_ascend.__version__)"如果这一步报错说找不到某个算子库,通常是CANN的算子包没装全,需要补装Ascend-cann-kernels包。
源码编译的话,流程大致是:
git clone https://github.com/vllm-project/vllm-ascend.git cd vllm-ascend pip install -e .源码编译对CANN版本和编译器版本要求更严,如果pip能装成功,没必要折腾源码。
4.2 Qwen3.5模型的下载与格式转换
模型可以从ModelScope或者HuggingFace下载。国内环境用ModelScope更快:
pip install modelscope modelscope download --model Qwen/Qwen3.5-32B-Instruct --local_dir ./Qwen3.5-32B-Instruct下载下来的是HuggingFace格式的权重,vLLM Ascend可以直接加载,不需要额外转换。但如果你要用量化版本,比如GPTQ或AWQ,需要确认vLLM Ascend是否支持对应的量化算子。昇腾平台对量化的支持不如英伟达全面,我实测下来INT8的W8A8量化支持比较好,INT4的AWQ在某些版本上会有算子缺失的问题。
如果要用INT8量化,可以用昇腾提供的量化工具,或者直接用社区已经量化好的版本。我这次用的是社区版Qwen3.5-32B的INT8量化权重,加载后显存占用从64GB降到了约34GB,留出了足够的KV Cache空间。
注意:模型下载后检查一下文件完整性,特别是
config.json和safetensors索引文件。有时候下载中断会导致文件损坏,加载时报的错会很隐晦,让人以为是框架问题。
4.3 目录结构和权限检查
模型目录的权限要注意,vLLM进程的运行用户必须有读权限。我遇到过因为模型放在root目录下、普通用户读不了导致加载失败的情况。建议把模型放在一个公共可读的路径,比如/data/models/,然后chmod -R 755。
目录结构大致是这样:
/data/models/Qwen3.5-32B-Instruct/ ├── config.json ├── generation_config.json ├── model-00001-of-00015.safetensors ├── ... ├── tokenizer.json ├── tokenizer_config.json └── vocab.json确认safetensors文件数量和索引文件里记录的一致,少一个都会加载失败。
5. 启动vLLM Ascend服务与参数调优
5.1 最简启动命令
先跑一个最简的启动命令,确认整条链路能通:
python3 -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen3.5-32B-Instruct \ --served-model-name qwen3.5-32b \ --tensor-parallel-size 1 \ --dtype bfloat16 \ --max-model-len 8192 \ --port 8000这里几个参数解释一下。--tensor-parallel-size 1表示单卡,如果你是多卡要改成对应的卡数。--dtype bfloat16指定精度,910B对bfloat16支持良好。--max-model-len 8192限制最大上下文长度,这个值直接影响KV Cache的显存占用,设太大容易OOM。
启动过程中会打印一堆日志,重点看有没有报错,以及最后有没有出现Uvicorn running on http://0.0.0.0:8000。如果卡在加载模型阶段很久,可能是权重读取慢,耐心等;如果直接报错退出,看错误信息定位。
5.2 关键参数的计算与选择
--max-model-len和--gpu-memory-utilization这两个参数是显存占用的主要调节旋钮,需要配合着算。
--gpu-memory-utilization默认是0.9,意思是vLLM最多用90%的显存。对于64GB的卡,就是57.6GB。模型权重占了34GB(INT8量化后),剩下约23GB给KV Cache和激活。
KV Cache的占用可以用这个公式估算:
KV Cache大小 = 2 × 层数 × 注意力头数 × 头维度 × 序列长度 × batch大小 × 精度字节数以Qwen3.5-32B为例,假设64层、40个注意力头、头维度128、bfloat16(2字节),那么每个token的KV Cache占用约为:
2 × 64 × 40 × 128 × 2 = 1,310,720 字节 ≈ 1.25MB8192的序列长度,单条请求就是约10GB。所以23GB的KV Cache空间大概能同时支撑2条8192长度的请求,或者更多短请求。这就是为什么--max-model-len不能乱设——设成32768的话,单条请求就要40GB,直接OOM。
实际部署时,我建议先设一个保守的--max-model-len,跑起来后用压测工具测实际并发,再逐步调整。--gpu-memory-utilization可以设到0.92左右,留一点余量给系统。
5.3 多卡张量并行的配置
如果要跑72B或者想提高并发,就得上多卡。假设你有4张64GB的910B,可以这样启动:
python3 -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen3.5-72B-Instruct \ --served-model-name qwen3.5-72b \ --tensor-parallel-size 4 \ --dtype bfloat16 \ --max-model-len 16384 \ --gpu-memory-utilization 0.9 \ --port 8000--tensor-parallel-size 4表示用4张卡做张量并行。昇腾的多卡通信走HCCL,性能比英伟达的NVLink差一些,但比PCIe要好。多卡启动时要注意卡之间的拓扑,尽量用同一台机器内的卡,跨机器的通信开销会大很多。
启动多卡服务时,日志里会显示每张卡的加载进度,如果某张卡加载失败,整个服务起不来。常见问题是某张卡被其他进程占用,用npu-smi info确认所有卡都是空闲的。
实操心得:多卡启动第一次会比较慢,因为要做权重的分片和通信初始化。如果超过10分钟还没起来,检查一下HCCL的配置,有时候需要手动指定网卡或者设置
HCCL_IF_IP环境变量。
6. 性能实测与调优记录
6.1 测试环境与压测方法
我的测试环境是单台服务器,1张910B 64GB,CPU是鲲鹏920,内存512GB,系统Ubuntu 22.04 aarch64。模型是Qwen3.5-32B的INT8量化版,max-model-len设为8192。
压测工具用的是vLLM自带的benchmark脚本:
python3 benchmarks/benchmark_serving.py \ --backend openai \ --model qwen3.5-32b \ --base-url http://localhost:8000 \ --dataset-name sharegpt \ --num-prompts 200 \ --request-rate 5这个脚本会模拟真实请求,输出吞吐、延迟等指标。--request-rate 5表示每秒发5个请求,可以调整来测不同负载下的表现。
6.2 实测数据与瓶颈分析
在request-rate为5的情况下,实测结果大致如下:
| 指标 | 数值 |
|---|---|
| 输出吞吐 | 约420 tokens/s |
| 首token延迟(P50) | 约380ms |
| 首token延迟(P99) | 约1.2s |
| 单请求生成速度 | 约28 tokens/s |
| 并发请求数 | 约8-10 |
这个成绩和同级别的英伟达A100比,大概能到60%-70%的水平。差距主要在算子优化和通信效率上,这是生态成熟度的客观差距,不是配置能弥补的。
瓶颈分析下来,主要有两个:一是首token延迟偏高,因为prefill阶段的计算密集,昇腾的矩阵运算单元利用率还没拉满;二是并发上去之后,KV Cache的显存带宽成为瓶颈。910B的HBM带宽和A100比有差距,这是硬件层面的。
6.3 几个有效的调优手段
调优这块我试了几个方向,有见效的也有没用的。
有效的:
第一,开启--enable-prefix-caching。如果业务里有大量重复的system prompt,这个能显著降低首token延迟。我实测在system prompt固定的场景下,首token延迟降了约30%。
第二,调整--max-num-batched-tokens。这个参数控制单次batch的最大token数,默认值偏保守。适当调大能提高吞吐,但调太大反而会因为显存碎片导致性能下降。我试下来设成4096比较合适。
第三,用--quantization显式指定量化方式。如果模型是INT8量化的,不指定的话vLLM可能按FP16加载,白白浪费显存。
没用的:
尝试过调整--block-size,对性能影响微乎其微。也试过开--enforce-eager,反而更慢了,因为失去了图模式的优化。
注意:调优是个反复试的过程,每次只改一个参数,记录结果,别一次改一堆,否则出了问题不知道是哪个参数导致的。另外,压测时机器上不要跑其他任务,否则数据不准。
7. 常见问题与排查技巧实录
7.1 启动阶段的典型报错
报错一:RuntimeError: NPU out of memory
这是最常见的。原因通常是--max-model-len设太大,或者--gpu-memory-utilization设太高。解决方法是先降低这两个值,跑起来后再逐步往上调。另外确认一下模型是不是真的INT8量化版,如果误加载了FP16权重,显存直接翻倍。
报错二:ImportError: libascend_hal.so: cannot open shared object file
CANN环境变量没source。执行source /usr/local/Ascend/ascend-toolkit/set_env.sh,或者检查LD_LIBRARY_PATH里有没有CANN的lib路径。
报错三:HCCL error: communication init failed
多卡启动时的通信初始化失败。检查所有卡是否空闲,检查HCCL的网卡配置。有时候需要设置export HCCL_IF_IP=<本机IP>来指定通信网卡。
7.2 运行阶段的性能问题
问题:吞吐上不去,GPU利用率低
先用npu-smi info看卡的利用率和显存占用。如果利用率长期低于50%,说明batch没打满。检查--max-num-seqs是不是设太小了,默认是256,一般够用。另外看请求的输入输出长度,如果都是短请求,prefill和decode频繁切换,效率会低。
问题:首token延迟忽高忽低
可能是prefix caching没开,或者请求的prompt长度差异太大。开启prefix caching,并且尽量让同类请求的prompt结构一致。
7.3 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 启动即OOM | max-model-len过大/精度不对 | 降低长度,确认量化版本 |
| 找不到NPU设备 | 驱动未装/CANN未source | npu-smi info,source环境变量 |
| 多卡通信失败 | HCCL配置/卡被占用 | 检查卡状态,设置HCCL_IF_IP |
| 吞吐低 | batch未打满/短请求多 | 调大max-num-seqs,合并请求 |
| 首token慢 | prefix caching未开 | 开启enable-prefix-caching |
| 模型加载卡住 | 权重文件损坏/权限不足 | 校验文件,检查读权限 |
7.4 几个独家避坑技巧
第一个,模型加载慢的时候别急着kill进程。72B的模型从磁盘加载到显存,在机械盘上可能要十几分钟,SSD也要几分钟。我一开始以为卡死了,kill了好几次,后来发现只是慢。
第二个,vLLM Ascend的日志级别可以调,默认的INFO级别日志很多,排查问题时可以设VLLM_LOGGING_LEVEL=DEBUG看更详细的信息,但生产环境记得调回去,否则日志会撑爆磁盘。
第三个,如果要用Docker部署,基础镜像一定要选对。昇腾有官方的ascendhub镜像仓库,里面有预装好CANN和torch_npu的镜像,能省掉大量环境配置工作。自己从裸镜像装CANN,光是依赖就能折腾半天。
第四个,压测的时候注意warmup。第一次请求会触发算子编译,延迟特别高,要把前几个请求排除掉再统计。我一般先发20个请求做warmup,再开始正式压测。
8. 生产部署的几点补充
如果要把这套东西放到生产环境,还有几件事要做。
服务进程的管理,建议用systemd或者supervisor托管,别用nohup裸跑。进程挂了要能自动拉起,日志要能轮转。我见过用nohup跑然后日志把磁盘写满导致服务挂掉的案例。
监控方面,昇腾有npu-smi可以采集卡的利用率和显存,配合Prometheus和Grafana能做可视化。vLLM本身也暴露了metrics接口,在/metrics路径下,可以采集请求数、延迟、吞吐等指标。这两套指标结合起来,基本能覆盖大部分监控需求。
高可用方面,单机部署始终有单点故障风险。如果业务不能接受停机,至少要做两台机器做负载均衡。vLLM的OpenAI API是无状态的,前面挂一个Nginx或者HAProxy就能做负载均衡。注意会话保持的问题,如果业务依赖多轮对话的上下文,要么在客户端维护历史,要么做会话粘性。
版本管理这块,昇腾的软件栈升级比较频繁,升级前一定要在测试环境验证。我遇到过CANN小版本升级后,某个算子行为变化导致输出结果不一致的情况。生产环境升级要谨慎,做好回滚预案。
最后说一句,国产算力的部署体验和英伟达比确实还有差距,文档分散、报错信息不友好、社区资料少,这些都是现实。但跑通之后,整套方案的稳定性是没问题的,我这边连续跑了两周没出现过崩溃。如果你也在做类似的事情,希望这篇内容能帮你少走点弯路。