KV Cache:推理性能的命根子
理解 KV Cache 的复用语义、容量公式、分页与共享机制,以及从分配到驱逐的生命周期
自回归 decode 每步都会再次读取历史上下文。KV Cache 把各层已经计算出的 Key/Value 保存下来,使模型只需为新 token 生成新的 K/V;代价是常驻显存和每步历史读取量随序列增长。
- 一句话:KV Cache 用容量和带宽换掉历史 K/V 的重复投影计算,是自回归推理的基础状态。
- 三个判断:先按模型 cache schema 算 bytes/token;PagedAttention 管碎片和映射而不改变语义 payload;prefix cache 命中、页共享与 offload/驱逐属于生命周期问题。
- 核心模型:MHA/GQA 的逻辑容量为
2 × layers × kv_heads × head_dim × tokens × bytes/element,再乘请求数并按并行分片与 allocator 开销修正。 - 边界:公式不是所有架构的统一答案;MLA、稀疏/滑窗、量化、TP 分片、Scale、页尾浪费和运行时 workspace 都会改变物理占用。
1. KV Cache 到底复用了什么
Attention 的完整坐标与 MHA/GQA/MLA 差异见 Attention 架构演化。这里只保留 KV 语义:
step t
current hidden state -> Q_t, K_t, V_t
Q_t reads cached K_1...K_t and V_1...V_t
K_t, V_t append to cache
attention output -> next model sublayer
没有 cache 时,每个 decode step 都要重新从历史 hidden states 投影出 K/V;有 cache 后只投影新增 token,但当前 Query 仍需读取可见历史。它减少的是重复生成历史 K/V,不是取消历史 attention。
每层投影参数不同,因此每层都有自己的 cache。下图把 QKV 投影、decode 追加与跨请求 prefix 复用放在一起:
还要区分两个容易混淆的复用:
- 请求内 KV Cache:同一序列 decode 时复用自己的历史状态。
- 跨请求 Prefix Cache:另一个请求在匹配前缀上复用此前保存的状态。
二者使用相同类型的 payload,但命中判定、所有权和淘汰策略不同。
2. 容量账:先算逻辑 payload
对 MHA/GQA/MQA,单个 token 的全模型逻辑 payload 为:
若 条请求都缓存 个 token:
| 变量 | 含义 |
|---|---|
| K、V 两份 | |
| Transformer 层数 | |
| KV heads;不要误用 Query heads | |
| 每个 KV head 的维度 | |
| cache payload 每元素字节数 | |
| 、 | 每请求已缓存 token 与并发请求数 |
例如 、、、BF16 时:
一条 8K-token 请求的逻辑 payload 约为 1 GiB;32 条同长度请求约为 32 GiB。这里尚未计页尾浪费、Scale、对齐、元数据和 workspace。
全局量、per-rank 量与物理量
模型级公式得到的是全局逻辑 payload。若 KV heads 沿 TP ranks 分片,理想均分下:
但实际 layout 取决于 num_kv_heads、TP 映射、复制策略和 kernel。不能在未确认分片合同前机械除以 TP。
运行时真正占用通常是:
physical allocation
= live payload
+ block rounding / fragmentation
+ quantization scales and alignment
+ allocator metadata
+ implementation-specific workspace
因此模型可容纳的理论 tokens、allocator 可分配 tokens 和线上稳定容量是三个不同口径。
3. Attention 架构如何改变 cache schema
| 架构 | 每 token 每层主要缓存 | 变化点 |
|---|---|---|
| MHA | 2 × q_heads × head_dim | 每个 Q head 有独立 K/V |
| GQA | 2 × kv_heads × head_dim | 多个 Q heads 共享一组 K/V |
| MQA | 2 × head_dim | 所有 Q heads 共享一组 K/V |
| MLA | latent KV + RoPE branch | 缓存低维表示,不按 KV head 直接计数 |
| Sliding/Sparse | 由可保留/可访问区域决定 | token 数不再等于完整历史 |
| Recurrent | 固定或分层状态 | 不保存同形态的逐 token KV |
这张表只用于选公式。GQA 的训练与表达折中属于 Attention 主责页;MLA 的低秩、RoPE 解耦和矩阵吸收见 DeepSeek MLA。
4. PagedAttention 管的是什么
连续预留 max_seq_len 会产生页内浪费,也要求请求增长时寻找更大的连续空间。PagedAttention 借用虚拟内存思路,把逻辑 token blocks 映射到不连续的物理 cache blocks:
request block table
logical block 0 -> physical block 7
logical block 1 -> physical block 2
logical block 2 -> physical block 15
运行时按需分配新 block,attention kernel 通过 block table 找到历史 K/V。它带来三个系统能力:
- 请求无需预留一整段连续
max_seq_len空间。 - 活跃序列可独立增长、结束和回收物理 blocks。
- 多个逻辑序列可以引用同一物理 prefix blocks。
PagedAttention 不会自动减少同一批 live tokens 的语义 payload。它主要减少预留与碎片损失,并让共享、抢占和迁移更易实现;具体 block size 与 allocator 策略仍会影响页尾浪费和 kernel locality。
5. Prefix Cache 与 Copy-on-Write
跨请求 prefix 命中必须满足运行时定义的等价条件,通常至少包括 token 序列、模型/权重或 adapter、位置语义,以及会影响 hidden state 的其他输入。字符串相同不一定代表 token 与执行上下文相同。
命中后,多个请求可以让 block table 指向同一组只读物理 blocks:
request A: [shared 0, shared 1, A-private 2]
request B: [shared 0, shared 1, B-private 2]
当请求要修改共享尾块时,Copy-on-Write 分配私有副本;完整只读 blocks 继续共享。由此必须分别观测:
- prefix hit 的 token 比例;
- 实际复用的物理 bytes;
- CoW 复制与未满尾块的浪费;
- 命中查找和加载引入的延迟。
“命中率高”并不自动等于“TTFT 必然按同比例下降”,端到端修正见 KV Cache Hit Ratio 修正模型。
6. 一条 cache 的生命周期
从请求进入到资源回收,可以按以下状态审计:
- Lookup:查询可复用 prefix;miss 不是零字节,而是需要新建的状态。
- Allocate:为 miss tokens 预留逻辑 blocks 与物理 blocks。
- Populate:prefill 计算并写入各层 K/V。
- Append:decode 每步追加当前 token 的 K/V。
- Share / CoW:只读 prefix 被其他请求引用,写入时拆分尾块。
- Preempt / Offload:容量压力下暂停请求,或把部分 pages 迁到 Host/远端层。
- Evict / Free:策略淘汰可复用 prefix,完成请求释放私有 blocks。
排障时应把“没有命中”“命中了但未驻留 GPU”“驻留但正在迁移”“被淘汰”分成不同状态,不能都折算为 cache miss 或零成本。
7. 长上下文的四类控制手段
| 手段 | 控制对象 | 收益 | 边界 |
|---|---|---|---|
| GQA/MLA | bytes/token | 降低常驻容量与读取量 | 需要模型原生结构与匹配 kernel |
| KV 量化 | bytes/element | 进一步缩小 payload | 要计 Scale/对齐并验证质量与实际 kernel |
| Sliding/Sparse/Compression | 保留或访问的 token | 限制长上下文增长 | 改变可见区域或近似语义 |
| Offload/Remote Cache | 驻留层级 | 用 Host/网络容量换 HBM | 增加传输和调度延迟 |
这些手段作用点不同,可以组合,但不能把理论位宽比、逻辑容量和校准后线上容量混成一个数字。
8. 容量与性能检查表
- 模型保存的是 MHA/GQA KV、MLA latent,还是其他状态?
- 公式是全局量还是 per-rank,TP 下是否复制或均分?
- dtype 是否包含 Scale、对齐与 metadata?
- 统计的是 live payload、已分配 blocks,还是最大可分配容量?
- prefix hit 是否需要从 Host/远端加载?
- OOM 来自 KV、本轮 activation、graph pool,还是其他 workspace?
- 结论是配置估算、allocator 实测,还是业务负载校准?
只有先统一这些口径,KV 容量、并发和 TTFT 才能比较。
相关页面
- Attention 架构演化 — MHA/GQA/MQA、MLA、稀疏与递推结构的统一坐标。
- DeepSeek MLA — latent KV 的 cache schema 与 serving 边界。
- Causal Attention 命中面积 — prefix hit 后为何出现
1-h²。 - KV Cache Hit Ratio 修正模型 — compute、load、overlap 与 TPM 口径。
- 批处理与调度 — cache block 与请求调度如何协同。
- 推理框架对比 2026 — 当前 serving stack 的实现与选型边界。
← 被以下页面引用(17)
- 推理框架对比 2026:从 Engine 到 Serving Stackai-systems · synthesis
- Chunked Prefill 深入分析:调度、Chunk Size 与 Attention 形状ai-systems · synthesis
- CSA/HCA 注意力:DeepSeek-V4 的混合压缩稀疏机制ai-systems · synthesis
- DeepSeek MLA:低秩 KV Cache 与推理效率ai-systems · synthesis
- KV Cache Hit Ratio 修正模型:从直觉到统一公式ai-systems · synthesis
修改历史12 次提交
- docs: refine LLM inference knowledge systemxiaocheng··
f8756b9 - docs(wiki): deepen quantization researchxiaocheng··
98221ea - docs(wiki): map modern attention evolutionxiaocheng··
90df777 - feat(wiki): enforce lifecycle metadata and search aliasesxiaocheng··
1dad7ef - feat(wiki): connect core topics and add reading seriesxiaocheng··
9fc9884 - Embed QKV prefix cache diagram in KV cache articlexiaocheng··
d894027 - fix(wiki): clean all lint errors to enable strict CI (PR-3)xiaocheng··
9acd1f2 - docs(ai-systems): 补充介绍DeepSeek-V2的MLA KV Cache压缩技术xiaocheng··
e122647 - docs(llm-inference): add kv_heads and head_dim conceptual explanationxiaocheng··
7aad448 - docs(ai-systems): add comprehensive LLM inference documentationxiaocheng··
dfe9ab1