一些编译cuda的笔记
环境配置
CUDA支持离卡编译,因此CUDA Toolkit和其它各路编译套件完全可以装在没有N卡的机器上,然后完成所有编译工作。但是编译完的程序当然还是需要在有N卡的机器上才能运行
- Visual Studio: C/C++编译器
如果电脑原先没有安装VS,那只安装VS Build Tools就足够了:从微软官网获取Visual Studio 2026生成工具(页面下拉,在所有下载里面选择“用于Visual Studio的工具”就能找到。或者,如果需要兼容性,可以选择VS2022或者VS2019然后在安装的时候,选择“使用C++的桌面开发”。组件可以适当精简,比如截至当前的cuda版本(13.0),NVIDIA官方支持2019和2022
MSVC AddressSanitizer和vcpkg包管理器可以不需要。如下图所示:

安装完成后,编译器默认并不会被添加到环境变量,也不建议直接把编译器添加到PATH。
在开始菜单中可以看到专用的工具项,从这里启动就可以进入相应的cmd和powershell:

为了简单快捷,可以自己在喜欢的位置创建一个空文件夹,里面编写一个setcl.bat内容如下(假设VS Build Tools安装到默认路径):然后把这个文件夹添加进环境变量1
2@echo off
"C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Auxiliary\Build\vcvars64.bat"PATH,之后只要在任意位置运行setcl命令,就能等效于立即启动x64 Native Tools Command Prompt for VS如果想要测试,可以编译一个示例,看看是否正常工作。示例代码来自Microsoft官方文档一般都是在64位主机上编译目标为64位的程序,所以干脆直接启动这个,免得混淆。
如果希望在命令行快捷启动其它的,可以按照开始菜单的启动项右键→打开文件位置→右键快捷方式→属性查看对应的启动项到底是在执行什么指令,然后用类似的方式搓成脚本。▶Chello.c
1
2
3
4
5
6
7#include <stdio.h>
int main()
{
printf("Hello, World! This is a native C program compiled on the command line.\n");
return 0;
}1
2cl hello.c
hello.exe▶C++hello.cpp
1
2
3
4
5
6#include <iostream>
using namespace std;
int main()
{
cout << "Hello, world, from Microsoft C++!" << endl;
}1
2cl /EHsc hello.cpp
hello.exe - Cuda Toolkit: Cuda编译器
前往NVIDIA Cuda Toolkit官网下载,安装的时候自定义组件仅选择Runtime和Development两项即可:
安装完成后,同样可以编译一个示例测试一下。注意虽然nvcc直接添加进PATH了,但是仍然需要截至cuda 13.0 update 2版本,nvcc尚未官方支持vs2026。如果使用vs2026,会提示编译器不受支持,需要在编译时添加参数
-allow-unsupported-compiler。可以使用环境变量把编译参数固定下来,NVCC_PREPEND_FLAGS将编译参数插入在其它参数的开头,NVCC_APPEND_FLAGS将编译参数追加在其它参数的末尾。cl.exe提供C/C++编译器,所以仍然需要确保VS环境处于激活状态。▶CUDAhello.cu
1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <stdio.h>
__global__ void hello_from_gpu ()
{
printf("Hello from GPU! \n");
}
int main (void)
{
hello_from_gpu<<<4,4>>>();
cudaDeviceSynchronize();
return 0;
}编译后会输出16行”Hello from GPU!”
1
2nvcc hello.cu
hello.exe - cuDNN库:可能需要
前往NVIDIA cuDNN官网,下载安装即可,可以在自定义里面选择是需要cuda13的版本还是cuda12的版本。虽然写的是12.9,但是对cuda12全兼容,如果cuda用了12.8也可以放心使用。
- 其它可选工具
建议全局安装一个cmake和ninja,如果不安装也问题不大,pip里面也有这两个工具。1
winget install Kitware.CMake Ninja-build.Ninja
编译记录
xformers
由于flash-attn2目前最后一个能在Windows上编译的版本是2.7.4,从2.8开始截止当前版本2.8.3在Windows上均会编译失败,因此xformers内置的flash-attn2在升级到2.8之后也无法在Windows上通过编译。
xformers最后一个绑定flash-attn2.7.4的版本是0.0.30,已知可以稳定配合pytorch2.7,因此如果官方包遇到兼容性问题(尤其是Blackwell GPU),自己编译一版0.0.30去配合torch2.7.1+cu128应该是个不错的选择。
从0.0.31开始,Windows上无法编译出含flash-attn2的xformers轮子。不是很建议尝试在旧xformers上强行为新版torch编译,适配大概率会比较差。
或者,编译一版不含flash attention的xformers,只使用mem eff优化也存在可行性,并且这样的话,编译所需时间会极大幅度缩短,内存压力也会极大幅度减小。即使没有fa2,单纯的xformers经测试也会比torch sdp更省显存。
- 获取源码:从github获取最新源码,或者从pypi获取指定版本的源码压缩包。 从github获取的时候要clone submodule:
从这个commit开始,xformers主动提前适配了torch2.10nightly版本的API,弃用了对torch2.9版本的支持。
因此,如果需要为torch2.9编译,那就不能直接连同submodules一起clone,而需要使用git checkout或者git reset --hard先切回旧版本:1
2
3git clone https://github.com/facebookresearch/xformers.git
cd xformers
git reset --hard 513e7a43aba20b22b6b52a32a5d17896ff0e2bd7然后再执行下文的
git submodule update --init --recursive或者如果1
git clone https://github.com/facebookresearch/xformers.git --recurse-submodulesclone的时候忘记,那么进入xformers源码根目录下执行:对于pypi获取的压缩包,解压并且删除里面的1
git submodule update --init --recursivexformers.egg-info文件夹。git在默认设置下使用Windows的旧版API,其不支持长路径,需要通过git config开启长路径支持:
1
git config --global core.longpaths true或者,如果有洁癖不想动全局配置,那就
clone的时候不要--recursive-submodules,然后为仓库单独开启长路径之后再执行git submodule update。
如果没有开启长路径,然后clone期间出现Filename is too long报错,不要抱有任何侥幸心理,把源码文件夹整个删除,开启长路径支持之后再重新整个拉取,这是确保文件夹里的源码和预期一致的最稳妥的方法。 - 准备环境:建议使用venv虚拟环境,然后
pip安装计划和xformers搭配的torch以及xformers的依赖numpy。如果前面没有安装全局的ninja,这里就一起安装一个,否则没有并行编译会纯纯龟速。
与此同时,如果有用uv的习惯,那后面启动编译直接用uv就好(也可以pip install uv);或者用更官方一点的方式,pip install build setuptools wheel。 - 微调
setup.py(可选):- 首先咱不喜欢xformers在版本号里面记录编译日期;用git commit hash就足以指示编译的准确版本。因此删除开头的
import datetime;然后定位到get_local_version_suffix()函数,修改:1
2
3
4
5
6
7
8
9
10def get_local_version_suffix() -> str:
if not (Path(__file__).parent / ".git").is_dir():
# Most likely installing from a source distribution
return ""
- date_suffix = datetime.datetime.now().strftime("%Y%m%d")
git_hash = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"], cwd=Path(__file__).parent
).decode("ascii")[:-1]
- return f"+{git_hash}.d{date_suffix}"
+ return f"+{git_hash}" - 然后,由于flash attention v3仅支持A100,H100等计算卡,不支持Geforce游戏卡;计算卡要用也是在Linux上用,而Linux上既有满血的torch flash attn2也有xformers官方的满血fa3,因此Linux上自编译xformers的必要性也小些。所以修改源码直接屏蔽fa3编译:
1
2
3
4def get_flash_attention3_nvcc_archs_flags(cuda_version: int):
+ return []
if os.getenv("XFORMERS_DISABLE_FLASH_ATTN", "0") != "0":
return [] - 下调nvcc threads:由于nvcc编译flash attn部分极其费内存,把nvcc threads调小降低内存压力:
1
2
3
4
5
6
7
8if cuda_version >= 1102:
nvcc_flags += [
"--threads",
- "4",
+ "1",
"--ptxas-options=-v",
]
if sys.platform == "win32":
- 首先咱不喜欢xformers在版本号里面记录编译日期;用git commit hash就足以指示编译的准确版本。因此删除开头的
- 设置环境变量:
XFORMERS_DISABLE_FLASH_ATTN根据编译需求设置:
设为0:开启xformers内置的flash-attn,包括fa2和fa3;此时这个选项仅是开启fa的必要条件,开启后上文屏蔽fa3的设置依旧有效;在高版本下编译fa2极大可能失败。
设为1:直接禁用fa2和fa3,只编译最基础的xformers mem eff优化。
对于旧版本xformers0.0.30,这个环境变量默认会取0;当前版本0.0.33已经改为默认取1XFORMERS_PT_FLASH_ATTN设置为0
Windows下pytorch根本没有编译flash attn组件,此选项让xformers编译自己打包的的flash attn,如果xformers版本高于0.0.30则不要开启,大概率会导致编译失败。
如果上一个环境变量已经设为1禁用了flash attn,那么这个环境变量会被无视。TORCH_CUDA_ARCH_LIST按以下规则设置:比较重要
首先根据NVIDIA文档确定GPU与cuda arch版本号的关系。然后确定编译目标:不在此表上的GPU已经不在cuda13的支持范围;如果需要在cuda12或者旧版本上为老显卡编译,参看NVIDIA文档
- 如果只想自用,那就只填自己的(例如RTX50系填
12.0); - 如果想自己选择几代不同的架构,就用分号分隔(例如如果想同时支持RTX40和50系,填
8.9;12.0); - 如果想搞成类似官方的泛用包,这里(在huggingface上)记录了一些xformers官方用过的环境变量可以照抄;
- 如果不设置,xformers的默认行为会有所差异:flash attn2模块里面设定了一个默认列表,会根据cuda版本的不同启用不同的默认列表,然后将flash attn2的模块为列表中的所有架构编译;flash attn3模块则是检测本机是否是8.0(A100等)或者9.0(H100等),如果是,就针对本机架构编译,否就直接不编译。其它模块不确定,但是应该是针对本机架构编译。为了确保精准控制自己的需求,建议设置;
- 如果手动设置的列表里面不含8.0和9.0,那么fa3模块会自动不启用,因此上文的手动屏蔽此时不再需要。
- 如果只想自用,那就只填自己的(例如RTX50系填
XFORMERS_PT_CUTLASS_ATTN可选设置,控制是否借用torch的cutlass的开关,设置为0禁用,默认启用。这个影响不大,不设保持默认行为也可,稍微节约一点点编译时间;设为0是模拟旧版本的行为MAX_JOBS控制ninja的并行数:特别需要注意,因为xformers编译过程中,部分环节特别特别吃内存;而ninja的默认行为是把并行数直接开到CPU线程数+2,对于常规的消费级电脑,如果编译fa2,这个设置几乎一定会爆内存。
作为参考,在fa2能通过编译的时候,咱自己的个人配置MAX_JOBS=2能不爆16GB内存,开到3就会开始触发大量虚拟内存交换,甚至会导致中断退出。根据自己的内存调整即可。咱测试的时候没动过nvcc threads,如果按照上文把nvcc threads降低,那有可能这个数值可以开高点,这样至少纯C++无CUDA的部分可以编译快点。
如果选择不编译flash attention部分,在前面把nvcc threads减到1的前提下16G内存可以直接开8,编译很快就会完成。NVCC_FLAGS:对于vs2026如果上文没有配置-allow-unsupported-compiler,xformers的编译程序接受这个环境变量作为追加编译参数,设置在这也是可以的。
- 启动编译: 导航到xformers源码的根路径,如果使用uv,就执行
可以使用
chcp 65001把输出切换到UTF-8编码,这样msvc编译器输出的中文不会乱码,可以更清晰地查看日志输出。如果使用build,在以下两种命令选一种;build的默认日志可能输出没那么多,如果缺少安全感就加个1
uv build . --no-build-isolation-v多看看日志刷屏注意上述命令的默认行为是,先把源码打包成源码包(sdist),就像pip的源码压缩包那样;然后对源码包执行编译。1
2python -m build . -n
pyproject-build . -n
如果想直接只编译whl,那就额外添加--wheel参数。
经过漫长的编译之后,产物会出现在dist文件夹,妥善保存。按照python现行规范,直接用
python执行setup.py的行为都应当被替代:旧命令 新命令 python setup.py installpip install .python setup.py developpip install -e .python setup.py sdist,python setup.py build以及python setup.py bdist_wheelpython -m build其中后者会默认在TEMP目录下创建隔离的编译环境,视情况添加类似
--no-build-isolation的参数改变这个行为。
上游flash-attn可能要注意的
额外提一下编译flash-attn可能需要注意的事项,和编译xformers无关,编译xformers不用看本节。