得物技术公众号文章

国产算力·昇腾 910C 大模型推理优化(上):镜像更新与参数调优,提速 40%|得物技术

文中详细给出每一项优化的动机、实现(含具体源码改动路径与关键代码片段)、验证数据与踩坑记录(包括十余项实测无效或负向的尝试),希望为同样在国产算力上做推理优化的团队提供一份可直接复用的工程参考。

得物技术

公众号 · 9 篇文章

目录

一、背景

    1.昇腾NPU与英伟达GPU软硬件架构对比

    2.英伟达与昇腾架构核心差异概览

二、软硬件环境

    1.硬件配置

    2.模型信息:Qwen3.5-35B-A3B

    3.软件环境

    4.压测方法与指标定义

三、性能基线与瓶颈分析

    1.基线数据

    2.Profile分析

    3.优化路线图总览

四、优化实践(一):镜像版本与参数调优

    1.推理框架版本升级

    2.集合通信下沉到 AIVector:HCCL_OP_EXPANSION_MODE=AIV

    3.静态 kernel 预编译(Static Kernel)

    4.HBM 分配器调优:虚拟内存映射 + pin_memory + page_size

    5.host 侧内存分配优化:jemalloc 替换 glibc malloc

    6.算子缓存与 batch memcpy

    7.MoE 权重预取(Weight Prefetch)

    8.HCCL 通信参数与 CPU 绑定

    9.cudagraph 配置:模式、捕获尺寸与 warmup

    10.实测无效或负向的尝试(避坑清单)


本文系统性地总结了我们以华为昇腾 910C 为国产算力底座,在大模型在线推理场景下完成国产化适配与性能优化工作。项目从建立 Qwen3.5-35B-A3B 在 910C 上的性能基线出发,通过 MindStudio 进行性能剖析定位瓶颈,按照「镜像与版本升级 → 参数与环境变量调优 → vllm-ascend 源码修改 → 算子层融合优化 → MTP 投机解码」五个层次逐层推进,累计落地 20 余项有效优化。


最终成果:在 910C-560t(单卡双 die、TP=2)上,未开启投机解码时,TPOT(Time Per Output Token,单 token 解码延迟)从基线 14.22ms 降至 7.8ms(c=1,下降 45.1%);开启 MTP 投机解码并叠加全部源码级优化后,真实业务场景的数据集上 c=1/c=4/c=8 的 TPOT 分别达到 5.0ms/8.8ms/11.9ms,较基线分别下降 64.8%/49.9%/43.8%;在 GSM8K 公开数据集上 c=1 TPOT 达到 4.2ms。同时,同一套优化方法论在 Qwen3.6-27B 上也得到验证(c=1 从 30.7ms 降至 11.8ms)。


文中详细给出每一项优化的动机、实现(含具体源码改动路径与关键代码片段)、验证数据与踩坑记录(包括十余项实测无效或负向的尝试),希望为同样在国产算力上做推理优化的团队提供一份可直接复用的工程参考。

一

背景

当前国内 NVIDIA GPU 供给持续紧张,H20 已无新增算力供给,核心业务算力的国产化替代已迫在眉睫。华为昇腾 910C 作为主流国产算力,已在实际业务的离线推理场景完成投产验证,证明了国产算力在批处理场景下的可行性。然而,在在线实时推理场景中,由于业务对响应时间(RT)要求较为严苛,直接部署大参数模型会导致 RT 无法满足 SLA,因此 910C 暂时无法直接替换现有在线服务。为保证 910C 能应用于在线推理业务,通过轻量级小模型(如 Qwen3-32B、Qwen3.5-35B-A3B、Qwen3.5-27B、Qwen3.6-27),实现在线场景下国产算力的平滑替代。

昇腾NPU与英伟达GPU软硬件架构对比

图片

昇腾与英伟达架构对比

英伟达与昇腾架构核心差异概览

图片

项目目标:以华为昇腾 910C 为核心国产化算力底座,完成在线推理优化的目标为:建立 Qwen3.5 系列模型在 910C 上的性能基线,并以 H20 作为对照组;通过系统优化将低并发(c=1/4/8)TPOT 与 H20 的差距缩小到 80%。

