自回归解码有一个朴素实现:每生成一个新 token,把完整序列重新过一遍模型。这在数学上完全正确,在工程上完全不可接受——第 $t$ 步要重算前 $t-1$ 个 token 的 K、V 投影,而这些投影从不改变。KV Cache 的想法一句话:算过的 K、V 存下来,每步只算新 token 的那一份。下面把这笔账算清楚,包括计算量、显存量,以及 GQA 这类变体省的是什么。
记号
层数 $L$,隐藏维度 $d$,注意力头数 $H$,每头维度 $d_h = d/H$,序列当前长度 $t$。只看单头单层的情形做推导,最后乘回去。
无 cache 时重算什么
第 $t$ 步生成时,输入是前 $t$ 个 token 的隐藏表示 $X\in\mathbb{R}^{t\times d}$(实际是逐层传递的,这里看单层)。注意力需要
$$ K = XW_K,\quad V = XW_V,\quad q = x_t W_Q, $$
其中 $x_t$ 是第 $t$ 个 token 的表示。关键观察:$X$ 的前 $t-1$ 行与上一步完全相同(因果掩码保证历史 token 的表示不受新 token 影响),所以 $K$ 的前 $t-1$ 行、$V$ 的前 $t-1$ 行都是上一步算过的旧值。无 cache 实现每步重算 $XW_K$、$XW_V$,投影代价是 $2t d^2$ 次乘加;注意力打分 $qK^\top$ 是 $t d_h$ 量级。投影是 $O(td^2)$,打分是 $O(td)$,投影占大头。
整个生成长度为 $n$ 的序列,无 cache 总投影成本:
$$ \sum_{t=1}^{n} 2td^2 \cdot H_{\text{头折算}} = O(n^2 d^2). $$
有 cache 时算什么
缓存历史的 $K_{
$$ \underbrace{O(d^2)}_{\text{新 token 的投影}} + \underbrace{O(td)}_{\text{与历史的注意力}} . $$
$n$ 步总计:
$$ O(nd^2) + O(n^2 d). $$
对比无 cache 的 $O(n^2 d^2)$:投影项从平方级降到线性级。典型场景 $d\approx 4096$、$n\approx 2048$,投影部分的加速比达到数千倍。这就是"decode 阶段没有 KV cache 根本没法用"的定量含义。
注意打分项 $O(n^2 d)$ 是省不掉的:自回归要求每个新 token 和所有历史 token 做注意力,这是模型结构决定的固有成本,不是实现的浪费。FlashAttention 优化的是它的访存常数,GQA/MLA 优化的是它的显存占用,但量级本身动不了。把"能省的"(重复投影)和"不能省的"(历史注意力)分开,是理解一切后续推理优化工作的前提。
还有一个常被忽视的点:有 cache 之后,第 $t$ 步的输入只有一个 token,FFN、LayerNorm、所有投影矩阵都只对这个 token 算。于是 decode 每步的计算量是 $O(d^2)$ 级的矩阵-向量乘,而不是矩阵-矩阵乘——这正是 decode 阶段访存受限(memory-bound)的根源:每步都要把全部权重从显存搬一遍,算力却闲着。算一笔 roofline 账:7B 模型 bf16 权重约 14 GB,A100 显存带宽约 2 TB/s,单请求 decode 的理论上限就是 $2000/14 \approx 140$ token/s,和实测数量级完全吻合。提高吞吐的正路是增大 batch 摊薄这次搬运,而 batch 的上限又被下面要算的 KV cache 显存卡住——推理系统的所有权衡都拴在这条链上。
prefill 一侧的账
decode 的账算完了,prefill 也不能略过——两侧的结构完全不同。prefill 把 $n_p$ 个输入 token 一次性并行算完:投影是 $O(n_p d^2)$ 的矩阵-矩阵乘,注意力打分与加权是 $O(n_p^2 d)$。矩阵-矩阵乘的算术强度(FLOPs/字节)高,GPU 利用率可以打满,prefill 是 compute-bound,和 decode 的 memory-bound 恰成镜像。这个不对称有两个直接推论。其一,TTFT 随提示长度是"线性投影 + 平方注意力"的叠加,短提示时线性项主导,上万 token 的长提示里平方项开始咬人——chunked prefill、prefix 复用这些优化压的就是这两项。其二,prefill 和 decode 抢资源的方式不同,混部时长提示的 prefill 会把 decode 的流式延迟打毛,这是新一代框架把两个阶段拆到不同实例部署的系统动因。
cache 的访存账:带宽随长度一起涨
显存公式 $2Lndb$ 有个常被忽略的孪生兄弟:decode 每步要把整份 KV cache 从显存读一遍,访存量每步是 $2Ltdb$。权重读取是定值(每步约 $2P$ 字节,$P$ 为参数量),cache 读取随 $t$ 线性增长,交叉点大约在 $t\approx P/(Ld)$。对 LLaMA-7B 这是 $7\times10^9/(32\times4096)\approx 5$ 万 token,看似很远;但 batch 会改写等式——权重读取被 batch 摊薄,cache 读取却随并发数线性叠加,32 并发时交叉点提前到一千多 token。这定量解释了长上下文 decode 为什么越跑越慢,也解释了 GQA 的价值为什么随上下文变长而上升:它砍的不只是显存,还有等比例的带宽。
顺带回答一个自然的反问:能不能像梯度检查点那样用重算换缓存?不行。重算历史 K/V 需要把前缀整段的所有层重算一遍,每步代价退回 $O(td^2)$,延迟随长度退化;decode 是延迟敏感路径,K/V 只能物化。能做的只有压缩——量化砍 $b$、GQA/MQA 砍有效 $d$、MLA 换存储对象、H2O/StreamingLLM 这类淘汰策略砍 $n$——而不是消除。
显存账本
先把 prefill 和 decode 两个阶段分开,因为 cache 对它们的意义不同。prefill 处理输入提示的 $n_p$ 个 token,一次性并行算完全部 K/V 并填入缓存——这是矩阵-矩阵乘,算力受限(compute-bound),GPU 利用率很高。decode 逐 token 生成,每步矩阵-向量乘,权重搬运占主导。所以推理系统把 TTFT(首 token 延迟)绑定在 prefill 上、把流式速率绑定在 decode 上优化,两个阶段甚至值得分开部署——这是 vLLM 之外新一代推理框架的共同方向。
缓存的代价是显存。每个 token、每层、每个头要存 K 和 V 各 $d_h$ 个数。总字节数:
$$ M = 2 \times L \times n \times H d_h \times b = 2Ln d\, b, $$
$b$ 是每参数字节数(fp16/bf16 为 2)。注意 $Hd_h$ 恰好收回 $d$,公式很干净:KV cache = 2 × 层数 × 序列长 × 隐藏维 × 精度字节数。
代入 LLaMA-7B 的数:$L=32$、$d=4096$、bf16、$n=4096$:
$$ M = 2\times 32\times 4096\times 4096\times 2\ \text{字节} = 2\ \text{GiB} $$
这是每个请求的量。换成每 token 的口径更好记:LLaMA-7B 每 token 的 KV cache 是 $2Ld\,b = 2\times 32\times 4096\times 2 = 512$ KiB。批量 32 个并发、每个 4096 token,就是 64 GiB——一张 80G 卡装完 7B 权重只剩 60 多 G,KV cache 直接决定并发上限。对 70B($L=80, d=8192$)每 token 约 2.5 MiB,同长度每请求 10 GiB。长上下文服务贵,贵在这里。
GQA 与 MLA 省的是哪一项
公式 $2Lndb$ 里能砍的因子逐个看:$L$、$n$ 砍不动(任务决定),$b$ 靠 KV 量化(INT8/INT4 砍 2~4 倍),剩下 $d$ 这一项实际是"KV 头数 × 每头维度"。GQA 把 $H$ 个 query 头分组共享一份 K/V,比如 8 个 KV 头服务 32 个 Q 头,cache 直接除以 4;MQA 是极端情形只有 1 个 KV 头,除以头数。DeepSeek 的 MLA 换了个角度:把 K、V 投影到一个低秩潜向量(维度远小于 $d$)存起来,用时再升维还原,cache 大小正比于潜维度而非 $d$。三条路线——量化砍 $b$、GQA/MQA 砍有效 $d$、MLA 换存储对象——都是在这个显存公式上做因式分解。
再往下还有一层系统级的优化值得知道名字:PagedAttention 把 KV cache 按固定大小的块(block)分配,逻辑上连续、物理上离散,和操作系统虚拟内存的分页完全同构,解决的是不同请求长度不一造成的显存碎片;prefix caching 则让共享相同提示前缀的请求复用同一份缓存块。两者不改变显存公式本身,但把公式的实际水位压下了不少——做容量规划时用公式算理论值,用这两个特性估算实际折扣。
小结
KV Cache 的全部逻辑:因果结构保证历史 K/V 不变 → 缓存它们 → decode 每步从"重算整段"变成"算一个 token",投影成本从 $O(n^2d^2)$ 降到 $O(nd^2)$,注意力打分 $O(n^2d)$ 不可省(那是自回归的固有成本)。代价是 $2Lndb$ 的显存,这个公式同时解释了为什么长上下文贵、为什么 GQA/量化/MLA 都围绕它做文章。推理优化的起点,就是把这两个公式写在纸上。