Prhub

#26780 [PD] Optimistic prefill

原始 PR 作者 cctry 合并时间 2026-06-02 16:16 文件变更 11 提交数 16 评论 21 代码增减 +571 / -92

执行摘要

PD 分离下乐观预填充:提前开始 prefill 计算以降低 TTFT

PD 分离中 prefill 必须等待 bootstrap(prefill 与 decode scheduler 到达时间偏差、decode 端分配延迟、bootstrap server/握手开销)完成后才能计算,直接增加 TTFT。乐观预填充旨在减少这一等待,降低首 token 延迟。

该 PR 设计在性能与复杂性之间做了良好权衡(重试预算、fallback)。建议仔细审阅 pop_bootstrapped 中的循环控制与 metadata buffer 生命周期逻辑。对于使用 PD 的团队,建议在预发环境开启 --optimistic-prefill-retries=1 并监控 retry 指标,确认收益后逐步上调。同时关注后续与 HiCache 等组件的集成计划。

讨论亮点

Review 中聚焦以下问题:

  • 死锁风险(head-of-line blocking)(correctness):gemini-code-assist[bot] 指出 pop_bootstrapped 中使用 break 导致 metadata buffer 耗尽时后续失败请求无法回退,可能死锁。作者接受并改为 continue
  • 测试成功计数器(testing):ShangmingCai 询问 test_survive_requests 是否应增加成功断言。作者说明所有请求预期失败,不需要计数器;ShangmingCai 接受。
  • KV cache 释放(correctness):ShangmingCai 担心 requeue 前未释放 KV cache 导致泄漏。作者确认 finalize_bootstrap 失败时已通过 assertion 保障释放。
  • 文档建议(documentation):ShangmingCai 建议将新特性写入文档。作者同意计划后续默认启用时更新。
  • HiCache 兼容性(design):weibingo 认为 HiCache 前缀匹配在前可能不影响,作者回应 abort 和取消操作复杂,需后续重构。

实现拆解

  1. 配置入口与校验:在 server_args.py 添加 --optimistic-prefill-retries 参数,并在 _handle_other_validations 中校验:当 pipeline parallel size > 1、启用 hierarchical cache 或模型使用 mamba radix cache 时自动禁用并输出警告。
  2. Bootstrap 队列扩展:在 prefill.py 中将 PrefillBootstrapQueueadd 方法拆解为 create_sender(创建 KV sender 并标记 pending_bootstrap)和 finalize_bootstrap(分配 metadata buffer、设置 bootstrap 完成时间、准备发送)。新增 ensure_metadata_buffer 确保索引可用。
  3. 乐观调度循环:在 pop_bootstrapped 中,对 Bootstrapping 状态的请求不再等待,直接允许其进入 waiting 队列;在 get_num_optimistic_prefill_retries 中控制重试预算;在 scheduler 的 process_batch_resulthandle_bootstrap_failure 中处理重试、回退与失败清理。
  4. 请求时间统计更新:在 req_time_stats.py 新增 reset_prefill_retry_time 重置重试相关时间戳;新增 prefill_retry_count 指标;移除 alloc_wait_duration,调整 bootstrap_duration 语义。
  5. 缓存命中率修正:在 metrics_reporter.py 中 cache hit 统计时减去 reprocessed_log_input_tokensreprocessed_log_hit_tokens,避免重试导致虚高。
  6. 失败注入与测试:在 disaggregation/utils.py 重构为 _poll_with_failure_injection 支持运行时失败概率注入;新增集成测试文件覆盖 gsm8k 准确率、logprob、故障注入等场景。
文件 模块 状态 重要度
python/sglang/srt/disaggregation/prefill.py 预填充调度 modified 8.84
test/registered/disaggregation/test_disaggregation_optimistic_prefill.py 乐观预填充测试 added 7.98
python/sglang/srt/disaggregation/utils.py 工具函数 modified 7.78
python/sglang/srt/server_args.py 服务配置 modified 6.78
python/sglang/srt/observability/req_time_stats.py 请求时间统计 modified 7.19
python/sglang/srt/managers/scheduler_components/metrics_reporter.py 指标报告 modified 6.25
python/sglang/srt/managers/schedule_policy.py 调度策略 modified 6.25
python/sglang/srt/managers/scheduler.py 调度器 modified 6.18
python/sglang/srt/managers/schedule_batch.py 批次管理 modified 4.99
python/sglang/srt/environ.py 环境变量 modified 4.89
test/registered/unit/managers/test_prefill_adder.py 预填充加法器测试 modified 3.28

关键符号

should_force_retry maybe_release_metadata_buffer create_sender ensure_metadata_buffer finalize_bootstrap advance_logprob_pt reset_prefill_retry_time _poll_with_failure_injection is_aborted get_num_optimistic_prefill_retries

关键源码片段

python/sglang/srt/disaggregation/prefill.py core-logic

核心实现文件,包含乐观预填充的全部流程:bootstrap 队列扩展、请求重试、metadata 缓冲区管理、失败处理。