二

软硬件环境

硬件配置

本次优化使用的机器型号为昇腾 910C-560t,单卡双 die 形态:一张加速卡上封装两个 chip(die),每个 chip 拥有独立的 64GB HBM,整机 8 卡共 16 个 die。

图片

模型信息:Qwen3.5-35B-A3B

优化工作的主力模型是 Qwen3.5-35B-A3B——总参数约 35B、激活参数约 3B 的 MoE(Mixture of Experts)混合架构模型。该模型的架构特点对推理优化策略影响很大,这里展开说明:


  • 混合注意力结构:模型包含 30 层 GDN(Gated DeltaNet)线性注意力层与 10 层全注意力(Full Attention)层交替,GDN 层使用递归状态(recurrent state/mamba state)代替 KV Cache,decode 阶段每层都要执行 causal_conv1d、L2Norm、recurrent kernel 等一系列小算子,算子数量多、launch 开销占比高,是优化的重点对象。

  • MoE + 共享专家:每个 MoE 层除路由专家(routed experts, GroupedMatmul)外还有一个共享专家(shared expert),decode 低并发时专家权重从 HBM 读取的带宽开销显著,这为权重预取(weight prefetch)与多流重叠提供了优化空间。

  • Gemma 风格 RMSNorm:归一化权重以 (1.0 + weight) 的形式参与计算,若不做缓存,每层每步都会产生一次加法 + 拷贝开销。

  • MTP(Multi-Token Prediction)头:模型自带 MTP 模块,可直接用于投机解码(speculative decoding),训练时未开启 MTP 的真实业务场景的微调模型也可以在推理时直接受益。

图片

需要特别说明的是框架版本这条线:vllm-ascend 社区版本迭代很快,不同版本在图编译、MoE 通信(MC2/AllToAll)、投机解码等路径上的实现差异很大。我们早期的基线建立在 0.22.0rc1 上,优化中期切换到 0.23.0rc1 并获得了 15% 以上的直接收益(见 4.1 节),最终所有源码级优化都迁移并固化在正式发布的 v0.23.0 基线的内部分支上。

压测方法与指标定义

为保证数据可比,所有优化实验都遵循统一的压测协议:


  • 核心指标:TPOT(Time Per Output Token,decode 阶段每个输出 token 的平均延迟,在线场景主优化指标)与 TTFT(Time To First Token,首 token 延迟),对照组为 H20 单卡(vLLM 0.23.0)。

  • 默认负载:输入 1024 token、输出 512 token(min/max prompt 与 min/max tokens 分别锁定为 1024/512),并发取 1/4/8 三档,覆盖在线低并发场景。

  • 数据集:基线使用 random dataset(排除缓存与数据分布影响),后期验证使用真实业务场景的数据集与 gsm8k 公开数据集。

  • profile 工具:(昇腾官方 profiler)采集 NPU/host 时间线,通过 MindStudio 定位每个 decode step 内部的算子耗时、通信耗时与 host 侧空隙。


标准的 evalscope 压测命令如下:

evalscope perf \  --parallel 1 4 8 \  --number 40 80 80 \  --warmup-num 20 \  --model <model_name> \  --url http://127.0.0.1:5001/v1/chat/completions \  --api openai \  --stream \  --dataset random \  --max-tokens 512 --min-tokens 512 \  --min-prompt-length 1024 --max-prompt-length 1024 \  --prefix-length 0 \  --tokenizer-path <path/to/model>/qwen3.5-35b-a3b \  --outputs-dir "./my_output"

三

性能基线与瓶颈分析

基线数据

在 910C-752t(单卡双 die、TP=2)、vllm-ascend 0.22.0rc1、默认开启 cudagraph、random 数据集、输入 1024/输出 512 条件下,测得 Qwen3.5-35B-A3B 的基线如下:

图片

