大模型KV缓存五倍速秘诀:首字延迟背后内存瓶颈

大语言模型生成文本时存在明显的"首字延迟"现象,这源于KV缓存机制的工程权衡。该技术KV Caching通过存储先前计算的键值向量避免重复计算,将推理速度提升约5倍,代价是消耗大量GPU显存。预填充阶段构建缓存最耗时,后续解码阶段仅需处理新token。第一下慢得像老牛拉车,后面快得像火箭升空,原因全在这里。


你以为模型在思考,其实它在算账

每次你打开聊天界面,盯着屏幕,等第一个字蹦出来,那几百毫秒的沉默,其实是显卡在拼命烧算力。
第一个词慢得让人怀疑人生,后面的词却像机关枪一样哒哒哒往外冒。

这背后没有神秘学,没有玄学,只有一个朴素到爆炸的工程决策:缓存。

大型语言模型的核心结构叫“Transformer结构”:听着像变形金刚很酷,干的活其实很朴实——它接收一串词,把每个词变成一串数字向量,然后通过一层层计算,给出下一个词的概率。

Transformer其实是个流水线工厂在批量生产隐藏状态

Transformer是一个超级流水线工厂,输入的每个词元token都会在这条流水线上走一遭,最终每个词元token都拿到属于自己的隐藏状态包裹。
这些隐藏状态接着被投射到词汇表空间,变成一堆叫logits的分数,每个分数对应词汇表里一个单词的当选概率。

听起来很公平对吧,每个词元token都有发言权。但真相残酷得很,只有最后一个词元token的logits才真正有用。
从这个概率分布里采样出下一个词,把这个新词贴到输入序列末尾,然后整个流程再来一遍。

这就是自回归生成的本质,一个贪吃蛇一样不断吞噬自己尾巴的过程。

关键洞察在这里炸裂,要生成下一个词,你只需要最新那个词元的隐藏状态,其他所有隐藏状态都是中间过程的工业废料,用完就该扔的那种。

整个流程可以这么理解:

  • 输入一堆词
  • 每个词变成一个隐藏状态
  • 隐藏状态再映射成词表分数
  • 从最后一个词的分数里抽样
  • 得到下一个词
  • 把新词拼回去
  • 继续循环

听着像流水线工厂对吧?没错,就是流水线。

关键点来了:真正决定下一个词的,只是“最后那个词”的输出结果。前面所有词的输出都属于中间产物。

你每天看到的“智能涌现”,底层全是重复劳动。
模型以前每生成一个新词,都把前面所有词的中间结果再算一遍。
显卡累得想罢工。



注意力机制到底在干嘛

注意力机制里的三角恋关系决定了谁该被记住

钻进Transformer的每一层内部,每个词元token都会被强制分配三个身份向量,一个叫查询Query向量,一个叫键Key向量,一个叫值Value向量。
可以把它想成:

  • Q 是“我想找什么”
  • K 是“我有什么标签”
  • V 是“我携带的信息”

注意力机制的计算过程就像一场大型相亲现场,查询向量跑去跟所有键向量打分配对,然后用这些配对分数给值向量加权求和。

计算方式是:

  • 用 Q 去和所有 K 做匹配打分
  • 打分结果加权所有 V
  • 得到输出

现在把目光死死锁定在最后一个词元token身上,这家伙的注意力计算只关心两件事,自己的查询Query向量长什么样,以及整个序列里所有键Key向量和值Value向量的集体记忆

所以真相浮出水面了,要算出我们唯一关心的那个隐藏状态,每个注意力层只需要最新词元token的查询向量,加上所有历史词元token的键值对。

生成第 50 个词元token时,需要第 1 到 50 个元token的 K 和 V。
生成第 51 个词元token时,需要第 1 到 51 个词元token的 K 和 V。

前面 1 到 49 个词元token的 K 和 V 早就算过,而且输入没有变化,结果当然不会变化。

但愚蠢的模型每次都要重新算一遍,这就是赤裸裸的重复劳动。

这是标准的 O(n²) 级别重复开销!序列越长,浪费越离谱。

