
你在对话框按下回车后昇腾NPU里发生了什么打开一个对话 AI输入用 Python 写一个快速排序按下回车。等一两秒屏幕上开始一个字一个字地往外蹦结果。这个过程中你的输入走了一条什么样的路才变成一段通顺的代码输出CANN 在中间扮演了什么角色大模型推理在昇腾NPU上的全链路正是这篇文章要讲的事情——不堆概念从你发消息到 Token 一个个生成为止。这篇文章不堆概念而是从用户的每一次输入出发讲清楚大模型推理的全链路——从你发消息到 Token 一个个生成为止。Prefill读完整段输入你发了一条消息“用 Python 写一个快速排序”。这一行文本首先要被 Tokenizer 切成一个整数序列比如[45, 892, 312, 67, 1289, ...]。这个序列就是模型的第一输入。然后进入 Prefill 阶段——把整段输入一次性喂给模型做计算。输入 Token 序列 [45, 892, 312, 67, 1289, ...] → Embedding → Transformer Block × N → 最后一个 Token 的 Logits → Softmax → 第一个输出 Token这个过程走了模型的全部 Decoder Block。每个 Block 里都做了 Attention FFN。计算量很大但只需要做一次。CANN 在 Prefill 阶段扮演的角色AscendCL 把 Token 数据拷进 NPU 显存Graph Engine 在模型加载阶段已经做好了算子融合——Attention 用 FlashAttention 融合算子替代了三个分离的 BMMSoftmaxBMMRuntime 把融合后的算子序列分派到 AI Core 执行整个 Prefill 在 NPU 上是纯计算流水线中间不需要跟 CPU 交互Prefill 完成后模型内部的状态变了——每层的 Key 和 Value 被缓存下来这就是 KV Cache。比如一个 32 层的 LLaMA 模型每层缓存两个矩阵K 和 V大小都是[输入长度, 头维度 × 头数]。解码一个 Token 一个 Token 地生Prefill 得到第一个输出 Token 后进入解码阶段。这个阶段是逐 Token 的当前 Token → Embedding → Transformer Block × N → Logits → 下一个 Token ↑ | └────────── KV Cache ──────────────┘每生成一个 Token就把这个 Token 对应的 K 和 V 追加到对应层的 Cache 末尾。下一轮计算时只处理新 Token 的 Q用 Q 跟完整的 KV Cache 做 Attention。为什么这样分两阶段Prefill 把输入序列完整算一遍Cache 积累完毕。解码阶段每次只算新来的一个 Token复杂度从 Prefill 的O(n²·d)降到O(n·d)。Token 越长解码阶段的单步延迟越低。CANN 在解码阶段扮演的角色Runtime 管理 KV Cache 的动态增长——每步生成后更新 Cache 偏移地址GE 的图优化确保解码阶段的 Attention 走 Paged FlashAttention 路径只搬运新增的 K/VRuntime 的 Stream 机制让计算和 KV Cache 更新并行如果不用 KV Cache解码阶段也要从所有历史 Token 重新算 Attention——复杂度退回到O(n²·d)输出 1000 个 Token 的累计代价是 Prefill 的 1000 倍。用 Cache 后解码阶段的计算量只跟序列长度线性相关。KV Cache 为什么是关键资源大模型推理对显存的需求主要来自两个地方模型参数和 KV Cache。以 LLaMA-13B 为例FP16 精度模型参数约 26GBKV Cache 每层[2 × 序列长度 × 4096 × 40]40 个头32 层总 Cache 大小 ≈2 × n × 4096 × 40 × 32 × 2 bytes ~20MB × n序列长度 n4096 时KV Cache 占用约 80GB。这已经超过了模型参数本身的三倍。KV Cache 的管理直接决定了推理服务的并发度。一张 Ascend 910 的显存一般是 32GB。装下 13B 模型后剩余约 6GB只够 n4096 时服务的并发度极低。优化手段集中在几个方向Cache 量化把 FP16 的 K/V 量化成 INT8占用减半Paged Attention把 Cache 切成不连续的物理页通过页表映射逻辑地址避免碎片Prefix Cache共享相同前缀的请求复用 Cache聊天对话的 system prompt 部分不用重复计算CANN Runtime 在 8.0 版本后原生支持 Paged KV Cache 管理——K/V 的物理地址由 Runtime 分配和管理算子代码通过页表接口访问不需要应用层操心分页逻辑。推理全链路从输入到输出用一张流程图串起来用户输入文本 ↓ Tokenizer 编码 Token ID 序列 ↓ AscendCL aclmdlExecute GE 图执行Prefill 分支——FlowAttention 融合 ↓ Runtime 调度 NPU Prefill 计算全部 Decoder Block 跑一次 ↓ 缓存 KV Cache 第一个输出 Token ↓ 进入解码循环逐 Token 新 Token → AscendCL → GE → Runtime → NPU单 Block 计算 → 更新 KV Cache → 下一个 Token ↓ 重复直到 [EOS] Token 或达到最大长度 输出 Token 序列 ↓ Tokenizer 解码 最终文本从 CANN 视角看Prefill 和解码两个阶段在 AscendCL 接口上没有区别——都是调用aclmdlExecute。区别在于 GE 在执行时根据输入 Shape 的变化自动切换到对应的执行分支。Runtime 如何调度解码解码阶段 NPU 的工作模式跟前文的 YOLO 推理完全不同单次计算量极小——一个 Token 的 Attention FFN可能只有几十到几百 TFLOPs调用频率极高——每个 Token 都要调一次输出 1000 个 Token 就是 1000 次推理数据依赖连续——上一步的结果决定下一步的输入这对 Runtime 的调度提出了特殊要求。Stream 空泡问题。解码阶段每次推理的计算量小每次aclmdlExecute调用都有 Runtime 的调度开销。如果 Stream 没有 pipeline 好NPU 会在调度间隙空转。实测中不做流水线优化的场景下解码时 Runtime 的开销占总延迟的 15-25%。Batch 策略不同。解码阶段不同 request 的解码进度不同——request A 出第 5 个字时 request B 可能才出第 2 个字。连续 BatchContinuous Batching是应对方案NPU 每步推理时在 Batch 维度上混入不同进度的 request只要每个 request 的计算形状一致就可以合并。Runtime 需要支持 Batch 维度在推理步间动态变化。CANN Runtime 的 Batch Launch 接口在连续 Batch 场景下能显著降低调度开销——一次提交多个 request 的算子Runtime 内部做合并调度。大模型推理学习路线如果看完这篇文章想系统地掌握 CANN 大模型推理建议按这个顺序学习第一阶段跑通推理。从 AscendCL 入手——装好 CANN Toolkit配环境调一个 OM 模型做推理Prompt 8 的内容。不需要理解底层细节先看到输出。第二阶段理解图优化。学 Graph Engine 的原理——算子融合、内存优化、图执行链路。这阶段你会理解为什么 ATC 转换是关键步骤以及自动优化做了哪些事。第三阶段掌握 Runtime。深入 Device-Context-Stream 模型理解内存管理和异步执行机制。这阶段要学会自己编排多 Stream 流水线和检查 Memory 碎片。第四阶段优化大模型推理。FlashAttention 融合、Paged KV Cache、Continuous Batching、动态 Shape 管理——这些都是大模型专有的优化技术。建议结合 ops-transformer 仓库源码理解。CANN Runtime 仓库Graph Engine 图执行详解ops-transformer FlashAttention 实现CANN 端到端推理示例为什么 KV Cache 这么占显存KV Cache 的大小跟模型层数、注意力头数、头维度、序列长度成正比。以 LLaMA-70B 为例模型参数每层 KV 大小层数总 Cache (n4096)LLaMA-7B~13GB4MB × n32512MB × n → ~2GBLLaMA-13B~26GB6.4MB × n40768MB × n → ~3GBLLaMA-70B~140GB8MB × n801.9GB × n → ~7.8GB注意这些是单 request 的缓存。一台推理服务器通常要并发处理多个 request。100 个并发 requestLLaMA-70B 的 KV Cache 总量接近 800GB——远超单张 NPU 的显存。这就是分页 KV Cache 和 Cache 量化的意义——一个把缓存页化管理消除碎片一个把精度从 FP16 降到 INT8 直接减半。再加上 Prefix Cache 让共享前缀的 request 复用同一段缓存实际部署中的显存利用率可以从 30% 提到 80% 以上。大模型推理的瓶颈总结从上面整个链路可以看出大模型推理在昇腾上的瓶颈在不同阶段是不同的阶段瓶颈应对手段Prefill计算量密集n² 复杂度FlashAttention 融合 大 Batch解码单步Memory Bound搬运 KV CachePaged Attention Cache 量化整链Cache 增长显存压力Continuous Batching Prefix Cache调度Runtime 每次 Launch 开销Batch Launch 多 Stream 流水线这四个瓶颈的优化手段都不是独立的。FlashAttention 减少搬运提升了单步延迟但同时降低了 Cache 的访问模式复杂度。Paged KV Cache 解决了显存碎片问题但页表查找增加了访存延迟——需要跟更大粒度的 Cache 搬运策略配合。在实际部署中这些优化不是开了某个开关就完事而是一个系统工程理解每层瓶颈在哪、当前负载模式偏 Prefill 还是解码、Cache 的页表命中率够不够高。理解了全链路才能针对自己的负载模式做精细化调优。参考仓库CANN RuntimeGraph Engineops-transformercann-recipes-infer