基线阶段还同步建立了 Qwen3-8B/14B/32B 与 Qwen3.5-4B/9B/27B 等模型在 910C 与 H20 上的完整对照基线(涵盖单卡单 die、单卡双 die 两种形态,输入输出 1024-512/1024-1024/4096-512 三种负载),总体规律一致:TTFT(首 token 延迟)方面昇腾占优,H20 约为昇腾的 24 倍,说明昇腾在 prefill 阶段(计算密集)效率不错;TPOT(decode 延迟)方面 H20 占优,H20 约为昇腾的 0.60.65 倍,decode 是在线场景的主优化指标,也是本次优化的主攻方向。

Profile 分析

我们用 MindStudio 对 c=1 的 decode 时间线做了逐层剖析。Qwen3.5-35B-A3B 单个 decode step 要穿过 40 个 decoder 层,每层包含 GDN/全注意力 + MoE 两个子模块,一个 step 内要 launch 上千个算子。剖析结果可以归纳为六大类开销:


  • host 侧调度与下发开销:低并发下每个算子的计算时间极短(微秒级),host 侧的 kernel dispatch、aclnn 执行、event record/wait 成为主要开销。典型例子:triton slot_mapping kernel 固定约 131us 的 launch 开销;每层 conv 权重 transpose+contiguous 约 4.3us × 30 层 ≈ 129us/step。

  • host-device 同步与冗余拷贝:投机解码验证路径上,MinTokens logits processor 每步在 host 侧用 numpy 重建 (rows, toks) 掩码,并从分页内存做 H2D 拷贝——分页内存的 non_blocking 拷贝实际是同步的,直接把异步调度流水打断,实测占单步时间约 30%;model_runner 中还有大量每步无条件执行的 copy_to_gpu/fill_。

  • 集合通信开销:TP=2 下每层有多次 AllReduce。默认配置下 HCCL 的集合通信算子跑在 AI CPU 上,占用独立的 AI CPU 资源且与计算难以重叠;此外 compute_logits 会做全词表 logits 的 all-gather,通信量高达 O(batch × vocab_size),而贪婪采样其实只需要 argmax。

  • HBM 带宽瓶颈(MoE 权重读取):c=1 decode 时 batch 极小,MoE GroupedMatmul 是带宽受限的,专家权重从 HBM 加载的时间构成关键路径;shared expert 的权重加载与 routed expert 计算默认串行。

  • 图模式编译质量:aclnn 动态 shape kernel 在 replay 阶段仍要走 runtime GetWorkspaceSize 等流程;静态 kernel 预编译可以把这部分开销固化掉,但需要框架版本与编译选项支持。

  • 内存分配开销:host 侧 glibc malloc 在高频小对象分配场景下锁竞争明显;device 侧 HBM 分配器的 page 粒度影响碎片与映射开销。


这六类开销分别对应了后文的五个优化层次。总体思路是先易后难,先做容易见效的调整,比如版本升级、环境变量等参数调整,再做需要改源码的精细优化,最后再进行底层的算子融合。


下面是通过 MindStudio 进行昇腾性能分析是示例图:

图片

MindStudio 进行性能 profile 分析示例

优化路线图总览

整个优化过程历时约 1 个月,按层次与时间线归纳如下:

图片

四

优化实践(一):镜像版本与参数调优

本章所有记录不需要改源码、通过换版本或调参数/环境变量就能生效的优化。这部分投入产出比最高,建议任何做昇腾推理优化的团队先系统性地过一遍。

推理框架版本升级

优化初期我们的基线建立在 vllm-ascend 0.22.0rc1 上。将镜像切换到 0.23.0rc1(其余参数完全保持不变)后,c=1 TPOT 直接从 14.2ms 降到 12.0ms,下降 15.4%。

图片

升级 vllm-ascend 0.23.0rc1 后的压测结果(TPOT 12.0ms)


版本升级的收益来自社区在 MoE 通信路径、图编译、调度器等方面的持续迭代。之后我们又试验了基于 v0.23.0 的 nightly 镜像,额外收益约 0.1ms(提升很小)。这也给出一条经验:版本升级要趁早做、一次到位,后面所有源码级优化都应建立在最新稳定版本之上,避免将来再做一次痛苦的迁移。我们最终的工程基线是正式发布版 v0.23.0。


