简介:面向 Windows 平台 Qt 开发者的 CUDA 集成示例工程,解决在 Qt Creator 中引入 GPU 并行计算时工程结构不清、编译配置无从下手的问题,适合具备一定 C++ 与 Qt 基础、希望快速跑通 CUDA 项目的初中级开发者。压缩包共 7 个文件、约 5KB,包含 2 个 cpp 源文件、1 个 cu 核函数文件、1 个 h 头文件、1 个 ui 界面文件、1 个 pro 工程文件及 user 配置,体量轻巧却覆盖了从界面到 GPU 代码的完整最小闭环。已有 971 人学习下载。读者可据此查看 pro 工程中如何登记 cu 文件与链接 CUDA 库,对照主窗口与入口文件的调用关系理解主机端与设备端代码的组织方式,并以示例核函数为模板替换成自己的计算逻辑;同时可参考 user 配置了解构建与运行参数的设置思路,减少环境搭建与编译报错时的试错成本,为后续扩展图像处理、矩阵运算等并行任务提供可复用起点。
1. Qt Creator 里加 CUDA 卡在哪:Windows 下的构建图问题
在 Qt Creator 里按下构建,控制台冒出nvcc fatal : Cannot find compiler 'cl.exe' in PATH,或者更难受的unresolved external symbol __cudaRegisterFatBinary——这是我在 Windows 上做 Qt + CUDA 项目最常见的两个开场。qtcreator 中添加 cuda 这件事,真正卡人的地方不在 kernel 写得对不对,而在于把一个由 nvcc 驱动的编译单元塞进 qmake 或 CMake 的构建图里,同时让 MSVC 工具集、Qt Kit、CUDA Toolkit 三方的版本、位数、运行库全部对上。对不上,症状就是满屏红波浪线、链接期炸库、调试器一进 kernel 就断。
这篇笔记按我自己的落地顺序走:先把工具链对齐,再分别给 qmake 和 CMake 两套工程模板,然后集中收拾链接与架构类的坑,最后用一段冒烟代码确认整条链路真的通了。适合手上已有 Qt 上位机、想用 GPU 做图像预处理或矩阵运算的从业者,也适合第一次在 Windows 上把 .cu 文件拖进 Qt 工程的人。
2. 先对齐工具链:MSVC、nvcc 与 Qt Kit 的三方约束
2.1 三个不能碰的硬约束
在 Windows 上,CUDA 的主机编译器只有 MSVC 一条路走得顺(clang-cl 那条线另说,工程里少见)。这意味着 Qt 的 Kit、nvcc 调用的 cl.exe、CUDA Toolkit 自身支持的最高 MSVC 版本,必须是同一套。约束有三条:
第一,位数必须一致。Qt 6 在 Windows 上只发 64 位预编译包,CUDA 运行时库也走lib/x64,任何一环选了 32 位,链接期必然LNK1112: module machine type 'x86' conflicts with target machine type 'x64'。这条最好查,反而最少翻车。
第二,MSVC 工具集版本要在 CUDA 的接受范围内。常见情况是 CUDA 12 系列认 VS 2019 的 v142 和 VS 2022 的 v143,CUDA 11.x 对 v143 属于「能编但会警告」,需要给 nvcc 加-allow-unsupported-compiler才能过去。我一般会先看nvcc -V输出里的 host compiler 一行,再看 VS 安装目录下VC\Tools\MSVC\里那个版本号,两者对上再动手。
第三,Qt Kit 里的编译器绝对路径要指向同一个 MSVC 实例。机器上装了两个 VS 版本时,Qt Creator 自动检测出来的 Kit 可能挂在旧的 v142 上,而 nvcc 实际调的是新的 v143,两边 STL 头文件混用,报错位置会飘到xmemory里去,非常难查。
提示:装完 CUDA Toolkit 后不要急着在 Qt Creator 里加 Kit,先在普通命令行里把下面三条命令跑通,跑不通就说明系统层面还没对齐,进 IDE 只会更乱。
2.2 命令行三连验证
打开「x64 Native Tools Command Prompt」(不是普通 cmd),依次执行:
:: 1. 确认 nvcc 版本与它默认使用的主机编译器 nvcc -V :: 2. 确认当前环境里的 cl.exe 就是 Qt Kit 要用的那个 where cl cl :: 3. 确认 Qt 的 qmake 版本与位数 qmake -vnvcc -V的输出里会有一行Cuda compilation tools, release 12.x, V12.x.xx,同时如果主机编译器有问题,它会直接告诉你Cannot find compiler 'cl.exe' in PATH。where cl返回的路径要和 Qt Creator 里 Kit 显示的编译器路径前缀一致,比如都在...\VC\Tools\MSVC\14.3x.xxxxx\bin\Hostx64\x64\下面。qmake -v的Using Qt version一行末尾会标_64,没标的赶紧换 Kit。
这三条命令的作用是把「IDE 里看不到的环境变量」摊到明面上。Qt Creator 从桌面快捷方式启动时,继承的是用户级环境变量,不是 VS 开发者命令行那套。所以哪怕你在命令行里nvcc跑得好好的,进 Qt Creator 照样找不到 cl.exe——这不是玄学,是环境变量作用域问题,后面第 3 章会用-ccbin彻底绕开它。
2.3 在 Qt Creator 里锁定 Kit
打开「工具 → 选项 → Kits」,先看「编译器」页里 C++ 那一栏有几个 MSVC 条目,记住你打算用的那个的完整路径。再回到「构建套件」页,新建或复制一个 Kit,把以下几项固定下来:
| 配置项 | 取值 | 说明 |
|---|---|---|
| 编译器 C++ | 上一步记下的 cl.exe | 必须和 nvcc 的 host compiler 同源 |
| Qt 版本 | Qt 6.x MSVC2022 64bit | 位数与 CUDA 运行库一致 |
| 调试器 | cdb.exe(Windows SDK 自带) | gdb 调不了 MSVC 产物 |
| CMake 生成器 | Ninja | 比 NMake 快,且对 CUDA 语言支持更稳 |
| 环境 | 手动加入 CUDA 的 bin 目录 | 见下方说明 |
「环境」那一栏点「更改」,把C:\Program Files\CUDA\Toolkit\v12.x\bin追加到PATH前面。这样做的目的是让 Qt Creator 启动的子进程能找到nvcc.exe,但注意——它仍然不能保证 nvcc 找到 cl.exe,因为 cl.exe 不在 PATH 里,只在 VS 的开发者环境里。这个缺口必须靠-ccbin补,下面第 3 章会给出写法。
3. qmake 工程接入:用 QMAKE_EXTRA_COMPILERS 挂 nvcc
3.1 为什么必须用 EXTRA_COMPILERS
qmake 不认识.cu后缀。你把 .cu 文件写进SOURCES,qmake 会把它当未知文件默默忽略,最后链接期报一堆unresolved external symbol——典型的黑匣子现象:编译日志干干净净,链接一塌糊涂。所以正确姿势是用QMAKE_EXTRA_COMPILERS声明一条自定义编译规则,把.cu → .obj这一步交给 nvcc。
这条规则的几个关键字段:
cuda.input:匹配哪些源文件,通常写CUDA_SOURCES变量。cuda.output:产出 obj 的路径,必须落在OBJECTS_DIR或OUT_PWD下,否则清理构建时会残留。cuda.commands:真正执行的命令行,nvcc 的路径建议写绝对路径。cuda.variable_out:把产物挂到OBJECTS变量上,链接器才看得见。cuda.name:在编译输出面板里显示的名字,排错时有用。
3.2 一份可直接抄的 .pro 片段
# ---- CUDA 环境定义,路径按实际安装位置改 ---- CUDA_DIR = "C:/Program Files/CUDA/Toolkit/v12.4" CUDA_ARCH = sm_75 # 按显卡算力改:20系=75/86,30系=86,40系=89 CUDA_BIN = $$CUDA_DIR/bin # nvcc 找不到 cl.exe 的根治办法:把 Qt Kit 的 C++ 编译器目录喂给 -ccbin MSVC_BIN = $$dirname(QMAKE_CXX) # QMAKE_CXX 是 cl.exe 全路径 INCLUDEPATH += $$CUDA_DIR/include DEPENDPATH += $$CUDA_DIR/include # 运行库:动态链接 cudart,Debug 版 Qt 下用 cudartd 并不存在,统一用 cudart LIBS += -L$$CUDA_DIR/lib/x64 -lcudart # ---- 声明 .cu 文件 ---- CUDA_SOURCES += kernels/gpu_smooth.cu \ kernels/gpu_blur.cu # ---- 自定义编译规则 ---- cuda.input = CUDA_SOURCES cuda.name = CUDA_COMPILE cuda.output = $$OUT_PWD/$$OBJECTS_DIR/${QMAKE_FILE_BASE}_cuda.obj cuda.variable_out = OBJECTS cuda.dependency_type = TYPE_C # 注意:-Xcompiler 后的 MSVC 选项要加引号,否则 qmake 会吃掉斜杠 cuda.commands = $$CUDA_BIN/nvcc -c \ -arch=$$CUDA_ARCH \ -ccbin \"$$MSVC_BIN\" \ -Xcompiler \"/MD /EHsc /Zi\" \ -I$$CUDA_DIR/include \ -I$$PWD \ --use_fast_math \ ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} QMAKE_EXTRA_COMPILERS += cuda逐段解释几个容易被忽略的点。
$$dirname(QMAKE_CXX)这一步是整个片段的核心。它从 qmake 已知的 C++ 编译器路径里反推出 MSVC 的 bin 目录,再通过-ccbin传给 nvcc。这样无论 Qt Creator 是正常启动还是从任务栏启动,nvcc 都能找到 cl.exe,不用去改系统 PATH,也不会污染全局环境。前提是你的 Kit 里编译器配置是对的,如果 Kit 挂的是 mingw,-ccbin会指向一个跟 MSVC 无关的目录,nvcc 立刻报错——这也算一种快速自检。
-Xcompiler "/MD /EHsc /Zi"里的/MD必须和 Qt 的构建配置一致。Qt 官方 MSVC 包是动态运行库,Release 下是/MD,Debug 下是/MDd。nvcc 默认按/MD编,所以 Debug 构建时 .cu 产出的 obj 用/MD、Qt 侧用/MDd,混进同一个 exe 里会出现 CRT 堆不匹配,症状是运行时在free或operator delete处随机崩,堆栈指向ntdll。稳妥写法是按构建类型分支:
CONFIG(debug, debug|release) { CUDA_RUNTIME = /MDd } else { CUDA_RUNTIME = /MD } cuda.commands = ... -Xcompiler \"$$CUDA_RUNTIME /EHsc /Zi\" ...cuda.output里的${QMAKE_FILE_BASE}是 qmake 内置变量,取的是不含扩展名的文件名。如果你的工程里有两个不同目录下的同名 .cu(比如ui/render.cu和core/render.cu),这个命名会撞车,后编译的覆盖前面的,链接时用的可能是错的 obj。血泪经验:要么保证文件名全局唯一,要么在 output 里拼上$$dirname(QMAKE_FILE_IN)的处理结果。
3.3 头文件与接口边界
.cu 文件里不要 include Qt 的任何头文件。nvcc 前端对某些 C++ 模板特性和 Qt 的宏展开容忍度不高,一旦 Q_OBJECT 宏进到 .cu 里,报错信息会指向 moc 生成文件,完全看不出源头。我一般把边界划成两层:
// kernels/gpu_smooth.h —— 纯 C 接口,Qt 侧只认这个头 #pragma once #ifdef __cplusplus extern "C" { #endif // 输入输出都用裸指针 + 尺寸,不出现 QString / QImage int gpu_smooth_rgba(unsigned char* d_src, unsigned char* d_dst, int width, int height, int radius); #ifdef __cplusplus } #endifQt 侧拿到 QImage 后,用bits()取裸指针传给封装函数,由.cu内部完成cudaMalloc/cudaMemcpy或直接操作设备指针。这样切的好处是 .cu 完全独立于 Qt,可以单独用 nvcc 命令行编译测试,出问题能二分定位是 CUDA 侧还是 Qt 侧。
.cu文件也要记得加进DISTFILES,否则 Qt Creator 的项目树里看不到它,改完不会触发重新构建,你会以为改了没生效。
3.4 增量编译的坑
qmake 的自定义编译器默认不带依赖生成,改 .cu 里 include 的头文件不会触发重编。常见做法是给规则加依赖命令,但 nvcc 的-M在 Windows 上输出格式和 qmake 期望的不完全兼容,容易引入新的解析错误。我的处理是放弃自动依赖,改成在 .pro 里显式列头文件:
cuda.depends = $$CUDA_DIR/include/cuda_runtime.h \ $$PWD/kernels/gpu_common.h数量不多时这样最省心。真要上规模,还是转 CMake,那边 CUDA 作为一等语言,依赖追踪是现成的。
4. CMake 工程接入:enable_language(CUDA) 与 Qt 的协同
4.1 CMake 侧的三条选型理由
新工程我一律推荐 CMake,理由不是时髦,而是三条具体收益。一是 CUDA 在 CMake 3.8 之后是官方支持的语言,.cu自动识别,不用手写编译规则。二是依赖追踪自动生成,改头文件会正确触发重编。三是架构列表通过CMAKE_CUDA_ARCHITECTURES统一管理,多卡分发和多架构 fatbin 只要改一行。
代价是 CMake 版本必须跟上。CMAKE_CUDA_ARCHITECTURES从 3.18 引入,并且受策略 CMP0104 控制:3.18 之后如果这个变量为空,配置阶段直接报错。所以cmake_minimum_required(VERSION 3.20)是比较安全的起点。
4.2 一份可用的 CMakeLists.txt
cmake_minimum_required(VERSION 3.20) # 关键:CUDA 必须在 project() 里声明为语言,后面 .cu 才会被自动识别 project(GpuQtDemo LANGUAGES CXX CUDA) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 目标显卡架构;不写会走 CMake 的策略默认值,可能编不出你要的 fatbin set(CMAKE_CUDA_ARCHITECTURES 75 86) set(CMAKE_CUDA_STANDARD 17) # Qt 部分 find_package(Qt6 REQUIRED COMPONENTS Widgets) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) # CUDA 运行时库,用 imported target 比手写 -lcudart 稳 find_package(CUDAToolkit REQUIRED) add_executable(GpuQtDemo src/main.cpp src/mainwindow.cpp kernels/gpu_smooth.cu # 直接列进来即可,不需要 set_source_files_properties ) target_include_directories(GpuQtDemo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/kernels ${CUDAToolkit_INCLUDE_DIRS} ) target_link_libraries(GpuQtDemo PRIVATE Qt6::Widgets CUDA::cudart ) # Debug 构建时给 nvcc 加 -G,否则断点进不了 kernel target_compile_options(GpuQtDemo PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:-Xptxas=-v> $<$<AND:$<COMPILE_LANGUAGE:CUDA>,$<CONFIG:Debug>>:-G> )几个参数值得单独说。
LANGUAGES CXX CUDA里 CUDA 的顺序无所谓,但一定要在project()里出现。漏了这一步,后面add_executable里放 .cu 会被 CMake 当成未知扩展名忽略,跟 qmake 那个坑一模一样。
CMAKE_CUDA_ARCHITECTURES 75 86生成的是多架构 fatbin,同一个 exe 可以跑在 20 系和 30 系上。代价是编译时间和产物体积上升。只在自己机器上跑,写一个就行。写成native是 3.24 之后的能力,自动探测本机算力,单机开发时很方便,但产物换机器就可能跑不了。
CUDA::cudart是 CUDAToolkit 包提供的 imported target,它会自动带上正确的 include 路径、库目录和 64/32 位区分。手写-lcudart时如果漏了-L,或者路径写了lib/x86,链接期就炸。用 target 能避开这一类。
-Xptxas=-v会在编译时打印每个 kernel 的寄存器用量和 shared memory 占用,调优时非常有用。-G只在 Debug 下开,注意它和--use_fast_math互斥,同时开会导致 nvcc 直接报错。
4.3 在 Qt Creator 里打开 CMake 工程
「文件 → 打开文件或项目」选 CMakeLists.txt,Qt Creator 会让你选 Kit。这一步有两个细节:
一是生成器选 Ninja。NMake 在 CUDA 多配置场景下容易出问题,尤其是 Debug/Release 切换时 obj 目录混淆,症状是改了代码不生效。Qt Creator 默认一般就是 Ninja,但如果你是从旧工程复制过来的,检查一下构建设置里有没有残留的 NMake 配置。
二是首次配置完成后,去「项目 → 构建 → CMake」里看一眼CMAKE_CUDA_COMPILER是否被正确探测到。如果显示CMAKE_CUDA_COMPILER-NOTFOUND,说明 CMake 找不到 nvcc,手动加一条缓存变量指到nvcc.exe全路径即可。这里同样可能遇到 host compiler 不匹配,CMake 会给出明确提示,按提示设CMAKE_CUDA_HOST_COMPILER指向 cl.exe。
混合工程里还有一处容易翻车:Qt 的AUTOMOC扫描头文件时会把kernels/gpu_smooth.h也扫一遍。如果那个头里出现了Q_OBJECT之外的 Qt 宏(比如误 include 了<QString>),moc 生成的代码会被塞进 CUDA 的编译单元,nvcc 立刻报一堆语法错。解法是把纯 C 接口的头文件排除出 moc 扫描范围,或者干脆不给它加任何 Qt 依赖。
5. 避坑与排查:五个真实翻车现场
5.1 编译期:nvcc 找不到主机编译器
现象:Qt Creator 构建输出第一行就是nvcc fatal : Cannot find compiler 'cl.exe' in PATH,但你在自己的命令行里跑nvcc -V完全正常。
原因:Qt Creator 进程的环境变量里没有 VS 开发者命令行注入的PATH,INCLUDE、LIB也是空的。nvcc 靠这些变量定位 cl.exe 和标准库。
解决:按第 3 章的写法用-ccbin显式指定 MSVC 的 bin 目录;CMake 工程则设CMAKE_CUDA_HOST_COMPILER。不要图省事去改系统 PATH 加 VS 目录,那会污染所有进程,后面装别的工具链时冲突。
5.2 链接期:unresolved external symbol __cudaRegisterFatBinary
现象:所有 .cu 都编译通过,链接阶段报找不到__cudaRegisterFatBinary、cudaMalloc之类的符号。
原因有两个常见来源。一是.cu根本没被编译——qmake 下漏了QMAKE_EXTRA_COMPILERS,或者 CMake 下project()里没写 CUDA,导致 .cu 被当成资源文件跳过,但头文件里的声明还在,所以编译期不报错。二是链接时少了cudart.lib,或者位数选错。
解决:先看构建目录里有没有生成*_cuda.obj,没有就是没编译,回头检查规则;有 obj 还报错,就是链接参数问题,检查LIBS里的-L路径是否指向lib/x64,以及-lcudart是否被加进了最终链接命令行。CMake 下直接用CUDA::cudart,别手写。
5.3 编译期:Unsupported gpu architecture
现象:nvcc fatal : Unsupported gpu architecture 'compute_89',或者反过来 kernel 跑起来报no kernel image is available for execution on the device。
原因:前者是 CUDA Toolkit 版本太老,不认识新显卡的算力编号;后者是编译时指定的架构和实际运行的显卡不匹配。这两个是一体两面,本质都是-arch参数和硬件对不上。
解决:先用设备的算力编号查清楚,再改CUDA_ARCH。运行时可以用一段代码打印实际设备的算力,跟编译参数对照:
// 放在 main 最前面,一行输出定位架构问题 #include <cuda_runtime.h> #include <cstdio> static void printDeviceInfo() { int dev = 0; cudaGetDevice(&dev); // 当前使用的设备号 cudaDeviceProp prop{}; cudaGetDeviceProperties(&prop, dev); // 拿设备属性 printf("device: %s, sm_%d%d, driver API %d.%d\n", prop.name, prop.major, prop.minor, prop.major, prop.minor); }prop.major和prop.minor拼起来就是算力编号,比如 8 和 6 对应sm_86。把CUDA_ARCH改成这个值重新编译,问题基本消失。要兼顾多台机器就写多个架构,代价是编译变慢。
5.4 运行期:Debug 构建下堆崩溃
现象:Release 正常,Debug 一跑就崩在free或operator delete,调用栈里有ntdll和ucrtbase。
原因:CRT 运行库不一致。Qt Debug 用的是/MDd,nvcc 默认给的是/MD,两个不同的 CRT 实例在同一进程里各自维护堆,跨模块释放内存必然出事。这个坑很隐蔽,因为编译和链接都不报错。
解决:按第 3.2 节的写法按构建类型分支-Xcompiler的运行库参数。CMake 侧更省事,set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")一行统一 C++ 和 CUDA 两侧。
5.5 调试期:断点进不了 kernel
现象:host 代码断点正常,一进 kernel 就卡住或者直接跑完,cuda-gdb在这台机器上不可用(Windows 上没有)。
原因:Qt Creator 配的调试器是 cdb,它只能调 host 代码,看不到设备执行流。而且 Debug 构建时如果没给 nvcc 加-G,kernel 是优化过的,行号和变量都对不上。
解决:Windows 上做 kernel 级调试,走图形化工具那条线更实际——在代码里加cudaDeviceSynchronize()配合cudaGetLastError(),把错误定位到具体的 launch 语句;再配合-lineinfo加工具做性能分析。日常排查我依赖的是「每次 kernel 调用后立刻检查错误」这个习惯:
// 每个 kernel launch 后立刻检查,把异步错误变成同步报错 myKernel<<<grid, block>>>(d_ptr, n); cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { qFatal("kernel launch failed: %s", cudaGetErrorString(err)); } cudaError_t sync = cudaDeviceSynchronize(); // 只在调试期加,发布版去掉 if (sync != cudaSuccess) { qFatal("kernel exec failed: %s", cudaGetErrorString(sync)); }不加同步时,kernel 里的越界访问会延后到下一次 API 调用才报错,栈已经指到别处去了,这是最典型的黑匣子场景。
6. 验证与进阶:一段冒烟测试把链路跑通
配置完别急着写业务代码。我现在的固定动作是先跑一段冒烟测试,把「能编译、能链接、能分配设备内存、能启动 kernel、能回读结果」这五件事一次性验掉。这段代码单独建一个控制台工程,不掺 Qt,最小化变量:
// smoke.cu —— 独立编译:nvcc -arch=sm_75 smoke.cu -o smoke.exe #include <cstdio> #include <cuda_runtime.h> __global__ void addOne(int* d, int n) { int i = blockIdx.x * blockDim.x + threadIdx.x; if (i < n) d[i] += 1; // 边界判断别省,越界是异步报错 } int main() { const int n = 8; int h[n]; for (int i = 0; i < n; ++i) h[i] = i; int* d = nullptr; if (cudaMalloc(&d, n * sizeof(int)) != cudaSuccess) { printf("malloc failed: %s\n", cudaGetErrorString(cudaGetLastError())); return 1; } cudaMemcpy(d, h, n * sizeof(int), cudaMemcpyHostToDevice); addOne<<<(n + 31) / 32, 32>>>(d, n); // 向上取整算 block 数 if (cudaGetLastError() != cudaSuccess) { // launch 参数错误在这里暴露 printf("launch failed: %s\n", cudaGetErrorString(cudaGetLastError())); return 2; } if (cudaDeviceSynchronize() != cudaSuccess) { printf("exec failed: %s\n", cudaGetErrorString(cudaGetLastError())); return 3; } cudaMemcpy(h, d, n * sizeof(int), cudaMemcpyDeviceToHost); for (int i = 0; i < n; ++i) printf("%d ", h[i]); printf("\n"); cudaFree(d); return 0; }这段能跑通,说明驱动、Toolkit、架构参数三层都对。跑不通就按返回码定位:1 是设备不可用或显存不足,2 是 launch 配置错(block 超过 1024、shared memory 超限),3 是 kernel 内部越界或非法访存。
冒烟过了之后,再把同样的逻辑搬进 Qt 工程。搬的时候只替换入口:Qt 侧拿到 QImage 的bits()指针,调.cu导出的 C 接口,内核逻辑一个字不改。这样一旦出问题,你立刻知道是新引入的构建配置,还是原本就有的逻辑。
进阶上有两个我会常备的技巧。一个是-Xptxas -v看寄存器用量,如果某个 kernel 的 register 数超过 64,occupancy 会明显掉,改用__launch_bounds__限制一下往往有惊喜。另一个是多架构分发,用CMAKE_CUDA_ARCHITECTURES "75;86;89"一次编出三份 fatbin,发给不同显卡的同事时不用重编,代价是编译时间大概翻三倍——本地开发时我会临时改回单架构,出包时再放开。
从那以后我每次新建 Qt + CUDA 工程,不管是 qmake 还是 CMake,都先把冒烟测试跑一遍再动业务代码。这个习惯帮我省掉的返工时间,比任何调试技巧都多——因为构建配置类的问题一旦混进业务代码里,你就再也分不清是逻辑错还是环境错了。希望帮到你。
本文还有配套的精品资源,点击获取