每天学一点AI知识|DAY 13 - Prefill、Decode 与 KV Cache
大模型推理通常分成 Prefill 和 Decode。Prefill 一次处理已经存在的完整提示词,并建立各层历史 K/V;Decode 则必须等待上一步选出的 Token,每次只生成一个新 Token。KV Cache 保存历史 K/V,避免每一步从头重复计算,但会随着上下文和并发规模增长,占用越来越多的存储空间。
在 DAY 12 中,我们看到自回归模型怎样逐 Token 生成回答:计算下一 Token 分布,选出一个 Token,把它追加到上下文,再继续预测。
如果只看这条循环,很容易产生一个新问题:每生成一个 Token,模型都要重新看到完整上下文。假设提示词已经有几千个 Token,回答又不断变长,难道每一步都要从第一个 Token 开始重算一遍吗?
真实的大模型推理通常会把过程分成两个阶段:
Prefill:先处理完整提示词,建立历史状态Decode:利用已有状态,逐个生成新 Token
为了避免 Decode 时反复计算历史位置的注意力表示,推理系统还会使用 KV Cache,把各层已经计算完成的 Key 和 Value 保存下来。
于是,整个推理过程可以先压缩成:
完整提示词↓Prefill:并行处理已知位置,建立 KV Cache↓生成第一个新 Token↓Decode:只计算新位置,查询并扩展 KV Cache↓继续逐 Token 生成

今天,我们就沿着一次真实回答的时间顺序,理解 Prefill、Decode 与 KV Cache等概念。
一、一次推理开始时,模型真正收到什么
用户看到的输入可能只有一句问题:
为什么大模型生成答案时需要 KV Cache?但模型实际处理的提示词往往不只包含这句话,还可能包括:
系统指令; 开发者或应用层指令; 历史对话消息; 当前用户问题; 检索到的资料; 工具调用返回的结果; 为格式控制加入的模板或特殊 Token。
这些内容会被组织成一条 Token 序列。假设完整提示词包含 n 个 Token,可以写成:
x₁, x₂, x₃, …, xₙ在回答真正开始之前,这些 Token 都已经存在。模型不需要猜测 x₂、x₃ 或 xₙ 是什么,因为它们已经由输入提供。
模型首先要做的是处理这条完整提示词,让每一层为各个位置计算内部表示,并得到开始生成所需的下一 Token 分布。
这个阶段就是 Prefill。
二、Prefill:回答开始前先处理完整提示词
Prefill 可以理解为回答前的一次完整前向传播。
模型先把完整提示词转换成 Embedding,再让整条序列依次通过每一层 Transformer:
完整提示词↓ Tokenizer 与 Embedding第 1 层 Transformer↓第 2 层 Transformer↓……↓最后一层 Transformer↓得到第一个新 Token 的概率分布
Prefill 阶段的输入在请求开始时就已全部给定。进入某一层时,该层会同时收到所有提示词位置的上一层表示,并通过矩阵运算批量计算这些位置。
批量计算不代表每个位置都能读取整条提示词。各位置的可见范围仍由因果掩码控制,下一节具体说明。
三、因果模型为什么仍能并行处理提示词
“不能看到未来”很容易被误解成“只能串行计算”。实际上,信息依赖关系与硬件执行方式不是一回事。
假设提示词有四个位置。因果掩码规定:
位置 1 只能读取位置 1位置 2 可以读取位置 1、2位置 3 可以读取位置 1、2、3位置 4 可以读取位置 1、2、3、4
这四个位置的可见范围不同,但完整输入已经存在。模型可以一次计算所有位置的 QKᵀ 分数矩阵,再把每一行中不允许读取的未来位置遮住。
所以:
一次并行计算多个位置≠每个位置都能看到完整序列
因果掩码负责控制信息从哪些位置流向哪些位置;矩阵运算则让硬件同时处理许多已经存在的输入。
当然,Transformer 的不同层之间仍有先后依赖:第 2 层要接收第 1 层的输出,第 3 层要接收第 2 层的输出。这里所说的并行,主要是同一层内对多个已知 Token 位置进行批量计算,而不是所有层完全同时完成。