CANN 层面同理:从 CANN 9.0.0 镜像更换为 CANN 9.1.0 镜像后,c=1 TPOT 从 8.8ms 降到 8.5ms(-3.4%),并且 9.1.0 是后续 SuperKernel stream fusion 等编译选项(见 6.2 节)的前置依赖。

集合通信下沉到 AIVector:

HCCL_OP_EXPANSION_MODE=AIV

默认情况下,HCCL 的集合通信算子(主要是 AllReduce)由 AICPU 执行。AICPU 是独立于 AI Core 的计算单元,其算力与带宽都弱于 AI Core,且通信算子跑在 AICPU 上时与主计算流难以重叠,成为 decode 关键路径上的串行开销。


通过设置环境变量,可以把集合通信算子「展开」到 AIV(AI Vector)单元上执行:export HCCL_OP_EXPANSION_MODE=AIV。


在 Qwen3.5-35B-A3B(0.23.0rc1 基线)上,该配置使 c=1 TPOT 从 12.0ms 降到 11.2ms(-6.6%);此前在 Qwen3-8B 上验证时为 9.9ms → 9.25ms(-6.5%),收益稳定。值得注意的是,vllm-ascend 社区后来也在特定模型(如 MiniMax-M2)的 ACL graph 捕获路径上默认开启了同样的配置,侧面印证了该优化的通用性。

图片

HCCL_OP_EXPANSION_MODE=AIV 压测结果

静态 kernel 预编译(Static Kernel)

vllm-ascend 的图模式默认使用动态 shape 的 aclnn kernel:即使是 cudagraph 捕获的固定 batch size,replay 时 runtime 仍要执行 GetWorkspaceSize 等动态推导逻辑。静态 kernel 预编译的思路是:在服务启动的 graph capture 阶段,提前为每个 cudagraph_capture_size(如 [1,2,4,8,...])预编译静态 shape 的 aclnn kernel,把 workspace 计算固化进编译产物,replay 时直接走预编译的静态路径。


开启方式(注意多卡场景必须设置 LOCAL_WORLD_SIZE):

export ASCEND_RT_VISIBLE_DEVICES=0,1export LOCAL_WORLD_SIZE=2            # 多卡必须设置vllm serve /root/workspace/models/Qwen3-32B \  --tensor-parallel-size 2 \  --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \  --additional-config '{"ascend_compilation_config": {"enable_static_kernel": true}}'

收益与模型规模强相关:Qwen3-32B 上叠加前序优化后,c=1 TPOT 从 31.5ms 降到 30.9ms(-1.9%);而在 Qwen3.5-35B-A3B 上,叠加 HCCL AIV 后收益高达 18.7%(11.2ms → 9.1ms),因为 A3B 激活参数小、decode step 内算子多且碎,host 侧动态推导开销占比更高。

图片

静态 kernel + HCCL AIV 叠加后的压测结果


静态 kernel 编译本身也有一些坑:编译过程中会调用 CANN 工具链,部分头文件路径与 CANN 版本相关。我们在容器环境里遇到过 cann_compat.h 缺失导致编译失败的问题,最终通过启动脚本内联生成兼容头文件解决(见下篇)。

HBM 分配器调优:

虚拟内存映射 + pin_memory + page_size

torch_npu 的设备内存分配器支持一组通过 PYTORCH_NPU_ALLOC_CONF 控制的调优项。针对在线低并发场景,我们开启了虚拟内存映射(expandable segments)、1GB 大页、pin memory 后台线程等组合:


export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True,base_addr_aligned_kb:16,page_size:1g,pinned_use_background_threads:True,pin_memory_expandable_segments:True。


各项的含义:

  • expandable_segments:True:采用虚拟内存映射方式分配 HBM,segment 可按需扩展,缓解碎片问题,大张量分配不再要求物理连续。

  • page_size:1g:把虚拟内存页大小调整为 1GB,减少页表项数量与映射开销。

  • pinned_use_background_threads:True/pin_memory_expandable_segments:True:pin memory(锁页内存)分配走后台线程,避免阻塞推理主流程,同时锁页内存池可扩展。

  • base_addr_aligned_kb:16/roundup_power2_divisions:16:控制分配对齐与分桶粒度,减少小对象分配的碎片。


