Prefill 阶段如何优化?
简化版
Prefill 会并行处理完整 Prompt,计算所有输入 Token 的隐藏状态并建立 KV Cache。它通常是计算密集阶段,长 Prompt 的注意力计算与 KV 写入会显著拉高首 Token 延迟(TTFT)。
优化要从四层入手:业务层减少无效上下文,调度层做 Chunked Prefill 和长度感知批处理,算子层使用 FlashAttention、融合 Kernel 与合适精度,集群层考虑前缀缓存或 Prefill/Decode 分离。最终用真实输入长度分布验证 TTFT、吞吐、显存和回答质量。
详细版
Prefill 与 Decode 的瓶颈不同:Prefill 一次处理 S 个 Token,大矩阵乘法更容易充分利用 GPU,但标准注意力的计算量随 S² 增长;Decode 每步只处理一个 Token,更常受权重和 KV Cache 带宽限制。
常用优化组合是:
- 清理重复检索片段、历史对话和模板,先减少输入 Token。
- 使用 FlashAttention,避免显式物化巨大的注意力矩阵。
- 按 Token 数而不是请求数组批,并对超长 Prompt 分块。
- 对稳定公共前缀复用 KV Cache,同时严格校验模型与模板版本。
- 长上下文场景评估张量并行、序列并行或阶段拆分。
不能只追求 Prefill Tokens/s。输入裁剪可能损失证据,Chunked Prefill 可能增加调度轮次,前缀缓存会占用显存并引入隔离风险;因此要一起看 TTFT P95/P99、有效输入吞吐、缓存命中率、显存水位和任务质量。
完整版教学
1. Prefill 到底做了什么
给定长度为 S 的 Prompt,模型一次前向计算全部位置,并把每层注意力的 Key、Value 写入缓存。最后一个位置的 Logits 用于产生首个输出 Token,之后才进入逐 Token Decode。
Prompt tokens ──> embedding ──> Transformer layers ──> first-token logits
|
└──> KV Cache for positions 1..S
因此 Prefill 同时决定首 Token 何时出现以及 Decode 从多大的 KV Cache 起步。
2. 为什么长 Prompt 成本高
标准自注意力中,每个位置都要与前面位置交互。忽略常数后,注意力计算约为 O(S² × d),投影与前馈网络约为 O(S × d²);长度上升时,注意力部分会越来越突出。
KV Cache 容量则近似线性增长:
KV bytes = 2 × L × H_kv × D_head × S × bytes_per_element
例如序列长度翻倍,KV 容量约翻倍,但全注意力相关计算可能接近四倍。回答时要区分计算复杂度与缓存容量。
3. 第一步为何是减少无效 Token
不参与答案的 Token 既消耗 Prefill 算力,也永久占用本次请求的 KV Cache。RAG 中重复召回、过长网页导航、未压缩历史和冗余 Few-shot 都是常见来源。
应先做文档去重、按权限过滤、历史摘要和动态 TopK,再设输入预算。裁剪不能简单保留头尾;对事实问答应优先保留与查询相关且可引用的证据,并在评测集上检查答案召回是否下降。
4. FlashAttention 优化的是什么
朴素实现会把 S × S 注意力分数写入显存,再读回做 Softmax 和加权求和。FlashAttention 通过分块与在线 Softmax,让中间块尽量留在片上存储,减少高带宽显存读写。
它保持精确注意力语义,但不意味着理论计算量从平方变成线性。收益受序列长度、Head 维度、数据类型与硬件 Kernel 支持影响,必须用目标卡型实测。
5. 批处理应按 Token 预算
两个请求数相同的 Batch,可能分别包含 8 × 512 与 8 × 16000 个输入 Token,计算与显存完全不同。调度器应限制每轮 Prefill Token 总量,而不是只限制请求数。
| 组批方式 | 优点 | 风险 |
|---|---|---|
| 固定请求数 | 实现简单 | 长度波动会造成延迟尖峰 |
| 长度分桶 | Padding 少、执行稳定 | 等待凑桶可能增加排队 |
| Token 预算 | 资源控制更准确 | 调度与估算更复杂 |
在线请求还要设置最大输入长度,避免单个异常 Prompt 把整轮计算占满。
6. Chunked Prefill 如何降低干扰
超长 Prefill 若一次执行到底,会让已有 Decode 请求长时间得不到调度,TPOT 出现抖动。Chunked Prefill 把长输入切成若干 Token 块,与 Decode 迭代交错执行。
不分块: [---------- long prefill ----------][decode]
分块后: [chunk][decode][chunk][decode][chunk]
它改善调度公平性和尾部 TPOT,但会增加轮次、边界管理与可能的 Kernel 开销。块大小要结合 TTFT 与 Decode SLO 调整,而不是越小越好。
7. 前缀缓存何时有效
若大量请求共享完全相同的系统提示、Few-shot 或文档前缀,可缓存其 KV,命中后只计算新增后缀。收益近似取决于公共前缀长度、复用次数与命中率。
缓存键至少应包含模型权重版本、Tokenizer、模板内容和推理配置;不同租户的私有前缀不能越权复用。模板中放置动态时间戳或随机 ID 会破坏前缀相同,应把动态字段尽量后移。
8. 并行策略如何选择
张量并行把单层矩阵切到多卡,可降低单卡计算时间,但每层都有通信;流水线并行更适合模型装不下单机,却可能产生气泡;长序列还可采用序列或上下文并行分摊激活与注意力。
多卡并非天然降低 TTFT。如果输入不够长,通信和同步开销可能超过计算收益。应先确定目标模型是否能单卡部署,再根据 Profile 决定并行维度。
9. 精度与量化怎样影响 Prefill
FP16/BF16 到 FP8 或更低位宽可降低权重读取并提升 Tensor Core 吞吐,但 Prefill 通常计算更密集,收益取决于硬件的低精度算力和量化 Kernel。
量化必须回归长上下文能力、困惑度与目标任务指标。只验证短文本准确率不足以证明长 Prompt 的注意力数值稳定;同时要检查首 Token Logits 偏差是否影响生成结果。
10. Prefill/Decode 分离的取舍
混部时,Prefill 与 Decode 争用同一 GPU,却需要不同优化目标。分离部署可以让 Prefill 节点偏重算力、Decode 节点偏重带宽,并分别扩缩容。
代价是 Prefill 完成后必须把整份 KV Cache 传给 Decode 节点。网络带宽、拓扑与序列长度会决定传输是否抵消收益,还要解决节点比例失衡和失败重试问题。
11. 如何做性能诊断
先拆分排队时间和纯 Prefill 执行时间,再按输入长度分桶。TTFT 高但 GPU 执行短,问题多半在排队或组批;Kernel 时间随长度异常上升,则应检查注意力实现、Padding 与并行通信。
prefill_metrics = {
"queue_ms": scheduled_at - admitted_at,
"compute_ms": first_token_at - scheduled_at,
"input_tokens": prompt_token_count,
"effective_tokens_per_s": prompt_token_count / (compute_ms / 1000),
}
还应记录真实 Token 数而非字符数,因为中英文、代码和特殊模板的分词密度不同。
12. 怎样设计可相信的压测
从生产日志脱敏抽样输入长度、并发和到达间隔,覆盖短、中、长与极长四个桶。分别报告 TTFT P50/P95/P99、输入 Tokens/s、OOM、拒绝率、缓存命中率及回答质量。
对照实验每次只改变一个主要变量,例如 Chunk 大小或 FlashAttention 版本。灰度上线时设置 TTFT 和错误率回滚阈值,并保留旧引擎与旧调度配置。
Prefill 优化不是单纯把 Prompt 算得更快,而是在不丢关键信息的前提下,让首 Token 延迟可预测。
13. 常见误区与追问
- 误区:Prefill 与 Decode 可以用同一个吞吐指标衡量。 两者计算形态和用户体验指标不同。
- 误区:FlashAttention 把注意力复杂度变成线性。 它主要减少中间结果的显存读写。
- 误区:把 Prompt 截短一定没有质量代价。 被删掉的证据可能正是答案依据。
- 误区:前缀缓存命中只看文本相同。 模型、Tokenizer 和模板版本也必须一致。
- 误区:多卡一定降低 TTFT。 小负载下通信可能成为主要开销。
- 追问:Chunked Prefill 为什么改善 TPOT? 它避免长 Prefill 独占调度周期,让 Decode 能穿插执行。
- 追问:优化应先做哪一层? 先删除无效 Token,再优化调度和 Kernel,最后考虑复杂的集群拆分。
14. 加强记忆
- 先记职责:Prefill 处理完整输入并建立 KV Cache。
- 再记瓶颈:长序列计算、KV 写入和批次波动。
- 再记减法:去重、摘要、动态检索减少无效 Token。
- 再记算子:FlashAttention 与融合 Kernel 降低数据搬运。
- 再记调度:Token 预算、长度分桶、Chunked Prefill。
- 再记复用:稳定公共前缀才适合缓存。
- 最后记验收:真实长度分布下同时看 TTFT、吞吐、显存和质量。