KV Cache:大模型推理加速的关键
为什么大模型生成第一个 token 很慢,后面却越来越快?为什么上下文越长显存越吃紧?答案都指向同一个东西:KV Cache。这篇笔记记录它的原理和实际影响。
一、没有 KV Cache 会怎样
自回归生成时,每产出一个新 token,都要让它对之前所有 token 做注意力计算。如果每次都从头算一遍,第 n 个 token 的计算量是 O(n²),而且前面 token 的 Key、Value 被反复重算——它们的值其实从头到尾都没变过。
KV Cache 的思路很朴素:算过的 K、V 就存下来,下次直接用。每步只需为新 token 计算 Q、K、V,再让它和缓存里所有历史 K、V 做注意力,复杂度从 O(n²) 降到 O(n)。
二、显存占用有多大
缓存不是免费的,它按序列长度线性增长。估算公式:
显存 ≈ 2 × 层数 × 序列长度 × 隐藏维度 × 每元素字节数 × batch
(2 = K 和 V 各一份)
以 7B 模型(32 层、4096 维、FP16)为例,单条 8k 长度的对话,KV Cache 就要占约 4GB 显存——和模型权重本身是一个量级。这就是长上下文服务贵的原因。
三、Prefill 与 Decode 两个阶段
- Prefill(预填充):一次性处理输入 prompt,并行算出所有 token 的 K、V 填入缓存。这步是计算密集型,决定了"首 token 延迟"(TTFT)。
- Decode(解码):逐 token 生成,每步只算一个 token,但要把整个 KV Cache 从显存读出来。这步是显存带宽瓶颈,决定了生成速度。
这解释了开头的现象:第一个 token 慢是因为在跑 Prefill,之后快是因为每步只算一个 token 的注意力。
四、常见的优化方向
- MQA / GQA:让多个 Query 头共享同一组 K、V,缓存直接缩小数倍。现在的主流模型(如 Llama 3、Qwen)基本都用了 GQA。
- PagedAttention:vLLM 提出的方案,像操作系统管理内存页一样管理 KV Cache,解决显存碎片和浪费,吞吐量能翻好几倍。
- KV Cache 量化:把缓存压成 INT8 甚至 INT4,显存减半,精度损失通常可接受。
- 滑动窗口注意力:只保留最近一段窗口的缓存(如 Mistral),长文本下显存不再线性膨胀,代价是"记不住"太远的内容。
五、实践中能感知到的点
- 部署时 max_tokens 和并发数要一起算,KV Cache 显存随两者线性增长,超了就是 OOM。
- 多轮对话越来越慢是正常现象——缓存越来越长,每步 Decode 要读的数据越来越多。
- 同一份 system prompt 可以复用其 KV Cache(prefix caching),批量场景下能省不少 Prefill 开销。
一句话总结:KV Cache 是用显存换时间的经典 trade-off——把历史 token 的 K、V 缓存起来避免重复计算,它也是长上下文时代推理成本的主要来源之一。