分块预填充 Chunked Prefill

Chunked Prefill (Split-Fuse Prefill Scheduling)

分块预填充(Chunked Prefill)把超长请求的 Prefill 预填充阶段切成多个固定大小的 chunk,与正在进行的 Decode 解码请求混合组进同一批一起计算,从而消除长请求对短请求的队头阻塞、显著压低首 Token 延迟抖动,核心调参项是 chunk size。

详细解释

分块预填充(Chunked Prefill)是推理引擎调度层的一项关键优化,其核心思路常被称为 Split-Fuse:把一条超长请求的 预填充 计算拆成多个固定 Token 预算的小块(chunk),每一块与当前正在解码的其他请求融合(fuse)进同一个批次一起执行,而不是让这条长 prompt 一次性独占整个前向。这个思路最早由 Sarathi-Serve 系统化提出(见 Sarathi-Serve 论文),并在 vLLM 等主流引擎中落地为可调参数。要理解它为什么重要,得先看没有分块时会出什么问题:一条 32K Token 的 prompt 进来,引擎会把它当成一个巨大的 Prefill 批次一次算完,这期间整张卡的 SM 与算力都被占住,算完可能要几百毫秒,与此同时其他正在流式输出的短请求只能干等,这就是经典的队头阻塞(head-of-line blocking)。

它解决的核心痛点正是长 Prefill 独占一次前向造成的连锁反应。同批的 Decode 请求本该每步吐一个 Token,但被那条长 prompt 卡住后,相邻 Token 间隔 TPOT 出现尖峰,用户侧看到的就是「打字机突然停住」;更糟的是首 Token 延迟的 P99 会被偶尔出现的长请求反复拉高,形成难以预测的尖刺。开启分块预填充后,长 prompt 不再霸占整批,而是被摊到多个连续批次里逐步消化:每个批次固定放 512 至 2048 Token 的 Prefill 计算量,算完这一块就把配额让给解码请求,批处理调度器趁机把若干待解码请求塞进同批,一步步吐 Token。这样解码请求每一步都有机会插进批次,TPOT 尖峰被削平,P99 首 Token 延迟 也变得可预测得多。代价是长 prompt 自己的 TTFT 会略微变长,因为它不再享受一口气算完的特权,本质上是用一点 TTFT 换延迟分布的稳定。

分块预填充与 连续批处理 是互补关系:连续批处理决定「谁能在每一步自由进出批次」,是并发调度的骨架;分块预填充决定「一个批次里放多少 Prefill、多少 Decode 的计算量」,是批次内部的比例分配。两者配合时,引擎按 Token 预算做统一编排,把每一步的计算配额优先留给已开始的解码请求,剩余额度再用长 prompt 的分块去填,从而兼顾并发与公平。典型 chunk 预算区间是 512 至 2048 Token,2026 年公开档的引擎默认值常见 512 或 1024,需结合显卡算力与平均输出长度调参。配合 分页注意力 的 KV 分页管理,分块之间的 KV 可以跨批次无缝复用,不会因拆分而重算,这也是分块几乎不增加额外算力的原因。要判断分块效果,看 延迟分位数 的 P99 远比看平均值可靠。唯元智创 的推理网关默认为所有后端开启分块预填充,并按业务的历史输出长度分布自动推荐 chunk 预算,帮助企业在不增卡的前提下把 P99 抖动压到原来的一半以下。

Prefill 调度策略对比表

策略单次前向处理量队头阻塞P99 TTFT 表现Decode 抖动显存峰值典型支持框架
整段 Prefill(Full Prefill)一条 prompt 一次全部算完严重,长请求独占整批差,尖峰可达 5 至 8 秒高,TPOT P99 尖峰 300 毫秒以上高,需一次性容纳整段 KV所有引擎的基础模式
分块预填充(Chunked Prefill)按 chunk 预算分批,常见 512 至 2048基本消除,长请求不再独占好,P99 压到 2.0 至 3.0 秒低,TPOT P99 约 50 至 90 毫秒较低,KV 按需增长vLLM、SGLang 原生支持
分块 加 抢占式调度分块执行,必要时抢占并重排无,优先级可动态调整很好,P99 约 1.8 至 2.5 秒极低,TPOT P99 约 40 至 70 毫秒低,支持 KV 换出vLLM 抢占与重算机制
PD 分离(跨节点)Prefill 与 Decode 分池执行无,跨池彻底解耦好,但需高速互联支撑低,取决于 KV 传输链路分池管理,两池各自预留vLLM、SGLang 的 PD 模式
唯元智创 默认分块组合自适应 chunk 加 抢占式调度基本消除好,P99 约 1.9 至 2.6 秒低,TPOT P99 约 45 至 75 毫秒低,按需分配网关统一纳管后端引擎

chunk size 取值对比表