工程师看到这种重复劳动,血压自然飙升。



KV 缓存的核心思想

工程师们拍案而起,这不行,得把键值向量存起来。于是KV缓存横空出世,核心逻辑简单粗暴,每来一个新词元,只计算它自己的查询键值三个向量,然后把新的键值向量塞进缓存仓库,再从仓库里调取所有历史键值向量,用新的查询向量去跟完整的缓存键值大军做注意力计算。

思路简单到让人拍大腿:

  1. 每个词元的 K 和 V 只算一次。
  2. 算完存起来。
  3. 后面直接拿来用。

流程变成这样:

  1. 新词元进来
  2. 只计算这个新词元的 Q、K、V
  3. 把新的 K、V 追加进缓存
  4. 用新词元的 Q 去匹配所有缓存里的 K、V
  5. 得到结果

整个历史部分直接复用。

于是推理阶段变成:

  • 一次完整前向计算
  • 后面每步只算一个词元

速度直接起飞。

这就是KV缓存的全部秘密,每层每步只需要新增一个键和一个值,其他全部从内存读取。注意力计算本身还是要随着序列长度增长而增长,毕竟你要跟所有历史键值对视,但昂贵的投影计算从每步一次变成了每词元一次,这就是五倍速的来源。



为什么第一个词慢? 预填充阶段是显存地狱的入口

当你发出一段提示词,模型会一次性处理全部输入,这叫预填充阶段。

现在你能理解为什么第一个字慢得像蜗牛了。当你发送提示词的时候,模型必须在一趟前向传播里处理整个输入序列,计算并缓存每个词元的键值向量,这个阶段叫预填充,是整个请求里最吃算力的部分。

预填充阶段会:

  • 计算每个词元的 Q、K、V
  • 把所有 K、V 存入缓存

这一步计算量最大。提示越长,算得越久。


一旦缓存热起来,每个后续词元只需要单趟前向传播处理一个词元,速度直接起飞。那个让人抓狂的初始延迟有个专业名字叫首字延迟时间TTFT。
提示词越长,预填充越久,等待越煎熬。

优化TTFT的技术栈深不见底,分块预填充、投机解码、提示词缓存都是独立的大话题,但底层逻辑永远不变,构建缓存昂贵如黄金,读取缓存便宜如白菜。

这就是算力与时间的博弈。



显存爆炸催生了注意力机制的偷工减料艺术

缓存节省了计算,代价是占用显存。

每一层都要为每个词元存储 K 和 V。
模型层数越多,隐藏维度越大,缓存就越恐怖。

KV缓存的本质是用内存换计算速度。

每层都要为每个词元存储键值向量,拿Qwen 2.5 72B模型举例,80层网络,32K上下文长度,隐藏维度8192,单个请求的KV缓存就能吃掉好几GB的GPU显存。

当并发请求堆到几百个的时候,缓存占用的显存往往超过模型权重本身。

这就是分组查询注意力GQA和多查询注意力MQA存在的意义,让多个查询头共享键值头,大幅削减内存占用,质量损失微乎其微。
这也解释了为什么翻倍上下文长度这么难,窗口长度加倍,每个请求的KV缓存跟着加倍,能同时服务的用户数量直接腰斩。
内存成了规模化部署的紧箍咒,vLLM、TGI、TensorRT-LLM这些推理框架都在围绕KV缓存玩命优化。

你以为大模型吃算力,真正吃的是显存。显卡容量成了生死线。



本质总结

KV缓存干掉了自回归生成中的冗余计算,历史词元的键值向量永远不变,所以算一次存起来就完事。每个新词元只需要自己的查询键值三元组,然后注意力计算在完整缓存上跑一遍。实践中五倍速提升轻轻松松,代价是GPU显存成为规模化的瓶颈约束。

每个大模型服务栈都建立在这个思想之上,这就是现代AI工程的地基。



一句话点醒

生成式模型的效率革命,来源于“停止重复劳动”。

缓存带来速度。
显存带来成本。
工程永远在权衡。