Prhub

#1842 Split RolloutManager into InferenceController and RolloutExecutor

原始 PR 作者 fzyzcjy 合并时间 2026-09-04 12:20 文件变更 3 提交数 1 评论 1 代码增减 +241 / -215

执行摘要

将 RolloutManager 拆分为 InferenceController 与 RolloutExecutor,实现职责分离。

为了解决 RolloutManager 职责过多、难以维护和扩展的问题,并为未来异步和多训练器场景做准备。PR body 中明确指出这是追踪 issue #1837 “refactoring and enhancements”的一部分,旨在通过拆分使架构更清晰。

此 PR 是一个有意义的架构重构,值得精读,尤其关注两个新类之间的职责边界划分是否清晰合理。设计决策上,将推理基础设施管理与数据执行流分离,符合单一职责原则,是合理的架构演进。建议结合后续 PR#1843 一起阅读,以获得完整的重构图景。

讨论亮点

PR 本身仅有一条审查评论,由 guapisolo 批准,评论内容为:“w/ 1843 the split is complete”,表明此 PR 与后续 PR#1843 结合后,拆分工作才算完整。这暗示了本次拆分是渐进式重构的第一步,后续还有连接与异步化的工作。

实现拆解

  1. 提取基础设施管理层:新增 miles/ray/rollout/inference_controller.py 文件,定义 InferenceController 类。该类从原 RolloutManager 中提取了所有与推理服务器生命周期相关的逻辑,包括服务器启动 (__init__ 中调用 start_rollout_servers)、健康监控 (_health_monitors)、引擎管理 (get_updatable_engines_and_lock, _get_updatable_server)、权重卸载/加载 (offload, onload 系列方法)、单元启停 (start_cell, stop_cell) 以及 CI 故障注入等。
  2. 精简执行层:将原 miles/ray/rollout/rollout_manager.py 重命名为 miles/ray/rollout/rollout_executor.py,并将类名从 RolloutManager 改为 RolloutExecutor。在该类中移除了所有被提取到 InferenceController 的代码(如服务器管理、健康监控、引擎管理、卸载/加载等),使其专注于数据生成 (generate, eval)、数据转换 (convert_samples_to_train_data) 和检查点保存/加载 (save, load) 等执行逻辑。其 __init__ 方法不再接受 pg (PlacementGroup) 参数,_eval_fleet 改为可选属性。
  3. 更新测试以适配新结构:同步修改 tests/fast/rollout/test_checkpoint_eval.py,将测试中对 rollout_manager_mod.RolloutManager 的所有引用(包括模块导入、object.__new__ 实例化、monkeypatch.setattr 目标)更新为 rollout_executor_mod.RolloutExecutor,确保测试覆盖到重命名后的执行器类。
文件 模块 状态 重要度
miles/ray/rollout/inference_controller.py 推理控制器 added 9.21
miles/ray/rollout/rollout_executor.py 执行器 renamed 9.18
tests/fast/rollout/test_checkpoint_eval.py 评估测试 modified 4.91

关键符号

InferenceController.__init__ InferenceController.prepare_rollout InferenceController.prepare_eval InferenceController.dispose InferenceController.offload InferenceController.onload InferenceController.get_updatable_engines_and_lock RolloutExecutor.__init__ RolloutExecutor.generate RolloutExecutor.eval

关键源码片段

miles/ray/rollout/inference_controller.py entrypoint

新增的核心类,从原 RolloutManager 中提取了所有推理基础设施管理职责,是本次重构的主要产出。

class InferenceController:
    def __init__(self, args, pg):
        """初始化推理控制器,管理推理服务器和健康监控。"""
        self.pg = pg
        self.args = args
​
        # 如果仅调试训练,则不启动推理服务器
        if self.args.debug_train_only:
            self.servers: dict[str, RolloutServer] = {}
        else:
            # 启动推理服务器集群,并注册到 dashboard 和会话服务器
            self.servers = start_rollout_servers(args, pg)
            dashboard_hooks.register_router(args)
            init_http_client(args)
            start_session_server(args)
