原文:KV Caching in LLMs, Clearly Explained

在使用 ChatGPT 或 Claude 时,多半注意过这样一个现象:第一个 token 总是明显要等更久才出现,而之后的 token 几乎是瞬间接连涌出。
其背后的核心是,KV caching(键值缓存)。核心目的只有一个:让大语言模型的推理更快。

LLM 是如何生成 token 的

Transformer 会处理所有输入 token,并为每一个 token 产生一个隐藏状态(hidden state)。这些隐藏状态随后被投影到词表空间,得到 logits——即词表中每个词对应的一个分数。

但真正有用的,只有最后一个 token 的 logits。模型从中采样得到下一个 token,将其追加到输入序列,然后重复这个过程。

也就是:要生成下一个 token,只需要最近那个 token 的隐藏状态。其余所有隐藏状态不过是中间的副产物。

Attention 在计算什么

在每一个 Transformer 层内部,每个 token 都会被赋予三个向量:查询向量(Query, Q)、键向量(Key, K)和值向量(Value, V)。

Attention 的运算方式是:先用 query 与 key 相乘得到注意力分数,再用这些分数对 value 进行加权求和。

只看最后一个 token. $QK^T$ 矩阵的最后一行用到:

  • 最后一个 token 的 query 向量
  • 序列中所有 token 的 key 向量

而该行最终的 attention 输出用到:

  • 同一个 query 向量
  • 所有的 key 和 value 向量

也就是说,为了计算唯一需要的那一个隐藏状态,每一层 attention 都需要"最新 token 的 Q"以及"所有 token 的 K 和 V"。

冗余计算在哪里

生成第 50 个 token 时,需要第 1 到第 50 个 token 的 K、V 向量。生成第 51 个 token 时,需要第 1 到第 51 个 token 的 K、V 向量。

而第 1 到第 49 个 token 的 K、V 向量早已计算过了,它们并不会改变——同样的输入,必然产生同样的输出。然而模型在每一步都从头重新计算一遍。

这就造成了每一步 $O(n)$ 的冗余计算。在整个生成过程中,累计浪费的计算量高达 $O(n^2)$。

解决方案:KV Caching

与其在每一步都重新计算所有 K、V 向量,不如把它们存起来。

对于每一个新 token,只需执行以下四步:

  1. 仅为最新 token 计算 Q、K、V。
  2. 将新的 K、V 追加到缓存中。
  3. 从缓存中取出所有历史的 K、V 向量。
  4. 用新的 Q 与完整的缓存 K、V 一起运行 attention。

这就是 KV caching。每一步、每一层只产生一个新的 K 和一个新的 V,其余全部来自内存。

attention 的计算量仍然与序列长度成正比(毕竟要 attend 所有的 key 和 value),但产生 K、V 的那次昂贵投影运算,每个 token 只执行一次,而不是每个时间步执行一次。

Time-to-First-Token 的由来

现在就能理解为什么第一个 token 总是慢了。

当发送一段 prompt 时,模型会在一次前向传播中处理整个输入,为每个 token 计算并缓存 K、V 向量。这一阶段被称为 prefill(预填充)阶段,也是整个请求中计算量最大的部分。

一旦缓存"预热"完成,后续每一个 token 只需带着单个 token 做一次前向传播即可。

这段初始延迟被称为 TTFT(Time-to-First-Token,首 token 延迟)。prompt 越长,prefill 越久,等待就越长。优化 TTFT(分块预填充 chunked prefill、投机解码 speculative decoding、prompt 缓存)本身就是一个值得深入的话题,但底层逻辑始终如一:构建缓存代价高昂,读取缓存却很廉价。

用算力换内存

KV caching 的本质是用计算换内存。每一层都要为每个 token 存储 K、V 向量。

以 Qwen 2.5 72B 为例(80 层、32K 上下文、隐藏维度 8192),单个请求的 KV 缓存就可能消耗数 GB 的 GPU 显存。当并发请求数以百计时,缓存总量往往会超过模型权重本身。

正因如此,分组查询注意力(Grouped-Query Attention, GQA)多查询注意力(Multi-Query Attention, MQA) 应运而生:让多个 query 头共享同一组 key/value 头,在几乎不损失质量的前提下大幅削减显存占用。

这也解释了为什么翻倍上下文长度如此困难——窗口翻倍,每个请求的 KV 缓存就翻倍,能同时服务的用户就更少。为了应对这一瓶颈,还有另一项名为 Paged Attention 的技术专门解决显存利用效率的问题。

总结

KV caching 消除了自回归生成过程中的冗余计算。由于历史 token 产生的 K、V 向量永远不变,所以只需计算一次并存入缓存。每个新 token 只需要它自己的 Q、K、V,然后与完整缓存一起进行 attention 运算。

在实际工程中,这一技术能带来约 5 倍的加速。其代价是 GPU 显存,而在大规模服务场景下,显存恰恰成为最关键的瓶颈约束。

当今所有主流的 LLM 服务框架——vLLM、TGI、TensorRT-LLM——都是构建在这一思想之上。

最后修改:2026 年 06 月 27 日
如果觉得我的文章对你有用,请随意赞赏