# 文件 : python/sglang/srt/disaggregation/prefill.pyimport hashlib
from sglang.srt.environ import envs
from sglang.srt.managers.schedule_batch import Req
​
​
def should_force_retry(req: Req) -> bool:
    """测试钩子:基于环境变量概率强制请求进入乐观重试路径。
    用于集成测试中模拟重试场景。
    """
    retry_prob = envs.SGLANG_TEST_FORCE_OPTIMISTIC_PREFILL_RETRY_PROB.get()
    # 仅针对尚未重试且未被撤回的请求采样
    if retry_prob <= 0 or req.time_stats.prefill_retry_count > 0 or req.is_retracted:
        return False
    digest = hashlib.sha256(str(req.rid).encode()).digest()
    return int.from_bytes(digest[:8], "big") < retry_prob * 2 ** 64
​
​
def maybe_release_metadata_buffer(
    req: Req, allocator: ReqToMetadataIdxAllocator
) -> None:
    """安全释放请求关联的 metadata buffer 索引。
    在重试、失败、完成等路径中确保索引被归还。
    """
    if req.metadata_buffer_index >= 0:
        allocator.free(req.metadata_buffer_index)
        req.metadata_buffer_index = -1
​
​
class PrefillBootstrapQueue:
    # ... 其他方法省略 ...
​
    def create_sender(self, req: Req, num_kv_heads: int) -> bool:
        """创建 KV sender 但不入队,返回 False 表示容量超限。"""
        if self._check_if_req_exceed_kv_capacity(req):
            return False
        backend = (
            TransferBackend.FAKE
            if self.server_args.disaggregation_transfer_backend == "fake"
            else TransferBackend.NCCL
        )
        req.disagg_kv_sender = self.kv_manager.create_sender(
            req,
            kv_class=self.kv_class,
            tp_rank=self.tp_rank,
            tp_size=self.tp_size,
            pp_rank=self.pp_rank,
        )
        self._process_req(req)
        req.pending_bootstrap = True
        return True
​
    def ensure_metadata_buffer(self, req: Req) -> bool:
        """确保请求已分配 metadata buffer 索引,若无法分配返回 False。"""
        if req.metadata_buffer_index >= 0:
            return True
        if self.req_to_metadata_buffer_idx_allocator.available_size() == 0:
            return False
        req.metadata_buffer_index = self.req_to_metadata_buffer_idx_allocator.alloc()
        assert req.metadata_buffer_index is not None
        return True
​
    def finalize_bootstrap(self, req: Req) -> bool:
        """bootstrap 完成后初始化 sender,分配 metadata buffer,设置完成时间。
        返回 False 表示 metadata buffer 分配失败(非终结性失败)。
        """
        assert req.pending_bootstrap, "finalize_bootstrap is not idempotent"
        if not self.ensure_metadata_buffer(req):
            return False
        req.time_stats.set_bootstrap_done_time()
        num_kv_indices = len(req.origin_input_ids)
        decode_prefix_len = req.disagg_kv_sender.pop_decode_prefix_len()
        req.start_send_idx = decode_prefix_len
        num_kv_indices_to_send = num_kv_indices - decode_prefix_len
        # ... 续接页面分配与发送准备
        return True

评论区精华

pop_bootstrapped 中 break 引起死锁风险 正确性

gemini-code-assist[bot] 指出当 metadata buffer 耗尽时使用 break 导致后续失败请求无法回退,可能死锁,建议改为 continue。

结论:作者接受并修改为 continue,确保所有请求都能被处理。 · 已解决

测试中 success 计数器缺失 测试

ShangmingCai 询问 test_survive_requests 是否应增加成功断言,作者说明所有请求预期失败,无需计数器。

结论:ShangmingCai 接受,不添加计数器。 · 已解决

KV cache 在 requeue 前释放问题 正确性

ShangmingCai 担心 requeue 前未释放 KV cache 可能导致泄漏。作者确认 finalize_bootstrap 不应失败并已改为断言。

结论:问题澄清,已通过断言保障。 · 已解决

文档建议添加新特性说明 documentation

ShangmingCai 建议将 --optimistic-prefill-retries 加入文档。作者同意计划后续默认启用时更新。

结论:推迟到特性默认启用时更新文档。 · 已解决

HiCache 兼容性讨论 设计

weibingo 认为 HiCache 前缀匹配在前可能不影响乐观预填充。作者回应 abort 和取消操作复杂,需后续重构。

结论:当前暂不集成,留待后续重构。 · 已解决

风险与影响

  1. 重试风暴:高负载下大量请求重试可能加剧资源竞争;重试预算(最多 N 次)和 fallback 路径可缓解,需告警监控 num_prefill_retries 指标。
  2. Metadata buffer 耗尽:若分配失败则请求可能长时间等待或重复重试;代码已添加 fallback 回 bootstrap 队列,死锁风险通过 continue 修复。
  3. 兼容性限制:HiCache、Mamba、PP > 1 时自动禁用,但用户可能误配置导致静默降级;需文档明确说明。
  4. 性能影响:重试增加 scheduler 开销(释放/重新排队),但测试显示 TPOT/吞吐基本不变;极端场景下可能影响 ITL。
  5. 可观测性缺口:新增 retry 指标,但运维流程(告警阈值、面板)可能需要补充。

对用户端,启用后 TTFT 可降低 40%-50%,延迟敏感场景收益明显,默认向后兼容。对系统,PD 部署的 scheduler 逻辑复杂度增加,metadata buffer 占用小。对团队,核心调度、可观测性、配置模块均受影响,后续需规划与 HiCache/Mamba/PP 的集成重构。

死锁风险已修复 与 HiCache 不兼容 与 Mamba 不兼容 与 PP>1 不兼容 metadata buffer 耗尽 fallback 重试风暴需监控

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论