适用范围:大模型推理中的 KV Cache(计算机制、性能模型、存储布局、压缩方法与在线服务系统)。
文档目标:作为分阶段学习材料。完成后应能完成显存估算,说明主流推理系统中的 KV 管理方式,并对相关论文按优化类别进行归类。
0. 使用说明
本文将 KV Cache 作为独立专题,覆盖注意力计算、显存与吞吐模型、内核实现、压缩算法及在线推理系统。建议按阶段推进,并以各阶段验收标准作为完成判据。
| 阶段 | 内容 | 验收标准 |
|---|---|---|
| 1 | 计算机制 | 能够推导有/无 Cache 时单步 Decode 的 FLOPs 与访存量 |
| 2 | 性能模型 | 能够估算典型配置(如 7B/70B、8K/128K、FP16/INT8)下的 KV 显存占用 |
| 3 | 布局与内核 | 能够说明 PagedAttention、FlashDecoding 所解决的问题及其边界 |
| 4 | 压缩与近似 | 能够区分无损存储压缩、有损近似,以及注意力定义变更 |
| 5 | 服务系统 | 能够描述请求生命周期中 Cache 的创建、共享、换出与释放 |
| 6 | 模型结构 | 能够说明 GQA、MLA、线性注意力对 KV 存储形态的影响 |
各章末给出掌握要求与可选实验。不要求一次性通读;达到对应验收标准即可进入下一阶段。
1. 定义与作用
在自回归 Transformer 中,生成当前 token 需对已生成(及 prompt)序列计算注意力。该计算依赖历史 token 的 Key(K) 与 Value(V)。
若每一步对全部历史重新计算 K、V,计算复杂度随序列长度近似按平方增长。KV Cache 将已计算的 K、V 驻留于显存:后续步骤仅计算当前 token 的 Q、K、V,并与缓存中的历史 K、V 完成注意力。
定义: KV Cache 是注意力计算中已生成 token 的 Key、Value 的可复用存储,用于避免 Decode 阶段对历史状态的重复计算。
主要影响如下:
- 时延: 长上下文与多轮对话中,Decode 不再重算历史 K/V,逐步时延显著下降。
- 显存: Cache 规模随层数、KV 头数、序列长度与 batch 增长,通常构成推理显存的主要部分之一。
- 后续优化空间: 量化、分页分配、前缀复用、滑动窗口等,均围绕降低 KV 的存储与带宽开销、提高系统吞吐展开。
2. 计算机制
2.1 Prefill 与 Decode
| 阶段 | 输入 | KV 写入 | 计算特征 |
|---|---|---|---|
| Prefill | 完整 prompt 一次性输入 | 为 prompt 中各 token 写入 K/V | 计算密度较高,主要影响 TTFT(首 token 时延) |
| Decode | 每次一个或少量新 token | 仅追加当前 token 的 K/V | 通常为 memory-bound,主要影响 TPOT(每 token 时延) |
Prefill 完成 KV 的初始填充;Decode 在读取已有 Cache 的同时追加新条目。Cache 的主要收益出现在 Decode 阶段。
2.2 Query、Key、Value
- Q(Query): 当前 token 的查询向量。Decode 阶段通常仅计算当前 token 的 Q,一般不纳入 Cache。
- K(Key): 历史 token 的键向量,用于匹配。写入 Cache。
- V(Value): 历史 token 的值向量,用于加权聚合。写入 Cache。
注意力计算可写为:
1 | score = softmax(Q @ K^T / sqrt(d)) |
启用 Cache 时,K、V 由「历史缓存」与「当前步新计算的 K/V」拼接得到;Q 仅来自当前步。
2.3 张量形状
概念形状为:
1 | K, V: [num_layers, num_kv_heads, seq_len, head_dim] |
工程实现中通常按层分别存储,并引入 batch 或分页维度。须区分:
num_heads: Query 头数num_kv_heads: Key/Value 头数- MHA:
num_kv_heads = num_heads - GQA:
num_kv_heads < num_heads(若干 Query 头共享一组 K/V) - MQA:
num_kv_heads = 1 - MLA: 以低秩或压缩潜变量表示 K/V,推理阶段进一步降低 Cache 体积
在相同序列长度下,KV 显存与 num_kv_heads 成正比。因此 GQA/MQA 已成为开源模型的常见配置。
2.4 位置编码(RoPE)与缓存内容
LLaMA 类模型普遍采用 RoPE。实现层面需明确:
- Cache 中保存的是 旋转后的 K 还是 未旋转的 K(V 通常不施加旋转)。
- 多轮续写、前缀复用与投机解码回滚时,位置编号须与写入时一致。
- 位置外推(YaRN、NTK 等)改变旋转方式,并不取消 Cache,但影响长上下文可用性及窗口策略。
2.5 因果掩码
无 Cache 时,需对完整序列施加下三角因果掩码并重复计算。
启用 Cache 后,历史状态已按因果顺序写入;当前步仅需对区间 [0, current_len) 计算注意力。
实现中仍须处理 padding、变长 batch、分页块中的空洞,以及 chunked prefill 的局部因果约束。
2.6 掌握要求
- 给出无 Cache / 有 Cache 时,单步 Decode 的 FLOPs 量级及其与
seq_len的关系(线性或平方)。 - 说明 Query 通常不进入 Cache 的原因。
- 说明 GQA 降低 KV 存储的机制。
可选实验: 以单层注意力的最小实现对比「逐步重算全部 K/V」与「仅追加 Cache」,并记录各步矩阵乘尺寸。
3. 性能模型
Decode 阶段的逐步时延,往往受限于将历史 KV 自 HBM 反复读入计算单元,而非算术运算本身。
3.1 算力与带宽
- Prefill: 序列较长、计算密集,更容易饱和 Tensor Core。
- Decode: 单步计算量较小,但需读取完整历史 KV,通常表现为 memory-bound。
结合 Roofline 模型:短上下文下瓶颈可能仍在算力;序列延长后,瓶颈向内存带宽转移。
3.2 显存估算
在单一存储精度下,K 与 V 各占一份:
1 | KV_bytes ≈ 2 * num_layers * num_kv_heads * head_dim * seq_len * batch * sizeof(dtype) |
当 kv_hidden = num_kv_heads * head_dim 时,等价于:
1 | KV_bytes ≈ 2 * num_layers * kv_hidden * seq_len * batch * sizeof(dtype) |
说明:
- 须使用 kv_heads,不可误用 Query 头数。
- 采用 MLA、量化或其它压缩后,应按「实际缓存元素数量 × 每元素字节数」重新计算。
- 上述结果仅为 KV 本身;模型权重、激活、CUDA Graph 及分配碎片另计。
数量级参考:
| 配置 | 说明 |
|---|---|
| 7B 量级、GQA、FP16、2K | KV 相对权重量通常不占主导 |
| 70B 量级、128K 上下文、较大 batch | KV 往往首先成为显存上限 |
| FP16 → INT8(量化有效时) | KV 体积约降至原来的 1/2 |
| MHA → GQA(kv_heads 降至 1/8) | KV 体积约降至原来的 1/8 |
3.3 Batch 与并发
增大 batch 可摊销权重读取开销,但 KV 占用随 batch 线性增长。在线服务中的典型约束顺序为:
- KV 显存限制最大并发;
- 内存带宽限制 Decode 逐步时延;
- Prefill 与 Decode 混合执行时争用算力与显存。
3.4 关键指标
| 指标 | 含义 | 与 KV 的关系 |
|---|---|---|
| TTFT | 首个输出 token 时延 | 主要由 Prefill 决定;前缀缓存命中时可显著下降 |
| TPOT / ITL | 后续每 token 时延 | 主要由 Decode 读取 KV 的带宽决定 |
| Throughput | 系统 tokens/s | 由并发、调度及 KV 共享/分页共同决定 |
| Max concurrency / Max context | 可支撑的并发与上下文长度 | 直接受 KV 体积约束 |
3.5 掌握要求
给定 layers、kv_heads、head_dim、dtype、seq、batch,估算 KV 显存,并判断 Decode 瓶颈更接近算力还是带宽。
可选实验: 固定模型,扫描 seq_len ∈ {1K, 8K, 32K, 128K},记录 TTFT、TPOT 与显存,并与估算公式对照。
4. 存储布局与内核实现
4.1 连续存储与分页
| 方案 | 优点 | 限制 |
|---|---|---|
按 max_seq 预留连续张量 |
实现简单,内核友好 | 变长序列浪费显存;预分配易产生碎片,并出现实际未用满即显存不足 |
| PagedAttention(如 vLLM) | 按 block 分配,类似操作系统分页 | 需维护 block table;内核需间接寻址 |
PagedAttention 的核心是 显存管理(碎片、预留与共享),并不改变注意力数学定义。不同请求及公共前缀可按 block 共享同一物理 KV。
4.2 Layout
常见布局包括 BHSD、BSHD、按层拆分、K/V 交错存放等。布局影响:
- 追加 token 时是否可顺序写入;
- 注意力内核能否合并访存;
- 量化、换出及跨设备搬运的实现复杂度。
不存在与内核及量化方案无关的「全局最优」布局,须与具体实现绑定评估。
4.3 写入、扩展与衔接
高频操作包括:
- Prefill 完成整段写入;
- Decode 每次追加 1 个 token(投机解码时可能为多个);
- 超出当前 block 容量时分配新块;
- Chunked prefill:将 prompt 分块计算,并按正确位置拼接 KV;
- 抢占与恢复:换出后再换入时,位置映射须保持一致。
4.4 内核要点
- FlashAttention: 减少注意力中间结果对 HBM 的读写。
- FlashDecoding / split-K: 在序列长、batch 小的 Decode 场景下,对 K/V 分片并行计算后再归约。
- 算子融合: 尽量减少 QKV 投影、RoPE、Cache 写入、注意力与输出投影之间的内核启动次数。
- Warp specialization: 部分 warp 负责数据搬运,部分 warp 负责计算。
学习重点在于优化目标:降低 KV 的 HBM 读取量,并提高已读数据的计算复用。
4.5 存储精度与计算精度
常见做法是 Cache 以较低精度驻留(INT8 / FP8 / INT4),计算注意力时再反量化至较高精度。须分别记录:写入量化、驻留格式、计算格式。由于数值分布、RoPE 及 outlier,K 的量化难度通常高于 V。
4.6 掌握要求
说明 vLLM、SGLang、TensorRT-LLM 等系统维护 block table(或等价间接层)的原因。
建议阅读路径: FlashAttention / FlashDecoding → PagedAttention → 开源实现中 reshape_and_cache、flash_attn_with_kvcache 等接口的调用关系。
5. 压缩、稀疏与近似方法
阅读文献前应先分类,避免将不同性质的工作一律称为「压缩」。
| 类型 | 含义 | 代表方向 |
|---|---|---|
| 无损或近无损存储压缩 | 注意力公式不变,主要降低体积与带宽 | KV 量化:KIVI、KVQuant、QServe 等 |
| 有损驱逐 / 稀疏 | 丢弃或合并部分历史 token 的 KV | StreamingLLM(sink 与窗口)、H2O、SnapKV |
| 结构或注意力定义变更 | 训练或结构上缩小 Cache,或改为固定状态 | GQA、MLA、SWA、YOCO、线性注意力 / SSM |
对任意论文,建议首先确认:
- 推理阶段注意力公式是否发生变化;
- 最坏情况下 Cache 是否仍随
seq_len线性增长; - 质量损失主要出现在短上下文、长上下文,还是特定任务(如检索、代码)。
5.1 量化
关注:K 与 V 是否独立量化;粒度(per-token / per-channel / 分组);outlier 与 RoPE 后的 K;反量化开销是否抵消带宽收益。
5.2 稀疏与驱逐
关注:保留策略(最近窗口、注意力权重较高的 token、sink token);驱逐发生在 Decode 过程中还是 Prefill 之后一次性完成;对远距离精确匹配任务(复制、长依赖)的影响。
StreamingLLM 的典型做法是保留序列起始处的若干 attention sink,并与最近窗口组合,稳定性通常优于朴素滑动窗口。
5.3 低秩、跨层共享与窗口注意力
- MLA: 将 KV 映射至低维潜状态,以降低 Decode 缓存宽度。
- 跨层共享: 并非每一层均保存完整 KV(如部分 YOCO / 跨层复用方案)。
- Sliding Window Attention: 每层仅关注局部窗口,Cache 可按窗口上限封顶,常与若干全局层配合。
5.4 检索式长上下文
远距离历史不全部驻留 GPU Cache,而置于外部存储或检索系统,按需载入。此时 Cache 不再等价于模型已处理过的全部 token,属于系统与算法的结合方案。
5.5 掌握要求
能够将一篇 KV 相关论文归入量化、驱逐或结构变更,并指出质量与效率的折中发生在何处。
6. 在线推理系统中的 Cache
单请求计算正确仅是基础;多请求之间的共享与调度决定系统成本与服务质量。
6.1 Prefix / Prompt Cache
对相同系统提示、工具描述、RAG 固定前缀或共用模板,可复用已计算的 Prefill 结果。命中时 TTFT 可下降一个数量级,幅度取决于前缀占比。
约束包括:前缀须在 token 级完全一致(含特殊 token 与模板空白);索引结构(hash 或前缀树)的维护方式;命中后位置编码的衔接。
6.2 Radix / 前缀树(如 SGLang)
按 token 前缀将多请求 KV 组织为树,公共前缀在物理上仅保存一份。适用于树状采样、共享 system prompt 的高并发、分支对话及搜索式解码。
与 PagedAttention 的分工:分页解决块的分配;radix 解决哪些块可被共享。
6.3 多轮会话
多轮对话应优先在已有 Cache 上续写,避免每轮将完整历史作为新 prompt 重新 Prefill。须处理:历史截断或摘要导致的 Cache 失效;中间插入的工具结果与系统消息;用户回改历史内容后,须自分叉点起作废对应 KV。
6.4 换出(Offload)
数据路径一般为 HBM → 主机内存 → SSD。收益取决于:
- PCIe / NVLink 带宽相对于重新 Prefill 的成本;
- 换出粒度(整请求或远历史 block);
- 换入是否位于 Decode 关键路径上。
可复用前缀宜优先保留于 GPU;访问频率极低的远历史再考虑换出。
6.5 调度
| 主题 | 与 KV 的关系 |
|---|---|
| Chunked prefill | 将长 prompt 分块,降低 Decode 饥饿;KV 按块写入 |
| Prefill / Decode 分离(PD disaggregation) | Prefill 节点向 Decode 节点传输 KV,网络载荷即 KV 体积 |
| 抢占 | 须支持 KV 换出或丢弃后重算;策略影响尾部时延 |
| 连续 batching | 各请求 seq_len 不同,更依赖分页与 block table |
6.6 投机解码与多 token 预测
草稿模型或 MTP 可能先行写入候选 token 的 K/V。校验失败时须将 Cache 回滚至分叉点,不得将未接受 token 的 KV 保留为历史状态。
6.7 多设备并行
- Tensor Parallel: 注意力头被切分,各设备保存对应 KV;并发能力仍受单卡 HBM 限制。
- Pipeline Parallel: 不同层位于不同设备,KV 随层分布。
- PD 分离与多机: KV 成为网络载荷,可考虑量化后再传输,并结合层内并行切分。
6.8 掌握要求
能够描述如下生命周期:请求到达 → 查询前缀缓存 → 未命中则 Prefill、命中则跳过对应计算 → Decode 追加 → 结束释放或进入会话池 → 可能被量化、共享、换出或抢占。
7. 与训练及模型结构的关系
若结构采用 MHA 且窗口不受限,推理侧优化无法改变 KV 随序列长度线性增长这一基本事实。
| 方向 | 对 KV 的影响 |
|---|---|
| GQA / MQA | 减少 kv_heads |
| MLA | 减少每 token 缓存宽度 |
| 窗口注意力 / 混合窗口 | 为 Cache 设置上限 |
| 线性注意力、SSM、RWKV、Mamba 及混合结构 | Cache 转为固定大小状态,不再按 token 线性保存 K/V |
| 长上下文训练与位置外推 | 决定 32K/128K/1M 等长度是否可用,以及 RoPE/Cache 的长度上限 |
| MoE | 专家权重不进入 KV;注意力层 KV 须单独核算。通信开销与显存占用分属不同预算 |
本章用于区分:所谓「长上下文能力」是将完整 128K KV 驻留 GPU,还是窗口、检索与压缩的组合结果。
8. 推荐学习顺序
建议按序完成。跳过性能估算直接阅读系统名词,容易停留在概念层面而无法做定量判断。
- 推导单步 Decode 的 FLOPs 及自 HBM 读取的字节数。
- 掌握 KV 显存公式,并用真实模型配置验算。
- 区分 MHA / GQA / MQA / MLA 对 Cache 形状的影响。
- 说明 FlashAttention / FlashDecoding 减少的访存类别。
- 说明 PagedAttention 如何处理变长序列、碎片与 block 共享。
- 说明前缀复用(Prompt Cache / RadixAttention)的命中条件与收益。
- 在量化、驱逐、MLA 中选择一条路径深入,避免并行铺开。
- 掌握服务侧要点:PD 分离、换出、连续 batching、投机解码回滚。
- 对照线性注意力 / SSM / 混合结构,判断 KV 是被压缩还是被状态替换。
9. 自测问题
可用于掌握程度检查。建议按「结论、机制、数量级或实例」作答。
- Decode 逐步时延较高,同时显存主要被 KV 占用,二者如何同时成立?
- GQA 成为常见配置的原因是什么?MLA 额外解决了哪一类开销?
- PagedAttention 优化的是显存管理还是注意力算法本身?
- Prefix cache 命中时,TTFT 为何可能下降一个数量级?
- 量化 KV 时,K 的难度通常高于 V 的原因是什么?
- 128K 上下文下,瓶颈更可能来自计算还是 KV 搬运?
- 连续 batching 中,按
max_seq预分配连续 KV 为何容易在未真正用满时出现 OOM? - 投机解码失败时,为何必须回滚 KV?
- PD 分离中网络传输的对象是什么?量化 KV 对该部署形态的意义何在?
- StreamingLLM 中的 sink token 针对何种失效模式?
10. 最小实验清单
不要求集群环境。单卡小模型即可用于建立数量级直觉。
| 实验 | 方法 | 观察项 |
|---|---|---|
| 显存随长度变化 | 固定 batch=1,增加 prompt 长度 |
显存是否近似线性增长,Decode 时延变化 |
| 显存随 batch 变化 | 固定长度,提高并发 | 先出现 OOM 还是先出现时延上升 |
| 前缀复用 | 两次请求共享较长 system prompt | 第二次请求的 TTFT |
| 量化 KV | 在框架支持时启用 INT8/FP8 KV | 显存、TPOT,以及简单问答与长复制任务质量 |
| 多轮续写与全量重算 | 同一对话的两种接口用法 | Prefill 耗时差异 |
实验记录须包含:模型名称、kv_heads、dtype、seq、batch、推理框架,否则结果不可复现。
11. 术语表
| 术语 | 说明 |
|---|---|
| KV Cache | 已计算注意力 Key/Value 的缓存 |
| Prefill | 一次性处理 prompt 并填充 KV |
| Decode | 逐 token 生成,读取并追加 KV |
| GQA/MQA | 通过减少 K/V 头数降低 Cache 规模 |
| MLA | 以压缩潜状态表示 KV |
| PagedAttention | 以分页块管理 KV 显存 |
| Prefix / Radix Cache | 跨请求共享公共前缀的 KV |
| FlashDecoding | 面向 Decode 的注意力内核优化 |
| PD disaggregation | 将 Prefill 与 Decode 部署于不同节点 |
| Attention sink | 序列起始处需保留的少量 token,用于缓解窗口截断导致的稳定性问题 |
12. 可扩展方向
本文为学习大纲,而非论文综述。后续扩展建议每次只加深一章:
- 实现: 对照 vLLM / SGLang 的 KV 分配路径与注意力内核调用链;
- 文献: 按量化、驱逐、结构三类各选若干篇精读,记录问题、方法、公式是否变更及评测场景;
- 服务: 前缀命中率、KV 传输量、PD 分离带宽预算。
13. 小结
- Decode 依赖历史 K/V;Cache 以存储换取历史状态的免重复计算。
- 主要代价为显存占用与 HBM 带宽;长上下文场景下二者通常主导成本与时延。
- 工程上宜先处理布局与分页,再处理跨请求共享与调度,然后考虑量化与驱逐。
- 结构层面可通过 GQA、MLA、窗口注意力或线性状态,从源头降低乃至替代随长度增长的 KV。
- 评估任一项优化时,应明确其降低的是计算量、访存量、显存占用,还是跨请求重复 Prefill。