跳转到主要内容

Chunked Prefill 深入分析:调度、Chunk Size 与 Attention 形状

从调度合同、Attention/GEMM 形状与验收矩阵理解 Chunked Prefill 的收益和边界

· 约 8 分钟阅读

Chunked Prefill 把一个长 Prompt 的 Prefill 拆成多个可调度片段。它改变的是一次连续占用 GPU 的时间与每轮 kernel shape,不会凭空减少 Prompt token、最终 KV Cache 或完整模型计算。

这篇聚焦“怎么切、切完改变了什么、如何验收”。Continuous Batching 与 KV 分页的基础合同见 批处理与调度KV Cache

30 秒复习
  • 一句话:Chunked Prefill 用多轮 scheduler token budget 换取更细的抢占点,并在 TTFT、TPOT、吞吐和资源峰值之间做权衡。
  • 三个判断:Decode backlog 高时小 chunk 更可能保护 TPOT;Prefill kernel 过碎时大 chunk 更可能保护 TTFT/吞吐;P/D 分离后它仍可控制 Prefill 池公平性、峰值和 KV 发送节奏。
  • 核心模型Ci=min(Niremaining,Bitoken)C_i=\min(N_i^{\text{remaining}},B_i^{\text{token}}),且 Attention 形状为 Qi=Ci, Ki=Pi+CiQ_i=C_i,\ K_i=P_i+C_i
  • 边界:最佳 chunk 不能从原始 ISL 或框架默认值推出,必须结合 effective prefill、并发、MoE 路由、硬件和 SLO 实测。

1. 问题与调度合同

如果调度器把长 Prompt 当作不可切分任务,它可能在一次较长的 Prefill 中延迟已有 Decode:

完整 Prefill:
decode -> [ long prefill ---------------- ] -> decode

Chunked Prefill:
decode -> [chunk] -> decode -> [chunk] -> decode

同一个请求必须完成自己的全部 Prefill 后才能开始 Decode。交错发生在不同请求之间:A 的长 Prompt 尚未完成时,B/C 的 Decode 可以在 chunk 边界继续推进。

设请求还剩 NiremainingN_i^{\text{remaining}} 个输入 token,本轮可分配 budget 为 BitokenB_i^{\text{token}}

Ci=min(Niremaining,Bitoken)C_i=\min\left( N_i^{\text{remaining}}, B_i^{\text{token}} \right)

CiC_i 是 scheduler chunk,不是模型层内部的 tile,也不是固定配置里的最大上下文。总 budget 还要容纳其他 Prefill 和 Decode token:

iCi+NdecodeBiteration\sum_i C_i + N_{\text{decode}} \le B_{\text{iteration}}

Chunked Prefill 的直接收益是新增调度边界;直接代价是更多轮 scheduler、元数据准备和 kernel 启动。

2. Chunk Size 改变哪些底层形状

设第 ii 个 chunk 新处理 CiC_i 个 token,之前已有 PiP_i 个 token 的 KV。

Attention

新 token 既要看本 chunk 中较早的 token,也要看过去的 KV:

Qlen=Ci,Klen=Pi+CiQ_{\text{len}}=C_i,\qquad K_{\text{len}}=P_i+C_i

其 causal attention 工作量可近似写成:

Wattn,iCiPi+Ci22W_{\text{attn},i} \propto C_iP_i+\frac{C_i^2}{2}

因此:

  • 后续 chunk 即使 CiC_i 相同,也会因 PiP_i 增长而更重;
  • 切块前后的理论总 Attention 工作量接近,主要变化是 shape 与执行粒度;
  • prefix hit 后,suffix 的 QQ 变短,但 KK 仍包含 cached prefix,计算不一定按 miss token 比例线性下降。

Dense FFN / GEMM

本轮多个请求可在 token 维 flatten:

X: [total_scheduled_tokens, hidden]
W: [hidden, intermediate]

chunk 太小会缩短 GEMM 的 MM 维,让 launch、调度和小 shape 效率更显著;多个小请求合批则可能重新撑大 MM

MoE

