Prhub

#6069 [fully_async] fix: fix rollouter/idle compute in async-mode

原始 PR 作者 chenjiaoAngel 合并时间 2026-04-20 17:50 文件变更 1 提交数 3 评论 1 代码增减 +7 / -3

执行摘要

修复完全异步 rollouter 中空闲比计算逻辑,避免除零错误和时序不一致。

根据PR body中的测试截图和描述,当rollout速度极慢时,rollouter/idle指标会出现大于1的情况,这不符合指标定义(空闲比应在0-1之间)。作者通过实验发现此问题,并提交修复以确保异步训练监控指标的准确性。

该PR代码简洁,修复了明确的边界条件问题,值得快速浏览以了解完全异步训练中监控指标的计算细节。重点关注reset_staleness方法中时间戳处理的改进方式。

讨论亮点

reviewer gemini-code-assist[bot] 指出原实现存在代码重复和潜在的ZeroDivisionError风险(当time.time()self.step_start_time相同时)。建议重构为使用单一时间戳以确保计算一致性,并设置默认值提高安全性。但最终提交的修复采用了更直接的边界条件处理,未完全采纳重构建议。

实现拆解

  1. 核心逻辑修复:修改verl/experimental/fully_async_policy/fully_async_rollouter.py中的reset_staleness方法。
    - 将rollout_version_time的计算改为max(time.time() - self.step_start_time, 1e-6),避免除零错误。
    - 增加条件判断:当self.idle_start_time > self.step_start_time时,按原公式计算空闲比;否则将rollout_active_time设为rollout_version_time,空闲比设为0。
  2. 影响范围:此变更仅影响完全异步训练模式下的监控指标计算,不改变训练算法本身。修复后空闲比始终保持在[0,1]范围内,且避免了极端情况下的计算错误。
  3. 测试配套:本次变更未包含测试文件,但PR body中提供了实际运行截图作为验证。
文件 模块 状态 重要度
verl/experimental/fully_async_policy/fully_async_rollouter.py 异步策略 modified 5.51

关键符号

reset_staleness

关键源码片段

verl/experimental/fully_async_policy/fully_async_rollouter.py core-logic

这是唯一被修改的源码文件,包含完全异步 rollouter 的核心逻辑,修复了空闲比计算的关键 bug。

async def reset_staleness(self):
    """
    Reset staleness samples after parameter update.
    Returns timing_raw dictionary for metrics.
    """
    async with self.lock:
        self.paused = False
        self.condition.notify_all()
        # 每次参数更新后,重置陈旧样本计数
        self.staleness_samples = len(self.active_tasks) + await self.message_queue_client.get_queue_size()
        timing_raw = {}
        # 关键修复 1:确保版本时间至少为 1 微秒,避免除零错误
        rollout_version_time = max(time.time() - self.step_start_time, 1e-6)
        # 关键修复 2:仅当空闲开始时间晚于步骤开始时间时才计算空闲比
        if self.idle_start_time > self.step_start_time:
            rollout_active_time = self.idle_start_time - self.step_start_time
            idle_ratio = 1 - rollout_active_time / rollout_version_time
        else:
            # 否则认为整个版本周期都在活动,空闲比为 0
            rollout_active_time = rollout_version_time
            idle_ratio = 0
        timing_raw["fully_async/rollouter/active_time"] = rollout_active_time
        timing_raw["fully_async/rollouter/version_time"] = rollout_version_time
        timing_raw["fully_async/rollouter/idle_ratio"] = idle_ratio
​
        print(
            f"[FullyAsyncRollouter][Public][reset_staleness] "
            f"reset staleness_samples to: {self.staleness_samples} "
            f"idle_ratio: {timing_raw['fully_async/rollouter/idle_ratio']:.4f}"
        )
        self.step_start_time = time.time()
    return timing_raw

评论区精华

空闲比计算逻辑的健壮性改进 正确性

reviewer 指出原实现存在代码重复和潜在的 ZeroDivisionError 风险,建议使用单一时间戳并设置默认值。

结论:提交者采用了更直接的边界条件处理(增加 max 保护和条件判断),未完全采纳重构建议,但解决了核心问题。 · 已解决

风险与影响

  1. 回归风险低:变更仅影响指标计算逻辑,不涉及核心训练流程。
  2. 性能影响可忽略:增加的条件判断和max操作开销极小。
  3. 兼容性无影响:指标格式保持不变,仅修正计算错误。
  4. 潜在风险:如果idle_start_timestep_start_time的时序关系在其他场景下异常,可能导致指标计算不准确,但不会引发崩溃。
  1. 对用户影响:完全异步训练用户将获得更准确的rollouter空闲比监控指标,有助于诊断训练瓶颈。
  2. 对系统影响:修复了指标计算边界条件,提升系统监控的健壮性。
  3. 对团队影响:此修复针对实验性功能(fully_async),不影响主训练流程,维护成本低。
边界条件处理 实验性功能

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论