四、Prefill 不只是“读懂提示词”,还会建立 KV Cache
当提示词经过每一层 Attention 时,模型已经为各个历史位置计算出了该层的 Key 和 Value。
这些 K/V 不只服务于 Prefill 当下的计算。回答开始以后,新位置的 Query 还会反复查询它们。
因此,推理系统通常会把每一层提示词位置的 K/V 保存下来:
第 1 层:提示词所有位置的 K/V第 2 层:提示词所有位置的 K/V第 3 层:提示词所有位置的 K/V……最后一层:提示词所有位置的 K/V
这组按层保存的历史 Key 和 Value,就是回答开始时的 KV Cache。
所以 Prefill 同时完成两件事:
处理完整提示词,计算第一个新 Token 的分布; 建立后续 Decode 可以直接复用的历史 K/V。
五、从 Prefill 进入 Decode 的分界点
Prefill 结束时,模型已经处理完提示词,并得到紧接在提示词之后的下一 Token 分布。
解码策略从中选出第一个新 Token。假设提示词最后一个位置是 xₙ,选出的新 Token 是 xₙ₊₁:
提示词:x₁, x₂, …, xₙ第一个新 Token:xₙ₊₁
从这一刻开始,模型进入 Decode 阶段。
两者的边界可以这样理解:
Prefill 处理的是已经一次性提供的输入 TokenDecode 处理的是模型刚刚逐步生成的新 Token
Prefill 的输入在阶段开始时全部已知;Decode 的下一个输入必须等待上一步实际选出来以后才能确定。
六、Decode:为什么每一步只能新增一个 Token
DAY 12 已经说明,自回归生成会把前一步输出追加到上下文。
生成 xₙ₊₂ 时,模型必须知道 xₙ₊₁;生成 xₙ₊₃ 时,又必须知道 xₙ₊₂。可以写成:

其中:
x₁:ₙ₊₁ 包含提示词和第一个已生成 Token; x₁:ₙ₊₂ 又多包含第二个已生成 Token; 每一步的条件都取决于上一步实际选出的结果。
因此,单条回答在时间维度上存在不可消除的先后关系:
生成 Token 1↓ 等待选择结果生成 Token 2↓ 等待选择结果生成 Token 3↓……
这就是 Decode 难以像 Prefill 一样,把同一条回答未来的许多 Token 一次并行算完的原因。未来 Token 还不存在,它们正是模型要决定的结果。
七、Decode 串行,不等于 GPU 每次只做一次小运算
“每次只生成一个 Token”描述的是生成顺序,不表示模型内部只进行一次标量计算。
对一个新 Token,模型仍然要经过全部 Transformer 层。每一层仍包含向量投影、Attention、前馈网络和其他大量矩阵运算;多个注意力头、隐藏维度、batch 中的不同请求也可以并行计算。
真正不能并行决定的是同一条序列中尚未生成的未来步骤:
同一个 Decode 步骤内部→ 仍有大量并行计算同一条回答的相邻生成步骤→ 后一步必须等待前一步 Token
这种区别很重要。Decode 的瓶颈不是“模型突然不使用 GPU 并行能力”,而是自回归依赖让每次前向计算只能把这条序列向前推进一个 Token。
八、如果完全不使用 KV Cache,会发生什么
假设提示词和已生成内容一共有 1,000 个历史 Token,现在要生成第 1,001 个位置。
没有 KV Cache 时,系统为了得到新位置在每一层需要查询的历史 Key 和 Value,只能重新处理完整的 1,000 个历史 Token,再加入新位置完成计算。
生成下一个 Token 时,历史长度变成 1,001,又要再次从头处理。
过程会变成:
生成第 1 个新 Token→ 重算提示词历史生成第 2 个新 Token→ 重算提示词 + 第 1 个新 Token生成第 3 个新 Token→ 重算提示词 + 前 2 个新 Token
大量历史位置的计算结果在相邻步骤之间没有变化,却被一遍遍重新生成。这会浪费计算,也会增加每个新 Token 的生成延迟。
KV Cache 就是为了保存其中可以安全复用的部分。
九、历史 K/V 为什么可以安全复用
历史 K/V 能够复用,依赖两个条件:
推理过程中模型参数保持固定; 因果注意力保证历史位置不依赖未来 Token。
假设当前已经存在三个位置 t₁、t₂、t₃。模型在某一层为它们计算出了:
k₁, k₂, k₃v₁, v₂, v₃
现在追加新 Token t₄。
由于因果掩码,t₁、t₂、t₃ 在当初计算时都没有读取未来的 t₄。追加 t₄ 不会回过头改变这些历史位置已经形成的层输入,也不会改变固定权重矩阵。
因此,原来的 k₁、k₂、k₃ 与 v₁、v₂、v₃ 仍然有效。

