CANN-昇腾NPU-前缀缓存-PrefixCaching怎么让相同prompt零计算
很多推理场景的 prompt 前缀是固定的(系统提示词、角色设定、Few-shot 示例)。PrefixCaching 把前缀的 KV Cache 缓存起来,相同前缀的请求直接复用,省掉 Prefill 计算。CANN 的 ATB 从 8.5 版本开始原生支持 PrefixCaching。
原理
普通推理:
请求1: [系统提示 500 token + 用户输入 50 token] → Prefill 550 token → Decode 请求2: [系统提示 500 token + 用户输入 80 token] → Prefill 580 token → Decode(重复计算前 500 token)PrefixCaching:
请求1: [系统提示 500 token + 用户输入 50 token] → Prefill 550 token → 缓存前 500 token 的 KV 请求2: [系统提示 500 token + 用户输入 80 token] → 只 Prefill 后 80 token(复用前 500 token 的 KV)前缀命中时,Prefill 计算量从 580 token 降到 80 token,TTFT 降 86%。
ATB 的 PrefixCaching 配置
fromatbimportLLM,PrefixCachingConfig model=LLM("meta-llama/Llama-2-7b-hf",device="npu:0",prefix_caching=PrefixCachingConfig(enable=True,max_prefix_length=4096,# 最大缓存前缀长度cache_capacity=48,# KV Cache 显存上限(GB)))显式指定前缀
最可靠的方式是显式告诉 ATB 哪部分是前缀:
system_prompt="你是一个专业的AI助手,请用中文回答问题。以下是几个示例:\n示例1: ...\n示例2: ..."# 把系统提示注册为前缀prefix_id=model.register_prefix(system_prompt)# 推理时指定前缀 IDresult=model.generate("请解释什么是昇腾NPU",prefix_id=prefix_id,# 自动复用系统提示的 KV Cache)register_prefix只做一次 Prefill,后续所有请求复用。
自动前缀检测
不显式指定前缀时,ATB 自动检测公共前缀:
model=LLM("meta-llama/Llama-2-7b-hf",device="npu:0",prefix_caching=PrefixCachingConfig(enable=True,auto_detect=True,# 自动检测公共前缀))# 这两个请求会自动复用公共前缀result1=model.generate("系统:你是AI助手\n用户:什么是CANN")result2=model.generate("系统:你是AI助手\n用户:什么是Ascend C")# "系统:你是AI助手\n" 是公共前缀,只计算一次自动检测的原理:对 prompt 做 token-level 的 hash 比对,找到最长公共前缀。
KV Cache 生命周期
缓存的 KV Cache 需要管理生命周期:
# 注册前缀(永久缓存,直到手动释放)prefix_id=model.register_prefix(system_prompt)# 使用前缀result=model.generate("问题",prefix_id=prefix_id)# 释放前缀(显存不够时)model.release_prefix(prefix_id)# LRU 策略(自动淘汰最久未用的前缀)model=LLM("model_id",prefix_caching=PrefixCachingConfig(enable=True,eviction_policy="lru",# LRU 淘汰策略max_cached_prefixes=64,# 最多缓存 64 个前缀))多轮对话的前缀复用
多轮对话天然适合 PrefixCaching——每一轮都是在前一轮基础上追加:
轮次1: [系统提示 500 + 用户输入 50] → 缓存 550 token 轮次2: [系统提示 500 + 用户输入 50 + 模型回复 200 + 用户输入 60] → 复用前 550,新增 260 轮次3: [系统提示 500 + 用户输入 50 + 模型回复 200 + 用户输入 60 + 模型回复 300 + 用户输入 40] → 复用前 810,新增 340# ATB 自动处理多轮对话的前缀复用messages=[{"role":"system","content":system_prompt},{"role":"user","content":"什么是CANN"},]# 每轮对话自动复用之前的 KV Cacheresult1=model.chat(messages)messages.append({"role":"assistant","content":result1})messages.append({"role":"user","content":"它和CUDA是什么关系"})result2=model.chat(messages)# 自动复用前缀实测效果
Llama2-7B,Atlas 800I A2,系统提示 500 token:
| 场景 | TTFT (ms) | Prefill 计算 (token) | 显存节省 |
|---|---|---|---|
| 无 PrefixCaching | 180 | 550 | 0 |
| PrefixCaching(命中) | 25 | 50 | 89% |
| PrefixCaching(未命中) | 180 | 550 | 0 |
500 token 前缀命中时,TTFT 从 180ms 降到 25ms,7 倍提速。
显存预算
PrefixCaching 的 KV Cache 占用显存。7B 模型,4096 token 前缀:
KV Cache 大小 = 2 (K+V) × num_layers × hidden_dim × seq_len × dtype_size = 2 × 32 × 4096 × 4096 × 2 (bf16) = 2 GB64GB 显存中,缓存 10 个不同前缀 = 20GB。剩余 44GB 给 decode KV Cache,还能服务约 20 个并发请求。
PrefixCaching 是在线对话服务的关键优化。相同系统提示的请求只 Prefill 一次,后续直接复用 KV Cache。ATB 8.5 原生支持,TTFT 降 7 倍。显存预算要提前规划。仓库在这里:
https://atomgit.com/cann/ATB
