大模型回答一句话时,会一个 token 接一个 token 地往后生成。前面几十个 token 已经算过,下一步仍要关注它们。若每次都从头计算前文的注意力状态,很多工作会重复。KV 缓存把各层已经得到的 key 和 value 留下来,下一步只补新 token 的部分。

我对照了 Hugging Face 的缓存策略和推理优化文档。它们把这件事讲得很直白。缓存不会替模型少读一段上下文,也不会改变采样规则。它省掉的是旧 token 上重复执行的计算,所以长回答和多轮对话更容易看到速度收益。

速度省下来了,显存开始往上涨

默认的动态缓存会随序列变长。每生成一个 token,各层都会增加新的 key 和 value。模型权重装进显存以后,剩余空间还要容纳这些状态、当前计算的中间结果和同时处理的其他请求。上下文越长,请求越多,缓存就越容易成为显存里的大户。

这也解释了一个常见现象。同一张卡跑短问答很稳,换成长文或多轮会话后,很快就减少并发,严重时直接显存不足。只看模型参数能不能装下,并不能判断服务能接多少请求。部署测试要把输入长度、输出长度和并发数一起放进去。

Transformers 提供了几种取舍。动态缓存按实际长度增长,通常省事。静态缓存先按最大长度预留空间,形状固定以后可以配合 torch.compile,代价是短请求也占着尚未使用的区域。量化缓存用较低精度保存 key 和 value,能压低显存用量,但会增加量化处理,还要检查模型输出是否受影响。

显存仍然不够时,可以把部分缓存移到 CPU。GPU 需要某一层时再把数据搬回来,容量压力会减轻,传输等待也会增加。这个办法适合先保住长请求,不适合拿来承诺更低延迟。滑动窗口模型还会限制部分层保留的历史长度,缓存增长到窗口上限后停住,普通全注意力层则会继续增长。

别把它和提示词缓存混在一起

KV 缓存通常服务于一次生成过程,保存模型内部已经算出的注意力状态。提示词缓存面向多次请求,复用相同前缀的处理结果,产品是否支持、能复用多久和怎样计费都由服务方决定。两者都叫缓存,观察指标并不相同。

检查 KV 缓存有没有帮上忙,可以先固定模型和硬件,分别记录首 token 等待、后续生成速度、峰值显存和最大并发。再换三组上下文长度,看看显存怎样增长。若只报一句“开启缓存后更快”,团队仍然不知道长会话能不能撑住。

对自建模型的人来说,KV 缓存几乎是生成服务的常规配置。团队要选择缓存放在哪里、用什么精度,以及服务愿意为长上下文留出多少显存。把这几项写进压测记录,扩容时才有可比较的依据。

参考资料