KV Cache 到底缓存了什么:大模型显存、首字延迟与吞吐量解析
KV Cache 到底缓存了什么:大模型显存、首字延迟与吞吐量解析
摘要:KV Cache 是自回归大模型推理中的核心状态。它避免模型在生成每个新 token 时重复计算历史 token 的 Key 和 Value,却也会随上下文长度、批量大小和层数持续占用显存。本文从注意力计算出发,解释缓存内容、显存估算、首字延迟、解码吞吐量,以及常见服务端优化为什么有效。
使用大模型 API 或本地推理框架时,经常能看到这些参数和指标:最大上下文长度、首 token 延迟、每秒输出 token 数、KV Cache 使用率和最大并发请求数。
这些概念并不是彼此独立的。它们都和 Transformer 在自回归生成阶段如何处理历史 token 有关。
从自回归生成说起
假设输入是:
KV Cache 可以减少
模型先处理全部输入 token,并预测下一个 token。得到新 token 后,模型再次基于“原输入 + 已生成 token”预测下一项,循环直到遇到停止条件。
如果每一步都重新计算所有历史 token 在每一层注意力中的中间结果,计算量会大量重复。KV Cache 的作用,就是保存历史 token 已经算过的 Key 和 Value,下一步只计算新 token 对应的部分。
注意力层里的 Q、K、V
简化后的单头注意力可以写成:
Q = XWq
K = XWk
V = XWv
Attention(Q, K, V) = softmax(QK^T / sqrt(d))V
这里:
X是当前层的输入表示;Q用来表达当前 token 在寻找什么信息;K用来和查询计算相关性;V提供被加权汇总的内容。
在生成第 t 个 token 时,新 token 的 Query 需要和第 1 到 t 个 token 的 Key 计算注意力,再对对应 Value 加权。
历史 token 的 K 和 V 在同一轮请求中不会变化,因此可以缓存。Query 只服务于当前这次注意力计算,通常没有必要像 K、V 一样跨生成步骤保存。这就是“KV Cache”名称的来源。
Prefill 与 Decode 是两个阶段
推理通常可以拆成两个阶段。
Prefill:处理整段输入
Prefill 阶段一次处理提示词中的多个 token,计算每一层的表示,并建立初始 KV Cache。
输入越长,需要处理的 token 越多,首 token 往往出现得越晚。因此 TTFT 不只受网络影响,也受提示词长度、模型规模、批处理调度和硬件状态影响。
Decode:逐个生成 token
Decode 阶段通常一次为每个活跃序列生成一个 token。借助 KV Cache,模型不需要重新计算历史 token 的 K 和 V,但新 Query 仍需要读取历史缓存并执行注意力。
因此,KV Cache 消除了大量重复计算,却没有让生成成本变成与上下文长度完全无关。上下文越长,每步需要读取和参与注意力的历史状态仍然越多。
KV Cache 为什么占显存
一个粗略估算式是:
KV Cache 字节数
≈ 2 × 层数 × token 数 × KV 头数 × 每头维度 × 每元素字节数 × 批量大小
式子开头的 2 分别代表 Key 和 Value。
为了理解量级,假设一个示例模型具有 32 层、32 个 KV 头、每头维度 128,使用每元素 2 字节的 FP16 或 BF16,单个序列有 4096 个 token。
代入后:
2 × 32 × 4096 × 32 × 128 × 2
= 2,147,483,648 字节
≈ 2 GiB
这只是用来说明公式的假设模型,并不代表某个具体产品。实际模型可能使用多查询注意力或分组查询注意力,使 KV 头数少于 Query 头数;也可能对缓存采用更低精度,或者按页管理内存。
从公式可以看到,缓存会随这些变量近似线性增长:
- 活跃序列数量;
- 每个序列已经占用的 token 数;
- 模型层数;
- KV 头数量和每头维度;
- 缓存数据类型的字节数。
为什么 GQA 和 MQA 能减少缓存
标准多头注意力中,Query、Key 和 Value 通常都有多个头。多查询注意力(MQA)让多个 Query 头共享一组 K/V;分组查询注意力(GQA)则让一组 Query 头共享 K/V。
如果 Query 头数是 32,而 KV 头数从 32 减少到 8,前面的估算式中与 KV 头数相关的部分理论上会缩小到四分之一。
这能降低缓存占用和内存带宽压力,但模型结构的变化也会影响训练与质量权衡,不能把它简单理解成没有代价的运行时开关。
TTFT 与每秒输出 token 数为何不同
几个常见指标描述的是不同阶段:
| 指标 | 主要对应阶段 | 反映什么 |
|---|---|---|
| TTFT | Prefill、排队和网络 | 用户多久看到第一个 token |
| TPOT | Decode | 相邻输出 token 的平均时间 |
| tokens/s | Decode | 稳态生成速度 |
| 端到端时延 | 全流程 | 请求从发出到完成共花多久 |
长提示词可能明显增加 TTFT,但生成开始后的速度仍然可接受;也可能首 token 很快,却因为上下文很长或系统并发较高,后续 token 输出较慢。
因此,只看一个“总耗时”很难定位问题。测试推理服务时,至少应区分排队与网络时间、Prefill 时间、首 token 到达时间、Decode 阶段的 token 间隔和输出 token 总数。
连续批处理为什么提高吞吐量
不同请求的提示词长度和输出长度各不相同。如果服务端必须等同一批所有请求结束后才能接收下一批,计算资源会出现空闲。
连续批处理会动态移除已完成的序列,并把新请求加入当前调度。这样可以提高设备利用率和整体吞吐量。
但吞吐量与单请求延迟存在权衡:
- 批量更大,通常更容易充分利用加速器;
- 同时服务更多序列,会增加 KV Cache 总量;
- 调度不合理时,单个请求可能等待更久;
- 长短请求混合时,需要避免长请求持续影响短请求。
所以“每秒处理的总 token 更多”并不自动等于“每个用户都更快”。
Paged Attention 解决什么问题
每个请求最终会生成多少 token,在开始时往往并不确定。若为每个序列提前预留一整块最大长度的连续显存,会产生内部浪费;不同长度请求反复进入和退出,也会带来碎片问题。
Paged Attention 的核心思路是把 KV Cache 切成固定大小的块,并通过逻辑映射组织这些块,类似虚拟内存分页:
- 不要求一个序列的所有缓存物理连续;
- 按实际增长逐步分配块;
- 请求结束后可以回收块;
- 某些共享前缀场景下可以复用缓存块。
它主要改善缓存的内存管理与利用率,不会改变注意力需要读取历史 K/V 这一基本事实。
前缀缓存什么时候有效
许多请求可能共享相同的系统提示词、工具定义或固定模板。如果服务端能识别完全相同的前缀,就有机会复用该前缀已经计算出的 KV Cache,从而减少重复 Prefill。
要让前缀缓存更容易命中,可以考虑:
- 把稳定的系统指令放在前面;
- 避免在固定前缀中加入每次变化的时间戳或随机 ID;
- 保持工具定义和序列化格式稳定;
- 把用户每次变化的内容放在共享前缀之后。
但缓存命中还取决于服务端实现、模型版本、分词结果和缓存策略。客户端不能只凭 Prompt 看起来相似,就假定一定复用了缓存。
上下文越长不一定越好
更大的上下文窗口提供了容纳更多信息的能力,但会带来现实成本:
- Prefill 需要处理更多 token;
- KV Cache 占用增加;
- Decode 需要访问更长的历史状态;
- 无关信息可能干扰模型定位关键内容;
- API 费用通常与输入和输出 token 数有关。
实际应用中,比“一股脑塞入全部历史”更有效的做法通常包括:删除重复日志、对较早对话做结构化摘要、用检索选择相关文档片段,以及为必须保留的约束建立短而稳定的系统前缀。
这不是单纯压缩字数,而是在有限上下文中提高有效信息密度。
KV Cache 量化的收益与代价
如果把 KV Cache 从 FP16/BF16 降为 FP8 或更低精度,理论上可以减少缓存显存和读取带宽,让同一设备容纳更多并发或更长上下文。
但低精度可能引入量化误差,实际影响与模型、层、任务和实现有关。评估时不能只看显存下降,还应测试:
- 长上下文任务质量;
- 事实召回和指令遵循;
- 不同输入长度下的退化程度;
- 量化与反量化本身的运行开销;
- 服务端是否真正支持相应精度和内核。
一个更完整的推理测试记录
测试大模型服务时,可以记录下面这些字段:
model
prompt_tokens
completion_tokens
queue_time_ms
time_to_first_token_ms
total_time_ms
tokens_per_second
finish_reason
http_status
timestamp
如果服务端不返回阶段时间,可以在客户端至少记录请求开始、第一个流式数据块到达和请求结束三个时间点。
比较不同结果时,要保持模型、提示词、输出上限、采样参数和测试网络尽可能一致。少量请求只代表当时的观察,不足以证明长期服务能力。
常见误区
误区一:有 KV Cache 后,长上下文生成就不再变慢
KV Cache 避免重复计算历史 K/V,但每步注意力仍要处理历史缓存。它降低了成本,不是消除了上下文长度的影响。
误区二:显存只由模型权重决定
线上推理还要考虑 KV Cache、临时激活、运行时工作区、内存碎片和并发请求。能装下权重不代表能稳定服务目标上下文和并发量。
误区三:吞吐量高等于单请求延迟低
大批量和连续调度可以提高系统总吞吐,但请求可能经历更长排队或更高 token 间延迟。服务目标必须明确偏向吞吐、延迟,还是二者平衡。
误区四:上下文窗口应该始终用满
窗口是容量上限,不是内容目标。无关上下文会消耗 Prefill、缓存、带宽和费用,还可能降低回答聚焦程度。
结论
KV Cache 缓存的是每层注意力中历史 token 的 Key 和 Value。它用显存换计算,避免自回归生成过程中反复计算相同历史状态。
理解这一点后,很多现象可以串起来:
- 长 Prompt 会增加 Prefill 与 TTFT;
- 长上下文和高并发会推高 KV Cache 占用;
- GQA/MQA、分页管理和缓存量化能降低不同维度的开销;
- 连续批处理提高系统吞吐,却需要权衡单请求延迟;
- 精简上下文与稳定前缀既是 Prompt 工程,也是推理系统优化。
评估一个推理方案时,不应只问“模型能支持多少上下文”,还应问:在目标硬件、并发、延迟和质量要求下,真正需要保留多少有效上下文。
更多推荐

所有评论(0)