chunk size显存峰值TTFTTPOT适用场景
256最低最长,长 prompt 排队块数最多最平稳高并发、平均输出极短、极重视 TPOT 稳定
512低较长平稳多数在线对话的稳妥起点
1024中中较平稳通用推荐,兼顾 TTFT 与 TPOT
2048较高较短略回升平均输出长、并发不高的场景
4096高最短抖动明显回升仅适合以超长输入为主、几乎无并发的离线任务

多数线上业务的推荐区间是 512 至 2048,其中 512 与 1024 是最常见的甜点值。

实战经验与避坑

  1. chunk size 不要设得比模型的 max_num_batched_tokens 上限还大:分块的语义是「单个批次最多算多少 Token」,如果 chunk size 超过引擎一次前向能容纳的 Token 上限,多出来的部分只能被截断或退化成整段 Prefill,分块就失去了意义。正确做法是先确认引擎配置里的最大批处理 Token 数,把 chunk size 设成它的一个合理分数(常见是二分之一到四分之一),既能保证单批不超限,又留出余量给同批的 Decode 请求。上线前务必检查这两个参数的相对关系,很多「明明开了分块却没效果」的案例,根因就是 chunk size 被配置得比批处理上限还大,分块压根没生效。

  2. 必须与连续批处理同时开启,否则收益大幅缩水:分块预填充解决的是「批次里放多少 Prefill 计算量」,但如果引擎本身没有连续批处理、请求不能随时进出批次,那么分块算完的配额也没有新请求能接上,解码请求依旧要等前一批全部结束。所以正确姿势是两个开关一起打开:连续批处理负责让请求随时进出,分块预填充负责让每批的计算配额分配合理。实测对比中,只开连续批处理时遇到 32K 长 prompt 的 TPOT P99 抖动约 200 至 350 毫秒,两者同开后可压到 60 至 100 毫秒,同时集群吞吐还能再涨 20% 至 40%。

  3. 长上下文任务要把分块与 KV 分页、滑动窗口注意力组合使用:对于动辄几十万 Token 的超长上下文,单纯分块只解决了调度公平性,没有解决 KV 显存膨胀问题。更稳妥的组合是分块预填充加 分页注意力 的 KV 分页管理(避免显存碎片、支持增量复用),必要时再叠加滑动窗口注意力把超长序列的 KV 占用压下来。三者配合时,分块负责把长 prompt 摊平到多批、KV 分页负责让跨批的 KV 无缝拼接不重算、滑动窗口负责限制显存上限,这样才能在超长上下文下同时保住 TPOT 稳定与显存可控。

  4. 不要为了压 TTFT 把 chunk 设到极小,会拉低整体吞吐并抬高调度开销:直觉上 chunk 越小、解码请求插空机会越多、TPOT 越平稳,但代价是批次数量变多,kernel 启动与调度开销上升,整体每秒处理 Token 数反而下降。当 chunk 小到 256 以下时,调度开销常常吃掉收益,出现「TPOT 更稳了但总吞吐掉了两三成」的情况。经验做法是从 512 或 1024 起步,按 2 倍步长向上逼近,观察 TPOT 抖动从哪个点开始明显回升,在回升点前一档定稿;只有在平均输出极短且并发极高时才考虑降到 256。

  5. 要区分 TTFT 与 TPOT 两个指标分开评估,避免只优化一头:分块预填充对这两个指标的影响方向相反——它让长 prompt 的 TTFT 略微变长(典型增加 15% 至 30%),却让 TPOT 的 P99 大幅改善(典型下降 60% 至 80%)。如果只盯 TTFT 会误判为退步,只盯 TPOT 又可能忽略长请求排队过久。正确做法是同时看两组的 P50、P90、P99,并结合业务权重判断:重视流式打字体验的产品应优先保 TPOT,重视首屏出字速度的产品则要控制 TTFT 增幅,必要时把 chunk 调大一点换回 TTFT。

  6. 上线前必须用混合长短请求的压测流量验证,纯短请求测不出收益:分块预填充的收益全部来自「长请求打断短请求」这一现象,如果压测流量清一色是短 prompt,那么开不开分块几乎没有差别,很容易得出「没用」的错误结论。因此压测集必须刻意混入长尾:常见做法是让 5% 到 10% 的请求携带 8K 以上的长输入,其余为短请求,并观察这批长请求出现时短请求的 TPOT 是否被拖出尖峰。只有在这种混合流量下,分块带来的「TPOT P99 从数百毫秒降到几十毫秒」的改善才会显现,评估结论也才可信。

常见问题

