ELL3N HSU

Available in Chinese only.

← 文章

Prefill 与 Decode:输入处理和逐步生成

解释 Prefill 和 Decode 如何使用 KV Cache、各自如何批处理,以及应该观察哪些延迟指标。

一个流式接口可能很久才返回第一个字,也可能很快开始输出、随后断断续续。两种现象都能表现为“响应慢”,但要检查的工作不同。

先看一条请求如何开始回答,再看它怎样接着往下生成,就能找到这两种等待分别发生在哪里。下面讨论标准因果注意力的自回归生成,假定每步生成一个新 token;排队和网络时间会在解释指标时一起考虑。

从输入到逐步生成

Prefill 处理输入 token,为各层建立 KV Cache,并产生可用于采样首个输出 token 的 logits。一个请求的输入位置可以并行计算,也可以按顺序分块调度,不要求一次把整段输入放进同一个计算批次。

Decode 把最新 token 送入模型,读取已有的 KV,追加当前 token 的 KV,再得到下一个 token 的 logits。同一请求的后续 token 依赖此前输出;服务端仍可以把多个请求当前的 Decode 步合成一个 batch。

未复用前缀时,Prefill 处理输入、建立 KV 并产生首 token;若未满足停止条件,将最新 token 送入 Decode,读取历史 KV 并追加当前 KV,然后产生下一个 token,继续检查停止条件。未复用前缀时,Prefill 处理输入、建立 KV 并产生首 token;若未满足停止条件,将最新 token 送入 Decode,读取历史 KV 并追加当前 KV,然后产生下一个 token,继续检查停止条件。
图:未复用前缀时的生成循环。停止条件包括 EOS、停止字符串和输出长度上限;图中省略调度与网络传输。

宽图可横向滚动。查看原图查看原图

两个阶段都能批处理

Prefill 的输入通常包含较多 token,矩阵计算更容易形成较高的计算强度。Decode 每个请求每步处理的 token 少,还要读取模型权重与历史 KV,较容易受到显存带宽和调度影响。瓶颈仍取决于模型、输入输出长度、batch 大小和硬件,不能仅凭阶段名称判断。

比较项PrefillDecode
单个请求的处理对象输入 token,可能分块执行最新 token,逐步生成后续输出
批处理方式合并多个请求或输入块合并多个请求各自的当前步骤
常见压力计算吞吐、长输入占用调度时间权重与 KV 读取、逐步调度、缓存容量
相关指标TTFT:到首 token 的延迟,还包括排队等开销ITL:相邻输出间隔;TPOT:首 token 后的平均每 token 耗时

例如,把更多正在生成的请求放进同一批次,系统每秒可能输出更多 token,但每轮计算也可能更久,用户看到的相邻输出就会隔得更远。因此,比较 batch 配置时,要一起记录 TTFT、ITL/TPOT 和吞吐,并注明输入长度、输出长度与并发数。

如何验证瓶颈

固定模型、硬件和请求数据,先逐项改变调度配置或并发度,观察 TTFT 与逐 token 延迟,再结合 GPU 利用率、显存带宽和队列指标解释变化。例如,吞吐提高但 ITL 变长,可能是批次承载了更多请求,不能直接解释为每个请求生成得更快。

下面的函数只计算给定时间窗口内的 token 吞吐。调用时需注明 total_tokens 统计的是输出 token 还是输入加输出,它不能代替 TTFT 或 TPOT:

def tokens_per_second(total_tokens: int, elapsed_seconds: float) -> float:
    return total_tokens / elapsed_seconds

两个阶段共享调度资源时,下一步要看连续批处理如何安排每轮工作。如果已经准备压测,先明确延迟口径与负载模型,再比较配置。