新位置只需要计算自己的 q₄、k₄ 和 v₄。然后用新的 Query 查询:
k₁, k₂, k₃, k₄再按照注意力权重汇总:
v₁, v₂, v₃, v₄历史 K/V 不需要因为未来新增一个位置而重新计算,这就是缓存成立的核心原因。
十、每一个 Decode 步骤具体做什么
假设 KV Cache 已经保存了全部历史位置在每一层的 K/V。一个新的 Decode 步骤可以概括成:
新 Token 进入第 1 层↓计算新位置的 q、k、v↓新 q 查询第 1 层历史 K/V 与当前 k/v↓得到该层新位置的输出↓把新 k、v 保留在第 1 层缓存中↓进入下一层,重复相同过程
完成最后一层后,模型把新位置的最终隐状态映射成下一 Token 的 logits,再由解码策略选出下一个 Token。
然后,新选出的 Token 进入下一轮 Decode。

从整条序列看,每一步只新增一个位置;从模型内部看,每一层的 KV Cache 也会追加这个位置对应的一组 K/V。
需要注意,KV Cache 消除的是历史 K/V 的重复计算,不是让历史上下文完全“免费”。新 Query 仍要与可见的历史 Key 计算注意力分数,并按照权重读取历史 Value。上下文越长,需要访问的缓存通常也越大。
十一、为什么缓存 K 和 V,而不缓存历史 Query
Query 的职责是发起当前位置的查询。
历史位置的 Query 在生成过去位置时已经完成任务:
q₁ 用于计算位置 1 的 Attention 输出q₂ 用于计算位置 2 的 Attention 输出q₃ 用于计算位置 3 的 Attention 输出
当新位置 4 到来时,模型不会让 q₄ 去查询历史 Query。它需要做的是:
新 Query q₄↓ 与历史 Key 匹配k₁, k₂, k₃, k₄↓ 按权重读取历史 Valuev₁, v₂, v₃, v₄
过去的 Query 不会再次参与这个新位置的 Attention 计算,因此通常没有长期保存的必要。
这就是缓存被称为 KV Cache,而不是 QKV Cache 的原因。
十二、KV Cache 节省了什么
KV Cache 最直接的收益是减少重复计算。
没有缓存时,每生成一个新 Token,都需要重新处理完整历史,以再次得到各层历史位置的 K/V。使用缓存后,历史 K/V 可以直接读取,每一步只新增当前 Token 对应的计算。
可以对照理解:
所以,KV Cache 的基本交换关系是:
使用更多存储空间↓减少历史位置的重复计算↓降低逐 Token 生成延迟
它不是让模型少一部分参数,也不是压缩提示词,而是把已经算出的中间结果保存下来供后续步骤复用。
十三、KV Cache 的代价:上下文越长,缓存越大
缓存不会凭空出现。历史 K/V 必须存放在执行 Attention 时能够高效访问的内存中。
KV Cache 的规模主要随以下因素增长:
Transformer 层数; 当前序列中的历史 Token 数; batch 大小或并发序列数量; 每层 Key/Value 的维度; K/V 使用的数值精度。

可以用一个简化关系理解:

这里的正比关系强调影响方向,不是适用于所有模型架构的精确统一公式。不同模型可能使用不同的注意力头组织方式、K/V 头数量和缓存布局。
但基本趋势不变:
上下文更长 → 单条序列缓存更大并发更多 → 同时保存的序列缓存更多层数或 K/V 维度更大 → 每个 Token 的缓存成本更高数值精度占用字节更多 → 总缓存更大
十四、长上下文为什么不仅影响 Prefill
提示词变长时,Prefill 要处理更多已知 Token,因此回答开始前的计算量会增加。
同时,Prefill 建立的 KV Cache 也会更大。进入 Decode 后,每个新 Query 还要查询更长的历史 K/V。
所以,长上下文可能同时带来三类影响:
Prefill 需要处理更多输入位置; KV Cache 占用更多存储空间; Decode 中每个新 Query 需要访问更多历史 K/V。
KV Cache 避免了从头重算历史,却没有消除“历史本身越来越长”带来的全部成本。
这也是为什么“模型支持很长的上下文窗口”与“使用很长上下文仍然便宜、快速”需要分别看待。
十五、并发请求为什么容易受 KV Cache 限制
如果系统只服务一条序列,只需要保存这一条序列的 KV Cache。
如果同时服务很多用户,每条正在生成的序列都拥有自己的提示词、历史生成内容和相应 K/V。即使所有请求使用同一份模型参数,它们的 KV Cache 也不能简单合并成一份,因为每条序列的上下文不同。
可以对照:
模型参数→ 多个请求可以共同使用同一套权重KV Cache→ 每条活动序列都有与自身上下文对应的缓存
因此,并发数增加时,KV Cache 的总占用也会增长。推理服务不仅要考虑模型权重能否放入设备,还要为活动请求、上下文增长和生成过程预留缓存空间。
十六、KV Cache 应该放在哪里
KV Cache 没有“数学上必须放在显存”的性质。
它应该放在执行 Attention 计算的设备能够高效访问的内存中:
模型主要在 GPU 上运行时,KV Cache 通常保存在 GPU 可高效访问的显存中; 模型在 CPU 上运行时,KV Cache 可以位于系统内存; 某些系统也可能在不同层级之间移动或管理缓存,但数据搬运本身会带来成本。
这里的关键是计算设备能否以足够低的延迟和足够高的带宽读取历史 K/V。
下一篇讨论 CPU、GPU、内存与显存时,我们会继续解释为什么大模型计算不仅受算力影响,也会受到数据传输速度和存储容量限制。
十七、几个常见误解
误解一:Prefill 会无视因果掩码读取未来 Token
Prefill 可以并行计算多个已知位置,但因果掩码仍限制每个位置的可见范围。并行执行不等于允许未来信息流入过去位置。
误解二:Decode 阶段完全不能并行
同一条序列的相邻 Token 必须按时间顺序生成,但单个步骤内部的矩阵运算、不同注意力头以及 batch 中的多条序列仍可并行。
误解三:KV Cache 保存的是整段文本
文本 Token 仍然存在于序列中;KV Cache 保存的是模型各层为历史位置计算出的 Key 和 Value 数值张量,不是原始字符串副本。
误解四:有了 KV Cache,生成新 Token 的成本就与上下文长度无关
缓存避免了重新计算历史 K/V,但新 Query 仍要查询和读取历史缓存。上下文变长仍会增加缓存占用与 Attention 访问成本。
误解五:历史 Query 也应该一起缓存
过去 Query 已经完成过去位置的查询。新位置只使用新的 Query 匹配历史 Key,并读取历史 Value,因此历史 Query 通常不需要长期保存。
误解六:KV Cache 会修改模型参数
KV Cache 是本次推理产生的临时中间状态。它会随上下文变化,但不会因此把信息写入模型权重,也不等于模型获得了长期记忆。
十八、今天真正需要记住什么
今天的推理链路可以压缩为:
完整提示词↓Prefill:并行处理已知位置↓建立每一层的历史 KV Cache↓选择第一个新 Token↓Decode:每步只处理一个新位置↓新 Query 查询历史 K/V↓新 K/V 追加到缓存↓继续生成下一个 Token
核心结论有九条:
第一,Prefill 处理回答开始前已经存在的完整提示词,可以在因果掩码下并行计算多个位置。
第二,Prefill 不仅产生第一个新 Token 所需的分布,也会建立后续生成需要的各层 KV Cache。
第三,Decode 每一步的输入依赖上一步实际选出的 Token,因此同一条序列在时间维度上必须逐步生成。
第四,Decode 的相邻步骤串行,不代表单个步骤内部或不同请求之间完全不能并行。
第五,模型参数固定且使用因果注意力时,追加未来 Token 不会改变历史位置已经计算出的 K/V,因此历史 K/V 可以复用。
第六,每个 Decode 步骤只为新位置计算新的 Q、K、V,用新 Query 查询历史 K/V,再把新 K/V 追加进缓存。
第七,历史 Query 通常不缓存,因为新位置不会查询过去 Query,只会匹配历史 Key 并读取历史 Value。
第八,KV Cache 用存储空间换取更少的重复计算和更低的逐 Token 生成延迟。
第九,KV Cache 会随层数、历史 Token 数、并发序列数、K/V 维度和数值精度增长。
理解这三个概念以后,我们就能把大模型推理看成两个计算特征明显不同的阶段:Prefill 像一次性读完题目并做好笔记,Decode 则拿着这些笔记,一步一步写出答案。
下一篇,我们会从算法走向硬件,讨论 CPU 与 GPU 的设计目标有什么不同,模型权重和 KV Cache 为什么常放在显存中,以及高 FLOPS 为什么仍可能被内存带宽限制。
自检
为什么 Prefill 可以并行处理多个提示词位置,却仍然没有违反因果掩码? 为什么同一条回答的 Decode 步骤必须等待上一个 Token? 在参数固定的因果模型中,追加新 Token 为什么不会改变历史位置已经计算出的 K/V? KV Cache 节省了哪些重复计算,又增加了什么资源开销? 为什么历史 Query 通常不需要像历史 Key 和 Value 一样缓存?
夜雨聆风