01 · 长提示词怎样阻塞已在生成的请求
设一个正在生成的请求D已经准备好做下一次 Decode,另一个新请求P携带很长的提示词。如果服务把P的整个 Prefill 作为一个不可插入其他任务的大工作提交,D可能在它完成之前得不到下一轮执行机会。用户看到的现象是流式回答突然停顿。
分块 Prefill 把P的提示词按 token 位置拆成多个可调度片段。处理完一个片段,调度器可以安排D的下一步,再回到P。它主要改变系统执行线的排程粒度,提示词里有效的 token 仍都要处理。
02 · 分块之后,模型仍要看见完整历史
把8个提示词 token 拆为 [1,2,3]、[4,5,6]、[7,8]。第1块在每层留下位置1到3的 K/V。第2块输入位置4到6,并使用各层历史缓存;第3块再继续。绝对位置延续,不因分块从0重新开始。
| 正在处理 | Attention 应看见的位置 | 不能发生的错误 |
|---|---|---|
| 位置4的 Q | 1、2、3、4 | 只读本块,漏掉前三个位置。 |
| 位置5的 Q | 1、2、3、4、5 | 看见位置6,泄漏未来。 |
| 位置6的 Q | 1、2、3、4、5、6 | 把位置编号重置为本块局部位置。 |
| 位置8的 Q | 1到8 | 把缓存当成只有上一块而非全部有效历史。 |
因此第2块的分数表可以理解为3个查询对6个键,而不是3对3独立 Attention。因果掩码按绝对位置判断可见性:查询位置 i 只能看键位置 j≤i。张量是非方形时,尤其要核对后端掩码的对齐语义。
03 · 画清时间线:谁更早,谁更晚
以下是假设单执行通道的教学例子:完整 Prefill 12 ms;拆成3块,每块4 ms;同一生成请求的3轮 Decode 每轮2 ms。假设调度与切块无额外开销,每轮在前一轮完成后就绪。实际耗时应实测,不能从12 ms自动推出每块一定4 ms。
| 方式 | 执行顺序与时间 / ms | 可观察到的取舍 |
|---|---|---|
| 整段 Prefill | P 0–12 → D1 12–14 → D2 14–16 → D3 16–18 | D首轮14 ms完成;P在12 ms完成。 |
| 分块交替 | P1 0–4 → D1 4–6 → P2 6–10 → D2 10–12 → P3 12–16 → D3 16–18 | D首轮6 ms完成;P在16 ms完成。 |
在这个例子中,D下一轮完成时间从14 ms提前到6 ms,P自己的提示词却晚4 ms处理完;所有任务最终都在18 ms完成。分块改善了响应机会,未必减少总工作或提高吞吐。这是讨论延迟与公平性的例子。P还需最终输出与采样才能真正给用户返回首 token。
同样的工作,改变谁先完成
单执行通道;Prefill 总计12 ms,3轮 Decode 各2 ms;每个 Prefill 块后安排一轮可执行的 Decode,未计切块开销。实际切块不保证总成本恒定。真实服务可把多个 Decode 与部分 Prefill 合入同一批,不必总是像表中逐项执行。组合批次可能改善设备利用率;收益取决于 kernel、形状、资源与调度,不能用这个单通道模型直接预测。
依据:Sarathi-Serve · Taming Throughput-Latency Tradeoff in LLM Inference ↗
04 · 调度器怎样决定这一轮放哪些 token
可用一个 token budget 理解调度。假设本轮预算8个新处理位置,有3个 Decode 请求各需1个位置,剩余5个位置就可分给待 Prefill 的请求或继续它尚未完成的提示词。下一轮重新选择,而不是一旦开始长 Prefill 就必须立即全部做完。
budget = 8
# 仅表示一种 Decode 优先策略,不是所有引擎的唯一策略
for request in ready_decode_requests:
if resources_allow(request) and budget >= 1:
schedule_decode(request); budget -= 1
for request in eligible_prefill_requests:
n = min(request.remaining_prompt_tokens, budget)
if n > 0 and cache_capacity_allows(request, n):
schedule_prefill_chunk(request, n); budget -= n这里 budget 数的是本轮处理 token 数。它不代表每个 token 的耗时相同,也不代表只需为这8个位置准备 KV:Decode 还要访问各请求之前的历史,模型层、上下文长度与并行通信都会影响执行成本。
vLLM 的优化文档描述了 Decode 优先、用剩余预算安排 Prefill 并按需切块的策略,提供 max_num_batched_tokens 调整入口。它是一种具体实现,调参结果应在自己的负载上验证。
05 · 与其他“分块、并行”方法有什么区别
| 方法 | 切的是什么 | 主要解决什么 |
|---|---|---|
| 分块 Prefill | 提示词的 token 位置段,跨调度轮次推进 | 长输入对其他请求的阻塞。 |
| FlashAttention 的 tiling | 一个 Attention 运算内部的计算块 | 减少中间矩阵 HBM 读写。 |
| 分页 KV | 缓存的逻辑/物理存储块 | 管理容量、碎片、寻址与共享。 |
| 流水线并行 PP | 模型层分配到不同设备/阶段 | 分布模型容量与计算。 |
| Prefill/Decode 分离 | 两类阶段放到不同执行资源 | 资源专用化,同时引入 KV 传输。 |
前几种可以组合:提示词按调度块推进,每个块内部使用融合 Attention,KV 存入分页缓存。不要仅因为都有“块”,就把它们视为同一优化。
06 · 分得越小一定越好吗
- 块小:为其他请求让出执行机会更频繁,但可能增加启动和调度开销,让矩阵形状变小、设备利用率下降。
- 块大:更容易集中处理提示词,却可能延长其他请求的生成间隔。
- Decode 一直优先:需用等待时间、配额或其他策略避免长 Prefill 饿死。
- 并发和缓存受限:需同时检查可用 KV 容量、最大序列数、抢占与重算。
建议把 TTFT、ITL、吞吐放在同一张表中。TTFT 是请求开始到首 token 的时间;ITL 是流式输出相邻 token 之间的时间,可观察用户感到的停顿。平均值之外还要记录 p95/p99、失败与排队情况。不要把更低 ITL 等同于所有请求 TTFT 也更低。
07 · 先验证数学,再测试调度
下方 CPU 教学例子在两层因果 Attention 上比较完整8个位置与3/3/2分块的输出,并检查每层缓存长度最终都是8。它验证历史可见性与位置延续,没有模拟真实 vLLM 的合批、抢占和 GPU 性能。
实际服务先确认所用引擎与模型支持分块,再做关闭/开启的对照:同一模型、精度、输入输出长度分布、请求到达率和并行配置。记录真实批次中的 Prefill/Decode 组成,避免只看开关而不知道执行路径。
依据:vLLM · 调度优化与预算 ↗ · Sarathi-Serve · Taming Throughput-Latency Tradeoff in LLM Inference ↗
08 · 适用场景与跨平台条件
混合长提示词与流式生成、需要控制生成停顿的在线服务,是这一方法的典型问题场景。单个长提示词独占设备时,分块未必提速;如果目标是离线最大吞吐,最优块大小与在线低延迟服务也可能不同。
在 NVIDIA 或昇腾环境,分别确认调度器是否支持分块、缓存能否正确续写、对应 Attention 后端是否支持非方形因果掩码及混合批次。分块策略属于服务运行时能力,底层设备名称不能替代这些支持条件。
vLLM Ascend 的 main 功能矩阵列出了 Chunked Prefill 的实现入口。实际部署需继续检查所用发布版本、模型、Attention 后端及其他功能的组合条件;本文不把 main 的矩阵当作所有已安装版本的保证。
09 · 用这三个问题自查
- 处理第二块的位置5时,它能看位置1吗?能看位置6吗?
- 为什么例子里D更快获得下一轮输出,但P更晚处理完?
- 分块 Prefill 和 FlashAttention 为什么可以同时启用?
流程位置与关联方法
这个方法可能出现在多个位置;计算线包含模型计算与算子实现,执行线包含运行时、内存和通信。
资料与核查范围
原理依据以原始论文和官方文档为主;算例与时间线是教学推导。文档核查日期:2026-10-08。引用 main / latest 文档时,实际接口以安装版本为准。
- vLLM · 调度优化与预算 ↗
- Sarathi-Serve · Taming Throughput-Latency Tradeoff in LLM Inference ↗
- Transformers · How Caching Works ↗
- vLLM Ascend · 支持功能矩阵(main) ↗
代码为接入片段或教学伪代码;本页没有声称完成 NVIDIA / 昇腾硬件实测。