同一个模型,读完一大段提示词可能只用很短时间,接着却要一个字一个字地生成回答。输入有几千个 token,输出每步只有一个,为什么后者未必更高效?我们从这两个阶段的计算形状出发,再看 KV Cache 和推理框架的设计,会更容易理解时间究竟花在了哪里。
1. Prefill 与 Decode
我们先把一次请求拆开。设提示词长度为 $S$,模型隐藏维度为 $d_{model}$。模型先处理整段已知输入,再逐个生成新 token,分别对应 Prefill 和 Decode。
Prefill 一次送入全部提示词,各位置可以并行计算。注意力需要计算 $S\times S$ 的分数矩阵,矩阵乘法规模较大,GPU 通常能得到较高利用率。Decode 每一步只有一个新 token,它只产生一组新的 Query、Key 和 Value,再用新的 Query 读取全部历史 Key、Value。此时矩阵往往又小又窄,计算更容易受显存带宽限制。

图 1:Prefill 处理完整提示词,Decode 每轮只增加一个 token。
因此,我们等待第一个 token 时,主要在等待排队与 Prefill;第一个 token 出现后,体验更多取决于 Decode。只看一次请求的总耗时,很难分辨究竟是哪一部分慢了。要进一步解释这种差异,我们还得进入 Transformer 内部,看两阶段的矩阵究竟有多大。
2. Transformer 中的数据怎样流动
先按标准多头注意力计算。我们把 $B$ 条长度为 $S$ 的输入组成一批,嵌入后的张量为
$$X\in\mathbb R^{B\times S\times d_{model}}$$
以 $h$ 个注意力头为例,令每个头的维度为 $d_h=d_{model}/h$。线性投影和分头后,
$$Q,K,V\in\mathbb R^{B\times h\times S\times d_h}$$
每个 Query 都要和可见的 Key 匹配,再用得到的权重汇总 Value。把这两步写在一起,便是
$$A=\operatorname{Softmax}\left(\frac{QK^{\mathsf T}}{\sqrt{d_h}}+M\right), \qquad O=AV$$
$M$ 是因果掩码,将未来位置的分数设为负无穷。我们关注形状就能看到,$A$ 为 $B\times h\times S\times S$,$O$ 为 $B\times h\times S\times d_h$。各个头拼接并经过输出投影后,又回到 $B\times S\times d_{model}$,于是能与残差分支相加。门控 MLP 则在特征维上先扩展到 $d_{ff}$,再投影回 $d_{model}$。

图 2:一个 Transformer Block 中的主要张量形状。来源:JAX Scaling Book。
3. KV Cache 省掉了什么
现在考虑新 token 加入以后,哪些结果真的需要重算。由于因果注意力看不到未来,旧位置的隐藏状态不会因后面多了一个 token 而改变,各层的 Key、Value 也就可以继续使用。我们把它们保存下来,每轮只计算新位置的投影并追加到缓存,这就是 KV Cache。
代价是多了一份随上下文增长的存储。单层 MHA 中,$B$ 条序列各保存 $S$ 个位置,每个位置有两份 $d_{model}$ 维向量,所以元素数约为
$$2BSd_{model}$$
若模型有 $L$ 层,每个元素占 $b$ 字节,则总显存约为
$$M_{KV}=2LBSd_{model}b$$
这里的 $S$ 包含提示词和已生成 token。缓存会随并发数和上下文长度线性增长,所以长上下文服务即使装得下模型权重,也可能很快被 KV Cache 占满。
到这里,我们只是省去了旧位置的重复计算。新的 Query 仍要读取历史缓存:单头中,它的形状为 $1\times d_h$,与 $d_h\times S$ 的 Key 相乘,得到一行长度为 $S$ 的分数。计算很窄,读取的数据却越来越多,这也解释了为什么长上下文的 Decode 常受带宽限制。
4. MHA、MQA、GQA 与 MLA
既然缓存读取已经成为负担,我们自然会考虑能否少存一些。标准 MHA 为每个 Query 头配备独立的 Key、Value 头;如果让所有 Query 头共用一组,就得到 MQA。共享减少了缓存,也限制了表示的自由度。GQA 取一个中间方案,让若干 Query 头共用一组 Key、Value,在缓存量和模型效果之间折中。
如果 Key、Value 的头数为 $h_{kv}$,每头维度为 $d_h$,缓存元素数变为
$$2LBS h_{kv}d_h$$
DeepSeek 的 MLA 又换了一种思路:先把 Key、Value 联合压缩到低维 latent,推理时主要缓存压缩表示,需要计算时再恢复相应分量。它属于模型结构上的缓存压缩,与系统层面的 PagedAttention 并不是同一类优化。