​
        # 用于协调引擎权重更新的锁
        self.rollout_engine_lock = Lock.options(num_cpus=1, num_gpus=0).remote()
        self.rollout_id = -1
        # 初始化评估舰队(如果配置了评估 GPU)
        self.eval_fleet = EvalFleet(args, srv=self.servers["eval"]) if args.eval_num_gpus > 0 else None
​
        # 健康监控器列表,用于容错
        self._health_monitors = []
        self._rollout_ft_enabled = self.args.use_fault_tolerance and "rollout" in self.args.ft_components
        self._ci_fault_injection_pending = False
        if not self.args.debug_train_only and self._rollout_ft_enabled:
            # 为每个推理服务器组创建并启动健康监控器
            for srv in self.servers.values():
                for group in srv.server_groups:
                    monitor = RolloutHealthMonitor(group, args)
                    monitor.start()
                    self._health_monitors.append(monitor)
            self._ci_fault_injection_pending = self.args.ci_test
miles/ray/rollout/rollout_executor.py rename-or-move

原 RolloutManager 重命名并精简后的新类,专注于数据生成与转换执行逻辑。

@ray.remote
class RolloutExecutor:
    """负责运行 rollout 并将 rollout 数据转换为训练数据的类。"""
​
    def __init__(self, args):
        # 初始化日志、事件日志、对象存储等(与之前类似)
        event_logger_checkpoint.restore(args)
        configure_logger(args, source=RolloutManagerProcessIdentity())
        self.args = args
        self.weight_version: int | None = None
        # ... 其他初始化 ...
​
        # 加载数据源和 rollout 函数
        data_source_cls = load_function(self.args.data_source_path)
        self.data_source = data_source_cls(args)
        # ... 加载 rollout 函数的逻辑 ...
​
        self.rollout_id = -1
        self._eval_lock = asyncio.Lock()
        self._eval_fleet: EvalFleet | None = None # 不再在此处初始化,可能由外部设置
        self._metric_checker = MetricChecker.maybe_create(args)
        # 注意:不再包含 servers、health_monitors、engine_lock 等基础设施相关属性
​
    async def generate(self, rollout_id):
        """执行一次 rollout 数据生成并转换为训练数据。"""
        start_time = time.time()
        self.rollout_id = rollout_id
        # ... 后续是具体的数据生成、保存、转换、拆分逻辑 ...

评论区精华

拆分的完整性与后续步骤 设计

审查者 guapisolo 批准 PR 时评论:“w/ 1843 the split is complete”,指出本次拆分是系列重构的一部分,需要与 PR#1843 结合才能视为完成。

结论:确认本次拆分是初步但必要的一步,后续 PR#1843 将负责连接两个新类并使 API 异步化。 · 已解决

风险与影响

  1. 架构变更风险:作为核心 rollout 模块的重大重构,RolloutManager 的职责被一分为二,所有调用方(包括 train.py, train_async.py 等入口脚本)都需要相应调整。虽然本 PR 只改了三个文件,但其成功依赖于后续 PR(如 #1843)正确完成连接与适配。
  2. 功能遗漏风险:需要确保从 RolloutManager 中移除的代码(如 _health_monitors 管理、prepare_rollout 等)被完整且无误地迁移到 InferenceController。从 diff 看,迁移看似直接,但需关注状态(如 self.rollout_id)在新类中如何初始化与传递。
  3. 测试覆盖:测试文件已同步更新,但主要覆盖了 RolloutExecutor 的评估逻辑。对于 InferenceController 的新逻辑(特别是健康监控、引擎恢复等),可能需要后续测试补充覆盖。

直接影响:所有使用 RolloutManager 的模块和脚本需要适配新的类名和职责划分。这主要影响训练入口脚本(train.py, train_async.py 等)和与之交互的调度层。
间接影响:为后续重构(PR#1843, #1844)铺平了道路,使得异步 API 和方法重命名成为可能,长远来看有利于系统维护和功能扩展。
团队影响:工程师需要理解新的架构划分(Controller 管基础设施,Executor 管数据),这有助于未来更清晰地定位问题和进行开发。

核心路径变更 架构重构

关联 Issue

#1837 Tracking issue for refactoring and enhancements

完整报告

参与讨论