2. 环境搭建
2.1 核心原理:为什么环境搭建决定部署成败
ONNX Runtime 的 C++ 部署并非简单的“复制几个文件”,而是一套涉及编译器 ABI 兼容性、动态链接库搜索路径、依赖传递关系的工程体系。很多初学者在 Python 环境中运行模型毫无问题,一旦切换到 C++ 就遇到“找不到 onnxruntime.dll”或“链接错误 LNK2019”,根源往往在于环境搭建阶段埋下的隐患。
理解环境搭建的核心,需要抓住三个关键点:
- ABI 兼容性:ONNX Runtime 的 C++ API 使用 C++11 标准导出,但不同编译器(MSVC、GCC、Clang)生成的符号修饰规则不同。使用 MSVC 编译的库无法被 MinGW 直接链接,反之亦然。
- 运行时依赖:ONNX Runtime 动态库(.dll/.so/.dylib)本身依赖系统运行库(如 VC++ Redistributable、libstdc++),且需要正确设置动态库搜索路径。
- 头文件与库版本匹配:头文件(onnxruntime_cxx_api.h)中的 API 声明必须与库文件的实际导出符号严格对应,混用不同版本会导致难以排查的运行时崩溃。
经验法则:在 Windows 上优先选择 MSVC(Visual Studio)工具链,在 Linux 上优先选择 GCC 9+ 或 Clang 10+,并始终使用与 ONNX Runtime 官方发布页一致的编译配置。
2.2 方法流程:Windows + Linux 双平台搭建步骤
2.2.1 Windows 平台(MSVC + Visual Studio 2022)
Windows 下推荐使用 Visual Studio 2022 作为 IDE,配合 CMake 构建项目。ONNX Runtime 官方发布的 Windows 包分为 CPU 版和 GPU 版(需 CUDA),下载时需注意选择 win-x64 架构。
# 1. 下载 ONNX Runtime Windows 包(以 v1.17.1 为例)
# 访问 https://github.com/microsoft/onnxruntime/releases
# 选择: onnxruntime-win-x64-1.17.1.zip
# 2. 解压到指定目录,例如 C:\libs\onnxruntime
# 目录结构如下:
C:\libs\onnxruntime\
├── include\ # 头文件
│ ├── onnxruntime_cxx_api.h
│ └── onnxruntime_c_api.h
├── lib\ # 静态库与导入库
│ ├── onnxruntime.lib
│ └── onnxruntime.dll
└── bin\ # 运行时动态库
└── onnxruntime.dll
在 Visual Studio 中配置项目属性:
- C/C++ → 常规 → 附加包含目录:添加
C:\libs\onnxruntime\include - 链接器 → 常规 → 附加库目录:添加
C:\libs\onnxruntime\lib - 链接器 → 输入 → 附加依赖项:添加
onnxruntime.lib - 调试 → 环境:设置
PATH=C:\libs\onnxruntime\bin;%PATH%(或将 onnxruntime.dll 复制到 exe 同目录)
2.2.2 Linux 平台(GCC + CMake)
Linux 下推荐使用 CMake 的 FetchContent 或 find_package 方式管理依赖。以下展示使用 FetchContent 自动下载并编译 ONNX Runtime 的配置:
# CMakeLists.txt 核心片段
cmake_minimum_required(VERSION 3.18)
project(OnnxDeployDemo CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 方式一:FetchContent 自动拉取(适合快速原型)
include(FetchContent)
FetchContent_Declare(
onnxruntime
URL https://github.com/microsoft/onnxruntime/releases/download/v1.17.1/onnxruntime-linux-x64-1.17.1.tgz
URL_HASH SHA256=your_checksum_here
)
FetchContent_MakeAvailable(onnxruntime)
# 方式二:find_package(需先安装到系统)
# find_package(onnxruntime REQUIRED)
add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE onnxruntime::onnxruntime)
若使用 vcpkg 管理依赖,安装命令更为简洁:
# 安装 vcpkg 后执行
vcpkg install onnxruntime:x64-linux
# 或 Windows 下
vcpkg install onnxruntime:x64-windows
# 然后在 CMake 中启用工具链
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=[vcpkg-root]/scripts/buildsystems/vcpkg.cmake
2.3 案例实战:最小可运行推理程序
环境搭建完成后,编写一个最小程序验证 ONNX Runtime 能否正确加载并执行模型。以下代码使用 C++ API 加载一个简单的 ONNX 模型并执行前向推理:
#include <onnxruntime_cxx_api.h>
#include <vector>
#include <iostream>
int main() {
// 1. 创建环境(日志级别为警告)
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test_env");
// 2. 创建会话选项并设置优化级别
Ort::SessionOptions session_options;
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
// 3. 加载模型(请替换为实际模型路径)
const char* model_path = "model.onnx";
Ort::Session session(env, model_path, session_options);
// 4. 获取输入输出信息
Ort::AllocatorWithDefaultOptions allocator;
auto input_name = session.GetInputNameAllocated(0, allocator);
auto output_name = session.GetOutputNameAllocated(0, allocator);
std::cout << "Input: " << input_name.get() << std::endl;
std::cout << "Output: " << output_name.get() << std::endl;
// 5. 构造输入张量(假设输入为 1x3x224x224 的 float 数据)
std::vector<int64_t> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1 * 3 * 224 * 224, 0.5f);
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(
OrtArenaAllocator, OrtMemTypeDefault);
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
memory_info, input_data.data(), input_data.size(),
input_shape.data(), input_shape.size());
// 6. 执行推理
const char* input_names[] = {input_name.get()};
const char* output_names[] = {output_name.get()};
auto output_tensors = session.Run(Ort::RunOptions{nullptr},
input_names, &input_tensor, 1,
output_names, 1);
// 7. 获取输出结果
auto& output_tensor = output_tensors.front();
auto output_info = output_tensor.GetTensorTypeAndShapeInfo();
auto output_shape = output_info.GetShape();
std::cout << "Output shape: ";
for (auto dim : output_shape) std::cout << dim << " ";
std::cout << std::endl;
return 0;
}
编译命令(Linux):
g++ -std=c++17 demo.cpp -I/path/to/onnxruntime/include \
-L/path/to/onnxruntime/lib -lonnxruntime \
-Wl,-rpath,/path/to/onnxruntime/lib -o demo
2.4 常见误区与排障复盘
| 误区/错误 | 现象 | 根因分析 | 解决方案 |
|---|---|---|---|
| 混用编译器 | 链接错误 LNK2019 / undefined reference | MSVC 与 MinGW 的符号修饰不同 | 统一使用 MSVC 或 GCC,不混用 |
| 忽略运行时 DLL | 程序启动报“找不到 onnxruntime.dll” | 动态库未在搜索路径中 | 将 DLL 复制到 exe 目录或设置 PATH |
| Debug/Release 不匹配 | 内存访问冲突或崩溃 | ONNX Runtime 仅提供 Release 版 | 项目必须使用 Release 配置链接 |
| 版本混用 | 编译通过但运行崩溃 | 头文件与库版本不一致 | 确保 include 和 lib 来自同一版本包 |
| CUDA 版误用 CPU 包 | 无法使用 GPU 执行 | 未安装 CUDA 版 ONNX Runtime | 下载对应 CUDA 版本的包并配置 CUDA 环境 |
重要提醒:ONNX Runtime 官方发布的 Windows 包仅支持 MSVC 编译的 C++ 程序。若使用 MinGW 或 Cygwin,需要自行从源码编译,过程复杂且容易出错。对于生产环境,强烈建议使用 MSVC + Visual Studio 或 CMake + Ninja 工具链。
2.5 环境验证清单
完成搭建后,建议按以下清单逐项验证,确保环境万无一失:
- 头文件可访问:编译一个仅包含
#include <onnxruntime_cxx_api.h>的空程序,确认无编译错误。 - 链接成功:链接时能正确找到
onnxruntime.lib或libonnxruntime.so。 - 运行时加载:运行程序时能加载动态库,无“DLL not found”错误。
- 模型推理:使用 2.3 节的最小程序加载一个真实 ONNX 模型,确认输出形状符合预期。
- 优化级别生效:设置
ORT_ENABLE_ALL后,对比推理耗时,确认优化生效。