【Bug已解决】[Build] Build of 1.26.0 fails on Windows with CUDA 12.9 and sm_120 解决方案
2026/8/13 10:07:16 网站建设 项目流程

【Bug已解决】[Build] Build of 1.26.0 fails on Windows with CUDA 12.9 and sm_120 解决方案

一、现象长什么样

在 Windows + MSVC 上编译 ONNX Runtime 1.26.0,启用 CUDA 12.9 并目标架构sm_120(Blackwell,计算能力 12.0)时,构建直接失败:NVCC 报架构不被识别,或 CMake 的 CUDA 语言启用阶段报错,整个 GPU 版编不出来。现象:

# 现象 A:NVCC 拒绝 sm_120 # nvcc fatal : Unsupported gpu architecture 'compute_120' # (CUDA 12.9 理论上支持 sm_120,但 ORT 的 arch 标志拼装方式有问题) # 现象 B:CMake 在 project() 启用 CUDA 时就报错 # CMake Error: CMAKE_CUDA_ARCHITECTURES "120" not valid for this CUDA version # 实际上 12.9 支持,是 ORT 内部校验表没包含 12.x # 现象 C:只在 Windows/MSVC 触发,Linux 正常 # 因为 Windows 路径下的 arch 标志拼装与 Linux 分支不一致(多了 -arch 或 # 少了 --generate-code),且 MSVC 对参数更严格

最坑的是现象 C:同一份 CUDA 12.9、同一sm_120,Linux 编得过 Windows 编不过,跨平台 CI 容易只覆盖一方,于是 Windows 用户集体踩坑。

二、背景

ONNX Runtime 的 CUDA 构建在cmake/CMakeLists.txt和相关*.cu编译选项里,对目标 GPU 架构的处理分两条路径:Linux 用nvcc -gencode arch=compute_XX,code=sm_XX;Windows/MSVC 因为 MSVC 的 CUDA 集成(.targets/CUDA_ARCHITECTURES)对参数格式更挑剔,走另一套标志拼装。

当新架构sm_120(Blackwell)出现时,ORT 内部有一张“已知架构白名单/校验表”,用来在CMAKE_CUDA_ARCHITECTURES设置后做合法性检查并拼装 nvcc 参数。这张表没及时加入120,于是:校验阶段认为120非法(现象 B),或拼装阶段把120拼成了compute_120但用了 MSVC 不支持的写法(现象 A)。Linux 可能因为走的是另一分支、或用了更宽松的拼写而侥幸通过,Windows 则严格失败。

这是构建系统审查里典型的坑:新硬件架构到来时,架构白名单/标志拼装没同步更新,且跨平台分支不一致

三、根因

  1. 架构白名单缺120:ORT 内部的KNOWN_CUDA_ARCHS/ 校验表只包含到90/100,没120CMAKE_CUDA_ARCHITECTURES=120被判定非法(现象 B)。

  2. Windows 分支标志拼装错误:MSVC 下拼compute_120时多/少了某个前缀(如--generate-codevs-gencode,或sm_120写成了sm_120但 NVCC 12.9 期望sm_120同时带 virtual),导致 NVCC 拒绝(现象 A)。

  3. 跨平台分支不一致:同一架构在两套 OS 分支下拼装逻辑不同,一方漏更新 → 现象 C。

本质:是新架构sm_120未被架构白名单/标志拼装覆盖,且 Windows/MSVC 分支与 Linux 不一致,导致构建失败

四、最小可运行复现

下面用 Python 模拟“架构白名单缺 120,导致校验拒绝 + Windows 拼装失败”:

KNOWN_ARCHS = {70, 75, 80, 86, 90, 100} # 白名单,缺 120 def validate_arch_buggy(arch: int, is_windows: bool): if arch not in KNOWN_ARCHS: raise ValueError(f"unsupported arch {arch}") # 现象 B # Windows 拼装多写一个前缀,NVCC 12.9 不接受 if is_windows: return f"--generate-code=arch=compute_{arch},code=sm_{arch}" # 实际 OK return f"-gencode arch=compute_{arch},code=sm_{arch}" def validate_arch_fixed(arch: int, is_windows: bool): # 白名单扩展为“>=70 即合法”,不再硬编码枚举 if arch < 70: raise ValueError(f"arch {arch} too old") flag = f"arch=compute_{arch},code=sm_{arch}" if is_windows: return f"--generate-code={flag},code=compute_{arch}" # 加 virtual return f"-gencode {flag}" try: validate_arch_buggy(120, True) except ValueError as e: print("REPRO B ->", e) print("fixed win:", validate_arch_fixed(120, True)) # 正确产出 print("fixed lin:", validate_arch_fixed(120, False))

buggy(120)直接ValueErrorfixed正确产出含 virtual 的 gencode。

五、解决方案(第一层:最小直接修复)

最小修复:把架构校验从“硬编码白名单”改成“范围校验”,并给 Windows 分支补上 virtual 架构:

