Ascend Transformer Boost 实战:大模型推理性能提升的秘密
摘要:本文深入剖析了昇腾NPU在大模型推理中的性能瓶颈及优化方案。通过AscendTransformerBoost(ATB)技术,针对Transformer推理中的算子调度开销、显存碎片和MoE动态路由三大核心问题,提出了一系列优化策略。重点分析了Attention优化、KVCache管理、算子融合、动态Shape适配和Batch调优等关键技术,展示了从320TPS提升至1680TPS(提升425
摘要:大模型在昇腾 NPU 上推理,不优化只有 GPU 同档算力的 40-60%。Ascend Transformer Boost(ATB)是 CANN 生态的 Transformer 加速库,专门解决昇腾上 Transformer 推理的算子调度开销、显存碎片、MoE 动态路由三大瓶颈。本文从真实推理链路出发,拆解 ATB 在 Attention 优化、KV Cache 管理、算子融合、动态 Shape 适配、Batch 调优上的设计决策和性能收益,每个优化点都给前后对比数据。
标签:#昇腾NPU #CANN #ATB #大模型推理 #算子融合 #KV Cache #性能优化
SEO关键词:Ascend Transformer Boost, 昇腾大模型推理优化, ATB 算子融合, KV Cache 管理, MoE 推理加速, Batch 调优, 昇腾 NPU 吞吐提升
去年帮一个团队把 Qwen2.5-72B 部署到昇腾 910B 集群上。模型能跑,但 decode 吞吐 320 TPS,客户说 GPU 上能到 1800。差了 5 倍多。
跑 msprof 一看:算子执行占 41%,算子调度开销 23%,显存申请释放 15%,Host-NPU 搬运 12%,Stream 同步 9%。NPU 的 Cube/Vector 单元有 59% 的时间在等——等调度、等显存、等数据。
装上 ATB,开 FlashAttention + PagedAttention + W8A16 量化,吞吐从 320 拉到 1680 TPS。涨了 425%。
不是昇腾算力不够,是默认配置没打开加速特性。
Transformer 推理瓶颈到底在哪
大模型推理分两个阶段:Prefill(首 token 生成,计算密集)和 Decode(后续 token 逐个生成,访存密集)。
Prefill 阶段,一次处理整个输入序列,矩阵乘的计算量大,Cube Unit 是瓶颈。Decode 阶段,每次只处理 1 个 token,但要从 HBM 读整个 KV Cache,HBM 带宽是瓶颈。
实测数据(Qwen2.5-7B,910B 单卡,FP16):
| 阶段 | 耗时占比 | 瓶颈 |
|---|---|---|
| Attention(Prefill) | 35% | Cube(QK^T 计算量大) |
| KV Cache 读取(Decode) | 42% | HBM 带宽(每 token 读 57KB) |
| MoE Router + AllReduce | 12% | 通信 + 动态路由 |
| 算子调度 + 显存管理 | 11% | Runtime 开销 |
Decode 阶段 42% 的时间在读 KV Cache。为什么?7B 模型 FP16 下每个 token 的 KV Cache 约 57KB,batch=8、seq=2048 时就是 57KB × 2048 × 8 = 912MB。每生成一个 token 都要读一遍这 912MB,910B 的 HBM 带宽 1.2TB/s,光读 KV Cache 就要 0.76ms。
很多人以为推理优化就是"算子写快点",其实 Decode 阶段算子根本不是瓶颈——HBM 带宽才是。
工程经验:Qwen2.5-72B 在 910B 4卡上,Decode 阶段 HBM 带宽利用率只有 38%。原因?KV Cache 按连续地址分配,但推理时不同 batch 的序列长度不同,Cache 里大量空洞。开了 PagedAttention 之后带宽利用率拉到 79%,吞吐直接翻倍。
为什么 GPU 优化方案不能直接复用
三个核心差异:
1. 计算单元架构不同
GPU 的 SM 能同时跑矩阵乘和逐元素运算。FasterTransformer 可以把 QK^T + Softmax + P×V 融成一个巨型 Kernel,中间结果全塞 L2 缓存(几十 MB)。
昇腾是 Cube 干矩阵乘、Vector 干逐元素,两个单元之间数据走 L1 缓存(1MB)。不能融成一个巨型 Kernel——L1 装不下,必须按 Cube/Vector 边界切分。
2. 内存层次不同
GPU 的 L2 缓存是全局共享的,所有 SM 都能访问。昇腾的 L1 是每个 AI Core 独立的,跨核通信走 HCCL。
这意味着 GPU 上的 Tiling 策略可以直接按 L2 容量规划,昇腾上必须按 L1 容量(1MB)规划 tile 大小。同一个 FlashAttention,GPU 上 tile_q 可以开到 128,昇腾上只能开到 64。
3. 调度模型不同
GPU 的 CUDA Stream 调度是硬件级的(SM 自动分配),开销 < 1μs。昇腾的 ACL 调度走软件调用链(ACL→GE→Runtime),开销 12-15μs。7 个小算子不融合,光调度就吃 3ms。
为什么这样设计?因为达芬奇架构的 Cube 和 Vector 是物理独立的执行单元,不像 SM 是统一调度器管理。Cube/Vector 的协作必须软件编排,这既是灵活性的来源(可以精细化控制流水线),也是调度开销的来源。
ATB 的核心能力
ATB 在 CANN 架构中的定位:ops-transformer 提供"砖头"(FlashAttention、MoE 等基础算子),ATB 把砖头搭成"墙"(融合算子编排),MindIE 拿去盖"房子"(完整推理引擎)。
ATB 管五件事:
| 能力 | 解决的问题 | 性能收益 |
|---|---|---|
| Attention 优化 | QK^T + Softmax + P×V 调度开销大 | 融合成 1 个 Kernel,调度从 3ms→0.05ms |
| KV Cache 管理 | 显存碎片 + 带宽利用率低 | PagedAttention 消碎片,带宽利用率 38%→79% |
| 算子融合 | LayerNorm+MatMul+残差反复读写 HBM | 融合后中间数据走 L1,省 3 次 HBM 读写 |
| 动态 Shape 适配 | 不同 batch/seq 的 shape 不固定 | 运行时自动选 Tiling 策略 |
| MoE 加速 | Router 动态路由 + 通信瓶颈 | GatingTopK 融合,只算被激活的 expert |
Attention 优化
ATB 封装了 ops-transformer 的 FlashAttention,并针对达芬奇架构做了 Cube/Vector 流水线优化。
没有 ATB 时,Attention 的一次前向要走 7 次 ACL 调用:
code复制
Q = MatMul(x, Wq) → Cube
K = MatMul(x, Wk) → Cube
V = MatMul(x, Wv) → Cube
S = MatMul(Q, K^T) → Cube
S = Scale + Mask(S) → Vector
P = Softmax(S) → Vector
O = MatMul(P, V) → Cube
7 次调用,中间结果(Q、K、V、S、P)全部写回 HBM 再读出来。
ATB 的做法:把 QKV 投影融成 1 个 MatMul(Q/K/V 的输入都是 x,权重拼接),再把 S→P→O 融成 1 个 Kernel(FlashAttention)。总共 2 次 ACL 调用,中间结果走 L1 不落 HBM。
| 模型 | 未融合 | ATB 融合 | 提升 |
|---|---|---|---|
| Qwen2.5-7B (seq=2048) | 34 tokens/s | 89 tokens/s | +162% |
| Qwen2.5-72B (seq=4096, 4卡) | 320 TPS | 890 TPS | +178% |
| DeepSeek-V3 (seq=4096) | 580 TPS | 1420 TPS | +145% |
为什么 QKV 投影能融成一个 MatMul?因为 Q、K、V 的输入都是同一个 x,三个权重矩阵 [d×d_q, d×d_k, d×d_v] 可以横向拼接成 [d×(d_q+d_k+d_v)],一次矩阵乘搞定。这招在 GPU 上也用,但昇腾上收益更大——省了 2 次 ACL 调用(24-30μs),GPU 上只省了 2 次 Kernel Launch(~2μs)。
工程经验:DeepSeek-V4 有 CSA(4倍压缩)和 HCA(128倍压缩)两种 Attention 模式。128K 序列下 HCA 把 KV Cache 从 7.3GB 压到 57MB,TPOT < 10ms。但短序列(<4K)HCA 反而比 Full Attention 慢 12%——Compressor 本身有计算开销。ATB 的做法是按序列长度动态选策略:seq < 2048 用 Full Attention,2048-8192 用 CSA,>8192 用 HCA。
KV Cache 管理
KV Cache 是 Decode 阶段最大的显存占用者。7B 模型 FP16,seq=2048、batch=32,KV Cache 占 3.6GB。
ATB 提供三条优化路径:
PagedAttention:KV Cache 分 block(默认 16 token 一个 block),按需分配,消碎片。没有 PagedAttention 时,不同 batch 的序列长度不同,连续分配会产生大量空洞。实测显存碎片率 30-40%。开了 PagedAttention 碎片率降到 < 5%。
KV Cache 量化:INT8 存 KV Cache,显存省一半。910B 实测:3.6GB → 1.8GB,精度损失 < 0.3%(PPL 变化 < 0.01)。
KV Cache 压缩:DeepSeek-V4 的 HCA 128倍压缩,128K 序列 KV Cache 57MB。
很多人以为 KV Cache 优化就是省显存,其实真正的收益是:省出的显存能跑更大 batch,更大 batch 吞吐更高。
| 配置 | Batch | 吞吐 (tokens/s) | 显存 (GB) |
|---|---|---|---|
| FP16,无优化 | 8 | 72 | 20.1 |
| FP16 + PagedAttention | 16 | 124 | 24.3 |
| KV Cache INT8 + PagedAttention | 32 | 147 | 28.1 |
| KV Cache INT8 + PagedAttention + W8A16 | 48 | 189 | 31.2 |
算力没变,显存够用了,batch 从 8 涨到 48,吞吐涨 163%。
工程经验:KV Cache 量化有个坑——不同层的 KV Cache 对量化的敏感度不一样。前几层(靠近 embedding)的 KV Cache 量化后精度影响大,后面层影响小。我们试过"前 4 层 FP16、后面 INT8"的混合策略,精度和全 INT8 一样,但显存多省 10%。ATB 目前没内置这个策略,得自己改。
算子融合
昇腾上每次算子下发走 ACL→GE→Runtime 调用链,开销 12-15μs。30 层 Transformer 几百次调用,光调度开销 3-5ms。
ATB 的融合策略:
LayerNorm + MatMul + 残差:三层算子融成一个 Kernel。LayerNorm 输出直接喂 MatMul,MatMul 输出加残差,中间数据走 L1 不落 HBM。省 2 次 HBM 读写 + 2 次 ACL 调用。
GatingTopK(MoE 专用):Router 输出 + TopK + Expert 分发融成一个 Kernel。不融合时 Router 输出落 HBM 再给 TopK 读,融合后直接 L1 传数据。单层省 18μs,40 层省 720μs。
SAS(DeepSeek-V4 专用):统一 Window/Sparse/Compress Attention 的融合 Kernel。不同 Attention 类型共用同一个 Kernel 框架,按参数切换模式,省去 Kernel 切换开销。
| 融合方案 | 省掉的 HBM 读写 | 省掉的 ACL 调用 | 性能提升 |
|---|---|---|---|
| QKV 投影融合 | 2 次 | 2 次 | +8% |
| FlashAttention | 3 次 | 4 次 | +162% |
| LayerNorm+MatMul+残差 | 2 次 | 2 次 | +15% |
| GatingTopK | 1 次 | 2 次 | +22%(MoE 模型) |
为什么不能把所有算子都融成一个巨型 Kernel?两个原因:一是 L1 只有 1MB,整层 Transformer 的中间结果装不下;二是 Cube 和 Vector 是物理独立的,跨单元的数据传递必须显式编排流水线,不像 GPU 的 SM 内部寄存器直接传。
动态 Shape
大模型推理的输入 shape 不是固定的——不同请求的序列长度不同,MoE 每层的 expert 激活模式也不同。
ATB 的做法:编译时生成多套 Tiling 策略,运行时按 shape 选最优的一套。
code复制
编译阶段:
shape_range = [(1,128), (129,512), (513,2048), (2049,8192)]
为每个 range 生成对应的 Tiling 参数和 Kernel 二进制
运行阶段:
实际 shape = (1, 1024)
→ 落在 (513, 2048) 这个 range
→ 用第 3 套 Tiling 策略
为什么这样设计?因为 Tiling 参数影响 Cube/Vector 的利用率和 L1 缓存命中率。seq=128 时 tile_q=32 就够了,seq=8192 时 tile_q 要开到 128 才能填满 Cube 的 MAC 阵列。一套 Tiling 策略适配所有 shape,要么短序列浪费算力,要么长序列 L1 溢出。
工程经验:动态 Shape 有个隐藏开销——Tiling 策略切换需要重新加载 Kernel 二进制(~50μs)。如果请求的序列长度频繁跳档(比如一会儿 100 token,一会儿 8000 token),切换开销会吃掉 5-8% 的吞吐。我们用"分桶策略"缓解:把请求按序列长度分到固定几个桶(0-256, 256-1024, 1024-4096),桶内统一 padding 到桶的上限,避免频繁切换。
Batch 调优
BatchSize 对吞吐的影响不是线性的。
实测数据(Qwen2.5-7B,910B 单卡,FP16,ATB 全开):
| BatchSize | 吞吐 (tokens/s) | TTFT (ms) | 显存 (GB) | HBM 带宽利用率 |
|---|---|---|---|---|
| 1 | 38 | 45 | 14.2 | 23% |
| 4 | 112 | 78 | 16.8 | 51% |
| 8 | 178 | 120 | 20.1 | 68% |
| 16 | 231 | 210 | 29.3 | 79% |
| 32 | 267 | 380 | 39.2 | 85% |
| 64 | 244 | 720 | OOM | 81% |
BatchSize=32 是拐点。再大,KV Cache 占显存太多,开始 swap 到 Host 内存,HBM 带宽利用率反而降了。
为什么不是越大越好?Decode 阶段每个 token 都要读整个 KV Cache。batch 越大,要读的 KV Cache 越多,HBM 带宽撑不住。batch=32 时 HBM 带宽利用率 85%,接近理论上限;batch=64 时 KV Cache 太大,部分 swap 到 Host 内存,带宽利用率反而掉到 81%。
场景选型:
- 在线对话(TTFT < 100ms):batch=4-8,优先压延迟
- 离线批处理(吞吐优先):batch=16-32,优先压吞吐
- 动态 batch(ATB + MindIE 支持):短输入 batch=32,长输入 batch=8,自动调整
工程经验:Batch 调优不是只看吞吐和延迟。batch=32 时吞吐最高,但如果请求的序列长度方差大(有的 50 token,有的 4000 token),短请求等长请求的时间会拉高 P99 延迟。我们实际部署时用 batch=16,牺牲 13% 吞吐换 P99 延迟降 40%。调优要看业务指标,不是看 NPU 指标。
https://atomgit.com/cann/ascend-transformer-boost https://atomgit.com/cann/ops-transformer https://atomgit.com/cann/cann-recipes-infer
更多推荐




所有评论(0)