Prhub

#35191 [Scheduler] Cap prefill-delayer queue target by admission capacity

原始 PR 作者 paulzhang-tm 合并时间 2026-08-18 21:40 文件变更 3 提交数 2 评论 2 代码增减 +40 / -4

执行摘要

prefill 延迟队列目标改用 admission capacity 封顶

PR body 明确指出:prefill-delayer queue trigger caps its accumulation target with max_prefill_bs, an observed high-watermark that can remain near one under trickle arrivals. This allows each request to immediately satisfy the trigger and keeps prefills fragmented. 即真实请求逐个到达时,max_prefill_bs 被压到 1,queue_min = min(running * ratio, 1) 也变成 1,等待队列几乎总是满足条件,延迟器形同虚设,prefill 无法聚合成大 batch。

值得调度器方向的读者精读。重点学习两点:一是用配置容量替代运行时观测高水位做目标封顶的取舍;二是保留 fallback 保证向后兼容的写法。单测构造的 max_prefill_bs=1 场景清晰复现问题,可作为同类调度修复的测试范式。若后续要推广,建议补充真实流量下 TTFT 与 prefill 批大小的基准数据。

讨论亮点

该 PR 没有实质性的 review 代码讨论(review_comments_count=0),ispobock 直接 APPROVED。唯一的交互是 ispobock 发起 /rerun-test test/registered/scheduler/test_prefill_delayer.py,github-actions 在 8-gpu-h200 环境执行通过。值得记录的设计权衡由 PR body 和代码注释给出:未配置 prefill_max_requests 时回退到 max_prefill_bs,保证老用户行为不变;同时 max_delay_ms 仍是墙钟兜底,避免队列目标放大后最坏情况 TTFT 失控。

实现拆解

1. 变更入口:读取 admission capacity

  • 文件:python/sglang/srt/managers/prefill_delayer.py
  • 在 PrefillDelayer.init 中新增 self._prefill_max_requests = server_args.prefill_max_requests。该值来自全局 ServerArgs,表示 admission 层的请求数上限,是比运行时观测高水位更稳定的封顶依据。

2. 核心逻辑:queue target 封顶与回退

  • 在 _negotiate_should_allow_prefill 的 queue 触发分支中,先计算 queue_capacity:配置了 prefill_max_requests 时使用它,否则回退到 global_max_prefill_bs_max。
  • 随后 queue_min_effective = min(int(running_batch * ratio), queue_capacity)。这样即使观测到的 max_prefill_bs 为 1,排队目标也不会被压到 1,延迟器能真正等到队列积累到足够数量。
  • slot_condition 分支仍使用 global_max_prefill_bs_max,不受本次改动影响。

3. 配置文档同步

  • 文件:python/sglang/srt/server_args.py
  • 更新 prefill_delayer_queue_min_ratio 的 help 文本,把 min(running_req * ratio, max_prefill_bs) 改为 min(running_req * ratio, prefill_max_requests),并注明未配置请求上限时回退观测 max_prefill_bs 的行为。

4. 回归测试

  • 文件:test/registered/scheduler/test_prefill_delayer.py
  • 为 NegotiateTestCase 增加 prefill_max_requests 字段,并在 _run_negotiate_test 构造 SimpleNamespace 时传入。
  • 新增 queue_trigger_uses_admission_capacity 用例:running=500、max_prefill_bs=1、queue_len=1、ratio=0.02、prefill_max_requests=128,断言两次调用后仍应 delay;该用例在旧逻辑下因为 queue_min_effective=1 会直接放行,能精确复现 bug。

5. 验证与 CI

  • PR 内报告 pytest -q test/registered/scheduler/test_prefill_delayer.py -k test_negotiate 通过,OSS pre-commit hooks 通过。
  • ispobock 触发 /rerun-test 后,github-actions 在 8-gpu-h200 上重新执行该测试文件并通过。
文件 模块 状态 重要度
python/sglang/srt/managers/prefill_delayer.py 调度器 modified 5.84
test/registered/scheduler/test_prefill_delayer.py 调度测试 modified 5.41
python/sglang/srt/server_args.py 启动参数 modified 4.62

关键符号

PrefillDelayer.__init__ PrefillDelayer._negotiate_should_allow_prefill

关键源码片段

python/sglang/srt/managers/prefill_delayer.py core-logic

调度器核心逻辑所在:读取 prefill_max_requests 并在 queue trigger 中用它封顶排队目标,同时保留未配置时的观测值回退,是本次修复的主路径。