该组合在叠加前序优化的基础上,使 c=1 TPOT 从 9.1ms 降到 8.8ms(-3.3%)。后期配合 jemalloc(见 4.5)时,我们改用了更精简的组合:expandable_segments:True,roundup_power2_divisions:16。

图片

虚拟内存 + pin_memory + page_size 调优后的压测结果

host 侧内存分配优化:

jemalloc 替换 glibc malloc

vLLM 的 Python/host 侧每步会产生大量小对象分配(tensor 元数据、调度结构、通信描述符等)。glibc 的 ptmalloc 在多线程高频小对象场景下存在 arena 锁竞争与碎片膨胀问题。我们通过 LD_PRELOAD 将分配器替换为 jemalloc:


export LD_PRELOAD=/usr/lib64/libjemalloc.so.2。


收益随并发增大而增大:c=1 -0.1ms、c=4 -0.3ms、c=8 -0.6ms。原理上并发越高,host 侧线程间分配竞争越激烈,jemalloc 的 thread cache 结构优势越明显。这是一项零风险的通用优化,建议默认开启。

算子缓存与 batch memcpy

aclnn 算子在 host 侧有一层执行信息缓存,默认缓存条目有限。ACLNN_CACHE_LIMIT 用于配置单算子执行时 host 侧缓存的算子信息条目数:


export ACLNN_CACHE_LIMIT=100000

export VLLM_ASCEND_ENABLE_BATCH_MEMCPY=1


ACLNN_CACHE_LIMIT=100000,提高 host 侧算子缓存上限,避免 decode 过程中缓存淘汰导致的重复解析,实测 p99 从 9.1ms 降到 8.9ms,收益不大但稳定,对消除尾延迟毛刺有帮助;VLLM_ASCEND_ENABLE_BATCH_MEMCPY=1,把分散的小块 H2D/D2H memcpy 合并为批量拷贝,减少拷贝次数与 launch 开销。

MoE 权重预取(Weight Prefetch)

c=1 decode 时 MoE 层的 GroupedMatmul 是带宽受限的:专家权重体量远大于激活数据,每个 step 都要从 HBM 读取。weight prefetch 机制利用 NPU 的 prefetch 能力,在当前层计算时提前把下一层的权重搬进 L2 Cache,用计算时间掩盖权重加载延迟。


开启方式:

--additional-config '{..., "weight_prefetch_config": {"enabled": true}, "pa_shape_list": [1,4,8]}'

这里有三个关键细节。预取阈值改动(源码):上游 vllm_ascend/ops/weight_prefetch.py 中 MOE_PREFETCH_TOKEN_THRESHOLD = 96,即 batch 中 token 数少于 96 时不做 MoE 预取——这是为吞吐场景设计的保守值。而我们的在线场景恰恰是 c=1~8 的极小 batch,decode 时 token 数等于并发数,永远达不到阈值。将阈值改为 1 后,c=1 decode 也能触发预取:

MOE_PREFETCH_TOKEN_THRESHOLD = 1   # 原值 96

pa_shape_list:显式声明 decode 的 batch 形状列表 [1,4,8],让 paged attention 与预取逻辑对这些低并发形状做针对性处理。shared expert 预取扩展(源码):原实现只预取 routed expert 的 w13(gate_up)权重,我们在 preprocess 阶段追加了对下一层 shared expert gate_up 权重的预取(_get_moe_shared_expert_gate_up_prefetch_weight),使其在当前层 routed expert GroupedMatmul 执行期间提前进入 L2,填补多流重叠暴露出的空窗。


收益:c=8 时 TTFT 改善(prefill 阶段 expert 权重提前进 L2 cache)、c=4 TPOT 改善 0.2ms、c=1 改善 0.1ms。

HCCL 通信参数与 CPU 绑定

