
每次你用 ChatGPT 或 Claude 时,其实都见过这个现象。第一个 token 出现得明显更慢。之后剩下的 token 几乎立刻开始流式输出。
这不是 UI 小毛病,而是一个有意为之的工程决策,叫 KV caching。它能让 LLM 推理速度大约提升 5 倍。
我们从第一性原理出发,看看它到底是怎么工作的。
第 1 部分:LLM 如何生成 token
Transformer 会处理所有输入 token,并为每个 token 生成一个 hidden state。这些 hidden state 会被投影到词表空间,产生 logits(词表中每个词一个分数)。
但真正有用的,只有最后一个 token 的 logits。你从这些 logits 里采样,得到下一个 token,把它追加到输入后面,然后重复这个过程。
关键洞察是:要生成下一个 token,你只需要最近那个 token 的 hidden state。其他所有 hidden state 都只是中间副产物。
第 2 部分:Attention 实际上在算什么
在每一层 transformer 里,每个 token 都会得到三个向量:query(Q)、key(K)和 value(V)。Attention 会把 query 和 key 相乘得到分数,再用这些分数去加权 value。
现在只关注最后一个 token。
QK^T 的最后一行会用到:
- 最后一个 token 的 query 向量
- 序列中所有 key 向量
这一行最终的 attention 输出会用到:
- 同一个 query 向量
- 所有 key 和 value 向量
所以,为了计算我们唯一需要的那个 hidden state,每一层 attention 都需要最新 token 的 Q,以及所有 token 的 K 和 V。
第 3 部分:冗余在哪里
生成第 50 个 token,需要第 1 到第 50 个 token 的 K 和 V 向量。生成第 51 个 token,又需要第 1 到第 51 个 token 的 K 和 V 向量。
第 1 到第 49 个 token 的 K 和 V 向量早就算过了。它们没有变化。同样的输入,同样的输出。但模型每一步还是会从头重新计算它们。

这就是每一步 O(n) 的冗余工作。放到整段生成里,就是 O(n²) 的浪费计算。
第 4 部分:修法
不要在每一步都重新计算所有 K 和 V 向量,而是把它们存起来。对于每个新 token:
- 只为最新 token 计算 Q、K 和 V。
- 把新的 K 和 V 追加到 cache 里。
- 从 cache 里取出之前所有 K 和 V 向量。
- 用新的 Q,对完整缓存下来的 K 和 V 跑 attention。
这就是 KV caching。每一层、每一步只新增一个 K 和一个 V。其他一切都从内存里取。
Attention 计算本身仍然会随序列长度增长(因为你仍然要 attend 到所有 key 和 value 上)。但生成 K 和 V 的昂贵投影,每个 token 只做一次,而不是每一步都做一次。
第 5 部分:Time-to-First-Token
现在你就能看出为什么第一个 token 慢了。
当你发送一个 prompt 时,模型会用一次 forward pass 处理整个输入,为每个 token 计算并缓存 K 和 V 向量。这就是 prefill 阶段,也是整个请求里计算最重的部分。
cache 热起来之后,后续每个 token 只需要带着一个 token 做一次 forward pass。很快。
最开始这段延迟叫 time-to-first-token(TTFT)。prompt 越长,prefill 越长,等待也越久。优化 TTFT(chunked prefill、speculative decoding、prompt caching)本身是一个很深的话题,但背后的动态始终一样:构建 cache 很贵,读取 cache 很便宜。
第 6 部分:取舍
KV caching 是用内存换计算。每一层都会为每个 token 存 K 和 V 向量。以 Qwen 2.5 72B 为例(80 层、32K context、hidden dim 8192),单个请求的 KV cache 就可能消耗数 GB 的 GPU 内存。并发请求达到几百个时,它占用的内存往往会超过模型权重本身。
这就是 grouped-query attention(GQA)和 multi-query attention(MQA)存在的原因:在多个 query head 之间共享 key/value head,削减内存,质量损失很小。这也是为什么把 context length 翻倍很难。窗口翻倍,每个请求的 KV cache 也翻倍,并发用户数就会下降。
tl;dr
KV caching 消除了自回归生成过程中的冗余计算。过去的 token 总是会产生相同的 K 和 V 向量,所以你只需要计算一次并存起来。每个新 token 只需要自己的 Q、K 和 V。然后 attention 对完整 cache 运行。
实际中大约能提速 5 倍。代价是 GPU 内存,而在规模化服务中,GPU 内存会成为硬约束。每个 LLM serving stack(vLLM、TGI、TensorRT-LLM)都建立在这个思路之上。
就到这里!如果你觉得有启发,转发给你的网络吧。
找我 → @akshay_pachaar ✔️
获取更多关于 LLM、AI Agent 和机器学习的见解与教程!