Prhub

#7728 [rollout] fix: default IPC weight transfer on Ascend A5 hardware

原始 PR 作者 kyle-zhangchi 合并时间 2026-09-07 15:36 文件变更 3 提交数 2 评论 0 代码增减 +166 / -10

执行摘要

修复 Ascend A5 硬件因 HDK 版本问题无法开启 IPC 的 bug

在 Ascend A5 硬件上,由于 HDK(Hypervisor Development Kit)版本问题,使用原有的 IPC 版本检查逻辑(依赖 software_version 和 cann_version)会错误地判定 IPC 不受支持。PR body 中明确指出:“Fix the issue of A5 being unable to open IPC due to the HDK version problem.” 这是一个针对特定硬件的兼容性修复,旨在恢复 A5 硬件的 IPC 功能。

本 PR 是一个重要的、针对性强的 bugfix。它解决了特定硬件上的功能阻断问题,且实现清晰,测试覆盖全面。推荐精读,关注点在于:

1) AscendHardwareVersion 枚举的设计及其作为未来硬件适配扩展点的潜力;
2) get_npu_versions 返回值签名变更的影响范围确认;
3) 测试中通过 @patch.dict(sys.modules, {...}) 模拟 torch_npu 模块的方法。

讨论亮点

本 PR 没有公开的 review 讨论评论。根据提交记录,共两个提交:‘ipc_fix patch’ 和 ‘pre-commit fix’。审核者 wucong25 最终状态为 ‘APPROVED’。

实现拆解

  1. 定义硬件版本枚举:在 verl/utils/device.py 中新增 AscendHardwareVersion 枚举类(IntEnum),用于表示 NPU 芯片代次,包含 NONE、A2、A3、A5 和 MAX_VERSION 等成员。
  2. 增强 get_npu_versions 函数:修改 verl/utils/device.py 中的 get_npu_versions 函数。
    • 在函数开头尝试导入 torch_npu 并调用 torch_npu.npu.get_device_name() 来获取硬件名称。
    • 根据设备名称字符串(如 'Ascend910_95', 'Ascend950', 'Ascend910_93', 'Ascend910B', 'A2G')推断并返回对应的 AscendHardwareVersion 枚举值。
    • 函数返回值从 (software_version, carn_version) 变更为 (hardware_version, software_version, carn_version)。
  3. 调整 IPC 支持检测逻辑:在 verl/plugin/platform/platform_npu.py 的 PlatformNPU.is_ipc_supported 方法中。
    • 更新导入语句以包含 AscendHardwareVersion。
    • 解包 get_npu_versions 返回的三元组为 hardware_version, software_version, carn_version。
    • 新增短路逻辑:如果 hardware_version == AscendHardwareVersion.A5,则直接返回 True,不再调用 check_ipc_version_support。
  4. 配套测试:修改 tests/utils/test_check_ipc_version_support_on_npu.py。
    • 新增辅助函数 _fake_torch_npu 用于模拟不同硬件名称。
    • 新增 TestGetNPUVersions 类下的多个测试用例(如 test_hardware_version_a5_910_95, test_hardware_version_a3 等)验证 get_npu_versions 对不同设备名称的正确分类。
    • 新增 TestIsIPCSupported 类,专门测试 PlatformNPU.is_ipc_supported 方法,包括 A5 短路返回 True 和非 A5 委托给版本检查两种场景。
    • 调整了现有测试用例(如 test_get_npu_versions_success)以适应新的三元组返回值。
文件 模块 状态 重要度
verl/utils/device.py 设备工具 modified 7.52
verl/plugin/platform/platform_npu.py 平台插件 modified 5.88
tests/utils/test_check_ipc_version_support_on_npu.py IPC 版本检查测试 modified 7.45

关键符号

get_npu_versions PlatformNPU.is_ipc_supported _fake_torch_npu

关键源码片段

verl/utils/device.py dependency-wiring

核心变更文件,新增硬件版本枚举并重构 `get_npu_versions` 函数以返回硬件型号。

# 在 verl/utils/device.py 中新增的硬件版本枚举和检测逻辑
from enum import IntEnum, unique
​
@unique
class AscendHardwareVersion(IntEnum):
    """Ascend NPU 芯片代次枚举。"""
    NONE = 0 # torch_npu 不可用或无法识别
    A2 = 2 # Ascend910B, A2G
    A3 = 3 # Ascend910_93
    A5 = 5 # Ascend910_95, Ascend950
    MAX_VERSION = 999 # 未知或更新型号
​
def get_npu_versions() -> tuple[AscendHardwareVersion, str, str]:
    """获取 NPU 芯片代次、软件版本和 CANN 工具包版本。
​
    Returns:
        tuple[AscendHardwareVersion, str, str]: 三元组 (hardware_version, software_version, cann_version)
    """
    # 尝试通过 torch_npu 获取设备名称以判断硬件型号
    try:
        import torch_npu
        device_name = torch_npu.npu.get_device_name()
    except Exception:
        hardware_version = AscendHardwareVersion.NONE
    else:
        if "Ascend910_95" in device_name or "Ascend950" in device_name:
            hardware_version = AscendHardwareVersion.A5
        elif "Ascend910_93" in device_name:
            hardware_version = AscendHardwareVersion.A3
        elif "Ascend910B" in device_name or "A2G" in device_name:
            hardware_version = AscendHardwareVersion.A2
        else:
            hardware_version = AscendHardwareVersion.MAX_VERSION