MoE 还要经过 routing。每个 expert 实际收到的 token 数取决于本轮总 token、top-k 与路由分布:

Ne=t1[eTopK(t)]N_e=\sum_t \mathbf{1}[e\in \operatorname{TopK}(t)]

小 chunk 可能让 grouped GEMM 变碎,并增加 dispatch/combine 相对占比。Dense 模型上的最佳值不能直接复用到 MoE。

3. 四个核心权衡

目标一般倾向需要同时观察
保护 Decode TPOT/ITL 尾部更小 chunkDecode backlog、scheduling gap
缩短长请求 TTFT更大 chunk排队、完整 Prefill 轮数
提高 tokens/s/GPU中到大 chunkAttention/GEMM/MoE shape
控制 activation/workspace 峰值小到中 chunkKV 水位、并发、抢占

这不是单调关系。chunk 变小后,TPOT 可能改善,也可能因 engine overhead 与碎片化反而恶化;chunk 变大后,单次 kernel 更高效,也可能制造更长的不可抢占区间。

真正的优化目标应写成带约束的问题,例如:

maxSLO Goodput(C)s.t.TTFTP99STTFT,TPOTP99STPOT\max \operatorname{SLO\ Goodput}(C) \quad \text{s.t.}\quad \operatorname{TTFT}_{P99}\le S_{\text{TTFT}}, \operatorname{TPOT}_{P99}\le S_{\text{TPOT}}

4. Policy:固定值之外还要看什么

4.1 Effective Prefill

原始输入长度不是本轮实际计算量:

Nprefill,eff=NinputNprefix,reusedN_{\text{prefill,eff}} =N_{\text{input}} -N_{\text{prefix,reused}}

若复用 KV 在 CPU、SSD 或远端,还要单独加入加载/传输延迟,不能把 cache hit 当作免费。

4.2 Decode Backlog

一个简单的自适应策略是:

decode backlog 高 -> 降低 chunk 上限,增加让出机会
decode backlog 低 -> 提高 chunk 上限,尽快完成 Prefill

它只是起点。若 workload 抖动大,还需 hysteresis、最小 chunk 与最大等待时间,避免策略来回震荡。

4.3 Length Bucket

Bucket 决定请求进入哪个队列或 worker,chunk 决定单请求本轮推进多少 token:

输入形态可评估策略
短 Prompt不切或较大 chunk,优先 TTFT
中等 Prompt结合 backlog 调整
长 Prompt限制单轮占用,保护公平性
超长 Promptchunk + admission control + KV 水位保护

4.4 Tail Chunk

最后一个很小的 chunk 可能产生低效 shape。可以评估合并尾块、设置最小粒度或允许最后一轮超出软 budget,但必须确认不会破坏 Decode SLO 与显存上限。

5. 在聚合式与 P/D Serving 中的含义

部署形态Chunked Prefill 的主要作用新增关注点
Prefill/Decode 同池在长 Prefill 间插入 Decode,降低阶段干扰TTFT 与 TPOT 权衡
P/D 分离Prefill 池内公平、峰值控制、KV 生成/发送粒度KV transfer、Prefill queue

P/D 分离不会自动消除长短 Prompt 竞争;它只是把 Decode 移出这组竞争。若 KV 只能在完整 Prefill 后发送,chunk 也不会自动形成传输流水,需要以 connector 的真实生命周期为准。

框架是否支持分块发送、流控和失败恢复,属于 Serving Stack 与框架选型 的实现合同。

6. 最小实验与验收

第一轮实验应固定模型、精度、硬件、engine 版本和 workload,仅扫描 chunk/token budget:

维度建议覆盖
输入短、中、长、超长桶;同时记录 effective prefill
输出短输出与长输出,改变 Decode backlog
并发低、中、高或明确 arrival process
模型dense / MoE / hybrid attention
cachemiss、典型 hit、远端 KV load

至少采集:

request_id, phase, chunk_id, token_range,
scheduled_tokens, past_kv_tokens, prefix_hit_tokens,
batch_num_seqs, batch_num_tokens,
TTFT, TPOT/ITL, throughput, SLO-goodput,
scheduling_gap, KV_watermark, preemption_count,
kernel_name, kernel_shape, kernel_duration

