1. 先说结论:KV Cache 到底是什么
一句话:
KV Cache 是 Transformer 在自回归生成过程中,把历史 Token 已经计算出来的 Attention Key 和 Value 保存下来,后面生成新 Token 时直接复用,避免重复计算历史 Token 的 K/V。
注意,它主要解决的是:
同一次生成过程中,历史上下文不要每生成一个 Token 就重新算一遍。
比如模型要生成:
我 喜 欢 学 习
当已经生成:
我 喜 欢 学
现在要生成"习"。
如果没有 KV Cache,模型为了生成"习",可能重新处理:
我
我 喜
我 喜 欢
我 喜 欢 学
历史内容会被重复计算。
有了 KV Cache:
我 喜 欢 学
这些历史 Token 对应的 K、V 已经保存了。
生成"习"时,只需要计算:
新 Token 的 Q、K、V
然后:
新 Q
↓
和历史所有 K 做 Attention
↓
利用历史所有 V 聚合
这就是核心。
2. 为什么偏偏缓存 K 和 V,而不是 Q?
先回到 Self-Attention。
假设输入 hidden state:
XX
会通过三个线性映射:
Q=XWQQ=XW_Q K=XWKK=XW_K V=XWVV=XW_V
Attention:
Attention(Q,K,V)=softmax(QKTdk)VAttention(Q,K,V) = softmax \left( \frac{QK^T}{\sqrt{d_k}} \right)V
假设现在已经有:
Token1 Token2 Token3 Token4
正在生成 Token5。
对于 Token5 来说,我们真正需要的是:
Q5Q_5
去查询之前:
K1,K2,K3,K4,K5K_1,K_2,K_3,K_4,K_5
再根据 Attention Weight 聚合:
V1,V2,V3,V4,V5V_1,V_2,V_3,V_4,V_5
也就是说,历史 Token 的 K/V 后面仍然会不断被查询。
但是历史 Token 的 Query:
Q1,Q2,Q3,Q4Q_1,Q_2,Q_3,Q_4
后面基本不会再用。
因为我们只需要计算当前新 Token 的输出。
所以:
历史 K/V 有复用价值,历史 Q 没有。
因此叫:
KV Cache
而不是:
QKV Cache。
3. 没有 KV Cache 会发生什么?
假设:
Prompt = 1000 tokens
模型开始生成 100 个新 Token。
第一个输出 Token:
处理 1000 tokens
第二个:
处理 1001 tokens
第三个:
处理 1002 tokens
......
如果每一次都把历史 Token 完整重新经过 Transformer:
计算会大量重复。
粗略理解,没有 Cache 时生成长度为 NN:
1+2+3+⋯+N1+2+3+\cdots+N
是:
O(N2)O(N^2)
级别的重复历史计算。
有 KV Cache 后,每一步只需要对新增 Token计算新的 K/V,再和缓存的历史 K/V 做 Attention。
因此自回归 Decode 会快很多。
4. Prefill 和 Decode 是理解 KV Cache 的关键
LLM 推理通常可以拆成两个阶段。
假设用户输入:
请分析下面这段 VPN 流量......
一共有 2000 tokens。
Prefill 阶段
模型一次性处理:
2000 input tokens
在 Transformer 的每一层,都会生成这 2000 个 Token 的:
K,VK,V
然后把它们存进 KV Cache。
所以 Prefill 后:
KV Cache =
Prompt 2000 tokens 对应的所有 K/V
接下来开始生成。
Decode 阶段
生成第一个 Token:
只计算新 Token 的 Q/K/V
然后:
Q_new
↓
Attention(K_cache + K_new)
↓
V_cache + V_new
生成完以后:
K_new
V_new
继续 append 到 Cache。
然后生成下一个。
所以整个过程是:
Prompt
↓
Prefill
↓
建立 KV Cache
↓
Decode Token1
↓
追加 K1/V1
↓
Decode Token2
↓
追加 K2/V2
↓
......
一句话背:
Prefill 建 Cache,Decode 用 Cache 并持续追加。
5. 为什么长上下文的 KV Cache 特别吃显存?
这就进入工程部分了。
KV Cache 不是只缓存一层。
假设一个 Transformer 有:
32 layers
那么每层都需要缓存 K 和 V。
KV Cache 大小近似:
Memory≈2×L×T×Hkv×D×BytesMemory \approx 2 \times L \times T \times H_{kv} \times D \times Bytes
其中:
-
22:K 和 V 两份
-
LL:Transformer 层数
-
TT:当前上下文 Token 数
-
HkvH_{kv}:KV Head 数
-
DD:每个 Head 维度
-
Bytes:数据类型,例如 FP16 是 2 Bytes
假设:
32 layers
32 KV heads
head_dim = 128
context = 8192
FP16 = 2 bytes
KV Cache 近似:
2×32×8192×32×128×22 \times32 \times8192 \times32 \times128 \times2
我们算一下,大约就是几个 GB 量级。
而且这是:
单个 Request。
如果同时跑:
Batch = 16
KV Cache 还会进一步膨胀。
所以在线 LLM Serving 里,很多时候真正限制并发的不是模型权重,而是:
KV Cache 显存。
6. 为什么长上下文模型越来越重视 GQA / MQA?
传统 Multi-Head Attention,可能:
Q heads = 32
K heads = 32
V heads = 32
那么需要缓存 32 个 K head 和 32 个 V head。
但其实多个 Query Head 可以共享同一组 K/V。
于是就有了 MQA:
Multi-Query Attention
例如:
Q heads = 32
KV heads = 1
所有 Q Head 共享同一套 K/V。
KV Cache 大幅下降。
后来又有 GQA:
Grouped-Query Attention
例如:
Q heads = 32
KV heads = 8
每几个 Q Head 共享一套 K/V。
这是性能和质量的折中。
因此如果面试官问:
为什么现在很多 LLM 使用 GQA?
一个非常好的回答就是:
除了推理效率,GQA 能显著减少 KV Head 数,因此降低 KV Cache 显存占用,提高长上下文和高并发 Serving 能力。
7. KV Cache 为什么会随着生成不断变大?
因为每生成一个 Token:
K_new
V_new
都会被加入 Cache。
例如:
Prompt = 4000 tokens
Generated = 1000 tokens
最后 KV Cache 对应的是:
5000 tokens
而不是只保存 Prompt。
所以:
KVMemory∝SequenceLengthKVMemory \propto SequenceLength
上下文越长:
KV Cache 越大
生成越长:
KV Cache 继续增大
这也是为什么:
128K context
不只是 Attention 算得慢,KV Cache 也会非常巨大。
8. KV Cache 减少的是哪部分计算?
这个问题容易答错。
KV Cache 并没有让 Attention 变成完全 O(1)。
因为当前 Token 的 Query:
QtQ_t
仍然要和历史所有:
K1,...,KtK_1,\dots,K_t
做 Attention。
所以当上下文变长,单步 Decode 成本仍然会上升。
KV Cache 节省的是:
不需要重新计算历史 Token 在每一层的 K/V 和其他前向过程。
但:
QtK1:tTQ_tK_{1:t}^T
仍然需要做。
所以更精确地说:
KV Cache 消除了大量历史状态重复计算,但当前 Token 仍需读取整个历史 KV。
这也是为什么长上下文推理仍然慢。
9. KV Cache 和 Prompt Cache 到底什么区别?
这是你现在最需要彻底区分的。
KV Cache
场景:
一次 LLM 请求
例如:
User Prompt
↓
生成 500 Tokens
这 500 个 Token 自回归生成时复用历史 K/V。
它主要解决:
单次生成中的历史计算复用。
Prompt Cache / Prefix Cache
场景:
多次独立 LLM 请求
Call 1:
System + Skill + Tools + User
Call 2:
System + Skill + Tools + Observation
因为前缀:
System + Skill + Tools
一样,所以推理系统可能复用之前 Prefill 的结果。
它解决:
多个请求之间相同 Prompt Prefix 的重复计算。
你可以直接记:
KV Cache:一次生成内部。
Prompt Cache:多次请求之间。
底层都可能和 KV state 有关系,但工程语义完全不同。
10. Prompt Cache 底层是不是也可能缓存 KV?
是。
这一点稍微深入一点。
如果两个请求共享:
System Prompt
+
Skill
+
Tool Schema
那么这些 Token 的 Transformer 前向计算是相同的。
理论上完全可以缓存这些前缀 Token 在每层的:
K/V state
下一次直接复用。
所以 Prompt Cache 底层通常与 prefix 对应的 KV 状态复用有密切关系。
但是面试时不要把两个词混着用。
因为:
KV Cache 描述的是模型自回归推理的基本机制。
而:
Prompt Cache 通常描述 Serving 系统跨 Request 的前缀复用策略。
11. 什么是 PagedAttention?
这个很值得你补一下,因为做 LLM Infra 经常问。
传统 KV Cache 有一个问题:
每个请求长度不同。
比如:
Request A:2000 tokens
Request B:3700 tokens
Request C:900 tokens
如果每个 Request 都提前申请一大块连续显存:
最大 8192 tokens
那么会造成大量空间浪费和显存碎片。
这和操作系统内存管理很像。
PagedAttention 的思路就是:
不要要求一个请求的 KV Cache 必须存储在一整块连续物理显存里。
而是把 KV Cache 切成固定大小的 Block/Page。
例如:
Request A logical KV:
Block 0
Block 1
Block 2
Block 3
物理显存里可能是:
Page 8
Page 21
Page 4
Page 17
由一个类似 Page Table 的结构做映射。
这样能提高:
显存利用率
动态增长能力
Batch 并发能力
vLLM 的重要设计之一就是 PagedAttention。
你可以把它类比:
操作系统的虚拟内存分页。
非常容易理解。
12. 为什么 Continuous Batching 和 KV Cache 强相关?
普通 Batch:
A B C D
一起开始,一起结束。
问题是:
A 可能只生成 20 个 Token:
A 已经结束
D 可能还需要生成 1000 个。
如果必须等 D:
GPU 利用率很差
Continuous Batching 的做法是:
A 一结束:
立刻把新请求 E 塞进 Batch
于是:
B C D E
继续执行。
但这样每个请求的 KV Cache:
长度不同
开始时间不同
结束时间不同
因此 Serving 系统必须能够灵活管理不同 Request 的 KV Cache。
这就是为什么:
Paged KV Cache + Continuous Batching
经常一起出现。
13. KV Cache Quantization 是什么?
KV Cache 太吃显存怎么办?
一个办法是:
把 KV Cache 本身量化。
比如原来:
FP16
2 bytes/value
改成:
INT8
1 byte/value
理论上 KV Cache 显存可以接近减半。
甚至:
FP8
INT4
进一步压缩。
但和模型权重量化一样:
会引入数值误差。
尤其 K 会直接参与:
QKTQK^T
所以量化误差可能影响 Attention 分布。
工程上需要在:
显存
速度
质量
之间做 trade-off。
14. KV Cache Offload 又是什么?
如果 GPU 显存不够,可以把一部分 KV Cache:
GPU
↓
CPU RAM
甚至:
SSD
称为 Offload。
优点:
可以支撑更长上下文或更多并发。
缺点:
PCIe / 内存传输延迟很高。
于是可能变成:
计算不慢
等数据搬运很慢
所以 Offload 是典型:
用 latency 换 capacity。
15. 什么叫 KV Cache Eviction?
假设上下文:
128K
GPU 放不下全部历史 KV。
那可以考虑:
是否真的所有历史 Token 都必须保留?
一种思路就是:
只保留近期 Token
+
少量重要 Token
删除不重要的历史 KV。
这叫:
KV Cache Eviction / Compression
但注意:
删除以后模型可能无法再完整关注被删掉的信息。
所以这是:
显存与上下文质量的 trade-off。
16. Sliding Window Attention 和 KV Cache 有什么关系?
如果模型规定每个 Token 只关注最近:
4096 tokens
而不是全部历史:
100000 tokens
那么 KV Cache 理论上也只需要保留最近窗口里的相关 KV。
例如:
t = 10000
模型只看:
5905 ~ 10000
那么早期 KV:
1 ~ 5904
可以被丢弃。
这样:
KVMemoryKVMemory
不会无限随序列增长。
所以 Sliding Window Attention 的重要工程价值之一就是:
控制 KV Cache 增长。
17. 为什么 Beam Search 会让 KV Cache 更麻烦?
假设普通生成只有一条路径:
A → B → C → D
只维护一份 KV Cache。
Beam Search:
A
├─ B1
│ ├─ C1
│ └─ C2
└─ B2
├─ C3
└─ C4
不同 Beam 共享前面的 Prefix:
A
但后面会分叉。
如果傻乎乎给每一个 Beam 复制完整 KV:
显存爆炸
所以好的 Serving Engine 会尽量:
共享 Prefix KV,只复制分叉后的部分。
这和 Copy-on-Write 很像。
18. 为什么 Agent 系统也需要理解 KV Cache?
你可能会想:
我做的是 Agent,又不是 LLM Infra,为什么要学这么深?
因为很多 Agent 性能问题最终都会下沉到推理层。
比如你的 Agent:
LLM
↓
Tool
↓
Observation
↓
LLM
↓
Tool
↓
Observation
↓
LLM
如果每轮都重新发送:
System
Skill
Tools
History
Evidence
那么每一次都涉及:
Prefill
KV Cache 建立
Decode
如果 Serving 层支持 Prefix Cache:
前面稳定部分可以复用
否则:
每轮重新 Prefill
所以你设计 Agent 时:
Prompt 结构、Tool Schema 稳定性、调用次数
都会间接影响底层 KV 计算。
这就是为什么那位面试官会从:
Plan Compiler 补一个 C
突然问到:
Cache 和 Token。
他其实已经从 Agent Workflow 追到了 Serving Cost。
19. 一个非常关键的区别:Prefill 是 Compute-Bound,Decode 更容易 Memory-Bound
这个是面试高级一点的问题。
Prefill:
一次处理大量 Token
矩阵规模大,GPU Tensor Core 能充分利用。
因此更偏:
Compute Bound
Decode:
每次通常只有一个新 Token
但每生成一个 Token 都要读取大量历史 KV Cache。
所以大量时间可能花在:
显存读取 K/V
而不是算力。
因此 Decode 更容易:
Memory Bandwidth Bound
这也是为什么减少 KV Cache、使用 GQA、KV Quantization 等,对 Decode 很重要。
一句面试金句:
Prefill 更看算力,Decode 更看显存带宽和 KV Cache。
20. TTFT 和 TPOT
你以后做 LLM Serving 面试,这两个指标要认识。
TTFT
Time To First Token:
从请求到生成第一个 Token 的时间。
主要受到:
Prompt 长度
Prefill
排队
Cache Hit
影响。
TPOT
Time Per Output Token:
后续每生成一个 Token 平均需要多久。
更受:
Decode
KV Cache
Memory Bandwidth
Batch
影响。
因此:
Prefix Cache 通常更直接改善 TTFT。
而:
KV Cache 管理效率对 TPOT 和并发能力非常重要。
21. 面试官问:KV Cache 有什么缺点?
你不要只说"占显存"。
高分版:
KV Cache 用显存换计算,显著减少自回归生成中的重复计算,但代价是显存占用会随层数、KV Head 数、Batch Size 和 Sequence Length 线性增长。长上下文和高并发场景下,KV Cache 很容易成为 Serving 的容量瓶颈。
因此现在很多优化,比如 GQA/MQA、PagedAttention、KV Quantization、Offload、Sliding Window,本质上都在优化 KV Cache 的存储和访问效率。
这就是非常完整的答案。
22. 你现在需要达到的面试水平
如果有人突然问你:
什么是 KV Cache?
你 20 秒回答:
KV Cache 是自回归 Transformer 推理里的计算复用机制。Prefill 阶段先计算并保存历史 Token 在每一层 Attention 中的 Key 和 Value,后续 Decode 每生成一个 Token,只计算新 Token 的 Q/K/V,然后用新 Query 去 Attention 历史 KV,并把新的 K/V 继续追加到 Cache。这样不用每生成一个 Token 都重新计算整个历史上下文,大幅降低重复计算。代价是 KV Cache 显存会随着上下文长度和并发数增长,因此长上下文 Serving 经常通过 GQA、PagedAttention 和 KV Quantization 等技术优化。
如果继续问:
KV Cache 和 Prompt Cache 区别?
答:
KV Cache 主要是一次自回归生成内部 复用历史 K/V;Prompt Cache 是多个独立请求之间复用相同 Prompt Prefix 的 Prefill 结果。Prompt Cache 底层也可能复用对应的 KV 状态,但工程语义不同。
如果继续问:
为什么 GQA 能优化推理?
答:
因为多个 Query Head 共享更少的 K/V Head,KV Cache 的尺寸直接下降,从而降低显存占用和 Decode 的内存带宽压力。
如果继续问:
为什么 PagedAttention 有用?
答:
因为不同请求的 KV Cache 长度动态变化,连续大块分配容易造成显存浪费和碎片。PagedAttention 把 KV Cache 分成固定 Page/Block,通过映射管理逻辑连续、物理不连续的 KV,从而提高显存利用率和并发能力。