Prhub

#25621 Move pool-stats sampling to SchedulerPoolStatsObserver

原始 PR 作者 fzyzcjy 合并时间 2026-05-18 18:37 文件变更 4 提交数 1 评论 3 代码增减 +176 / -227

执行摘要

将 pool-stats 采样从 Mixin 移至独立组件

PR body 说明:"Mechanical cut + paste for the introduce-pool-stats-observer mech move." 目的是将池统计采样方法从 SchedulerRuntimeCheckerMixin 抽离到专用的 SchedulerPoolStatsObserver 组件,实现关注点分离,为后续进一步重构(如删除 mixin)做准备。

值得精读,作为 SRT 调度器逐步重构的范例。可以学习如何将静态辅助方法安全地迁移为组件方法,并系统性更新调用点。虽改动机械,但 review 中发现的两个优化点(冗余调用、缺失类型导入)值得关注。建议审阅者重点关注 scheduler_runtime_checker_mixin.py 的剩余类型导入修复。

讨论亮点
  • 缺失 PoolStats 导入:reviewer (gemini-code-assist) 指出,scheduler_runtime_checker_mixin.py 在删除对 PoolStats 的导入后,仍在其方法(如 _check_full_pool)中使用了 PoolStats 类型,这会导致静态分析错误,建议在 TYPE_CHECKING 块中恢复导入。
  • 冗余调用优化:在 get_pool_stats 方法中,当 is_hybrid_ssm 为 True 且 is_hybrid_swa 为 False 时,_get_mamba_token_info 被调用了两次(一次构造 pool_stats,一次叠加 mamba 字段),该分支可以优化为仅在同时启用两种模式时执行叠加逻辑。
  • 缺少返回类型注解:_get_mamba_token_info 方法缺少返回类型 PoolStats 的注解。

实现拆解

  1. 从 SchedulerRuntimeCheckerMixin 中删除 pool-stats 方法:删除了 streaming_session_count、active_pool_idxs、session_held_tokens、session_held_full_tokens、session_held_swa_tokens、session_held_req_count、session_held_mamba_slots、get_pool_stats 及其辅助方法(_get_token_info、_get_hisparse_token_info、_get_mamba_token_info、_get_swa_token_info),同时移除了不再需要的 dataclasses 和 PoolStats 类型的 import(后者有潜在问题,详见评论区精华)。
  2. 添加到 SchedulerPoolStatsObserver:在 python/sglang/srt/managers/scheduler_components/pool_stats_observer.py 中,将这些方法以实例方法形式添加到 @dataclass 装饰的 SchedulerPoolStatsObserver 类中,去掉 @staticmethod 装饰器,将 self 类型注解从显式 "SchedulerPoolStatsObserver" 简化为隐式 self,内部对同一类方法的调用从 SchedulerRuntimeCheckerMixin.method(self) 改为 self.method()。
  3. 更新调用点:在 scheduler.py(get_new_batch_prefill、on_idle)、scheduler_runtime_checker_mixin.py 的剩余方法(_check_full_pool、_check_swa_pool 等中的调用)以及 observability/scheduler_metrics_mixin.py(report_prefill_stats、report_decode_stats、get_loads 等)中,将形如 self.get_pool_stats(self.pool_stats_observer, ...) 的调用全部改为 self.pool_stats_observer.get_pool_stats(...)。
  4. 清理 import:在 scheduler_runtime_checker_mixin.py 中,移除了对 SchedulerPoolStatsObserver 和 PoolStats 的导入(但 reviewer 指出 PoolStats 仍被剩余类型提示使用,需要补回)。
文件 模块 状态 重要度
python/sglang/srt/managers/scheduler_runtime_checker_mixin.py 调度器 modified 8.69
python/sglang/srt/managers/scheduler_components/pool_stats_observer.py 池统计 modified 8.74
python/sglang/srt/observability/scheduler_metrics_mixin.py 观测 modified 5.81
python/sglang/srt/managers/scheduler.py 调度器 modified 5.55

关键符号

streaming_session_count active_pool_idxs session_held_tokens session_held_full_tokens session_held_swa_tokens session_held_req_count session_held_mamba_slots get_pool_stats _get_token_info _get_hisparse_token_info _get_mamba_token_info _get_swa_token_info

关键源码片段

python/sglang/srt/managers/scheduler_components/pool_stats_observer.py core-logic

