Prhub

#25442 Lift forward_ct/cur_batch and use direct access in watchdog

原始 PR 作者 fzyzcjy 合并时间 2026-05-16 09:22 文件变更 2 提交数 1 评论 1 代码增减 +5 / -3

执行摘要

提升 watchdog 属性初始化顺序,移除防御性 getattr

watchdog 守护线程在首次 tick 时读取 forward_ct 和 cur_batch,如果 init 中尚未设置这些属性,则必须回退到 getattr 防御式访问。PR 将属性提升到 init_soft_watchdog 之前,使属性存在性成为 init 的不变式,而不是分散在多个文件中的防御性约定。

值得快速合并。这是一个典型的机械重构,提升了代码可读性并消除不必要的防御性模式,可作为后续类似清理的参考。

讨论亮点

无 review 评论或讨论。

实现拆解

  1. 在 scheduler.py 中提前初始化属性:在 Scheduler.__init__ 中,将 self.forward_ct: int = 0self.cur_batch: Optional[ScheduleBatch] = None 的赋值移动到 self.is_initializing = True 之后、self.init_soft_watchdog(server_args) 之前。
  2. 在 scheduler_runtime_checker_mixin.py 中移除 getattr:在 create_scheduler_watchdog 函数中,将 getattr(scheduler, "forward_ct", 0) 改为 scheduler.forward_ct,将 getattr(scheduler, "cur_batch", None) is not None 改为 scheduler.cur_batch is not None
  3. 保持功能不变:不引入行为变更,仅重构代码以消除防御性模式。
文件 模块 状态 重要度
python/sglang/srt/managers/scheduler.py 调度器 modified 5.33
python/sglang/srt/managers/scheduler_runtime_checker_mixin.py 调度器 modified 5.57

关键符号

Scheduler.__init__ create_scheduler_watchdog

关键源码片段

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

将 forward_ct 和 cur_batch 初始化提前到 init_soft_watchdog 调用之前,确保 watchdog 线程首次读取时属性已定义。

# python/sglang/srt/managers/scheduler.py - Scheduler.__init__ (partial)
# 变更前:forward_ct 和 cur_batch 在 init_soft_watchdog 之后才被初始化(如果存在)
# 变更后:在 init_soft_watchdog 之前就明确初始化为默认值def __init__(self, ...):
    self.is_initializing = True
    # init_soft_watchdog 启动一个守护线程,首次 tick 时会读取这些属性
    self.forward_ct: int = 0
    self.cur_batch: Optional[ScheduleBatch] = None
    self.init_soft_watchdog(server_args)
    # ... 其余初始化
python/sglang/srt/managers/scheduler_runtime_checker_mixin.py core-logic

移除 watchdog 创建函数中针对 forward_ct 和 cur_batch 的 getattr 防御性访问,改为直接属性访问。

# python/sglang/srt/managers/scheduler_runtime_checker_mixin.py - create_scheduler_watchdog (partial)
# 变更后:直接访问 scheduler.forward_ct 和 scheduler.cur_batch,
# 因为现在这些属性保证在调度器构造完成时已初始化。def create_scheduler_watchdog(
    scheduler: Scheduler, watchdog_timeout: float, soft: bool = False
) -> WatchdogRaw:
    def dump_info() -> str:
        if scheduler.is_initializing:
            return ""
        _, messages = scheduler._check_all_pools(scheduler.get_pool_stats())
        return (
            f"{scheduler.cur_batch.batch_size()=}\n"
            f"{scheduler.cur_batch.reqs=}\n" + "\n".join(messages)
        )
​
    return WatchdogRaw(
        debug_name="Scheduler",
        get_counter=lambda: scheduler.forward_ct, # 原为 getattr(scheduler, "forward_ct", 0)
        is_active=lambda: scheduler.is_initializing or scheduler.cur_batch is not None, # 原为 getattr(scheduler, "cur_batch", None) is not None
        watchdog_timeout=watchdog_timeout,
        soft=soft,
        dump_info=dump_info,
    )

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

风险极低。变更仅涉及属性初始化顺序调整和 getattr 替换,不改变任何运行时行为。但需确保 watchdog 线程在读取属性时调度器尚未完成初始化(例如在并发构造中)——当前架构下 init_soft_watchdog 立即启动守护线程,但属性在调用前已赋值,因此安全。

影响范围仅限于 Scheduler 初始化流程和 watchdog 创建函数。无外部行为变化,不涉及测试、配置或 API 变更。

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论