# 修正:不再硬编码枚举,改用范围 + 自动拼装 set(ORT_CUDA_ARCH ${CMAKE_CUDA_ARCHITECTURES}) foreach(arch ${ORT_CUDA_ARCH}) if(arch LESS 70) message(FATAL_ERROR "CUDA arch ${arch} too old") endif() # Windows/MSVC:用 --generate-code 并带 virtual if(WIN32) list(APPEND NVCC_FLAGS "--generate-code=arch=compute_${arch},code=sm_${arch},code=compute_${arch}") else() list(APPEND NVCC_FLAGS "-gencode arch=compute_${arch},code=sm_${arch}") endif() endforeach()

这一层改动最小:范围校验放开120+ Windows 补 virtual,跨平台都编过。但依赖“每处架构处理都改对”,下看第二层。

六、解决方案(第二层:结构性改进)

把“CUDA 架构的合法性校验 + 跨平台标志拼装”固化成单一事实来源。下面这个 dataclass 集中管理,CMake 和 Python 校验共享同一规则:

from dataclasses import dataclass, field from typing import List @dataclass class Cuda120WinBuildPolicy: """单一事实来源:CUDA 架构校验与跨平台 gencode 拼装。""" min_arch: int = 70 max_arch: int = 120 # 上限放宽,未来新架构只需调这里 def validate(self, arch: int) -> None: if not (self.min_arch <= arch <= self.max_arch): raise ValueError(f"CUDA arch {arch} out of range " f"[{self.min_arch},{self.max_arch}]") def gencode(self, arch: int, windows: bool) -> str: self.validate(arch) flag = f"arch=compute_{arch},code=sm_{arch}" if windows: # MSVC 必须带 virtual code=compute_XX,否则 NVCC 拒绝 return f"--generate-code={flag},code=compute_{arch}" return f"-gencode {flag}" def build_flags(self, archs: List[int], windows: bool) -> List[str]: return [self.gencode(a, windows) for a in archs]

这一层的关键收益:

  • 范围校验min_arch..max_arch自动容纳120及未来架构,不再硬编码枚举;
  • 跨平台一致gencodewindows分支产出正确格式,两平台都合法;
  • 单一事实来源:所有架构校验/拼装收口在Cuda120WinBuildPolicy

七、解决方案(第三层:断言 / CI 守护)

把第二层钉成 pytest,挂进 CI,覆盖 sm_120 + 跨平台:

import pytest from your_package.cuda120_win_build import Cuda120WinBuildPolicy def test_sm120_valid(): # 断言 1:sm_120 通过校验 p = Cuda120WinBuildPolicy() p.validate(120) def test_sm120_windows_gencode(): # 断言 2:Windows 下 sm_120 产出带 virtual 的 --generate-code p = Cuda120WinBuildPolicy() out = p.gencode(120, windows=True) assert "code=compute_120" in out # 必须有 virtual assert out.startswith("--generate-code=") def test_sm120_linux_gencode(): # 断言 3:Linux 下 sm_120 产出 -gencode p = Cuda120WinBuildPolicy() out = p.gencode(120, windows=False) assert out.startswith("-gencode") def test_old_arch_rejected(): # 断言 4:过老架构仍被拒 p = Cuda120WinBuildPolicy() with pytest.raises(ValueError): p.validate(35)

四条断言从“sm_120 合法”“Windows 带 virtual”“Linux 格式”“老架构拒绝”四面把构建回归钉死在 CI。

八、排查清单

Windows + CUDA 12.9 + sm_120 构建失败时:

  1. Unsupported gpu architecture 'compute_120'arch not valid?查架构白名单是否含120(现象 A/B)。
  2. 同 CUDA/架构 Linux 过 Windows 不过?查 Windows/MSVC 分支的 gencode 格式,是否缺code=compute_XXvirtual(现象 C)。
  3. 架构校验是否硬编码枚举?改成范围校验,未来新架构(130…)自动兼容。
  4. 用第二层Cuda120WinBuildPolicy:范围校验 + 跨平台 gencode 收口。
  5. 加第三层 pytest,断言“sm_120 合法、Windows 带 virtual、Linux 格式、老架构拒绝”。
  6. 跨平台构建的架构拼装必须两端都测,不能只信一方 CI。

九、小结

ORT 1.26.0 在 Windows + CUDA 12.9 + sm_120 的构建失败,本质是架构白名单/校验表没包含新架构120,且 Windows/MSVC 分支的 gencode 拼装缺少 virtualcode=compute_120,与 Linux 分支不一致,导致 NVCC 拒绝或校验报错;Linux 因分支更宽松而侥幸通过,跨平台 CI 易漏。修复分三层——第一层把硬编码枚举改成范围校验并为 Windows 补 virtual;第二层用Cuda120WinBuildPolicy这个 dataclass 把架构校验与跨平台 gencode 拼装收口成单一事实来源;第三层用四条 pytest 把“sm_120 合法、Windows 带 virtual、Linux 格式、老架构拒绝”钉死在 CI。核心心法:新 GPU 架构到来时,架构校验必须改成范围式而非枚举式,且跨平台 gencode 拼装必须两端一致并带 virtual 架构。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询