执行摘要
- 一句话:修复FP8在async RL和多节点rollout中的bug
- 推荐动作:值得关注的设计决策:将全局副作用改为opt-in环境变量,避免影响不相关场景;FP8配置的安全访问方式(先调用父类再使用self.config)可以作为代码规范。该PR适合rollout和trainer相关开发人员阅读。
功能与动机
PR描述指出两个修复点:'Fix FP8 config code path with async rl which uses standalone mode' 以及 'Add TLLM_DISABLE_NVLS_MNNVL to limit these env changes to TRTLLM async RL only, as NCCL_MNNVL_ENABLE=0 cause multinode rollout hang in TRTLLM sync rl.' 即修复FP8配置路径,并将NCCL环境变量限制在异步RL下,避免同步RL挂起。
实现拆解
- 修复FP8下dummy load_format绕过 (
trtllm_async_server.py):在TRTLLMHttpServer.__init__中,原来对非HYBRID且load_format为dummy时会强制设为auto;现在增加条件,当quantization == "fp8"时保留dummy,因为FP8权重在首次同步时填充,没有磁盘检查点。
- 修复KvCacheConfig构建 (
trtllm_async_server.py):在launch_server中,将原本直接传递给KvCacheConfig构造函数的关键字参数改为先构建字典再展开,避免后续**engine_kwargs展开时覆盖KvCacheConfig对象。
- 调整ServerAdapter初始化顺序并添加null检查 (
trtllm_rollout.py):在ServerAdapter.__init__中,将super().__init__调用提前,以便通过self.config访问quantization;同时添加对self.model_config.hf_config is not None的保护检查,避免AttributeError。
- 将GB200 NCCL WAR改为opt-in (
constants_ppo.py):原代码在SM major>=10时无条件禁用NVLS和MNNVL,现改为仅当环境变量TLLM_DISABLE_NVLS_MNNVL为1时才设置,限制在async RL场景。
- 更新fully_async shell脚本 (两个shell脚本):添加
export TLLM_DISABLE_NVLS_MNNVL=1和FP8启用注释,确保async RL用户在GB200上正常训练。
关键文件:
verl/workers/rollout/trtllm_rollout/trtllm_async_server.py(模块 rollout;类别 source;类型 core-logic;符号 TRTLLMHttpServer.init, TRTLLMHttpServer.launch_server): 核心逻辑变更:修复FP8下dummy load_format的绕过逻辑,并优化KvCacheConfig构建方式
verl/trainer/constants_ppo.py(模块 训练配置;类别 source;类型 core-logic;符号 _gb200_nccl_env, PPO_RAY_RUNTIME_ENV): 将GB200 NCCL WAR从无条件禁用改为通过环境变量TLLM_DISABLE_NVLS_MNNVL opt-in,避免影响sync RL
verl/workers/rollout/trtllm_rollout/trtllm_rollout.py(模块 rollout;类别 source;类型 core-logic;符号 ServerAdapter.init): 修复FP8量化配置设置:将super().__init__提前,并添加hf_config的None检查
verl/experimental/fully_async_policy/shell/grpo_30b_a3b_base_math_megatron_8_8_mis_trtllm.sh(模块 启动脚本;类别 other;类型 configuration): 为fully_async shell添加TLLM_DISABLE_NVLS_MNNVL环境变量以及FP8启用注释
verl/experimental/fully_async_policy/shell/grpo_8b_base_math_megatron_4_4_trtllm.sh(模块 启动脚本;类别 other;类型 configuration): 为fully_async shell添加TLLM_DISABLE_NVLS_MNNVL环境变量以及FP8启用注释
关键符号:TRTLLMHttpServer.init, TRTLLMHttpServer.launch_server, ServerAdapter.init
关键源码片段
verl/workers/rollout/trtllm_rollout/trtllm_async_server.py
核心逻辑变更:修复FP8下dummy load_format的绕过逻辑,并优化KvCacheConfig构建方式
# trtllm_async_server.py 关键改动:FP8 量化绕过 dummy load_format 检查
def __init__(self, ...):
...
# 非 HYBRID 模式 + load_format=dummy 通常需要从磁盘加载(设为 auto)
# 例外:FP8 没有磁盘检查点,权重在第一次同步时填充,因此保持 dummy
if (
self.rollout_mode != RolloutMode.HYBRID
and self.config.load_format == "dummy"
and self.config.quantization != "fp8" # 新增条件,避免 FP8 被错误设为 auto
):
logger.warning(...)
self.config.load_format = "auto"
async def launch_server(self):
...
# 将 KvCacheConfig 参数先构建字典再展开,防止 **engine_kwargs 覆盖整个对象
kv_cache_kwargs = {
"enable_block_reuse": self.config.enable_prefix_caching,
"free_gpu_memory_fraction": self.config.gpu_memory_utilization,
**kv_cache_overrides,
}
kv_cache_config = KvCacheConfig(**kv_cache_kwargs)
verl/trainer/constants_ppo.py
将GB200 NCCL WAR从无条件禁用改为通过环境变量TLLM_DISABLE_NVLS_MNNVL opt-in,避免影响sync RL
# constants_ppo.py 关键改动:GB200 NCCL 环境变量从全局禁用到 opt-in
_major, _ = get_device_capability()
# 注释:Opt-in GB200 NCCL WAR:设置 TLLM_DISABLE_NVLS_MNNVL=1 来禁用 Blackwell 上的
# NCCL_NVLS_ENABLE 和 NCCL_MNNVL_ENABLE。async-RL Megatron 在无 IMEX 的 GB200 节点上需要此设置,
# 否则 mbridge export_weights -> all_gather 会引发 NCCL 801 错误。
_gb200_nccl_env = {}
if (_major or 0) >= 10 and os.environ.get("TLLM_DISABLE_NVLS_MNNVL", "0") == "1":
_gb200_nccl_env = {"NCCL_NVLS_ENABLE": "0", "NCCL_MNNVL_ENABLE": "0"}
PPO_RAY_RUNTIME_ENV = {
"env_vars": {
...
**_gb200_nccl_env, # 只有条件满足时才注入禁用环境变量
},
}
verl/workers/rollout/trtllm_rollout/trtllm_rollout.py
修复FP8量化配置设置:将super().__init__提前,并添加hf_config的None检查
# trtllm_rollout.py ServerAdapter.__init__ 关键改动
def __init__(self, config, model_config, device_mesh, replica_rank=-1):
# 先调用父类初始化,才能安全访问 self.config
super().__init__(config, model_config, device_mesh)
# FP8 量化配置:仅在启用 fp8 且 hf_config 存在时设置 quantization_config
if self.config.quantization == "fp8" and self.model_config.hf_config is not None:
FP8_BLOCK_QUANT_KWARGS = {
"activation_scheme": "dynamic",
"fmt": "e4m3",
"quant_method": "fp8",
"weight_block_size": [128, 128],
}
self.model_config.hf_config.quantization_config = dict(FP8_BLOCK_QUANT_KWARGS)
...
评论区精华
在trtllm_rollout.py上,gemini-code-assist建议使用self.config.quantization代替config.get(),并添加hf_config为None的检查。该建议被采纳,最终代码中已体现。
- FP8配置安全访问和null检查 (correctness): 已采纳,在最终代码中体现
风险与影响
- 风险:回归风险:在sync RL场景下,之前的全局NCCL禁用被移除,sync RL用户不再受NCCL_MNNVL_ENABLE=0导致的挂起影响,但async RL用户需要显式设置TLLM_DISABLE_NVLS_MNNVL=1才能避免GB200的NCCL问题。FP8配置路径:init顺序调整可能影响其他初始化代码,但改动后更安全,且只影响FP8用户。兼容性:环境变量新增,旧脚本无需修改,但若有自定义启动脚本未设置该环境变量,在GB200 async RL上可能遇到NCCL错误(之前被全局禁用掩盖)。
- 影响:用户影响:使用TRTLLM rollout且启用FP8量化的用户,async RL场景下的FP8加载逻辑得到修复;GB200多节点async RL用户需要添加环境变量才能避免挂起;sync RL用户不再受早期强制禁用的影响。系统影响:改动集中在rollout worker和trainer配置层,范围有限。团队影响:明确了对GB200平台的环境变量控制策略,为后续类似问题提供参考。
- 风险标记:多节点同步RL挂起修复, FP8配置路径变更, 环境变量控制策略, 回归风险
关联脉络
参与讨论