Prhub

#5631 [rollout] feat: enable Async RL for trtllm rollout

原始 PR 作者 hchings 合并时间 2026-05-07 15:31 文件变更 12 提交数 26 评论 20 代码增减 +1180 / -55

执行摘要

TRTLLM 异步 RL 完整流程实现

PR body 明确指出 "Requires verl's trtllm version to be updated to include https://github.com/NVIDIA/TensorRT-LLM/pull/12272 [Merged]",并说明 "this MR only enables e2e async RL functionalities for trtllm rollout and tested convergence"。目的是在 TRTLLM 后端实现与 vllm 对等的异步强化学习训练流程,解决之前只能同步 rollout 的限制,并经过收敛性验证。

建议精读 trtllm_async_server.py 中 abort/resume 实现及 _resolve_chat_stop_tokens 函数,理解聊天模型生成结束控制的细节;关注 trtllm_rollout.py 中 standalone 设备网格初始化策略;跟踪后续性能优化 PR 以形成完整评估。

讨论亮点
  1. 测试配置路径合理性(gemini-code-assist):指出 config_dir 依赖当前工作目录,建议用 pathlib 确定项目根。hchings 回复为沿用现有测试惯例,未修改。
  2. Ray 集群清理方式(gemini-code-assist):建议不要使用 subprocess.run(['ray', 'stop']) 以免干扰并行测试。hchings 认为测试环境无现有 Ray 集群,两者并用无妨,保留。
  3. Standalone 模式 use_gpu 设计(wuxibin89 ↔ hchings):wuxibin89 对 replica.py 中新增 _standalone_use_gpu 表示困惑,建议由 rollout_mode 推导。hchings 解释 TRTLLM 的 Ray 进程结构与 vllm 不同,需独立控制。最终 wuxibin89 仍要求简化,但未完全达成一致。
  4. GB200 NCCL 环境变量范围(wuxibin89):担心 get_device_capability 在 Ascend NPU 上报错,后因 Ascend CI 通过而认为安全。
  5. 环境变量传播(tongyuantongyu):建议转发所有 TLLM_ 前缀环境变量给 Ray Actor。hchings 承诺后续 MR 处理,CI 已通过。
  6. CI 任务精简(wuxibin89):建议只保留 multi-replica 测试,移除 single-replica 和 fsdp2。hchings 在后续提交中调整。

实现拆解

  1. Abort/Resume 机制:在 verl/workers/rollout/trtllm_rollout/trtllm_async_server.py 中引入 asyncio.Event 控制生成暂停与恢复,映射 TRTLLM 的 pause_generation/resume_generation;新增 clear_kv_cache 实现、start_profile/stop_profile 集成 Nsys Profiler。
  2. TorchSampler 停止 token 修复:新增独立函数 _resolve_chat_stop_tokens,自动识别聊天模型附加停止 token(如 <|im_end|>),避免生成进入第二回合导致长度膨胀。
  3. Standalone 独立部署模式:在 trtllm_rollout.py 中,当 device_mesh 为 None 时通过 gloo 进程组构建 CPU 设备网格;获取 gpu_id 用于权重同步的 CUDA IPC 句柄创建。
  4. 权重更新与前缀缓存:在 trtllm_worker_extension.py 中新增 reset_prefix_cache 方法;添加临时类 RlhfWorkerExtension 替代上游尚未包含的 WorkerExtension(TODO 待 TRTLLM 版本升级后移除)。
  5. 环境兼容适配:在 verl/trainer/constants_ppo.py 中根据 GPU 计算代数(SM >= 10)自动禁用 NCCL_NVLS_ENABLENCCL_MNNVL_ENABLE,解决 GB200 集群 Megatron all_gather 崩溃。
  6. 测试与 CI:新增 tests/workers/rollout/rollout_trtllm/test_trtllm_abort.py 端到端测试;新增 .github/workflows/e2e_fully_async_policy_trtllm.yml 专用 CI 工作流;更新 tests/special_e2e/run_fully_async_policy.sh 脚本;补充 docs/workers/trtllm_worker.rst 文档说明。
