一句话:把已经算过的注意力中间结果存起来,下一步别重算。这一招把每 token 复杂度从 O(n²) 降到 O(n)——是 LLM 能"逐字流畅生成"的前提。
LLM 生成是自回归的:每生成一个新 token,都要让它"看到"前面所有 token。问题来了——前面那些 token 的某些中间结果,每一步都在重算。
生成第 N 个 token 时,注意力机制要让 tokenₙ 跟 token₁…tokenₙ₋₁ 全部算一遍关系。而 token₁…tokenₙ₋₁ 彼此的关系,在生成上一个 token 时已经算过一次了。每多一个 token,就要把历史全部重算一遍 → 总开销 O(n²)。序列一长,慢到没法用。
注意力里每个 token 被投影成三个向量。理解"哪些每步变、哪些不变"是理解 KV-Cache 的关键。
| 向量 | 含义 | 生成新 token 时变吗 |
|---|---|---|
| Q(Query) | 当前 token"想问什么" | 变——只属于当前新 token |
| K(Key) | 每个 token"能提供什么" | 旧的不变——历史 token 的 K 固定 |
| V(Value) | 每个 token"的实际内容" | 旧的不变——历史 token 的 V 固定 |
注意力分数 = Q·Kᵀ,再用它加权求和 V。关键洞察:历史 token 的 K 和 V 一旦算出就再也不变——只有当前 token 的 Q 是新的。所以 K、V 可以缓存复用,Q 不行。
逐步看生成 4 个 token 时,有/无缓存分别算了多少。点"下一步"逐 token 看清楚。
📊 原文实测(序列长度 4096):无缓存 = 16.8M 次注意力操作;有缓存 = 4,096 次。差 4096×。
KV-Cache 不是免费的——它把"算的开销"换成了"存的开销"。缓存要常驻 GPU 显存(HBM)。
| 模型/配置 | KV-Cache 显存占用 | 对照 |
|---|---|---|
| Llama3 70B @ FP16,序列 4096 | ~1.25 GB | HBM 共 80GB |
| 70B @ FP16,128K 上下文 | ~10 GB / 请求 | 单请求就吃这么多 |
这就是为什么 KV-Cache 显存往往是批大小的真正约束——不是算力先满,而是显存先被缓存塞满,没法再塞更多并发请求。
问题:不同请求序列长度不一,给每个请求预留连续大块 KV-Cache → 碎片化严重,显存浪费。
PagedAttention(vLLM 实现)的解法:把 KV-Cache 切成固定大小的页,按需分配,像操作系统的虚拟内存。
vLLM 的高吞吐,核心不是模型加速,而是把 KV-Cache 这块显存管好了——同样的 GPU,能同时塞下更多并发请求。这是工程优化的胜利,不是算法突破。
把 KV-Cache 放回整条链上,你会看清它为什么是枢纽:
KV-Cache = "历史的 K/V 算一次就够,存起来反复用"。它把自回归生成从 O(n²) 降到 O(n),代价是吃显存;PagedAttention 再把这块显存管好,让并发翻几倍。这是 LLM 推理能"又快又省"的工程基石。
中文精简讲解,基于 hammadabbasi.com 的 how-llms-work 与 inference 两文 KV-Cache 要点重构(非逐字翻译,原文 CC BY-NC-ND 4.0 禁止演绎)。数字与结构来自原文。· 2026-07-30