最终最佳实践脚本中还固化了以下通信与调度参数:


  • HCCL_BUFFSIZE=128:HCCL 通信缓冲区大小(MB),影响通信分块粒度,需按消息大小试参。

  • HCCL_DETERMINISTIC=false:关闭确定性归约,确定性模式会强制固定的分块与归约顺序,牺牲一部分通信效率换取结果可复现,在线服务不要求比特级复现,关闭后通信更快。

  • HCCL_WHITELIST_DISABLE=1:禁用 HCCL 白名单校验,减少通信建链开销。

  • COMBINED_ENABLE=1:开启算子组合(combined)标志,优化两个非连续算子的组合场景(在 35B-A3B 单独测试时未见收益,但与整体组合保留)。

  • OMP_NUM_THREADS=1/GE_USE_STATIC_MEMORY=1:限制 OpenMP 线程数避免 host 侧线程争抢,GE 使用静态内存减少图引擎动态分配。

  • enable_cpu_binding: true(additional-config):开启 worker 进程的 CPU 绑核,注意这与更激进的 CPU_AFFINITY_CONF=2 精细化绑核不同——后者实测是负优化(见 4.10)。

cudagraph 配置:

模式、捕获尺寸与 warmup

图模式是 decode 优化的基石,我们把所有 decode 都放进图里执行:


  • cudagraph_mode = FULL_DECODE_ONLY:只对纯 decode 步骤做整图捕获,prefill 与混合批走 eager 路径,兼顾性能与灵活性。

  • cudagraph_capture_sizes:非投机场景使用 [1,2,4,8],开启 MTP(num_speculative_tokens=4)后,每步实际执行的 token 数 = 并发数 / dp × (1 + 投机长度),例如 c=4、TP=2 对应 2×5=10,再考虑 bonus token,我们把捕获尺寸调整为 [5,20,40](详见 7.2 节),这一步是修复 MTP 下 c=4/c=8 从 10.4ms 劣化到 42ms 的关键。

  • cudagraph warmup 次数(源码):vllm_ascend/platform.py 中 cudagraph_num_of_warmups 从 1 提到 3,原 1 次 warmup 下,部分 aclnn 算子(尤其 MoE fused experts)首次执行仍残留 host 侧编译/初始化开销,提到 3 次后稳态命中率显著提升,低并发收益最大(约 -0.2~-0.3ms)。

实测无效或负向的尝试(避坑清单)

优化的另一半工作是证伪。下表记录了我们实测无效、报错或负向的参数尝试,供后来者避坑。所有结论都基于 Qwen3.5-35B-A3B、TP=2、vllm-ascend 0.23.0 链路实测:

图片

几个有价值的观察:其一,昇腾的环境变量多达数百个,很多开关与 SoC 型号、CANN 版本、模型结构强耦合,文档上的「推荐值」不能替代实测;其二,部分功能(如 MatmulAllReduce、FlashComm)在 910C(ascend910_93)上的算子库支持尚不完整;其三,同一优化在不同模型上结论可能相反(FINE_GRAIN_REPLAY_SYNC),建立「每模型必实测」的流程比记住任何具体参数都重要。


以上为《国产算力·昇腾 910C 大模型推理优化》上篇,聚焦镜像版本升级与参数调优。下篇将继续深入 vllm-ascend 源码修改、算子层优化与 MTP 投机解码,敬请期待。




往期回顾


1.动态规则的 Agent 实践:用确定性工程驾驭 LLM 的资产智能验收

2.发一张图,它居然"跃"出屏幕了?一文读懂得物 3D 空间图片技术实践 

3.别再只卷向量检索了,得物交易搜索如何用“生成式”实现召回范式跃迁?

4.指标平台:从语义底座到智能消费的实践路径|得物技术

5.企业级 MultiAgent 落地:Plan 模式与主子 Agent 协作|得物技术


文 / 映川


关注得物技术,每周三更新技术干货

要是觉得文章对你有帮助的话,欢迎评论转发点赞~

未经得物技术许可严禁转载,否则依法追究法律责任。

“

扫码添加小助手微信

如有任何疑问,或想要了解更多技术资讯,请添加小助手微信:

图片

前往微信阅读全文

内容来自公众号,可前往微信查看原文。

查看作者的更多文章 →订阅更新、RSS 与阅读指南 →