​
    # ... 后续的 software_version 和 cann_version 获取逻辑保持不变 ...
    return hardware_version, software_version, cann_version
verl/plugin/platform/platform_npu.py dependency-wiring

使用 `get_npu_versions` 的调用方,实现了针对 A5 硬件的 IPC 支持短路逻辑。

# 在 verl/plugin/platform/platform_npu.py 的 is_ipc_supported 方法中的关键逻辑
    def is_ipc_supported(self) -> bool:
        import subprocess
        from verl.utils.device import AscendHardwareVersion, check_ipc_version_support, get_npu_versions
​
        try:
            # 解包新的三元组返回值
            hardware_version, software_version, carn_version = get_npu_versions()
        except subprocess.CalledProcessError as e:
            raise RuntimeError(f"Failed to execute npu-smi command: {e}") from e
        except Exception as e:
            raise RuntimeError(f"Error checking IPC support: {e}") from e
​
        # 针对 A5 硬件的短路逻辑:直接支持 IPC
        if hardware_version == AscendHardwareVersion.A5:
            return True
        # 非 A5 硬件回退到原有的版本检查逻辑
        return check_ipc_version_support(software_version, carn_version)
tests/utils/test_check_ipc_version_support_on_npu.py test-coverage

配套测试文件,全面覆盖了新增的硬件版本检测逻辑和 IPC 支持判断逻辑。

# 在 tests/utils/test_check_ipc_version_support_on_npu.py 中,用于模拟不同硬件名称的辅助函数
import sys
from unittest.mock import MagicMock
​
def _fake_torch_npu(device_name):
    """构建一个模拟的 torch_npu 模块,其 npu.get_device_name 返回指定名称。"""
    fake = MagicMock()
    fake.npu.get_device_name.return_value = device_name
    return fake
​
# 示例测试用例,验证 A5 硬件(Ascend910_95)被正确识别
@patch.dict(sys.modules, {"torch_npu": _fake_torch_npu("Ascend910_95")})
@patch("subprocess.run")
@patch("platform.machine")
@patch("os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="version=8.3.rc1\n")
def test_hardware_version_a5_910_95(self, mock_file, mock_exists, mock_machine, mock_run):
    mock_run.return_value = Mock(stdout="Software Version : 25.5.0\n", check=True)
    mock_machine.return_value = "x86_64"
    mock_exists.return_value = True
    hardware_version, _, _ = get_npu_versions()
    self.assertEqual(hardware_version, AscendHardwareVersion.A5)
​
# 测试 A5 硬件在 is_ipc_supported 中的短路返回 True 行为
@patch("verl.utils.device.check_ipc_version_support")
@patch("verl.utils.device.get_npu_versions")
def test_a5_short_circuit_returns_true(self, mock_get_versions, mock_check):
    mock_get_versions.return_value = (AscendHardwareVersion.A5, "24.0.0", "7.0.0")
    from verl.plugin.platform.platform_npu import PlatformNPU
    platform = PlatformNPU()
    self.assertTrue(platform.is_ipc_supported())
    # 断言 check_ipc_version_support 未被调用,证实短路逻辑生效
    mock_check.assert_not_called()

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 硬件识别逻辑风险:get_npu_versions 中通过设备名称字符串片段(如 'Ascend910_95')判断硬件型号,若未来硬件名称格式发生变化,可能需要更新此映射。
  2. 依赖变更:get_npu_versions 的返回值签名从二元组变为三元组,这是一个 内部 API 变更。所有调用此函数的地方都需要同步修改。在本次 PR 中,已对 verl/plugin/platform/platform_npu.py 和测试文件进行了修改,但需要确保仓库内无其他遗漏的调用点。
  3. 短路逻辑的正确性:在 A5 硬件上直接返回 IPC 支持为 True,跳过了 software_version 和 carn_version 检查。这依赖于‘A5 硬件一定支持 IPC’这一前提假设,假设成立则逻辑正确。

用户影响:直接影响在 Ascend A5 硬件上使用 verl 进行训练的用户,修复了他们因 IPC 无法开启而可能遇到的功能限制或性能问题。
系统影响:增强了 verl 对多样化 Ascend NPU 硬件的兼容性和检测能力,将硬件识别逻辑集成到基础的设备工具模块中,为未来更细粒度的硬件适配奠定了基础。
团队影响:为硬件兼容性修复提供了清晰的实现模式:识别硬件 → 细化检测逻辑 → 配套测试。

内部 API 返回值变更 (get_npu_versions) 硬件识别逻辑依赖字符串匹配

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论