Prhub

#6702 [hardware] feat: add ROCm/HIP platform backend (PlatformROCm)

原始 PR 作者 xiaohong42 合并时间 2026-06-12 14:37 文件变更 4 提交数 1 评论 3 代码增减 +82 / -0

执行摘要

新增 AMD ROCm 平台后端 PlatformROCm

在 AMD ROCm 硬件上训练时,verl 原本会错误地选中 PlatformCUDA 后端,导致部分行为不兼容。本 PR 的目的是为 ROCm 提供专用的平台后端,通过继承 CUDA 后端并覆盖差异点,实现自动检测和正确配置,避免静默回退。

本 PR 设计清晰,通过类继承与最小化覆写策略为 AMD ROCm 添加了一等平台支持。建议关注以下设计点:1)PlatformROCm 继承 PlatformCUDA 而非完全从零实现,适用于 API 兼容性高的场景;2)通过 guard 在父类中短路避免误检测;3)在 rollout_env_vars 中提供用户可覆写的默认值。该模式可供后续新硬件平台(如 Intel GPU)参考。

讨论亮点

Gemini Code Assist Bot 在 review 中指出 platform_rocm.py 第 55 行,当 rocm-smi 找不到时返回 False 与注释中描述的“将 built torch.cuda 视为充分证据”矛盾,建议返回 True。作者 xiaohong42 回复表示保持 False 以与 PlatformCUDA.is_platform_available 行为一致,遵循最小改动原则,且父类在该场景下同样返回 False。该讨论无后续争议,状态为已解决。

实现拆解

  1. 新增 PlatformROCm 类 (verl/plugin/platform/platform_rocm.py):定义 PlatformROCm(PlatformCUDA),通过 @PlatformRegistry.register(platform="amd") 注册。覆盖以下方法:
    • vendor_name:返回 "amd"device_name 保持 "cuda" 因为 PyTorch ROCm 通过 hipify 暴露 torch.device("cuda")
    • is_platform_available:先检查 torch.version.hip 是否非空,以区分 AMD 和 NVIDIA;然后可选执行 rocm-smi 检测(镜像 PlatformCUDAnvidia-smi 检测逻辑)。
    • rollout_env_vars:继承父类后添加 SGLANG_USE_AITER 变量,默认启用 AITER 内核,允许用户通过环境变量覆盖。
    • ray_noset_envvars:添加 HIP_VISIBLE_DEVICESROCR_VISIBLE_DEVICES 的 NOSET 变量,防止 Ray 误管理。
  2. 修改 PlatformCUDA 平台检测 (verl/plugin/platform/platform_cuda.py):在 is_platform_available 方法中增加一段 guard,当 torch.version.hip is not None 时立即返回 False,确保 AMD 硬件不会误匹配到 CUDA 后端。
  3. 注册平台 (verl/plugin/platform/platform_manager.py):在模块底部的导入区添加 from .platform_rocm import PlatformROCm,使装饰器在模块加载时生效。
  4. 更新 CI 设备 API 检查 (tests/special_sanity/check_device_api_usage.py):将新文件 platform_rocm.py 加入 CUDA_KEYWORD_CHECK_WHITELIST,因为其内部合理引用了 torch.cuda
文件 模块 状态 重要度
verl/plugin/platform/platform_rocm.py 平台层 added 8.68
verl/plugin/platform/platform_cuda.py 平台层 modified 5.75
verl/plugin/platform/platform_manager.py 平台层 modified 4.75
tests/special_sanity/check_device_api_usage.py 设备 API 检查 modified 3.32

关键符号

PlatformROCm.is_platform_available PlatformROCm.rollout_env_vars PlatformROCm.ray_noset_envvars PlatformROCm.vendor_name PlatformCUDA.is_platform_available

关键源码片段

verl/plugin/platform/platform_rocm.py core-logic

新增的 ROCm 平台后端核心实现,定义 PlatformROCm 类并覆盖关键方法。

# Copyright (c) 2026 BAAI. All rights reserved.
"""AMD ROCm/HIP platform implementation.ROCm is largely CUDA-compatible: PyTorch on ROCm reuses the ``torch.cuda.*``
API surface via hipify, so most of ``PlatformCUDA`` works unchanged. This class
therefore subclasses ``PlatformCUDA`` and only overrides the parts that differ
on ROCm, following a "CUDA base + ROCm extensions" model (always extend the
parent via ``super()`` rather than re-implementing it).
"""import os
import shutilimport torchfrom .platform_cuda import PlatformCUDA
from .platform_manager import PlatformRegistry
​
​
@PlatformRegistry.register(platform="amd")
class PlatformROCm(PlatformCUDA):
    """Platform backend for AMD ROCm/HIP GPUs (reuses PlatformCUDA where compatible)."""
​
    @property
    def vendor_name(self) -> str:
        # NOTE: device_name stays 'cuda' on purpose — PyTorch ROCm exposes the
        # device type string as "cuda" (torch.device("cuda") works via hipify).
        return "amd"
