【Bug已解决】RuntimeError: UVA is not available 解决方案一、现象长什么样在启动某个用到多 GPU 或底层 CUDA 内存管理的程序如某些 NCCL 扩展、cupy、自定义 CUDA 库、或需要统一地址空间的训练框架时初始化阶段抛错报统一虚拟寻址不可用。典型日志RuntimeError: UVA is not available或者更笼统RuntimeError: UVA is not available几个特征帮你判断是不是同一个坑报错是UVA is not availableUnified Virtual Addressing统一虚拟寻址这是 CUDA 的一个能力特性缺失不是模型/权重错误。错误发生在初始化阶段建 NCCL 通信、分配跨卡内存、或某库探测设备能力时不是 forward。只在特定环境出现比如多卡环境、容器里、或某几张特定 GPU单机单卡、或换台机器就正常。常伴随「该库需要所有 GPU 都支持 UVA 且处于同一平台/驱动」只要有一张卡不满足或驱动不一致就整体不可用。日志里可能还有cudaDeviceProp相关、managedMemory/integrated等字段提示。二、背景UVAUnified Virtual Addressing统一虚拟寻址是 CUDA 的一个特性它让所有支持 UVA 的 GPU以及主机内存共享同一套虚拟地址空间这样一段指针可以直接区分「指向哪张卡的内存」无需手动在设备间转换地址。很多多 GPU 库NCCL 的某些用法、cupy、需要 P2P 的库依赖 UVA 来简化跨卡内存访问。UVA 的可用前提CUDA 文档口径所有参与的 GPU 都支持 UVAUVA 是计算能力 2.0 的 GPU 才支持的硬件特性现代 GPU 都支持但只要有一张卡不支持或被认为是 legacy 设备整个上下文的 UVA 就被判为不可用。所有 GPU 在同一驱动/平台下、可被同一上下文访问如果系统里混插了不同代际、或某张卡被另一个驱动实例/容器独占且地址空间不统一UVA 可能不可用。驱动与 CUDA 运行时支持UVA 由驱动提供驱动版本过旧或在某些虚拟化/容器环境里被限制UVA 探测失败。容器/虚拟化限制在容器里若 GPU 透传不完整、或CUDA_VISIBLE_DEVICES只暴露了部分卡而库试图访问「所有卡」的 UVA可能因「可见卡集合里出现不一致」而判不可用。MPS / 多进程服务干扰某些 MPS 配置下跨进程的地址空间不统一UVA 探测失败。当程序调用cudaDeviceGetAttribute(cudaDevAttrUnifiedAddressing, ...)或库在初始化时检测 UVA发现「不是所有设备都支持 UVA」或「地址空间不统一」就抛RuntimeError: UVA is not available。核心UVA 是「全局能力」——只要参与的设备集合里有一张不满足整体就不可用而很多库把它当成「要么全有要么全无」的硬性前提。三、根因根因一句话程序/库在初始化时要求 UVA统一虚拟寻址但当前环境的 GPU 设备集合中存在不支持 UVA 的卡、或各卡处于不同驱动/平台导致地址空间不统一、或在容器/MPS 下 UVA 探测被限制使得 CUDA 判定 UVA 不可用库随即抛出RuntimeError: UVA is not available。具体成因异构设备混插系统里混了不支持 UVA 的老卡或被判为 legacy拖垮整体。驱动/平台不一致多卡驱动版本不同、或某卡在另一地址空间UVA 统一失败。容器透传不全容器只暴露部分卡、或 GPU 透传不完整UVA 探测失败。MPS 干扰MPS 下跨进程地址空间不统一UVA 不可用。CUDA_VISIBLE_DEVICES切割不一致库要访问所有卡但可见集合被切成不一致的子集。库硬性要求 UVA 无降级库把 UVA 当硬前提不可用时直接崩而非回退到显式地址转换。核心矛盾UVA 是「全有或全无」的全局能力而环境里设备/驱动/容器配置的不一致让这个全局能力不满足库又无降级于是直接崩溃。四、最小可运行复现下面用纯 Python 模拟「设备集合里有一张不支持 UVA整体判不可用」# reproduce_uva.py # 复现设备集合里存在不支持 UVA 的卡 - 整体 UVA 不可用 def uva_available(devices: list) - bool: # UVA 要求所有设备都支持, 且地址空间统一 return all(d[uva] for d in devices) and len({d[platform] for d in devices}) 1 if __name__ __main__: # 现代卡都支持 UVA, 同平台 - 可用 good [{uva: True, platform: x}, {uva: True, platform: x}] print(全现代卡:, uva_available(good)) # True # 混插一张 legacy 卡(不支持 UVA) - 整体不可用 mixed [{uva: True, platform: x}, {uva: False, platform: x}] print(混插 legacy:, uva_available(mixed)) # False - RuntimeError运行python reproduce_uva.py会看到「只要有一张卡不支持 UVA整体就不可用」——正是该错误的成因。五、解决方案第一层最小直接修复最小修复确保参与计算的 GPU 集合是「同代际、同驱动、地址空间统一」的纯 UVA 支持设备并通过CUDA_VISIBLE_DEVICES只暴露这些卡剔除不支持 UVA 的卡。# fix_layer1_uva.py def select_uva_capable(devices: list) - list: 只保留支持 UVA 且平台一致的设备。 platforms {d[platform] for d in devices if d[uva]} kept [i for i, d in enumerate(devices) if d[uva] and d[platform] in platforms] return kept if __name__ __main__: devs [{uva: True, platform: x}, {uva: False, platform: x}] keep select_uva_capable(devs) print(保留的设备索引:, keep) # [0], 剔除 legacy 卡 print(设置: CUDA_VISIBLE_DEVICES, ,.join(str(i) for i in keep))命令行等价# 只暴露支持 UVA 的卡(如 0,1), 剔除 2(legacy) export CUDA_VISIBLE_DEVICES0,1这一层把「全局设备集合不一致导致 UVA 不可用」变成「用 CUDA_VISIBLE_DEVICES 裁掉不支持的卡保留纯 UVA 集合」。六、解决方案第二层结构性改进把「UVA 能力探测 设备裁剪」做成独立模块启动时自动检测每张卡是否支持 UVA、平台是否一致并给出应暴露的设备集合# fix_layer2_cap.py from dataclasses import dataclass, field dataclass class DeviceInfo: index: int uva: bool platform: str compute_capability: tuple def plan_uva(devices: list) - dict: 返回: uva_ok, 应选设备索引, 建议的 CUDA_VISIBLE_DEVICES。 uva_devs [d for d in devices if d.uva] platforms {d.platform for d in uva_devs} uva_ok len(uva_devs) len(devices) and len(platforms) 1 chosen [d.index for d in uva_devs] if platforms else [] return { uva_ok: uva_ok, chosen: chosen, cuda_visible_devices: ,.join(str(i) for i in chosen) if chosen else , dropped: [d.index for d in devices if not d.uva], } if __name__ __main__: devs [ DeviceInfo(0, True, x, (9, 0)), DeviceInfo(1, True, x, (9, 0)), DeviceInfo(2, False, x, (3, 5)), # legacy, 不支持 UVA ] print(plan_uva(devs))真实场景里DeviceInfo通过torch.cuda.get_device_properties(i)的unified_addressing字段和驱动信息填充。这样换机器/换卡时启动自动裁剪出纯 UVA 集合库不会再因 legacy 卡判 UVA 不可用。七、解决方案第三层断言 / CI 守护把「UVA 能力探测 设备裁剪」钉进断言和 CI# fix_layer3_guard.py # ---- pytest 用例进 CI ---- def test_all_uva_ok(): from fix_layer2_cap import plan_uva, DeviceInfo devs [DeviceInfo(0, True, x, (9, 0)), DeviceInfo(1, True, x, (9, 0))] r plan_uva(devs) assert r[uva_ok] and r[chosen] [0, 1] def test_legacy_dropped(): from fix_layer2_cap import plan_uva, DeviceInfo devs [DeviceInfo(0, True, x, (9, 0)), DeviceInfo(2, False, x, (3, 5))] r plan_uva(devs) assert not r[uva_ok] assert r[dropped] [2] assert r[chosen] [0] def test_mixed_platform_fails(): from fix_layer2_cap import plan_uva, DeviceInfo devs [DeviceInfo(0, True, x, (9, 0)), DeviceInfo(1, True, y, (9, 0))] assert not plan_uva(devs)[uva_ok]再加启动断言def assert_uva_ok(devices: list): from fix_layer2_cap import plan_uva r plan_uva(devices) assert r[uva_ok], ( fUVA 不可用: 不支持/平台不一致的设备 {r[dropped]} f请用 CUDA_VISIBLE_DEVICES{r[cuda_visible_devices]} 仅暴露 UVA 卡 )八、排查清单RuntimeError: UVA is not available按序查先确认是 UVA 能力缺失错误明确说 UVA不是 OOM/驱动错。查设备是否混插系统里是否插了不支持 UVA 的老卡计算能力 2.0 或被判 legacy剔除它。查驱动一致性多卡驱动版本是否一致不同驱动可能导致地址空间不统一。用 CUDA_VISIBLE_DEVICES 裁剪只暴露支持 UVA 且同平台的卡。查容器透传容器是否完整透传 GPU透传不全 UVA 探测失败。查 MPS 配置MPS 下跨进程地址空间可能不统一关 MPS 试。看 torch 设备属性torch.cuda.get_device_properties(i).unified_addressing逐卡看是否都 True。降级思路若库强制要求 UVA 且环境无法满足换用不依赖 UVA 的代码路径显式地址转换。升级驱动旧驱动可能 UVA 支持不全升级到统一新驱动。最后才动库优先在设备裁剪/驱动/容器层解决不要为绕开去改库源码。九、小结RuntimeError: UVA is not available根子是程序/库把 UVA统一虚拟寻址当硬性前提但当前 GPU 设备集合里存在不支持 UVA 的卡、或各卡驱动/平台不一致导致地址空间不统一、或在容器/MPS 下 UVA 探测被限制CUDA 判定 UVA 整体不可用。关键认识UVA 是「全有或全无」的全局能力——只要有一张卡不满足整体就不可用。修复三层第一层用CUDA_VISIBLE_DEVICES只暴露支持 UVA 的同平台卡剔除 legacy 卡第二层抽plan_uva()自动探测每张卡 UVA 支持与平台一致性给出应暴露集合第三层用 pytest 把「全 UVA 通过」「legacy 剔除」「平台混合失败」钉进 CI启动前断言。核心认识——UVA 依赖「设备集合的同质性」遇到该错误不要去改库而是检查并裁剪 GPU 设备集合同代际、同驱动、地址空间统一让全局能力重新满足。