图 3:几种注意力结构保存 KV Cache 的方式。
5. 延迟、吞吐与批处理
推理常见的三个指标是 TTFT、TPOT 和吞吐。TTFT 是从请求到达至生成首 token 的时间,主要受排队、Prefill 和调度影响;TPOT 是后续 token 的平均间隔,主要反映 Decode;吞吐则表示单位时间处理的 token 数或请求数。
如果同时处理多个请求,我们还能把多个小计算合成较大的批次,提高权重读取的复用率。不过,凑齐 batch 需要等待,吞吐提高也可能伴随更长的延迟。另一个问题是回答长度不同:静态 batch 中,短回答结束后留下的槽位要等长回答完成才能重新利用。Continuous Batching 允许在迭代之间补入新请求,减少这部分空闲。
6. PagedAttention
请求可以随时进入和退出,缓存该怎样分配就成了下一步问题。我们事先并不知道一个回答会有多长:预留太多会浪费显存,预留太少又要扩容;若还要求物理地址连续,分配就更麻烦。PagedAttention 借用操作系统分页的思路,将逻辑缓存切成固定大小的块,再通过块表找到分散的物理存储。图中需要关注的正是逻辑顺序与物理位置之间的区别。

图 4:逻辑上连续的缓存可以分散存放在不同物理块中。
这样做不会减少每个 token 所需的理论缓存量,却能减少预留和碎片造成的浪费,也方便不同请求动态伸缩。共享前缀或并行候选还可以引用相同的物理块,直到分支处再分配新块。
7. 量化与推测解码
权重量化主要降低模型权重的存储和读取成本,KV Cache 量化则直接作用于随序列长度增长的缓存。低比特格式会引入量化误差,实际效果取决于分组大小、缩放方式、异常值处理和校准数据,不能只比较位数。
前面的优化都在缩短一次计算或提高复用率,自回归的串行生成还在。我们也可以先用便宜的 draft model 提出若干候选,再让 target model 并行验证。难点在于,不能因为草稿生成得快,就任由它改变目标模型的采样分布。为此,在同一前缀下设 draft 分布为 $q$、target 分布为 $p$,候选 token $x$ 的接受概率取为
$$\alpha(x)=\min\left(1,\frac{p(x)}{q(x)}\right)$$
拒绝后再从校正分布采样,可以保证最终结果仍服从 target model 的分布。它能否加速取决于候选接受率、草稿模型成本以及一次验证的并行收益。草稿模型过弱时,拒绝次数会抵消加速效果。
8. 这些优化分别解决什么
回到开头的疑问,输入多却算得快,并没有违背计算量的直觉。Prefill 可以把大量工作并行组织成较大的矩阵乘法,Decode 则要逐步等待新 token,并反复读取权重与缓存。我们比较一个推理优化时,应先确定它改变了哪一部分:是读取量、存储浪费、设备空闲,还是串行步数。弄清这一点,才能判断它是否适合当前的上下文长度、并发量和延迟要求。
参考资料:Stanford CS336 Lecture 10;Kwon et al., Efficient Memory Management for Large Language Model Serving with PagedAttention;Leviathan et al., Fast Inference from Transformers via Speculative Decoding;Ainslie et al., GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints。