ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Windows下编译nvdiffrast:CUDA、PyTorch与MSVC版本对齐及setup.py修改指南

Windows下编译nvdiffrast:CUDA、PyTorch与MSVC版本对齐及setup.py修改指南 nvdiffrast 这个名字做神经渲染和可微渲染的朋友应该不陌生。它出自 NVIDIA 实验室是目前做可微光栅化绕不开的一个高性能库。但麻烦的是在 Windows 上想把它编译成功得先把 CUDA 工具链、MSVC 编译器和 Python 环境的版本全部对齐再对 setup.py 做一番手术否则你大概率会在各种诡异的 C 编译报错里反复横跳。我一开始也在这上面栽了不少跟头后来通过逐项排查环境变量、修改编译参数、重新整理版本组合总算是把整个流程跑通了。这篇文章就把完整过程整理出来包含修改 setup.py 的具体方案、环境配置的每一步细节、以及编译现场真实踩过的坑希望能帮在 Windows 上做相关开发的朋友把这扇门顺利打开。1. 从报错开始Windows 编译这道坎到底卡在哪1.1 nvdiffrast 到底是个什么库nvdiffrast 是 NVIDIA 开源的可微光栅化器简单说就是把传统图形学里的光栅化流程改造成可求导的版本。在做神经渲染、逆向渲染、纹理烘焙、材质估计这类任务时你经常需要把三角网格画成图像同时还要把图像对顶点坐标和属性的梯度回传回去。nvdiffrast 就是干这个的而且速度非常快目前很多开源项目在可微渲染这一步就直接拿来用。它不是一个纯 Python 库。代码核心是 C 和 CUDA——光栅化、纹理采样、抗锯齿等操作都需要在 GPU 上执行这就决定了你必须把源代码编译成 Python 的扩展模块也就是 Windows 下的 .pyd 文件。编译过程由 setup.py 驱动它会借助 PyTorch 的 cpp_extension 工具链自动调用你机器上的 C 编译器和 NVIDIA 的 nvcc 编译器。这就是整个问题的根源只要你的 C 编译器、CUDA Toolkit、PyTorch 或 Python 版本有任何一环不匹配编译就会死在半路上而且报错还不一定是版本不对这么直白常常是头文件找不到函数未定义语法错误这类绕弯子的信息。所以看完第一个报错别急着怀疑人生先顺着环境这条线捋一遍大概率能破案。1.2 为什么 Windows 下这么容易失败同样一份源码Linux 下可能敲两行 pip 就装好了Windows 下却步履维艰核心差异有三个。第一是编译器。Linux 下 CUDA 官方支持 gcc/g安装路径统一PyTorch 的扩展构建工具也能无缝找到它。Windows 下你必须用 MSVC也就是 Visual Studio 的 C 编译器而且要匹配 CUDA Toolkit 支持的版本区间。CUDA 11.8 最常见的是搭配 VS2019而 CUDA 12.x 对 VS2022 的支持比较完整。如果你的 CUDA 比较新但 VS 版本旧nvcc 会直接拒绝工作这在 Linux 生态里不太会出现。第二是路径体系。Linux 下 CUDA 的头文件和库文件目录几乎全系统一致编译器自己就能找到。Windows 下你得同时保证 PATH、INCLUDE、LIB 三套环境变量正确尤其是当你从普通命令行而不是从 Visual Studio 的开发者命令提示符启动时MSVC 根本不在搜索路径里编译一开始就会报找不到 cl.exe。第三是编译参数风格。setup.py 里如果写的是-O3、-stdc14、-fPIC这种 gcc 风格的参数MSVC 完全不认识会直接报非法参数。要在 Windows 上编译必须把这些参数改成/O2、/std:c17的 MSVC 风格同时还要给 nvcc 补上一些在 Linux 下默认就有的宏和选项。这就是为什么直接跑原版 setup.py 大概率失败的原因。这里其实有一个逻辑修改 setup.py 不是随意乱改而是要让编译工具链回到 Windows 语言体系里。搞清楚这条主线剩下的问题都是围绕它展开的。2. 环境准备先把四大件版本对齐2.1 版本矩阵Python、PyTorch、CUDA、VS 怎么配这一步别嫌麻烦我是直接把以下信息写在一张纸上的。版本对齐的逻辑是你的Python 版本决定兼容哪个PyTorch 版本PyTorch 版本决定官方提供哪个CUDA 编译版本的 wheelCUDA 版本又决定它支持哪个MSVC 版本。这条链一旦有一环是从网上随便装的后面编译时就会出现八竿子打不着的错误。我实际使用的组合是Python 3.10 PyTorch 2.1.2CUDA 12.1 版 CUDA Toolkit 12.1 Visual Studio 202217.9 以上。这套组合在 nvdiffrast 上没有任何问题。如果你显卡驱动比较老不想升级驱动那就选 PyTorch 2.1.2 的 cu118 版本 CUDA 11.8然后配合 VS2019 或 VS2022 都行。组件我的版本检查命令备注Python3.10python --version3.8~3.12 基本可用PyTorch2.1.2python -c import torch; print(torch.__version__)必须是带 CUDA 的版本CUDA Toolkit12.1nvcc --version与 PyTorch 的 CUDA 版本尽量一致Visual Studio2022 17.9在开发者命令行执行cl需安装 C 桌面开发工作负载这里有一个非常容易踩的坑很多人pip install torch装出来的是 CPU 版。PyTorch 在 Windows 上默认从 pypi 源安装时不带 CUDA 依赖这时编译 CUDA 扩展会直接报 Torch not compiled with CUDA enabled。检查方法很简单python -c import torch; print(torch.version.cuda)如果输出结果是None那说明当前 PyTorch 不支持 CUDA必须重新安装带 CUDA 的版本。官方安装命令一般是pip install torch2.1.2 --index-url https://download.pytorch.org/whl/cu121把 cu121 换成 cu118 就能装对应的 CUDA 11.8 版本。这一步没做对后面全白搭所以务必先确认。2.2 环境变量逐项配置与验证Windows 编译 CUDA 扩展环境变量至少要配齐 PATH、INCLUDE、LIB 三块。以 CUDA 12.1 为例CUDA_PATHC:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1 CUDA_HOME 与 CUDA_PATH 保持一致 PATH 追加 %CUDA_PATH%\bin;%CUDA_PATH%\libnvvp INCLUDE 追加 %CUDA_PATH%\include LIB 追加 %CUDA_PATH%\lib\x64注意 INCLUDE 和 LIB 原本可能是空的直接用新建就好如果以前配过其他 SDK 的目录务必用追加而不是覆盖。配置完成后开一个新的 cmd 窗口执行nvcc --version能看到版本号说明 PATH 生效再执行echo %INCLUDE%能看到 include 路径说明 INCLUDE 生效。还有一个更省事的办法直接通过 Visual Studio 的 x64 Native Tools Command Prompt for VS 2022 来编译。这个开发者命令提示符会自动把 MSVC 和 Windows SDK 的路径灌进环境变量你要做的只是在里面激活 conda 环境并保证 CUDA 的 bin 目录在 PATH 里。这种方式能少踩一半弯路我强烈推荐。注意如果你在一个普通 cmd 里执行 python setup.py第一步往往会报cl.exe 不是内部或外部命令。这不是 setup.py 的问题而是 MSVC 没有被加载进 PATH。方案就是上面说的换到开发者命令提示符里运行或者先在普通 cmd 里手动执行 vcvars64.bat路径大致是C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat。这些环境配置虽然琐碎但实际就是 Windows 下 C 编译的基座。很多人编译失败不是代码不行而是编译器根本没找到。2.3 别用 MinGW老老实实用 MSVC有些朋友不熟悉 MSVC看网上教程说装个 MinGW 或 TDM-GCC 也行于是拿 gcc 去编 CUDA 扩展。这里明确劝一句不要这么做。CUDA Toolkit 在 Windows 上官方只支持 MSVCMinGW 即使偶尔把 .cu 编过去了最后链接 PyTorch 扩展时也会因为 ABI 不一致、符号导出规则差异等问题产生各种玄学崩溃。与其花几个小时和 MinGW 搏斗不如老老实实装 VS 的 C 桌面开发组件一劳永逸。安装 VS 时勾选使用 C 的桌面开发确保里面包含 MSVC 编译器、Windows SDK、CMake 工具等。如果你的 VS 已经装好了但没选这个组件重新打开 Visual Studio Installer点修改把这个工作负载补上即可不需要重装整个 VS。这一步很容易被忽略但没它编译根本走不动。3. 修改 setup.py把编译参数拉回 Windows 的频道3.1 原版 setup.py 和 Windows 的冲突点在哪nvdiffrast 的 setup.py 用的是 PyTorch 的CUDAExtension和BuildExtension整体构建思路没问题问题出在具体的编译参数上。原版为 C 编译器和 nvcc 配置的参数默认偏向 Linux 工具链比如 gcc 风格的-O3、-stdc14这些参数到了 MSVC 下会被当成非法选项直接拒绝。还有一个隐蔽的问题MSVC 默认并不定义M_PI这类数学常量而 nvdiffrast 的 C/CUDA 代码里很可能会用到它。不加宏定义的话编译到某个头文件就会报M_PI undeclared identifier让人根本摸不着头脑。类似地还有strcpy的安全警告在 MSVC 下会以 C4996 的形式出现如果项目开启了警告即错误也会在编译中途被拦截。此外CUDA 侧的 nvcc 参数也需要针对可微渲染代码的特性做补全。--expt-relaxed-constexpr用于放宽设备侧 constexpr 的限制--expt-extended-lambda允许在设备代码中使用更灵活的 lambda 写法这两项在 Linux 版 setup.py 里可能已经有了但在 Windows 下手动补齐更稳妥。3.2 一份经过验证的 setup.py 改写方案下面是我的方案目标就是让 nvdiffrast 在 Windows MSVC nvcc 这套工具链下编译通过。注意源码路径要以你 clone 的实际仓库结构为准但编译参数和构建入口可以照抄import os import glob from setuptools import setup from torch.utils.cpp_extension import BuildExtension, CUDAExtension here os.path.dirname(os.path.abspath(__file__)) cuda_home os.environ.get(CUDA_HOME, rC:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1) def collect_sources(path): return sorted(glob.glob(os.path.join(here, src, path, *.cu)) glob.glob(os.path.join(here, src, path, *.cpp))) sources collect_sources(common) \ collect_sources(rasterize) \ collect_sources(texture) extra_compile_args { cxx: [ /std:c17, /O2, /D_USE_MATH_DEFINES, /D_CRT_SECURE_NO_WARNINGS, /D_SCL_SECURE_NO_WARNINGS, ], nvcc: [ -O3, --expt-relaxed-constexpr, --expt-extended-lambda, -D_USE_MATH_DEFINES, -Wno-deprecated-declarations, ], } setup( namenvdiffrast, version0.3.1, packages[nvdiffrast], ext_modules[ CUDAExtension( nvdiffrast.common._C, sourcessources, include_dirs[os.path.join(cuda_home, include)], library_dirs[os.path.join(cuda_home, lib, x64)], extra_compile_argsextra_compile_args, ) ], cmdclass{build_ext: BuildExtension}, )有几点解释一下。第一/D_USE_MATH_DEFINES是最容易被忽略的救命参数。没有它MSVC 下M_PI未定义的报错会让你以为是自己三角函数写错了。第二/std:c17必须显式指定。MSVC 默认标准已经很老了而 nvdiffrast 使用了不少相对现代的 C 特性不指定版本就会遇到各种语法层面的幺蛾子。第三include_dirs 和 library_dirs 不用写 PyTorch 的路径因为 BuildExtension 会自动注入 PyTorch 和 Python 的头文件路径但 CUDA 的头文件路径需要你补上避免编译到一半找不到cuda_runtime.h。这个写法直接替换原版 setup.py 即可。如果你的仓库源码里还有扩展模块比如 antialias、radiance 等子模块把对应的 .cu/.cpp 文件路径也加进 sources 列表。扩展模块的名字要和仓库代码里 import 的模块名保持一致仓库代码里写的是from nvdiffrast.common import _C那扩展名就叫nvdiffrast.common._C。3.3 顺手再优化一个编译速度参数Windows 编译 CUDA 扩展是真的慢尤其在默认情况下它会尝试生成所有显卡架构的代码从 compute_70 一路编译到 compute_120动辄就是五分钟起步内存 16G 以下还会直接崩给你看。这里有个小技巧提前设置环境变量TORCH_CUDA_ARCH_LIST把你本机显卡的算力架构列出来让它只编需要的那一个。这个环境变量只影响编译产物里包含的 GPU machine code不影响功能逻辑。比如你用的是 RTX 4080算力是 8.9设置set TORCH_CUDA_ARCH_LIST8.9之后编译时间能缩短一半以上。如果你的代码要发到不同显卡上跑可以用分号写多个架构set TORCH_CUDA_ARCH_LIST8.6;8.9;12.0但这样编译时间会按架构数量成倍增加自己权衡就好。4. 编译执行全流程与现场报错实录4.1 从 clone 到编译产物的完整命令序列我实际执行的完整过程如下。先到仓库目录创建并激活虚拟环境然后装 PyTorch带 CUDA再修改 setup.py最后编译。git clone https://github.com/NVlabs/nvdiffrast.git cd nvdiffrast conda create -n nvdiff python3.10 -y conda activate nvdiff pip install torch2.1.2 --index-url https://download.pytorch.org/whl/cu121 pip install numpy # 修改 setup.py按上面的方案替换编译参数 # 然后编译 python setup.py build_ext --inplacebuild_ext --inplace会在 nvdiffrast 仓库根目录生成 .pyd 文件导入包时优先加载当前目录下的扩展模块所以这个过程可以直接用于测试。成功之后你也可以再执行pip install .把它装进 site-packages正式供其他项目引用。整个编译过程大约需要 2~5 分钟取决于机器和 TORCH_CUDA_ARCH_LIST 的设置。编译结束后 nvdiffrast 目录下会多出几个后缀带 .pyd 的文件例如nvdiffrast.common._C.cp310-win_amd64.pyd看到它基本就说明编译链路走通了。4.2 我实际踩过的报错与排查思路编译过程中我先后遇到过四类报错每一类的排查思路都不一样这里直接按出现频率列出来。第一类Torch not compiled with CUDA enabled这个其实在装 PyTorch 时就该确认但我一开始确实图省事直接pip install torch装了 CPU 版。编译 nvdiffrast 到一半BuildExtension 检查 torch.cuda 是否可用时直接抛异常。解决办法就是卸载重装带 CUDA 的版本没有捷径。第二类error C2065: M_PI: undeclared identifier看到这个报错时我一度以为是源码缺头文件后来发现是 MSVC 默认不定义数学常量的问题。在 setup.py 的 cxx 参数里补上/D_USE_MATH_DEFINES之后问题消失。如果你不想改 setup.py也可以在源码顶部统一加#define _USE_MATH_DEFINES但那样要动多处文件不如改配置来得干净。第三类C1083: Cannot open include file: cuda_runtime.h这说明编译器把头文件搜索路径找遍了也没找到 CUDA 的头文件。先确认环境变量里有没有 CUDA_PATH然后在 setup.py 的 include_dirs 里手动指向C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1\include。两种情况都处理完就正常了。第四类nvcc fatal : Unsupported gpu architecture compute_XX通常是 TORCH_CUDA_ARCH_LIST 写错了算力数字。去显卡厂商官网查一下自己显卡的 Compute Capability填成正确的数字即可。下面是速查表报错特征根因处理办法Torch not compiled with CUDA enabledPyTorch 装成了 CPU 版重装--index-url .../cu121版本M_PI undeclared identifierMSVC 未定义数学常量setup.py 加/D_USE_MATH_DEFINESCannot open include file: cuda_runtime.hCUDA include 路径没配上设置 INCLUDE或 setup.py 加 include_dirscannot open file cudart.libCUDA LIB 路径没配上设置 LIB 指向%CUDA_PATH%\lib\x64cl.exe 不是内部或外部命令MSVC 不在 PATH使用 VS 开发者命令提示符nvcc fatal : Unsupported gpu architecture架构数字写错查准 Compute Capability最后补充一个细节。如果你用的 Python 是 3.12 及以上可能会遇到No module named distutils的报错这是因为 distutils 在新版本里被移除。可以升级 setuptools 解决pip install --upgrade setuptools。还有整个编译过程中 C 编译器和 nvcc 的输出信息量巨大看到密密麻麻的警告不要慌只要最后不出现 error 并生成了 .pyd就是成功了。5. 验证编译产物跑一个最小可微光栅化5.1 一条 python 命令确认导入正常装好之后先确认包能不能被 importpython -c import nvdiffrast.torch as dr; print(dr.__file__)如果输出的是仓库目录下的路径而不是报 ImportError说明扩展模块加载成功了。注意 Windows 下如果出现OSError: [WinError 126] 找不到指定的模块之类错误通常是因为额外依赖的动态库不在 PATH 里检查一下 CUDA 的 bin 目录是否在 PATH 中或者把%CUDA_PATH%\bin加到系统 PATH 后重启终端再试。5.2 用三个顶点验证光栅化确实可微下面这个脚本画一个简单三角形光栅化到 256x256 的图像上并输出光栅化结果的尺寸和非零片段数import torch import nvdiffrast.torch as dr # batch1一个三角形 v_pos torch.tensor([[ [-0.5, -0.5, 0.0], [ 0.5, -0.5, 0.0], [ 0.0, 0.5, 0.0] ]], dtypetorch.float32, devicecuda) t_pos_idx torch.tensor([[0, 1, 2]], dtypetorch.int32, devicecuda) mvp torch.eye(4, dtypetorch.float32, devicecuda) rast, _ dr.rasterize(mvp, v_pos, t_pos_idx, (256, 256)) print(rast shape:, rast.shape) print(valid fragments:, (rast[..., 3] 0).sum().item())如果一切正常你会看到非零片段数大于 0说明光栅化确实在 GPU 上跑起来了。至于可微性nvdiffrast 的核心优势就是光栅化输出的属性对输入顶点坐标是可导的你可以把rast.requires_grad打开试一下跑一个简单的反向传播确认梯度能回流到v_pos上。5.3 几个后续使用的细节提醒编译成功只是第一步实际项目里还有几个容易忽略的点。一个是 Compute Capability 与算子兼容性。如果换机器跑导出的 .pyd 只在编译时指定的架构下可用新机器需要重新编译或从一开始就在 TORCH_CUDA_ARCH_LIST 里把常用架构都编上。另一个是 float16 与混合精度。nvdiffrast 的纹理采样、抗锯齿算子对数据类型比较敏感使用 AMP 自动混合精度时记得检查中间张量的 dtype不然容易在插值阶段触发类型不匹配的报错虽然不常见但真遇到时很费解。再一个是多线程加载。可微光栅化本身非常快但如果你在训练循环里反复创建和释放光栅化输出的大张量显存碎片会逐渐恶化。建议在热循环外预先分配 buffer或者复用 rast 输出结构能明显减少显存波动。这些点是我在后续项目里实实在在碰到过的提前知道能省不少调试时间。最后分享一点经验。这一整套流程走下来我心里最深的体感是Windows 下编译这类带 CUDA 扩展的库本质不是在挑战代码本身而是在做版本和路径的对表。CUDA 版本锁死 VS 版本PyTorch 版本锁死 CUDA 版本Python 版本又锁死 PyTorch 版本这一环扣一环的链条看着枯燥却是整个流程里最不能出错的环节。我建议你在开工前把四个版本号都写下来一项项核对比你遇到报错再回头查高效十倍。至于 setup.py 的编译参数记住一句话让 MSVC 用 MSVC 的命令让 nvcc 用 nvcc 的命令参数风格不要混用。这套环境配置方案其实不限于 nvdiffrast任何要在 Windows 上编译的 PyTorch CUDA 扩展都可以参考一次折腾清楚后面就都顺了。
返回列表