Prhub

#33403 [Scheduler] Honor explicit min-free-slots thresholds

原始 PR 作者 paulzhang-tm 合并时间 2026-08-05 16:44 文件变更 3 提交数 7 评论 6 代码增减 +35 / -34

执行摘要

修复 min-free-slots 显式阈值被封顶,非 DFlash 可调

PR body 明确:显式阈值“currently capped to the DFlash auto-default formula, whose maximum is four”,导致非 DFlash 用户“deliberately accumulating a larger admission batch”时配置被忽略。该 PR 旨在让用户在非 DFlash 工作负载上按需放行更大的准入批处理。

值得精读:它演示了一个小型调度器准入参数如何在“用户显式配置 vs 自动启发式”之间做优先级设计,并伴随 PP 约束收紧。重点看 resolve_min_free_slots 的新分支顺序和 server_args 的启动校验,以及测试如何覆盖边界(8 以下集群、max_running 上限、1 禁用)。

讨论亮点

该 PR 没有正式的 review 评论;关键讨论集中在 issue 评论与提交演进中:

  • hnyls2002 通过 /rerun-test test_min_free_slots_delayer.py/rerun-test test_dflash.py 触发回归,github-actions 反馈两个重跑均通过。
  • 首次重跑被 github-actions 拦截:“Your PR is diverged relative to required base commit cdff33d”,要求先 rebase 到最新 main,最终通过合并 main 的提交完成。
  • 设计收敛体现于提交历史:从“Make min-free-slots override explicit”到“Keep DFlash min-free-slots automatic”,再到“scope dflash guards to auto-default; trim test comments”,最终确定“显式值优先、公式只做 DFlash 默认、小集群守卫不压制显式值”。

