摘要:大模型在昇腾 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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