KV Cache 讲透:为啥大模型“第一个字慢得要命”,后面却像开了机关枪?
你肯定见过这个场景:
- 你把一段 prompt 发给 ChatGPT/Claude
- 光标闪啊闪,半天才吐出第一个字
- 然后就开始连续输出,像开闸放水一样
这不是玄学。
背后就是 KV Caching(KV 缓存)。
这篇文章咱们不绕弯子,按你真正会遇到的推理链路,把它讲清楚:
- LLM 是怎么“一个 token 一个 token”吐字的
- Attention 里到底在算啥
- 计算浪费发生在哪里
- KV Cache 怎么把浪费砍掉
- 为啥第一个字最慢(prefill)
- 为啥 KV Cache 会把显存吃爆(以及工程上怎么救)
1)LLM 吐 token 的方式:每次只关心“最后一个位置”
Transformer 做推理时,会把输入序列里 每个 token 都过一遍网络。
得到一堆 hidden state:
- token1 → h1
- token2 → h2
- ...
- tokenN → hN
关键点来了:
真正用来预测“下一个 token”的,是 最后一个 token 的 hidden state(hN)。
前面的 h1…h(N-1) 并不是没用。
它们更像“为最后一步服务的中间产物”。模型需要它们来做上下文建模,但在“生成下一个 token”这件事上,只有最后一个位置负责出结果。
你可以把它想成:
- 你问模型一句话
- 模型要把整段上下文都读懂
- 真正开口回答时,只从最后这个位置往外续写
2)Attention 到底在算什么:Q、K、V 是怎么用的
每一层 attention 里,每个 token 都会变成三样东西:
- Q(Query):我现在要查什么
- K(Key):我这里有什么线索
- V(Value):线索对应的内容
当模型要算“最后一个 token”的输出,它会做这件事:
- 拿 最后一个 token 的 Q
- 去和 所有 token 的 K 做匹配(相似度)
- 用匹配权重去加权汇总 所有 token 的 V
一句话:
生成下一个 token 时,最后一个位置会“回头看”整个上下文。
这就是为啥大模型能记得你前面说的梗、你的约束条件、你的上下文细节。
3)真正的浪费:每生成一个 token,都在“从头复读”
现在看浪费点。
假设你已经生成到第 50 个 token。
你要生成第 51 个 token 时,attention 需要什么?
- token1~token50 的 K、V
- token51(当前最后一个位置)的 Q
问题是:
- 生成第 50 个 token 时,token1~token49 的 K、V 已经算过了
- 到生成第 51 个 token,token1~token49 的 K、V 根本没变
如果你每一步都把整段序列重新过一遍 Transformer,那就是:
- 反复重算老 token 的 K、V
- 序列越长,重复越离谱
复杂度直观感受:
- 不缓存:越写越慢,计算量接近 O(n²) 的增长(长对话直接炸)
- 有缓存:每步只做增量,增长更像 线性
你平时觉得“越聊越卡”,很多时候就是这里在作妖。
4)KV Cache 怎么干活:把算过的 K/V 存起来,后面直接用
KV Cache 的做法很粗暴,也很有效:
- 第一次把 prompt 全部跑一遍
- 把每层每个 token 的 K、V 都存起来
- 后续每生成一个新 token:
- 只算这个新 token 的 Q/K/V
- 把新 token 的 K/V 追加到缓存
- 用新 token 的 Q 去对“完整缓存”做 attention
你可以把 KV Cache 理解成:
以前每次写字都要把前面全文重新抄一遍;现在把全文复印件放旁边,写新句子只补最后一行。
这就是为什么主流推理框架离不开 KV Cache:
- vLLM
- TGI
- TensorRT-LLM
没 KV Cache 的推理,基本没法看。
5)为啥第一个字最慢:prefill 才是最吃算力的那一下 😵💫
你发 prompt 的那一刻,模型干的是一件“大工程”。
它要把整段输入一次性处理完:
- 全部 token 过网络
- 全层的 K/V 计算出来
- KV Cache 建好
这段叫:Prefill(预填充)。
Prefill 的特点:
- token 很多
- 并行计算量巨大
- GPU 负载高
等 prefill 做完,模型进入另一段:Decode(逐 token 解码)。
Decode 的特点:
- 一次只生成 1 个 token(自回归)
- 每步只算“新 token”的那点东西
- KV Cache 让历史部分不用重算
所以你看到的现象就很自然了:
- 第一个 token 慢:prefill 在干重活
- 后面快:decode 在吃缓存红利
如果你在做产品体验优化,建议把指标拆开看:
- TTFT(Time To First Token,首 token 延迟)
- TPS(Tokens Per Second,持续输出速度)
很多团队只盯 TPS,然后用户还在骂“怎么老半天不出字”,就是没把 TTFT 当回事。
6)代价:KV Cache 吃显存,吃到你怀疑人生
KV Cache 省的是计算,花的是显存。
而且是每个请求一份。
模型越大、层数越多、上下文越长、并发越高,KV Cache 越夸张。
你可以记住这条朴素规律:
- 长上下文 + 高并发
- 最先顶不住的往往不是算力
- 是显存(KV Cache 把卡塞满了)
有些大模型(比如 70B 级别)在长对话场景下,单请求的 KV Cache 就能吃掉好几 GB 显存。
并发一上来,KV Cache 甚至可能比模型权重还占地方。
这也是为啥工程界搞了很多“省 KV”方案:
- MQA/GQA:让多个 query head 共享更少的 K/V head,减少缓存体积
- Paged Attention:把 KV Cache 当成分页内存来管理,减少碎片,提升并发下的显存利用率
你照着做:推理优化时怎么用 KV Cache 思路排查问题
把下面这张“排查清单”贴到工位上都行。
✅ 你想让“首字更快”
盯 prefill:
- prompt 砍短一点(少废话、少长系统提示)
- 控制输入上下文长度(别无限拼历史)
- 用更快的推理引擎/更激进的 kernel 优化
✅ 你想让“持续输出更快”
盯 decode:
- 确认 KV Cache 真在用(很多自研代码一不小心就没 cache)
- batch/并发策略要合理(别让 GPU 空转)
✅ 你想让“并发更高不爆显存”
盯 KV Cache 占用:
- 上 GQA/MQA 的模型版本
- 用支持 Paged Attention 的框架(典型是 vLLM 思路)
- 限制每个会话的最大上下文长度(这是最直接也最有效的开关)
常见坑:很多人优化半天,其实输在这几件事上
- 以为慢是网络问题:其实是 TTFT(prefill)太慢
- 只看平均延迟:用户感知更在意“多久看到第一个字”
- 无限堆上下文:历史越长,prefill 越重,KV 越大,并发越差
- 没把 KV Cache 当成本:显存预算不做,线上迟早炸
一句话收尾
你看到的“第一个字慢、后面飞快”,不是模型在装。
是 prefill 在狠狠干活,KV Cache 建好后,decode 才开始享受增量计算的爽感。
真要把推理体验做好,别只盯模型有多大。盯住 TTFT、TPS、KV Cache 显存 这三件事,才是真正能让你少加班的东西。