实现拆解

  1. 准入阈值解析重构python/sglang/srt/managers/min_free_slots_delayer.py
    - resolve_min_free_slots 改为两段式判断:显式 user_value 存在时直接返回 min(user_value, max_running_requests)<=1 返回 None 禁用);未设置时仅 DFlash 家族且 max_running_requests >= 8 才使用 min(4, max(2, (max_running_requests + 5) // 6))。原“用户值一律被公式封顶、小集群一律禁用”的约束被移除。
    - 原因:让非 DFlash 工作负载能按业务需要累积更大批,同时不破坏 DFlash 的默认启发式行为。
    - 影响:<8 的集群守卫只作用于自动默认,显式值在小型集群下仍生效。

  2. 启动参数与兼容性校验python/sglang/srt/server_args.py
    - 更新 min_free_slots_delay 的 help 文本:显式值优先、受 max_running_requests 上限、1 禁用。
    - 在 check_server_args 中新增断言:pp_size > 1 时不允许设置该参数,因为流水线并行下可分配槽位数受 pp-max-micro-batch-size 限制,阈值可能永远无法满足。
    - 影响:PP 部署若显式设置该参数会在启动阶段报错,属一次性破坏性校验。

  3. 单元测试同步test/registered/scheduler/test_min_free_slots_delayer.py
    - 删除旧用例 test_small_cluster_disablestest_caps_to_formula,新增 test_explicit_value_survives_small_clustertest_non_dflash_uses_explicit_valuetest_explicit_value_is_capped_to_max_running_requeststest_explicit_one_disables_dflash_default 等,覆盖显式值优先级、max_running_requests 封顶、1 禁用、小集群守卫仅作用于自动默认等边界。
    - CI 回归:通过 /rerun-test 重跑 test_min_free_slots_delayer.py(ubuntu-latest)与 test_dflash.py(1-gpu-5090),均通过。

  4. 无附加配置/schema/部署改动:serving 启动参数不再受公式隐藏限制。

文件 模块 状态 重要度
python/sglang/srt/managers/min_free_slots_delayer.py 准入控制 modified 6.18
test/registered/scheduler/test_min_free_slots_delayer.py 单元测试 modified 6.18
python/sglang/srt/server_args.py 服务参数 modified 5.05

关键符号

resolve_min_free_slots MinFreeSlotsDelayer.should_delay ServerArgs.check_server_args test_explicit_value_survives_small_cluster test_non_dflash_uses_explicit_value test_explicit_value_is_capped_to_max_running_requests test_explicit_one_disables_dflash_default

关键源码片段

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

核心准入阈值解析逻辑重写:显式值优先并受 max_running_requests 上限,DFlash 自动默认保留公式,<8 守卫仅作用于自动默认。

from typing import Optional
​
​
def resolve_min_free_slots(
    user_value: Optional[int],
    max_running_requests: int,
    is_dflash_family: bool = False,
) -> Optional[int]:
    """解析 min-free-slots 阈值(None 表示禁用)。    优先级约定:
    1. 显式 user_value 始终优先,按 max_running_requests 封顶(<=1 视为禁用);
    2. 未设置且是 DFlash 家族时,fallback 到旧公式自动启用(<8 集群禁用);
    3. 其余情况保持禁用。
    注意:<8 守卫只属于自动默认,不再压制显式值。
    """
    max_running_requests = max(0, int(max_running_requests))
​
    if user_value is not None:
        # 显式值只受 max_running_requests 限制,不再被 DFlash 公式压缩
        threshold = min(user_value, max_running_requests)
        return threshold if threshold > 1 else None
​
    if is_dflash_family and max_running_requests >= 8:
        # 保留 DFlash 无配置时的历史行为
        return min(4, max(2, (max_running_requests + 5) // 6))
​
    return None
​
​
class MinFreeSlotsDelayer:
    """当运行槽位不足时推迟新 prefill 准入,凑成一批再放行。    典型场景:DFlash 的 draft prefill 每次准入成本高;阈值按 rank 本地计算,
    每个 DP rank 独立判断,避免一个拥挤 rank 拖住空闲 rank。
    """
​
    def __init__(self, min_free_slots: int):
        self._min_free_slots = min_free_slots
​
    def should_delay(self, *, running_bs: int, num_allocatable_reqs: int) -> bool:
        # 无 running 请求时不延迟;空闲 slot 不足阈值时才延迟
        return running_bs > 0 and num_allocatable_reqs < self._min_free_slots
python/sglang/srt/server_args.py configuration

CLI 帮助文本反映新行为,并在 check_server_args 中禁止 PP 下使用该参数。

if self.pp_size > 1:
    assert (
        self.disable_overlap_schedule and self.speculative_algorithm is None
    ), "Pipeline parallelism is not compatible with overlap schedule, speculative decoding"
    # 流水线并行下 per-microbatch 槽位受 pp-max-micro-batch-size 限制,
    # 阈值可能永远达不到,因此启动阶段直接拒绝该组合
    assert self.min_free_slots_delay is None, (
        "--min-free-slots-delay is not supported with pipeline "
        "parallelism: allocatable slots per microbatch are bounded by "
        "pp-max-micro-batch-size, so the threshold may never be reached"
    )

评论区精华

CI 重跑与 rebase 需求 测试

hnyls2002 通过 /rerun-test 触发 test_min_free_slots_delayer.py 和 test_dflash.py 重跑;github-actions 提示分支相对 base 提交 cdff33d 已 diverged,要求先 rebase。

结论:重跑均通过(ubuntu-latest 与 1-gpu-5090),最终通过合并 main 的提交完成 rebase。 · 已解决

显式阈值与 DFlash 自动默认的优先级设计 设计

提交历史显示设计从“完全覆盖”收敛为“显式优先、DFlash 自动默认不变”,并新增 PP 校验;最终提交 'scope dflash guards to auto-default; trim test comments' 明确了 <8 守卫只属于自动默认。

结论:合并策略:显式值始终优先;公式只做 DFlash 默认;<8 guard 不压制显式值;PP 下禁止使用该参数。 · 已解决

风险与影响

  • 调度准入行为变化:非 DFlash + 显式阈值时,较大阈值会让 prefill 等待更久以攒批,可能抬高 TTFT;PR body 明确未做基准测试,建议后续补充吞吐与首 token 延迟对比。
  • 小集群行为变化:<8 集群下显式阈值从“禁用”变为“生效”,可能改变既有服务的准入节奏。
  • PP 兼容性破坏:check_server_args 新增断言会让 pipeline parallelism 下设置该参数的服务启动失败,升级前需检查配置。
  • DFlash 回归面:未设置时行为不变,但显式值现在可大于 4;test_dflash.py 回归通过,风险可控。
  • 用户:非 DFlash 部署可通过 --min-free-slots-delay 控制准入批处理规模;显式设置 1 可关闭 DFlash 自动默认;PP 下必须移除该参数,否则启动失败。
  • 系统:准入调度逻辑变更,影响 prefill 延迟与批处理聚合;改动集中在 resolve_min_free_slotsMinFreeSlotsDelayer 本体决策逻辑未变。
  • 团队:CLI 文档与单元测试同步更新,行为契约发生变化,建议补充性能基准测试。
调度准入门控变更 缺少性能基准 PP 配置兼容性破坏 显式阈值改变小集群行为

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论