# PrefillDelayer.__init__ 中读取配置的 admission capacity。
self._prefill_max_requests = server_args.prefill_max_requests# _negotiate_should_allow_prefill 的 queue 触发分支:
queue_condition = False
if self._queue_trigger_enabled and global_running_batch_max > 0:
    # 队列目标上限:优先用配置的 prefill_max_requests 封顶,
    # 未配置时回退到观测到的 max_prefill_bs 高水位,保持历史行为不变。
    queue_capacity = (
        self._prefill_max_requests
        if self._prefill_max_requests is not None
        else global_max_prefill_bs_max
    )
    # queue_min 表示期望攒够的排队请求数;max_prefill_bs 在“细水长流”式
    # 到达下会被压到 1,导致触发器被每个请求立刻满足,prefill 持续碎片化。
    queue_min_effective = min(
        int(global_running_batch_max * self._queue_min_ratio),
        queue_capacity,
    )
    queue_condition = (
        queue_min_effective > 0
        and global_waiting_queue_max < queue_min_effective
    )
    # 墙钟超时仍是兜底:即使排队始终达不到目标,也会按时放行避免 TTFT 失控。
    if queue_condition and prev_state is not None:
        elapsed_ms = (time.perf_counter() - prev_state.start_time) * 1000.0
        if elapsed_ms >= self._max_delay_ms:
            queue_condition = False
test/registered/scheduler/test_prefill_delayer.py test-coverage

新增 prefill_max_requests 测试字段和 queue_trigger_uses_admission_capacity 回归用例,用 max_prefill_bs=1 的低观测水位场景精确复现 bug,并覆盖配置封顶与 skip_first_delayer 交互。

# 回归场景:running=500、观测 max_prefill_bs=1(trickle 到达导致高水位被压低)、
# queue_len=1、ratio=0.02。未修复时 queue_min_effective=min(10, 1)=1,
# queue_len 不小于 1,触发器不延迟;修复后按 prefill_max_requests=128 封顶,
# queue_min_effective=min(10, 128)=10,queue_len=1 < 10,应延迟。
NegotiateTestCase(
    name='queue_trigger_uses_admission_capacity',
    max_delay_passes=100,
    token_usage_low_watermark=0.8,
    queue_min_ratio=0.02,
    max_delay_ms=5000,
    prefill_max_requests=128,
    calls=[
        NegotiateCall(
            prefillable=[True, True, True, True],
            token_usage=[0.9, 0.9, 0.9, 0.9],
            running_batch=[500, 500, 500, 500],
            max_prefill_bs=[1, 1, 1, 1],
            waiting_queue_len=[1, 1, 1, 1],
            max_running_requests=1024,
        ),
        # skip_first_delayer 会吞掉第一次延迟,第二次相同调用必须真正延迟。
        NegotiateCall(
            prefillable=[True, True, True, True],
            token_usage=[0.9, 0.9, 0.9, 0.9],
            running_batch=[500, 500, 500, 500],
            max_prefill_bs=[1, 1, 1, 1],
            waiting_queue_len=[1, 1, 1, 1],
            max_running_requests=1024,
        ),
    ],
    expected_allow=False,
    expected_reason='delay',
)

评论区精华

/rerun-test 触发 prefill-delayer 专项测试 测试

ispobock 发起 /rerun-test test/registered/scheduler/test_prefill_delayer.py,github-actions 在 8-gpu-h200 上执行该测试文件并通过。

结论:单测在 8-GPU H200 环境通过,无额外 review 质疑。 · 已解决

风险与影响

  1. 调度器热路径变更:改动集中在 _negotiate_should_allow_prefill,每个 scheduler 迭代都会进入该分支,新增一次属性读取和 if 判断,开销可忽略。
  2. 行为兼容性:仅当 prefill_delayer_queue_min_ratio 配置且 prefill_max_requests 有值时才改变行为;未配置时 queue_capacity 取 global_max_prefill_bs_max,与旧逻辑完全一致。
  3. 配置不当风险:prefill_max_requests 设置过大会让 queue_min_effective 偏大,排队达不到阈值时依赖 max_delay_ms 放行,可能推高局部 TTFT;设置过小则聚合效果减弱,需要按实际流量调参。
  4. DP 一致性:queue_capacity 使用全局配置而非 per-rank 观测值,所有 rank 从同一 server_args 读取,不会出现 rank 间不一致;但测试覆盖偏向数值行为,未覆盖真实生产负载下的 batch 分布。
  5. 缺少性能基准:PR 没有给出真实流量下的 TTFT 或 prefill batch 大小基准,收益主要靠推理和用例论证。

影响范围限于启用 DP attention 且开启 prefill-delayer queue 触发器的部署。收益是 trickle 到达时 prefill 能聚合成更大 batch,降低碎片化和调度开销,潜在改善 TTFT;对模型计算无影响,PR 明确 Accuracy Tests not applicable。团队侧需要关注 server_args 文档更新,避免用户对 queue target 语义产生误解;测试基建新增的 prefill_max_requests 字段是后续调度测试的复用入口。

调度器核心路径变更 缺少真实流量性能基准 配置不当可能影响 TTFT DP 多卡行为依赖全局参数一致性

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论