推荐先画四条曲线:

chunk size -> TTFT P99
chunk size -> TPOT/ITL P99
chunk size -> SLO-goodput
chunk size -> kernel duration + scheduling gap

读图时遵循:

  • chunk 变小而 TPOT 不改善:阶段干扰可能不是主因;
  • TTFT 明显恶化、TPOT 改善很小:chunk 可能过碎;
  • 吞吐下降且 kernel 时间分散:检查 shape、plan 与 launch overhead;
  • MoE grouped GEMM / all-to-all 占比上升:检查每个 expert 的 token group。

7. 查证层:框架如何表达 Chunk

vLLM / TensorRT-LLM 参数如何映射到合同

不同版本的参数名与默认值会变化,使用时应查目标版本文档和启动配置。

在 vLLM 中,常见控制面包括:

参数族合同含义
max_num_batched_tokens单轮 token budget
max_num_seqs单轮 sequence 上限
partial-prefill / long-prefill 参数同时允许多少 partial prefill,以及长请求策略

Scheduler 输出每个 request 的 num_scheduled_tokens。Model runner 可把本轮 token flatten 成:

total_num_scheduled_tokens = sum(request.num_scheduled_tokens)
hidden = [total_num_scheduled_tokens, hidden_size]

同时保留 query_start_locseq_lensblock_table,确保不同请求的 Attention 不互相可见。

TensorRT-LLM 常把相关能力描述为 chunked context / chunked prefill,并与单轮 token 上限、paged KV 和 in-flight batching 联动。概念对应,不应假定参数名、默认策略或边界完全一致。

Trace 里哪些信号不能直接当成 Chunk Size

Attention backend 的 plan/run 次数可能对应模型结构,而不是 scheduler chunk 数。已有 Qwen3.5 Plus GB300 trace 中,前 64 个采集 step 出现 15 plan/run,与 60 层模型中每四层一个 full-attention layer 的结构相符;它不能证明 chunk 是 15 token 或 15 个请求。

同一 trace 最后一个采集窗口只有 2 plan/run 与 11 个 state/KV save 信号,但采集几乎贴着 execute_model 结束。它只能证明窗口中的最后一步不完整,不能单独断言是请求的最终 tail chunk。

要确认 chunk,优先找 scheduler 侧的 num_scheduled_tokenstoken_rangenum_computed_tokens 和 request/chunk identity。混合 GDN 模型内部的 64-token kernel chunk 见 GDN 与 Chunked Prefill,完整案例诊断见 Prefill Trace

8. 常见误区

  • 把它当成减少 Prefill FLOPs:它首先改变调度粒度;prefix reuse、模型压缩或更快 kernel 才可能减少/加速实际工作。
  • 只按 raw ISL 分桶:应同时看 prefix hit、KV load 与 effective prefill。
  • 把一个默认值套到所有硬件:算力、HBM、互联和 kernel 实现都会改变拐点。
  • 只看平均吞吐:流式服务必须同时看 TTFT、TPOT 尾部和 SLO-goodput。
  • 混淆 scheduler chunk 与 kernel chunk:前者分配请求 token,后者是算子内部实现。
  • 从 trace 次数反推 token 数:没有 request identity 与 token range 时只能提出假设。

9. 模拟器接口

模拟器不应只使用一个总 Prefill 时间。更可解释的形式是:

Tprefill(r)=iTchunk(Ci,Pi,shapei)+iOscheduler,i+iOruntime,iT_{\text{prefill}}(r) =\sum_i T_{\text{chunk}}(C_i,P_i,\operatorname{shape}_i) +\sum_i O_{\text{scheduler},i} +\sum_i O_{\text{runtime},i}

Decode 受到的阻塞取决于实际交错顺序,而不只是 Prefill 总 FLOPs。参数来自 trace 或校准表时,应保存硬件、engine 版本、模型、精度和 workload identity;缺失值不得默认为零。

相关页面

参考资料