文件 模块 状态 重要度
verl/workers/rollout/trtllm_rollout/trtllm_async_server.py TRTLLM 服务 modified 8.77
tests/workers/rollout/rollout_trtllm/test_trtllm_abort.py 测试 added 7.17
verl/workers/rollout/trtllm_rollout/trtllm_rollout.py Rollout 主类 modified 6.92
verl/workers/rollout/trtllm_rollout/trtllm_worker_extension.py 权重更新 modified 6.85
.github/workflows/e2e_fully_async_policy_trtllm.yml CI 配置 added 6.21
verl/trainer/constants_ppo.py 环境配置 modified 6.07
verl/workers/rollout/replica.py 副本管理 modified 5.52

关键符号

_resolve_chat_stop_tokens clear_kv_cache start_profile stop_profile reset_prefix_cache wait_for_engine_idle test_trtllm_abort

关键源码片段

verl/workers/rollout/trtllm_rollout/trtllm_async_server.py core-logic

核心变更文件:新增 `_resolve_chat_stop_tokens` 函数、`clear_kv_cache`、`start_profile`/`stop_profile` 方法;添加 abort/resume 逻辑通过 `_generation_allowed` 事件控制生成暂停与恢复;引入 `DistProfiler` 支持。

# verl/workers/rollout/trtllm_rollout/trtllm_async_server.pyfrom verl.utils.profiler import DistProfiler
​
​
def _resolve_chat_stop_tokens(model_config) -> tuple[int, list[int]]:
    """Return (end_id, stop_token_ids) for TorchSampler.    Both TRTLLM's samplers stops only on end_id.  For chat-format prompts the model
    naturally ends each assistant turn with a chat-end token (e.g. <|im_end|>
    for Qwen, <|eot_id|> for Llama-3) that is *different* from the base-model
    eos_token_id.  If end_id is set to the base eos the sampler ignores the
    chat-end token and the model loops into a second turn, inflating response
    lengths until max_tokens is hit.    For models without a distinct chat-end token the return values are
    identical to the current default (end_id = hf_config.eos_token_id).
    """
    eos_token_id = model_config.hf_config.eos_token_id
    # 统一为列表形式
    all_stop_ids: list[int] = list(eos_token_id) if isinstance(eos_token_id, list) else [eos_token_id]
​
    # 合并 generation_config 中的额外 eos token
    if model_config.generation_config is not None:
        gen_eos = model_config.generation_config.eos_token_id
        if gen_eos is not None:
            for t in gen_eos if isinstance(gen_eos, list) else [gen_eos]:
                if t not in all_stop_ids:
                    all_stop_ids.append(t)
​
    chat_end_id = None
    # 检测聊天模板特有停止 token,如 <|im_end|>、<|eot_id|>
    if model_config.tokenizer is not None:
        _chat_stop_strings = ["<|im_end|>", "<|eot_id|>", "<|end_of_turn|>"]
        _added_vocab = model_config.tokenizer.get_added_vocab()
        for stop_str in _chat_stop_strings:
            if stop_str in _added_vocab:
                tid = _added_vocab[stop_str]
                if tid not in all_stop_ids:
                    all_stop_ids.append(tid)
                if chat_end_id is None:
                    chat_end_id = tid # 将首个 match 的 chat-end id 设为 end_id,防止二次生成
​
    primary_end_id = chat_end_id if chat_end_id is not None else eos_token_id
    logger.warning(f"TRT-LLM stop token IDs: {all_stop_ids}, end_id: {primary_end_id}")
    return primary_end_id, all_stop_ids
verl/workers/rollout/trtllm_rollout/trtllm_worker_extension.py core-logic

新增 `reset_prefix_cache` 方法确保权重更新后清除前缀缓存;添加临时类 `RlhfWorkerExtension` 替代上游未包含的 `WorkerExtension`,提供 `wait_for_engine_idle` 接口。

# verl/workers/rollout/trtllm_rollout/trtllm_worker_extension.py
​
    def reset_prefix_cache(self) -> None:
        """Invalidate the KV cache prefix reuse state after weight updates."""
        # 权重更新后必须清除前缀缓存,否则旧前缀失效导致错误复用
        self.engine.reset_prefix_cache()
​
​
# TODO: remove this class and revert the non-VLM path in trtllm_async_server.py
# to use "tensorrt_llm.llmapi.rlhf_utils.WorkerExtension" once verl's TRT-LLM version
# is bumped to include https://github.com/NVIDIA/TensorRT-LLM/pull/13784.
class RlhfWorkerExtension(TrtllmWorkerExtension):
    """Minimal extension of TRT-LLM's WorkerExtension for non-VLM RLHF models."""
​
    @control_action_decorator
    def wait_for_engine_idle(self) -> None:
        """Block until the engine has no active or queued requests."""
        # TRTLLM 引擎在无请求时自动空闲,此处无需额外等待
        pass

评论区精华

测试配置路径的健壮性 测试

gemini-code-assist 指出 `config_dir` 依赖工作目录,建议使用 `pathlib` 从项目根构建路径,避免 pytest 在不同目录执行时失败。

结论:hchings 回复为沿用现有测试惯例,未修改。 · 已解决

Ray 集群清理方式 测试

gemini-code-assist 认为 `subprocess.run(['ray', 'stop'])` 过于暴力,可能干扰并行测试,建议仅使用 `ray.shutdown()`。

结论:hchings 认为测试环境无已有 Ray 集群,两者并用可接受,保留。 · 已解决

Standalone 模式 use_gpu 设计 设计

wuxibin89 质疑 `_standalone_use_gpu` 方法的必要性,认为可由 `rollout_mode` 推导。hchings 解释 TRTLLM 的 Ray 进程构造与 vllm 不同,需单独控制。

结论:wuxibin89 仍要求简化,但未达成一致,最终 approved。 · ongoing

GB200 NCCL 环境变量影响范围 正确性

wuxibin89 担心 `get_device_capability` 在 Ascend NPU 上抛异常,且 `NCCL_NVLS_ENABLE` 等变量可能影响非 Blackwell 设备。

结论:Ascend CI 通过,wuxibin89 认为安全,不再修改。 · 已解决

TLLM_ 前缀环境变量传播 设计

tongyuantongyu 建议转发所有 `TLLM_` 前缀环境变量给 TRTLLM Ray Actor,便于 profiling 等配置。

结论:hchings 承诺在后续 MR 中处理,当前 CI 已通过。 · ongoing

风险与影响

  1. 外部依赖版本:需 TRTLLM 包含 #12272 补丁,否则功能不可用;临时类 RlhfWorkerExtension 依赖未来版本 #13784。
  2. GB200 环境变量影响NCCL_NVLS_ENABLE=0NCCL_MNNVL_ENABLE=0 可能覆盖用户自定义环境,影响非 Blackwell GPU 的 NCCL 性能。
  3. Abort/Resume 状态管理:生成暂停与恢复的时序并发控制可能引入竞态,导致请求丢失或重复。
  4. Standalone 模式较新:未像 colocated 模式经过广泛验证,可能暴露资源调度或进程生命周期问题。
  5. Rollout 性能瓶颈:PR 自知存在 Python 侧 _update_requests 开销(尤其 TorchSampler),可能拖慢整体训练吞吐。
  1. 用户影响:TRTLLM 用户可启用异步 RL(设置 rollout.mode=async),但需更新 TRTLLM 镜像至包含所需 commit;现有同步 rollout 不受影响。
  2. 系统影响:新增 CI 工作流增加 GPU 资源消耗;constants_ppo.py 中环境变量生效范围需监控,避免 Blackwell 以外集群性能下降。
  3. 团队影响:后续需跟踪 TRTLLM 版本升级移除临时 hack(RlhfWorkerExtension_standalone_use_gpu),并推进 rollout 性能优化。
外部依赖性变更 GB200 专用 NCCL 配置 核心状态管理 (abort/resume) Standalone 模式稳定度 Rollout 性能瓶颈未优化

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论