在跨平台C++项目中引入机器学习推理引擎时,ONNX Runtime因其出色的性能表现和广泛的模型支持成为首选。然而,当开发环境锁定在Visual Studio 2019时,如何选择既符合工程管理规范又能保持跨平台兼容性的集成方案,成为许多中高级开发者面临的现实挑战。本文将深入剖析两种主流集成路径的技术细节与适用场景。
在Windows平台使用ONNX Runtime进行机器学习推理,首先需要明确几个关键决策点:是否需要保持Linux/macOS的编译兼容性?项目是否要求离线部署能力?团队协作中是否需要严格的版本控制?这些因素将直接影响集成方案的选择。
ONNX Runtime的CPU版本在VS2019中主要有两种获取方式:
提示:虽然NuGet是.NET生态的主要包管理工具,但其对C++项目的支持从VS2017开始已逐渐完善,特别是在VS2019中提供了原生CMake集成。
两种典型集成路径对比:
| 特性 | NuGet直接安装 | 手动解析nupkg+CMake配置 |
|---|---|---|
| 版本更新便利性 | ⭐⭐⭐⭐⭐(自动更新) | ⭐⭐(需手动替换文件) |
| 跨平台兼容性 | ⭐⭐(Windows优先) | ⭐⭐⭐⭐(配置可移植) |
| 离线部署支持 | ⭐(需NuGet缓存) | ⭐⭐⭐(可打包依赖) |
| 编译配置复杂度 | ⭐(图形界面操作) | ⭐⭐⭐(需编写CMake脚本) |
| 团队协作一致性 | ⭐⭐⭐⭐(版本锁定) | ⭐⭐⭐(需文档规范) |
这是最快捷的入门方式,适合Windows平台快速原型开发。在VS2019中新建C++控制台项目后,通过以下步骤完成集成:
cpp复制#include <onnxruntime_cxx_api.h>
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
Ort::SessionOptions session_options;
// ... 其他初始化代码
这种方式的优势在于:
但存在三个明显局限:
注意:如果项目后续需要迁移到Linux平台,所有NuGet相关的配置都需要重写,存在显著的移植成本。
对于追求跨平台一致性的项目,推荐采用CMake管理依赖。虽然需要更多初始配置,但能获得更好的工程管理体验。具体实现分为三个步骤:
从NuGet官网下载Microsoft.ML.OnnxRuntime的nupkg包后,实际上这是一个zip格式的压缩包,可以手动解压或使用NuGet命令行工具:
powershell复制nuget install Microsoft.ML.OnnxRuntime -Version 1.8.1 -OutputDirectory packages
解压后的目录结构包含关键资源:
code复制build/
└── native/
└── include/ # 头文件
runtimes/
└── win-x64/
└── native/ # Windows平台库文件
└── linux-x64/
└── native/ # Linux平台库文件
在项目根目录的CMakeLists.txt中,我们需要智能识别不同平台并配置相应路径:
cmake复制cmake_minimum_required(VERSION 3.12)
project(onnx_demo)
# 设置ONNX Runtime路径
if(WIN32)
set(ONNXRUNTIME_ROOT "path/to/unpacked/nupkg")
set(ONNXRUNTIME_LIB_PATH "${ONNXRUNTIME_ROOT}/runtimes/win-x64/native")
set(ONNXRUNTIME_LIB onnxruntime)
elseif(UNIX)
set(ONNXRUNTIME_ROOT "path/to/unpacked/nupkg")
set(ONNXRUNTIME_LIB_PATH "${ONNXRUNTIME_ROOT}/runtimes/linux-x64/native")
set(ONNXRUNTIME_LIB onnxruntime)
endif()
# 包含头文件
include_directories(${ONNXRUNTIME_ROOT}/build/native/include)
# 添加可执行文件
add_executable(demo main.cpp)
# 链接库文件
target_link_directories(demo PRIVATE ${ONNXRUNTIME_LIB_PATH})
target_link_libraries(demo PRIVATE ${ONNXRUNTIME_LIB})
不同于Linux系统,Windows在运行时需要明确知道动态库的位置。有三种解决方案:
将DLL复制到执行目录:
cmake复制# 在CMake中配置构建后复制操作
add_custom_command(TARGET demo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${ONNXRUNTIME_LIB_PATH}/onnxruntime.dll"
$<TARGET_FILE_DIR:demo>
)
设置环境变量(开发阶段推荐):
powershell复制# 在VS2019的开发人员PowerShell中执行
$env:PATH += ";${ONNXRUNTIME_LIB_PATH}"
修改系统PATH变量(生产环境不推荐)
经过多个实际项目验证,我总结出以下经验法则:
选择NuGet直装方案当:
选择CMake集成方案当:
对于大型项目,可以考虑混合模式——在Windows开发阶段使用NuGet,但通过CMake包装接口:
cmake复制# 尝试查找NuGet安装的ONNX Runtime
find_package(ONNXRuntime)
if(NOT ONNXRuntime_FOUND)
# 回退到手动配置模式
message(STATUS "Using manual ONNX Runtime configuration")
include(ManualONNXRuntime.cmake)
endif()
这种渐进式方案既保持了开发便利性,又为后续跨平台需求留出了扩展空间。
对于CMake方案,推荐将解压后的nupkg包纳入版本控制(Git LFS更佳),或在构建时自动下载:
cmake复制include(FetchContent)
FetchContent_Declare(
onnxruntime
URL "https://www.nuget.org/api/v2/package/Microsoft.ML.OnnxRuntime/1.8.1"
DOWNLOAD_NO_EXTRACT false
)
FetchContent_MakeAvailable(onnxruntime)
某些场景下可能需要静态链接ONNX Runtime:
cmake复制if(MSVC)
target_compile_definitions(demo PRIVATE ORT_STATIC)
target_link_libraries(demo PRIVATE onnxruntime_static)
endif()
在CMake中封装常用ONNX操作作为函数:
cmake复制function(add_onnx_model target model_path)
get_filename_component(model_name ${model_path} NAME_WE)
configure_file(${model_path} ${CMAKE_CURRENT_BINARY_DIR}/${model_name}.onnx COPYONLY)
target_sources(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/${model_name}.onnx)
set_property(TARGET ${target} PROPERTY VS_DEPLOYMENT_CONTENT 1)
endfunction()
实际项目中的常见坑点包括:
在最近的一个跨平台项目中,我们最终选择了CMake集成方案,虽然初期配置花费了2天时间,但后续在Linux服务器和Windows开发机之间的无缝切换证明了这个决策的价值。特别是在CI/CD流水线中,CMake的确定性构建表现出明显优势。