Prhub

#5760 [perf] feat: support partial-token window profiling for rollout in verl

原始 PR 作者 mengchengTang 合并时间 2026-06-05 10:34 文件变更 13 提交数 1 评论 3 代码增减 +263 / -83

执行摘要

新增 rollout 部分 token profiling 窗口配置

在长上下文强化学习中,对完整 decode 进行 profiling 会产生非常大的 trace 文件。仅收集关键的 token 区间(key token range)可以减少数据体积并加快解析分析速度。verl 需要暴露 profile_token_start / profile_token_end 参数来支持窗口式 profiling,并映射到 vLLM 和 SGLang 后端。

该 PR 值得精读,特别是 config.py 中字段添加与验证的方式,以及 build_vllm_profiler_args 中的映射逻辑。设计上采用了半开区间,与后端惯用参数一致。代码组织上将 TorchMemoryProfiler 独立成模块,体现了良好的模块化思想。建议在后续重构中提取公共验证函数。

讨论亮点

代码审查中,gemini-code-assist[bot] 指出 TorchProfilerToolConfigNPUToolConfig 中的验证逻辑完全重复,建议提取为模块级辅助函数 _validate_profiling_window(start, stop) 以遵循 DRY 原则。tardis-key 询问在 fullyasync 模式下的测试情况,mengchengTang 回复已验证通过,fullyasync 调用的是 replica 的 profiler 接口,无需额外改动即可支持。

实现拆解

  1. 配置字段定义:在 TorchProfilerToolConfigNPUToolConfig 中添加 profile_token_startprofile_token_end 可选 int 字段,并在 __post_init__ 中增加验证逻辑,确保 start < end 且非负。
  2. 后端参数映射:在 build_vllm_profiler_args 中将 profile_token_start 映射为 delay_iterations,将 end-start 映射为 max_iterations;在 build_sglang_profiler_args 中映射为 start_stepnum_steps
  3. 代码抽取:将原先内联在 profile.pyTorchMemoryProfiler 类移至独立的 torch_memory_profile.py 文件,并更新 profile.py 中的导入为延迟导入,同时清理不再需要的 memory_utils 导入。
  4. 配置同步:更新 rollout.yamlprofiler.yaml 模板,加入新字段的默认值 null;同步更新所有 _generated_*.yaml 以保持配置一致性。
  5. 测试与文档:在 test_server_profiler.py 中新增 4 个测试用例覆盖 vLLM、SGLang 以及 NPU 配置下的窗口映射。更新 ascend_profiling_en.rsttorch_profiling.md 文档说明新参数用法。
文件 模块 状态 重要度
verl/utils/profiler/torch_memory_profile.py 内存分析 added 8.4
verl/utils/profiler/profile.py 分析器 modified 8.06
verl/utils/profiler/config.py 配置层 modified 7.21
tests/utils/test_server_profiler.py 测试 modified 6.56
verl/trainer/config/rollout/rollout.yaml 配置 modified 4.1
verl/trainer/config/profiler/profiler.yaml 配置 modified 4.07
docs/ascend_tutorial/dev_guide/performance/ascend_profiling_en.rst 文档 modified 2.3
docs/perf/torch_profiling.md 文档 modified 2.23

关键符号

build_vllm_profiler_args build_sglang_profiler_args TorchMemoryProfiler.start TorchMemoryProfiler.stop test_build_vllm_profiler_args_with_profile_window test_build_sglang_profiler_args_with_profile_window

关键源码片段

verl/utils/profiler/config.py core-logic

核心配置变更:在 TorchProfilerToolConfig 和 NPUToolConfig 中添加窗口字段及验证,在 build_vllm_profiler_args 和 build_sglang_profiler_args 中实现参数映射。

@dataclass
class TorchProfilerToolConfig(BaseConfig):
    """Torch profiler tool config."""
    contents: list[str] = field(default_factory=list)
    discrete: bool = False
    # Start collecting profiler data from this response-token index.
    # None means collect from the beginning.
    profile_token_start: Optional[int] = None
    # Stop collecting profiler data at this response-token index (exclusive).
    # None means collect until the end.
    profile_token_end: Optional[int] = None
    name: str = "torch"
​
    def __post_init__(self) -> None:
        __support_contents = ["cuda", "cpu", "memory", "shapes", "stack"]
        for content in self.contents:
            assert content in __support_contents, (
                f"Profiler contents only supports {__support_contents}, but gets {content}"
            )
        assert isinstance(self.contents, list), \
            f"Profiler contents must be of type list, got {type(self.contents)}"
        start = self.profile_token_start
        stop = self.profile_token_end
        for name, value in (("profile_token_start", start), ("profile_token_end", stop)):
            if value is not None:
                assert isinstance(value, int), f"{name} must be int or None, got {type(value)}"
                assert value >= 0, f"{name} must be >= 0, got {value}"
        if start is not None and stop is not None:
            assert stop > start, f"profile_token_end must be > profile_token_start, got start={start}, stop={stop}"
​
​
def build_vllm_profiler_args(profiler_config, tool_config, rank):
    # ... existing code ...
    profile_token_start = getattr(tool_config, "profile_token_start", None)
    profile_token_end = getattr(tool_config, "profile_token_end", None)
​
    # vLLM uses 0 to indicate immediate start / no upper bound.
    delay_iterations = profile_token_start if profile_token_start is not None else 0
    max_iterations = (profile_token_end - profile_token_start) \
                     if (profile_token_start is not None and profile_token_end is not None) else 0
    # ... continue building args ...

评论区精华

验证逻辑重复 设计

gemini-code-assist[bot] 指出 TorchProfilerToolConfig 和 NPUToolConfig 中的 profile_token_start/end 验证逻辑完全重复,违反了 DRY 原则,建议提取公共辅助函数 _validate_profiling_window(start, stop)。

结论:PR 作者未回复此建议,目前验证逻辑仍有两份拷贝。建议后续迭代中提取公共函数。 · unresolved

fullyasync 兼容性 测试

tardis-key 询问在 fullyasync 模式下是否测试过该配置。

结论:mengchengTang 回复已验证,fullyasync 调用的是 replica 的 profiler 接口,无需额外改动即可支持。 · 已解决

风险与影响

  1. 配置兼容性风险:新字段默认为 None,不会影响已有配置,但用户如果误设置导致 start >= end 或负数,验证会报错,属预期行为。
  2. 验证逻辑代码重复:两个 Config 类的验证逻辑完全一致,后续若修改窗口语义(如改为闭区间)需要同步两处,有遗漏风险。建议按审查意见提取公共函数。
  3. 后端映射语义需同步:vLLM 和 SGLang 的 delay_iterations/max_iterations 语义可能随版本变化,需要保持同步。
  4. 模块抽取影响外部引用TorchMemoryProfilerprofile.py 移出,但 profile.py 中已调整为延迟导入,且该符号被重新导出,应保持兼容。

用户:现在可以通过 actor_rollout_ref.rollout.profiler.tool_config.npu.profile_token_start=20 actor_rollout_ref.rollout.profiler.tool_config.npu.profile_token_end=80 精确控制 profiling 收集窗口,减少 trace 体积。系统:无性能影响,功能仅在启用 profiler 时生效。团队:需要维护两套验证逻辑,建议后续重构。

验证逻辑代码重复 后端映射语义需同步 配置兼容性依赖默认值

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论