简介:面向C++开发者的GPU优化版ONNX Runtime推理库已打包提供,版本号1.18.0,适用于Windows 64位平台,核心用途是利用CUDA和cuDNN加速深度学习模型的推理过程,解决CPU处理计算密集任务时性能不足的痛点。压缩包内共有35个文件,主要包含16个.h头文件、4个.dll动态链接库、4个.lib静态导入库以及4个.pdb调试符号,同时附有Markdown说明文档、版本信息与许可证文件,整体体积201.61MB,目录组织规范,便于在Visual Studio等C++工程中直接引用。目前已有849人浏览学习,适合需要构建高性能推理服务的开发者和研究人员,典型场景包括图像识别、语音识别、自然语言处理等。通过这个压缩包,开发者可以将TensorFlow、PyTorch等框架导出的ONNX模型无缝迁移至Windows GPU环境,获得显著推理加速,同时随包文档也能帮助快速完成环境配置与API集成,整体上为Windows平台上的GPU推理落地提供了完整的基础设施。
1. onnxruntime 1.18.0 GPU 包:给 C++ 部署补上的一环
如果你手里有个训练好的 onnx 模型,想在 Windows 上用 C++ 把它跑起来,并且让 RTX 4060 这种显卡真正派上用场,那么 onnxruntime-win-x64-gpu-1.18.0.zip 这个预编译包就是绕不开的东西。它不像 PyTorch 那样把模型和数据管到底,而是只负责一件事——把 onnx 模型高效地调度到 CPU 或 GPU 上执行。我拆这个包最直观的感受是:头文件、lib、dll 都在里面,但光有文件不够,你得先把 CUDA 和 Visual Studio 的版本和它对上,否则连加载都过不去。这份笔记适合正在用 C++ 做推理部署的读者——新手可以照着走,熟手可以直接翻第 5 章看坑。
这个版本包是预编译的 x64 动态库,意味着不需要自己用 CMake 去编 onnxruntime 源码,省掉一大半编译时间。但它也有前提:本机必须有匹配的 CUDA 运行环境,并且工程要按动态库方式链接。下面从版本对齐开始一步步说。
2. 版本与环境对齐:先把 CUDA、cuDNN、VS 三件事钉死
拿到包第一件事不是写代码,是先核对环境。onnxruntime 的 GPU 包是“预先选好 CUDA 版本编译出来的”,运行时会动态加载本机 CUDA 运行库,任何一环对不上,后面全是坑。
2.1 解压后先看清目录结构
onnxruntime-win-x64-gpu-1.18.0.zip 解压出来是标准的发布目录结构,我拆包后第一件事就是按下面这张表确认每个目录的用途:
| 目录/文件 | 作用 | 编译期还是运行期 |
|---|---|---|
| include/ | onnxruntime C/C++ 头文件,C++ API 全在这里 | 编译期 |
| lib/onnxruntime.lib | 导入库,链接器靠它定位 dll 里的导出符号 | 编译期 |
| bin/onnxruntime.dll | 核心运行库,推理主 dll | 运行期 |
| bin/onnxruntime_providers_cuda.dll | CUDA Execution Provider,GPU 加速的真正实现 | 运行期 |
| bin/onnxruntime_providers_shared.dll | 多个 provider 共享的辅助 dll | 运行期 |
bin 目录下某些官方包还会带上 TensorRT 或者 DirectML 的 provider,用不上就直接忽略,不影响主链路。我一般把 include 和 lib 放在原路径引用,bin 下的 dll 按需拷贝到 exe 输出目录,或者把整个 bin 目录写进系统 PATH。这两种方式都行,但要注意:拷 dll 时不能只拷 onnxruntime.dll,provider dll 和 shared dll 必须一起拷,少一个运行时就给你表演 LoadLibrary 失败。
这个包是 x64 专用,工程平台必须设成 x64,在 Win32 平台上链接会直接报 LNK1112 平台冲突,这不是代码问题,是架构没对齐。另外 1.18.0 这个版本号在 API 上跟 1.17 有少量扩展,但 C++ 头文件的使用方式基本一致,网上搜到的老代码大多能直接编译通过,不用太担心兼容性。
2.2 CUDA、cuDNN、VS 版本怎么匹配
onnxruntime 的 GPU 包依赖本机安装的 CUDA 运行库,版本对不上,最典型的结果是 provider 创建失败,或者干脆 LoadLibrary 报错。1.18.0 这个包发布时,官方主推的是 CUDA 12 这一代,本机只装 11.8 的话,大概率翻车。这里说的“翻车”不是编译错,而是运行时才暴露,排查起来更难受。
按我实际配置成功的组合,整理了一张表,装之前再到官方版本兼容表核对一次:
| 组件 | 推荐组合 |
|---|---|
| onnxruntime | 1.18.0 (win-x64-gpu) |
| CUDA Toolkit | 12.x |
| cuDNN | 与 CUDA 12 配套的 8.9 或 9.x,以官方兼容表为准 |
| Visual Studio | 2019 (v142) 或 2022 (v143) |
| 显卡 | NVIDIA 计算能力 6.0 以上,RTX 4060 系列没问题 |
| CMake | 3.18+(只在自行编译源码时需要) |
RTX 4060 Laptop GPU 属于 Ada 架构,算力 8.9,跑 CUDA 12 没有硬件障碍。真正要小心的反而是笔记本双显卡:Intel 核显 + NVIDIA 独显这种组合,驱动装不好时 CUDA runtime 可能找不到独显,表现为“推理能跑,但慢得离谱”。后面第 5 章有专门一条讲这个。
2.3 用 dumpbin 检查 dll 依赖链
装完 CUDA 后,先别急着写代码。用 VS 的 x64 Native Tools 命令行跑一下 dumpbin,看看 provider dll 到底依赖了哪些运行库:
dumpbin /dependents D:\libs\onnxruntime\bin\onnxruntime_providers_cuda.dll输出会列出一串 dll 名字,重点关注cudart64_12.dll、cublas64_12.dll、cudnn64_*.dll这几类。然后在C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.x\bin和 cuDNN 的 bin 目录里确认这些文件存在。缺哪个补哪个,这一步能帮你少走一晚上弯路。
顺带说一句,cudart64_12.dll、cudnn64_9.dll这类文件属于 NVIDIA 的运行时库,装 CUDA Toolkit 和 cuDNN 才有,不是 onnxruntime 自己打包的。很多人以为 zip 里有 dll 就能跑,这是最大的误会。检查完依赖链,环境这块基本就钉死了,接下来才是工程接入。
3. C++ 工程接入:从零配好 include/lib/dll 并跑通一次推理
环境对齐后,进入正题:把 onnxruntime 的 C++ API 接进 Visual Studio 工程。我以 VS2022 + x64 + Release 为例,Debug 配置操作完全一样,只是输出目录不同。这一步的目标很简单:用 CPU 先把一个 onnx 模型跑通,GPU 的事下一章再说。
3.1 新建工程并配置包含目录和库目录
VS2022 里新建一个空项目,解决方案平台选 x64。然后打开项目属性页,依次设置:
- VC++ 目录 → 包含目录:加上
D:\libs\onnxruntime\include - VC++ 目录 → 库目录:加上
D:\libs\onnxruntime\lib - 链接器 → 输入 → 附加依赖项:填
onnxruntime.lib
这里的路径按你自己解压的位置改。链接器只认 lib 不认 dll,onnxruntime.lib是导入库,它记录了onnxruntime.dll里导出函数的地址映射,运行时才真正加载 dll。所以链接阶段只依赖 lib,运行阶段依赖 dll,两边缺一不可。
路径里不要带空格和中文,虽然 VS 能处理,但以后用脚本做自动构建时,空格会给你找麻烦。我习惯把第三方库统一放在D:\libs\这种纯英文路径下,省心。
3.2 把 dll 拷到输出目录
编译能过,跑起来可能直接报“找不到 onnxruntime.dll”。解决方式有两种,我推荐用生成后事件自动拷贝,一劳永逸:
xcopy /Y /D "D:\libs\onnxruntime\bin\*.dll" "$(OutDir)"在项目属性 → 生成事件 → 生成后事件 → 命令行里加上这行。/Y表示覆盖不询问,/D表示只复制比目标新的文件,$(OutDir)是 VS 内置的输出目录变量,Release x64 下就是x64\Release\。
这一步做完,生成的 exe 和 onnxruntime 所有 dll 就在同一个目录下了。用xcopy而不是手动拖拽的原因很实际:每次重新生成后 dll 都会被自动刷一遍,不会出现“上个月拷的 dll 被旧版本覆盖”这种玄学问题。
3.3 写第一个 CPU 推理代码
下面是完整的最小推理程序。先说明关键点:onnxruntime 的 C++ API 用ORTCHAR_T表示路径字符,Windows 下是wchar_t,所以模型路径必须用宽字符串L"...",这是新手最容易漏的地方。
#include <onnxruntime/core/session/onnxruntime_cxx_api.h> #include <vector> #include <iostream> int main() { // 1. 环境:日志级别用 WARNING,避免刷屏 Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "onnx-demo"); // 2. 会话选项:先用 CPU 跑通,GPU 配置下一章加 Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(4); session_options.SetGraphOptimizationLevel(ORT_ENABLE_ALL); // 3. 创建 Session,Windows 下路径必须是宽字符 const ORTCHAR_T* model_path = L"D:/models/resnet18.onnx"; Ort::Session session(env, model_path, session_options); // 4. 假设模型输入是 [1,3,224,224] 的 float32 std::vector<int64_t> input_shape = {1, 3, 224, 224}; size_t input_size = 1 * 3 * 224 * 224; std::vector<float> input_data(input_size, 0.5f); // 5. 创建 CPU Tensor,把 vector 的数据交给 onnxruntime Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu( OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor = Ort::Value::CreateTensor<float>( memory_info, input_data.data(), input_size, input_shape.data(), input_shape.size()); // 6. 输入输出节点的名字要从模型里取,这里先用占位符 const char* input_names[] = {"input"}; const char* output_names[] = {"output"}; // 7. 跑一次前向推理 Ort::RunOptions run_options; auto output_tensors = session.Run( run_options, input_names, &input_tensor, 1, output_names, 1); // 8. 取输出数据 auto& output = output_tensors.front(); auto type_info = output.GetTensorTypeAndShapeInfo(); std::vector<int64_t> out_shape = type_info.GetShape(); const float* out_data = output.GetTensorData<float>(); std::cout << "output[0] = " << out_data[0] << ", shape[0] = " << out_shape[0] << std::endl; return 0; }逻辑说明:第 1 步创建Ort::Env,它是全局环境,整个程序生命周期内只需要一个。第 2 步的SetIntraOpNumThreads(4)控制算子内部并行线程数,CPU 推理时设成物理核数的一半到四分之三比较稳,别无脑拉满,否则线程上下文切换会吃掉不少性能。第 5 步把vector<float>的数据指针直接交给 tensor,注意input_data的生命周期必须覆盖到session.Run返回之后,因为 onnxruntime 在异步场景下可能还在读这块内存。
模型输入输出节点名不是随便写的。你不知道名字时,可以用下面这段代码打印:
Ort::AllocatorWithDefaultOptions allocator; for (size_t i = 0; i < session.GetInputCount(); i++) { auto name = session.GetInputNameAllocated(i, allocator); std::cout << "input[" << i << "] = " << name.get() << std::endl; } for (size_t i = 0; i < session.GetOutputCount(); i++) { auto name = session.GetOutputNameAllocated(i, allocator); std::cout << "output[" << i << "] = " << name.get() << std::endl; }这一步建议每次都跑一次,因为很多模型导出时输入名是input.1或者onnx::Conv_0这种带后缀的名字,你猜不中。数据类型也一样,PyTorch 导出的模型输入float32,TensorFlow 转过来可能是float32也可能是float64,用GetInputTypeInfo可以查,但一般直接按float32处理就够用。
3.4 编译运行与 Release/Debug 的差异
代码写完后,按 Ctrl+F5 运行。如果一切正常,控制台会打印output[0]的值和你模型的输出形状。能跑到这一步,说明 include 目录、lib 目录、dll 拷贝三条链路全部打通了。
Debug 和 Release 在这里的差异只有一处:Debug 模式下 onnxruntime 的日志会更详细,但性能会明显差一些。做性能测试时一定要切 Release x64,并且把优化等级开到/O2。另外 Debug 下如果报ITERATOR_DEBUG_LEVEL相关的错,是 STL 迭代器调试级别不一致导致的,Bebug 工程链接 Release 编译的库偶尔会遇到,建议全部统一到 Release 配置。
4. 让 GPU 真正接管推理:CUDA Provider 与参数调优
CPU 跑通只是第一步。很多人以为“装好了 CUDA,onnxruntime 就自动用 GPU”,完全不是这么回事。onnxruntime 默认只启用 CPU Execution Provider,要让 GPU 干活,必须在 SessionOptions 里显式挂上 CUDA Execution Provider。
4.1 挂载 CUDA Execution Provider
onnxruntime 1.18 的 C++ API 推荐用OrtCUDAProviderOptions结构体配置,而不是老的OrtSessionOptionsAppendExecutionProvider_CUDA函数。代码如下:
#include <onnxruntime/core/session/onnxruntime_cxx_api.h> #include <iostream> #include <vector> int main() { Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "gpu-demo"); Ort::SessionOptions session_options; // 关键:配置 CUDA Execution Provider OrtCUDAProviderOptions cuda_options; cuda_options.device_id = 0; cuda_options.arena_extend_strategy = 0; // 0 = kNextPowerOfTwo cuda_options.cudnn_conv_algo_search = OrtCudnnConvAlgoSearchExhaustive; cuda_options.do_copy_in_default_stream = 1; // 把 CUDA EP 挂到会话上 session_options.AppendExecutionProvider_CUDA(cuda_options); // 图优化全开,线程数收敛,避免和 GPU 调度抢资源 session_options.SetGraphOptimizationLevel(ORT_ENABLE_ALL); session_options.SetIntraOpNumThreads(1); const ORTCHAR_T* model_path = L"D:/models/resnet18.onnx"; Ort::Session session(env, model_path, session_options); // 验证 EP 是否真的生效 auto providers = session.GetExecutionProviderNames(); for (const auto& name : providers) { std::cout << "EP: " << name << std::endl; } return 0; }逻辑说明:AppendExecutionProvider_CUDA把 CUDA EP 加到会话里,session 创建时 onnxruntime 会逐个尝试初始化 EP。GetExecutionProviderNames返回当前会话实际启用的 EP 列表,如果里面有"CUDA"三个字,才是真的把 GPU 接管了,否则只是假装成功。控制台输出里出现Added CUDA execution provider的日志,也说明挂载成功。
OrtCUDAProviderOptions里几个字段按场景调:device_id在多卡机器上指定用哪块显卡,默认 0 是第一块。arena_extend_strategy控制显存扩展策略,默认的 kNextPowerOfTwo 会按 2 的幂扩展显存,显存紧张时改成kSameAsRequested(值 1)更省。cudnn_conv_algo_search默认是 exhaustive 搜索,初始化慢但效果好,追求启动速度可以换 heuristic。do_copy_in_default_stream建议保持 1,让输入输出拷贝走默认流,避免和别的 CUDA 流冲突。
4.2 线程数与图优化等级怎么设
GPU 推理时,SetIntraOpNumThreads这个参数很多人不管,默认值是物理核数,结果就是 GPU 推理时 CPU 线程也在满负荷转,数据在 CPU 和 GPU 之间反复搬运,整体延迟反而更高。我一般 GPU 场景设 1~2,让 CPU 线程只做数据预处理和拷贝。SetGraphOptimizationLevel用ORT_ENABLE_ALL,它会做算子融合和常量折叠,ResNet 这种模型图优化后执行效率能明显提高。
ORT_DISABLE_ALL只有在调试模型输出不一致时才用,比如怀疑某个融合算子在 GPU 上有精度问题,关掉图优化对比一次输出,就能定位是不是融合导致的。这是排查 GPU 和 CPU 推理结果不一致的有效手段,后面避坑章节会再提。
4.3 用 Session Profiling 看算子在哪个设备上执行
想确认算子是跑在 GPU 还是悄悄回了 CPU,靠猜不靠谱。onnxruntime 自带 profiling,打开后会在工作目录生成一个 json 文件,记录每个算子的执行设备和耗时:
session_options.EnableProfiling("profiling_output");运行结束后生成的文件名带时间戳,比如onnxruntime_profile_xxx.json。直接用 VS Code 打开,搜索"cat"或"dur"字段,可以看到每个节点的耗时;更直接的是看provider字段,如果显示"CUDAExecutionProvider"说明算子跑在 GPU 上,显示"CPUExecutionProvider"说明算子被 fallback 到 CPU 了。
算子 fallback 是 GPU 推理性能上不去的主要原因。有些算子 CUDA EP 还没实现,onnxruntime 会默认把它丢回 CPU 跑,数据来回拷贝一次,性能直接崩。发现这种情况,我一般先升级 onnxruntime 版本,或者改模型结构,避免那些冷门算子出现在 GPU 分支中。每个算子 fallback 时日志里会有 warning,把日志级别调到ORT_LOGGING_LEVEL_INFO就能看到。
5. 避坑排查:五条最常见的 Windows GPU 推理翻车记录
这是整个部署过程里最容易踩的坑,我把它们按“现象 → 原因 → 解决”写成排查记录,每一条都是真实发生过的。
5.1 一启动就报找不到 cudart64_12.dll
现象:程序编译通过,运行时弹窗或者控制台输出LoadLibrary failed with error: 126,后面跟着cudart64_12.dll找不到。
原因:onnxruntime 的 GPU provider dll 依赖 NVIDIA 的 CUDA 运行库,这些 dll 不在 onnxruntime 的 zip 包里,必须由本机 CUDA Toolkit 提供。你装了 CUDA 11.8,provider 要的是 12.x 的cudart64_12.dll,自然找不到。另一种情况是你装了 CUDA 12,但 dll 路径没在系统 PATH 里,程序也找不到。注意 error 126 是ERROR_MOD_NOT_FOUND,它代表的不是“文件损坏”,而是“依赖链上某个 dll 缺失”。
解决:确认当前 CUDA 版本,nvcc --version看 Toolkit 版本。如果是 11.8,要么卸了重装 12.x,要么换成配套 onnxruntime 1.16 或 1.17 的 CUDA 11.8 GPU 包。如果版本已经是 12.x,把C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.x\bin加进系统 PATH,cuDNN 的 bin 目录也加进去,重启 VS 再跑。我后来习惯把这两个路径里的 dll 直接拷到 exe 目录,彻底绕开 PATH 顺序问题。
5.2 CUDA Execution Provider 创建失败
现象:代码里AppendExecutionProvider_CUDA没报错,但 session 创建时抛异常,日志里写着Failed to create CUDA execution provider,或者CUDA execution provider is not enabled。
原因:onnxruntime 在编译 GPU 包时固定了 CUDA 版本,1.18.0 的包要求 CUDA 12.x,本机是 11.8 时,provider 初始化时加载 cudart 失败,直接拒绝启动。还有一个可能:机器上同时装了多个 CUDA 版本,PATH 里排前面的恰好是旧版,加载到了错误的 cudart。
解决:先用nvidia-smi和nvcc --version确认驱动和 Toolkit 版本。驱动版本决定了最高支持到哪个 CUDA 版本,Toolkit 版本是实际编译用的。两者不一致时,以 Toolkit 为准。卸载多余版本,或者调整 PATH 顺序,把 12.x 的 bin 放到最前面。改完 PATH 后重启 VS,因为 VS 启动时只读一次环境变量。
5.3 第一次推理特别慢,后续才恢复正常
现象:程序启动后第一次session.Run耗时好几秒,后面几次就降到几十毫秒。看起来像“模型在前几次运行中被优化了”,但也有可能每次启动都慢一次,属于正常现象。
原因:onnxruntime 在做三件事:把模型加载进显存、执行 cuDNN 算法搜索、应用图优化。cudnn_conv_algo_search设为 exhaustive 时,对每个卷积层都会搜一遍候选算法,这个耗时在复杂模型上可能超过 10 秒。这些工作只在首次推理时发生,后续就复用缓存了。
解决:这不是故障,但会影响你的性能测试结论。做 benchmark 前先 warmup,让模型跑 3~5 次再计时,这样拿到的才是稳定推理耗时。如果首包延迟敏感,cudnn_conv_algo_search改成OrtCudnnConvAlgoSearchHeuristic,牺牲一点精度换启动速度。
5.4 笔记本双显卡:GPU 推理比 CPU 还慢
现象:笔记本是 Intel 核显 + NVIDIA RTX 4060 Laptop GPU 双显卡,程序能跑,但 GPU 推理耗时和 CPU 差不多,甚至更慢。
原因:onnxruntime 的 CUDA provider 默认走device_id = 0,但 Windows 上设备编号不一定按你想的顺序排。某些机器上 NVIDIA 独显可能被系统分配成 device_id=1,0 号实际是核显或者其他设备。CUDA 代码在核显上要么跑在软件模拟层,要么运行时 fallback 到 CPU,性能自然难看。还有一个隐藏因素:Windows 的图形卡默认给 Intel 核显,NVIDIA 显卡需要驱动面板里给“全局设置”指定高性能,否则 CUDA context 可能绑定到核显。
解决:先写个枚举 CUDA 设备的小程序,把每块显卡的名字和编号打出来,确认 4060 是 0 还是 1。一个是把device_id改为实际编号,二是在 NVIDIA 控制面板里把该程序的“首选图形处理器”设为“NVIDIA 高性能处理器”。双显卡笔记本做 GPU 推理,这两步是必做项。
5.5 线程数设满反而把 GPU 推理拖慢
现象:GPU 推理时 CPU 占用率接近 100%,单次推理延迟反而比 CPU 模式还高,频繁出现掉帧或者延迟尖刺。
原因:SetIntraOpNumThreads没设置,onnxruntime 默认使用全部物理核。GPU 推理链路里,数据预处理在 CPU 上做,每个算子张量还在 CPU 和 GPU 之间搬移,线程太多导致 CPU 上下文切换开销暴涨,反而拖慢了数据搬运。这是 I/O 密集型和计算密集型的典型冲突。
解决:GPU 模式下把SetIntraOpNumThreads设为 1 或 2,让 CPU 只做最少的调度和拷贝工作。CPU 和 GPU 推理共用一个 session 时,不要用同一套线程配置,分别建 session 选项。我一般会写一个配置结构体,按后端类型加载不同的线程数,避免这种问题反复出现。
6. 性能验证与收尾:用基线数据说话
GPU 有没有真的提速,不能靠“感觉快了”,要拿数据说话。我习惯的做法是:warmup 5 次,然后连续跑 20 次推理,算平均耗时和 P95 耗时。下面这段代码可以直接嵌到你的工程里:
#include <chrono> auto warmup_start = std::chrono::steady_clock::now(); for (int i = 0; i < 5; i++) { session.Run(run_options, input_names, &input_tensor, 1, output_names, 1); } auto t0 = std::chrono::steady_clock::now(); const int iterations = 20; for (int i = 0; i < iterations; i++) { session.Run(run_options, input_names, &input_tensor, 1, output_names, 1); } auto t1 = std::chrono::steady_clock::now(); double avg_ms = std::chrono::duration<double, std::milli>(t1 - t0).count() / iterations; std::cout << "avg inference time: " << avg_ms << " ms" << std::endl;对比时注意变量隔离:CPU 基线和 GPU 基线要开同样的图优化等级,否则对比出来的是“图优化 + GPU”的效果,不是 GPU 本身的增量。我一般先用 CPU provider 跑一遍拿一个数,再切 GPU provider 跑一遍,两张表放一起看。tensor 数据必须保证完全一致,包括初始化方式,不然离散的输入数据会导致 cuDNN 算法选择的缓存不一样,测试结果有偏差。
另一个习惯是验证 GPU 和 CPU 输出的一致性。同一份输入数据跑两次,CPU 和 GPU 的输出做一次逐元素对比,误差在 1e-4 量级是正常的,float32 在不同实现下有舍入差异;如果某个算子的输出差了 1e-1 甚至符号都反了,优先怀疑算子 fallback 或图优化把不安全的融合做了。这时候把SetGraphOptimizationLevel降一档再对比,基本能定位到具体算子。
从那以后我每次换 onnxruntime 版本,都强制走一遍这套流程:对版本兼容表、dumpbin 查依赖、warmup 测基线、GPU/CPU 输出对齐。这四个步骤加起来也就二十分钟,但能省掉后面数不清的排障时间。尤其是碰过 CUDA 11.8 和 12.x 互切那回之后,我再也不敢跳过版本核对直接写代码了。希望这些经验能帮你在 Windows 上把 onnxruntime GPU 部署少走一段弯路。
本文还有配套的精品资源,点击获取