新方法被添加到此文件的 SchedulerPoolStatsObserver 类,成为池统计采样的新家。是本次重构的核心接收端。

    def streaming_session_count(self) -> int:
        """返回当前处于 streaming 状态的 session 数量。"""
        return sum(
            1
            for session in self.session_controller.sessions.values()
            if session.streaming
        )
​
    def active_pool_idxs(self) -> set:
        """Pool idxs currently owned by reqs in last_batch / running_batch.        Used to decide which session slots' KV is owned by batch reqs
        (and thus counted via uncached_size, not session_held).
        """
        idxs = set()
        for batch in [self.get_last_batch(), self.get_running_batch()]:
            if batch is None or batch.is_empty():
                continue
            for req in batch.reqs:
                if req.req_pool_idx is not None:
                    idxs.add(req.req_pool_idx)
        return idxs
​
    def session_held_tokens(self) -> int:
        return self.tree_cache.session_held_tokens(self.active_pool_idxs())
​
    # ... 其他 session_held_* 方法类似 ...
​
    def get_pool_stats(self) -> PoolStats:
        """汇总所有 token 池的统计信息。"""
        if self.is_hybrid_swa:
            pool_stats = self._get_swa_token_info()
        elif self.is_hybrid_ssm:
            pool_stats = self._get_mamba_token_info()
        else:
            pool_stats = self._get_token_info()
​
        if self.enable_hisparse:
            pool_stats = self._get_hisparse_token_info(pool_stats)
​
        # swa + ssm can coexist: overlay mamba fields onto swa stats
        if self.is_hybrid_ssm:
            mamba_stats = self._get_mamba_token_info()
            pool_stats.is_hybrid_ssm = True
            pool_stats.mamba_num_used = mamba_stats.mamba_num_used
            pool_stats.mamba_usage = mamba_stats.mamba_usage
            pool_stats.mamba_available_size = mamba_stats.mamba_available_size
            pool_stats.mamba_evictable_size = mamba_stats.mamba_evictable_size
​
        return pool_stats

评论区精华

缺失 PoolStats 导入 正确性

gemini-code-assist[bot] 指出,在删除 from ... pool_stats_observer import PoolStats 后,文件中剩余的类型提示仍使用 PoolStats,这会导致静态分析错误。建议在 TYPE_CHECKING 块中恢复导入。

结论:需要添加 from ... pool_stats_observer import PoolStats 到 TYPE_CHECKING 块中,以修复静态分析。 · unresolved

冗余调用 _get_mamba_token_info 性能

在 get_pool_stats 中,当 is_hybrid_ssm 为 True 但 is_hybrid_swa 为 False 时,pool_stats 已经在 elif 分支中由 _get_mamba_token_info 初始化,但后续的叠加逻辑再次调用 _get_mamba_token_info,造成冗余。reviewer 推荐将叠加条件改为 if self.is_hybrid_ssm and self.is_hybrid_swa。

结论:应优化为仅在同时启用两种模式时执行叠加,避免重复计算。 · unresolved

缺少返回类型注解 style

_get_mamba_token_info 方法没有返回类型注解,reviewer 建议添加 -> PoolStats。

结论:添加返回类型注解以符合项目惯例。 · unresolved

风险与影响

  1. 导入缺失:如上所述,scheduler_runtime_checker_mixin.py 中 PoolStats 类型导入被移除,可能导致类型检查失败或潜在运行错误(若该类型在运行时被使用)。目前该文件剩余方法仅在类型提示中使用 PoolStats,且被 TYPE_CHECKING 条件包裹,不会在运行时出错,但仍需修复以保持静态分析健康。
  2. 冗余调用:在 PoolStatsObserver.get_pool_stats() 中,当 is_hybrid_ssm 且非 is_hybrid_swa 时重复计算 token stats,虽不影响正确性,会带来可忽略的性能开销(实际可能微不足道)。
  3. 测试覆盖:本次重构未包含测试变更,但方法体是字节等价的,风险较低。但仍应确保回归测试覆盖池统计逻辑。
  1. 对用户:无行为变化,所有统计输出和调度决策与重构前一致。
  2. 对系统:池统计逻辑更集中,便于后续维护和扩展。SchedulerRuntimeCheckerMixin 的删除为后续该 mixin 退休铺平道路。
  3. 对团队:需注意在合并后续 PR 时,若同时修改这些方法可能产生冲突。建议 CI 覆盖池统计的 E2E 测试。
导入缺失 重复调用 缺少类型注解

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论