我已经开了连续批处理,还需要再开 Chunked Prefill 吗?
需要,两者解决的问题不同,连续批处理管「请求何时进出批次」,分块预填充管「一个批次里放多少计算量」,缺一不可。先说连续批处理做了什么:它让新来的请求可以随时插进当前批次、已结束的请求随时退出批次,从而把 GPU 利用率从「等整批跑完」的 30% 至 50% 拉高到 80% 以上,这是并发的基础骨架。但它不解决一个关键问题:当一条超长 prompt 进入批次时,它仍然会在这一次前向里占据绝大部分计算量,整批的其他解码请求都要陪它一起等,这就是队头阻塞。分块预填充正是补上这一环,它把长 prompt 拆成 512 至 2048 Token 的小块,每块只占一部分批次配额,剩下的配额留给解码请求,于是解码请求每一步都能推进,TPOT 尖峰被削平。用一个类比说明:连续批处理像让更多人能随时上电梯,分块预填充像规定每个人一次只能占电梯一小块空间,两者配合才不会出现一个人堵死整部电梯的情况。实测数据也支持叠加:只开连续批处理时,遇到 32K 长 prompt 的 TPOT P99 抖动约 200 至 350 毫秒;两者同开后能压到 60 至 100 毫秒,同时集群吞吐还能再涨 20% 至 40%。因此结论很明确:只要你的业务里存在长短 prompt 混合、且有流式输出需求,连续批处理与分块预填充应当同时开启。对于使用现代推理框架的团队,通常只需在配置里把分块开关打开并把 Token 预算设成 512 或 1024 即可,框架会自动与连续批处理协同调度。唯一例外是纯离线批处理场景,没有并发延迟诉求,此时可只保留批处理无需分块。
chunk size 到底怎么定,调大和调小分别有什么影响?
绝大多数业务从 512 或 1024 起步最稳妥,调大偏吞吐与长 prompt 的 TTFT,调小偏 TPOT 稳定性与短请求公平性,本质是在两者之间找平衡。先看调大的影响:chunk 越大,单批里 Prefill 的计算占比越高,长 prompt 被切成的块数越少,它的 TTFT 越短、系统整体吞吐倾向越高;但代价是解码请求在该批里能分到的配额变少,TPOT 抖动会回升,最极端的情况就是 chunk 等于整条 prompt、等价于没有分块。调小的影响正相反:chunk 越小,解码请求插空的机会越多,TPOT 越平稳、短请求越公平,但批次数量变多、kernel 启动与调度开销上升,且长 prompt 的 TTFT 会被拉得更长,当 chunk 小到 256 以下时,开销可能吃掉收益。因此推荐做法是三步走:先取引擎默认值 512 或 1024 作为基线跑真实流量,记录 TTFT P50、TTFT P99 与 TPOT P99 抖动;再按 2 倍步长向上试 1024、2048,观察 TPOT 抖动从哪个点开始明显回升;最后在回升点前一档定稿。还要结合业务特征微调:如果平均输出很短(小于 64 Token)且并发高,可降到 256 至 512 换取更稳的 TPOT;如果平均输出很长、并发不高,可升到 1024 至 2048 以缩短长 prompt 的 TTFT。典型线上经验是,512 至 1024 能让 TPOT P99 稳定在 50 至 90 毫秒,而一旦超过 2048,抖动常会回升到 150 毫秒以上。最后提醒两点:一是每次只改一档并观察至少 30 分钟,别同时改多个参数;二是不同引擎对 chunk 的语义略有差异,有的按 Token 计、有的按算力预算计,务必先看框架文档再调。
为什么我开了分块预填充之后,TTFT 反而变长了?
这是预期内的正常现象,不是配置错误,分块预填充的设计目标就是用少量 TTFT 换取 TPOT 的稳定与整体吞吐的提升。原因很清晰:没有分块时,长 prompt 被当作一个巨大的 Prefill 批次一次算完,它的 TTFT 自然最短;开启分块后,同一条 prompt 被切成了好多块,每块要和当时排队的其他请求一起竞争批次配额,于是它要排队等好几个批次才能算完,TTFT 因此变长。典型值上,chunk 设为 512 至 1024 时,超长 prompt 的 TTFT 会比不分块时增加 15% 至 30%,而换来的是整个集群 TPOT P99 抖动下降 60% 至 80%、高并发吞吐提升 20% 至 50%。所以判断分块是否划算,绝不能只看单条长请求的 TTFT,而要看全局指标:如果你的业务是大量短请求与少量长请求混合、且都要求流式输出流畅,那么这点 TTFT 增长完全值得;但如果你的业务几乎全是长 prompt 且用户对首 Token 延迟极其敏感、又不需要流式体验,那分块确实不是好选择,可以调大 chunk 或干脆关闭。如果 TTFT 增长得离谱,比如从 1.5 秒涨到 8 秒,那多半是 chunk 设得过小(例如 128)或引擎的调度优先级配置有问题,此时应把 chunk 调回 512 至 1024,并检查是否把 Prefill 配额过度让给了 Decode。还有一个容易被忽略的因素:若底层不支持增量 KV 复用(例如老式连续显存分配),分块会导致已算过的 KV 被重复计算,那 TTFT 就不只是排队变长而是真的变慢,这种情况下应先升级到 分页注意力 这类支持增量 KV 的方案,再开启分块。综合来看,只要 TTFT 增幅在 30% 以内、而 P99 与吞吐明显改善,就说明配置合理。