为什么生成每个字都这么贵:KV Cache 的前世今生
你有没有注意过,ChatGPT 回答问题时是一个字一个字往外”蹦”的?这不是为了装酷——而是因为 Transformer 在生成时,每次只能确定一个字。而为了确定这一个字,它需要”看”前面所有的字。KV Cache,就是让这件事不至于慢到无法使用的关键机制。
一个字一个字:自回归生成
大语言模型(LLM)生成文本的方式叫自回归生成:每次只预测”下一个 token”(大致等于一个字或半个词),然后把预测结果加入上下文,再预测下一个……循环往复,直到生成结束。
生成”今天天气真好”这 6 个字,模型实际做了 6 步:
- 看完 prompt → 预测”今”
- 看完 prompt + “今” → 预测”天”
- 看完 prompt + “今天” → 预测”天”
- … 一直到”好”
每一步都需要对前面所有 token 做 attention 计算。这就引出了一个荒谬的问题。
没有缓存的世界:O(n²) 的噩梦
想象你在写一本书。每写一个新字,你都要从第一页开始把整本书重新读一遍。写第 100 个字时读 100 遍前文,写第 1000 个字时读 1000 遍。
这就是没有 KV Cache 时 Transformer 在做的事。
Attention 机制需要每个 token 的 Key 和 Value 向量来参与计算。如果不存储之前算过的结果,那生成第 $t$ 个 token 时就必须重新计算前面 $t-1$ 个 token 的全部 K 和 V——而这些值从上一步到这一步根本没有变过。
总工作量 = 1 + 2 + 3 + … + N = N²/2。
对于 4096 个 token 的生成,这意味着约 800 万次重复计算。这不只是慢——是慢到不可用。
KV Cache:算一次,存起来
解决方案极其直觉:前面 token 的 K 和 V 不会变,那就算一次存起来,以后直接查表。
有了 KV Cache:
- Prefill 阶段:把整个 prompt 一次性过模型(可并行),生成并缓存所有 token 的 K、V
- Decode 阶段:每步只算新 token 的 Q、K、V,新的 K、V 追加到 cache,用 Q 去查 cache 中所有 K 做 attention
计算量从 O(N²) 降到 O(N)。代价是——内存。
代价:内存爆炸
KV Cache 存储的是每一层、每一个 KV 头、每一个已处理 token 的 Key 和 Value 向量。
公式:KV Cache = 2 × 层数 × KV头数 × head维度 × 序列长度 × 数据精度
让我们用实际数字感受一下:
关键数字:
- LLaMA 7B @ 128K 上下文:KV Cache 需要 64 GB——模型权重才 14 GB,cache 是模型的 4.5 倍
- LLaMA 70B (GQA) @ 128K:约 40 GB——注意 70B 用了 GQA(只有 8 个 KV 头而非 64 个),已经压缩了 8 倍
而一张 A100 GPU 才 80 GB 总显存。装完模型后剩余空间有限,KV Cache 直接决定了:
- 能支持多长的上下文
- 能同时服务多少用户
这就是 KV Cache 的核心矛盾:没有它,生成慢到不可用(O(n²));有了它,内存压力巨大。
批处理的困境
一个用户的 KV Cache 还好说。但如果你要同时服务 32 个用户呢?
每个用户的上下文长度不同(有人问了一句话,有人贴了一整篇论文),每个用户的生成长度也不同。传统做法是为每个用户预分配”可能用到的最大内存”——大部分被浪费。
研究表明,传统系统中 60-80% 的 KV Cache 内存因碎片化而浪费。就像一个停车场画了大车位,结果来的全是小车——大量空间闲置。
这些问题催生了一整套 KV Cache 优化技术——从改架构(GQA、MLA)到改内存管理(PagedAttention)到降精度(量化)。下一篇我们逐个拆解。