​
    def is_platform_available(self, use_smi_check=False) -> bool:
        if not hasattr(torch, "cuda"):
            return False
        # Only ROCm (HIP) torch builds qualify as the AMD platform.
        if torch.version.hip is None:
            return False
        if use_smi_check:
            # In CPU-only Ray actors, torch.cuda.is_available() may return False
            # even though the cluster has GPUs. Fall back to rocm-smi check,
            # and if that's also unavailable (e.g. not on PATH), treat
            # torch.cuda being built as sufficient evidence.
            cmd = "rocm-smi"
            cmd_path = shutil.which(cmd)
            if cmd_path is None:
                # Fallback to common absolute paths if not found in PATH
                common_paths = [
                    f"/usr/bin/{cmd}",
                    f"/usr/local/bin/{cmd}",
                    f"/opt/rocm/bin/{cmd}",
                ]
                for path in common_paths:
                    if os.path.isfile(path) and os.access(path, os.X_OK):
                        cmd_path = path
                        break
                if cmd_path is None:
                    return False # Consistent with PlatformCUDA behavior
            if self.check_smi_command(cmd_path):
                return True
        return torch.cuda.is_available()
​
    def rollout_env_vars(self) -> dict[str, str]:
        # Extend CUDA's rollout env vars with ROCm-specific ones. SGLANG_USE_AITER
        # routes SGLang's non-attention kernels (RMSNorm/RoPE/MoE/quant) through AITER.
        # Default to "1" but honor an explicit user override (e.g. SGLANG_USE_AITER=0
        # to fall back to vLLM kernels).
        return {
            **super().rollout_env_vars(),
            "SGLANG_USE_AITER": os.environ.get("SGLANG_USE_AITER", "1"),
        }
​
    def ray_noset_envvars(self) -> list[str]:
        # On ROCm, HIP_VISIBLE_DEVICES takes precedence over CUDA_VISIBLE_DEVICES,
        # and ROCR_VISIBLE_DEVICES is also relevant, so tell Ray not to manage them.
        return super().ray_noset_envvars() + [
            "RAY_EXPERIMENTAL_NOSET_HIP_VISIBLE_DEVICES",
            "RAY_EXPERIMENTAL_NOSET_ROCR_VISIBLE_DEVICES",
        ]
verl/plugin/platform/platform_cuda.py core-logic

修改了 is_platform_available 方法,增加 HIP 检测门控,防止自动检测选错平台。

def is_platform_available(self, use_smi_check=False) -> bool:
    if not hasattr(torch, "cuda"):
        return False
    # On ROCm, torch.cuda is present too; defer to PlatformROCm so that
    # auto-detection does not pick CUDA on AMD hardware.
    if torch.version.hip is not None:
        return False
    if use_smi_check:
        cmd = "nvidia-smi"
        cmd_path = shutil.which(cmd)
        if cmd_path is None:
            common_paths = [
                f"/usr/bin/{cmd}",
                f"/usr/local/bin/{cmd}",
                f"/usr/local/cuda/bin/{cmd}",
            ]
            for path in common_paths:
                if os.path.isfile(path) and os.access(path, os.X_OK):
                    cmd_path = path
                    break
            if cmd_path is None:
                return False
        if self.check_smi_command(cmd_path):
            return True
    return torch.cuda.is_available()

评论区精华

rocm-smi 不可用时返回 False 是否合理 正确性

Gemini Code Assist Bot 指出注释说当 rocm-smi 不可用时应将 torch.cuda 视为证据,但代码返回 False。建议改为 True。作者回复保持 False 以与父类一致。

结论:保持 False,与 PlatformCUDA 行为一致,遵循最小改动原则。 · 已解决

风险与影响

  1. 平台检测正确性:若 ROCm 系统上 rocm-smi 不在任何搜索路径且 torch.cuda.is_available() 因某种原因返回 False(如 CPU-only actor),则 is_platform_available 会返回 False,导致平台检测失败。但此场景罕见,且与 CUDA 后端的现有行为一致(同样在 nvidia-smi 找不到时返回 False)。
  2. 继承风险PlatformROCm 继承 PlatformCUDA 的多数方法(如 set_devicedevice_count),若未来 CUDA 后端引入不兼容 ROCm 的修改,可能波及 AMD 用户;需通过 CI 或手动回归覆盖。
  3. 测试覆盖缺失:由于需要真实 AMD ROCm 硬件,未添加单元测试;手动验证覆盖了主要功能,但边缘路径缺少保护。

用户:AMD ROCm 用户在启动训练时会自动选用 PlatformROCm,环境变量 SGLANG_USE_AITER 默认开启,可通过显式导出覆盖。现有 CUDA 用户无感知,因 PlatformCUDA 新增的门控会跳过 AMD 硬件。系统:平台管理器当前支持三个后端:nvidia、npu、amd。get_platform() 的自动检测逻辑扩展为优先匹配 HIP 构建。团队:新增的 ROCm 后端基于继承实现,添加新方法时需注意同步。持续维护成本较低。

缺少自动化测试(需硬件) 继承 CUDA 可能引入兼容性问题 平台检测路径依赖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论