Ubuntu系统下C++版ONNX Runtime编译部署全流程实战指南 📅 2026/7/16 23:21:45 1. 项目概述为什么要在Ubuntu上折腾C版的onnxruntime如果你正在Ubuntu上做C的AI推理项目大概率已经听说过onnxruntime。这是一个由微软开源的跨平台推理引擎能让你轻松地在各种硬件上运行ONNX格式的模型。但当你兴冲冲地打开官方文档准备在Ubuntu上把C版本的库装起来时可能会发现事情没那么简单。官方提供的预编译包主要是Python的C版本要么是Windows的要么就需要你自己动手编译。这个过程说好听点是“锻炼动手能力”说直接点就是“踩坑之旅”。我最近就在一个边缘计算项目里遇到了这个需求需要在Ubuntu 20.04的服务器上用C部署一个目标检测模型。Python版本虽然方便但在资源受限且对延迟要求极高的场景下C是更优的选择。经过一番折腾我把从环境准备、源码编译、到最终集成测试的完整流程都走通了也记下了不少“血泪教训”。这篇文章就是这份实战记录的总结目标是让你能避开我踩过的坑在Ubuntu上顺利地把C版onnxruntime用起来。2. 环境准备与编译方案选型在动手之前我们得先搞清楚一件事我们到底需要什么onnxruntime的C库获取方式主要有两种一是直接下载官方预编译的库和头文件二是从源码开始编译。对于Ubuntu用户尤其是需要特定功能如CUDA支持、特定算子优化或特定版本的情况从源码编译往往是唯一可靠的选择。官方提供的Linux预编译包通常只包含Python wheelC的动态库和静态库需要我们自行构建。2.1 系统环境与依赖确认我的操作环境是Ubuntu 20.04 LTS这是一个长期支持版本社区资源丰富相对稳定。理论上Ubuntu 18.04及更高版本的操作流程大同小异。首先我们需要更新系统并安装一系列基础编译工具和依赖库。打开终端执行以下命令来搭建基础环境sudo apt update sudo apt upgrade -y sudo apt install -y build-essential cmake git wget接下来是核心依赖。onnxruntime的编译依赖于一些特定的库比如用于线性代数计算的libblas、序列化库protobuf等。我们一次性安装它们sudo apt install -y libssl-dev libprotobuf-dev protobuf-compiler libgtest-dev libgoogle-perftools-dev注意libgtest-dev是Google Test框架用于编译和运行onnxruntime自身的单元测试。如果你不打算运行测试可以不安装但建议装上因为在编译过程中运行测试是验证编译是否成功的好方法。2.2 编译工具链的版本考量CMake的版本至关重要。onnxruntime对CMake有最低版本要求通常3.18或更高。Ubuntu 20.04默认仓库的CMake版本3.16可能不够。我们需要安装更新的版本。这里我推荐使用Kitware提供的官方APT仓库来安装。wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg /dev/null sudo apt-add-repository deb https://apt.kitware.com/ubuntu/ focal main sudo apt update sudo apt install -y cmake cmake --version # 确认版本应高于3.18另一个关键工具是编译器。推荐使用GCC 9或Clang 10及以上版本。Ubuntu 20.04默认的GCC 9.4是完全可以的。2.3 源码获取与目录结构规划我们不直接从GitHub的main分支拉取因为开发分支可能不稳定。最好选择某个发布版本Release Tag的源码。这里我们以相对稳定的v1.16.3版本为例。# 创建一个专门的工作目录 mkdir -p ~/onnxruntime_build cd ~/onnxruntime_build # 克隆仓库并切换到特定标签 git clone --recursive https://github.com/microsoft/onnxruntime.git cd onnxruntime git checkout v1.16.3 git submodule update --init --recursive使用--recursive参数至关重要因为onnxruntime依赖一些子模块如onnx、eigen等。如果克隆时忘了后续必须用git submodule update命令补上否则编译必定失败。目录结构规划也很重要。我建议采用“源码目录”与“构建目录”分离的方式这是一种标准的CMake实践Out-of-source build。这样能保持源码目录的纯净方便清理和多次尝试不同的编译配置。# 假设当前在 ~/onnxruntime_build/onnxruntime (源码目录) mkdir -p build cd build现在我们的build目录就是接下来所有编译操作发生的地方。3. CMake配置开启你需要的功能进入build目录后真正的核心步骤开始了使用CMake生成构建系统。onnxruntime提供了大量的编译选项你需要根据你的需求来开启或关闭它们。一个最基础的、只支持CPU的配置命令如下cmake .. -DCMAKE_BUILD_TYPERelease \ -Donnxruntime_BUILD_SHARED_LIBON \ -Donnxruntime_ENABLE_PYTHONOFF让我解释一下这几个关键参数-DCMAKE_BUILD_TYPERelease指定构建类型为发布版。这会开启编译器优化如-O3生成的二进制文件性能更高但体积更大且不包含调试信息。如果是开发调试可以设为Debug。-Donnxruntime_BUILD_SHARED_LIBON编译生成动态链接库.so文件。这是最常见的使用方式。如果你希望将onnxruntime静态链接到你的最终程序中可以设为OFF来生成静态库.a文件。-Donnxruntime_ENABLE_PYTHONOFF因为我们只关心C库所以关闭Python绑定可以显著减少编译依赖和编译时间。3.1 高级功能配置GPU支持与执行提供程序如果你的机器有NVIDIA GPU并且已经安装了CUDA和cuDNN那么强烈建议启用CUDA支持这能带来数十倍甚至上百倍的推理加速。假设你的CUDA安装在默认的/usr/local/cuda路径配置命令需要添加cmake .. -DCMAKE_BUILD_TYPERelease \ -Donnxruntime_BUILD_SHARED_LIBON \ -Donnxruntime_ENABLE_PYTHONOFF \ -Donnxruntime_USE_CUDAON \ -DCUDA_TOOLKIT_ROOT_DIR/usr/local/cuda \ -Donnxruntime_CUDA_HOME/usr/local/cuda \ -Donnxruntime_USE_CUDNNON \ -DCUDNN_HOME/usr/lib/x86_64-linux-gnu # 或者你的cuDNN路径这里引入了“执行提供程序”的概念。onnxruntime的强大之处在于其可扩展的执行提供程序架构。除了默认的CPU提供程序你还可以通过编译选项集成CUDA用于NVIDIA GPU加速。TensorRTNVIDIA的高性能深度学习推理SDK能对模型进行进一步的图优化和内核融合通常比纯CUDA更快。OpenVINO英特尔针对其CPU、集成显卡和独立显卡的推理工具套件。CoreML苹果设备的推理引擎。启用TensorRT相对复杂需要额外指定TensorRT的路径并确保CUDA、cuDNN、TensorRT版本兼容。一个典型的配置片段如下-Donnxruntime_USE_TENSORRTON \ -DTENSORRT_HOME/path/to/TensorRT-8.x.x.x3.2 实用编译选项与避坑指南除了核心功能还有一些实用选项-Donnxruntime_BUILD_UNIT_TESTSON编译单元测试。建议开启编译完成后运行ctest可以验证编译是否成功。-Donnxruntime_BUILD_BENCHMARKSON编译性能基准测试工具。-Donnxruntime_USE_OPENMPON使用OpenMP进行CPU并行计算。对于多核CPU开启此选项能提升CPU推理性能。-Donnxruntime_USE_FULL_PROTOBUFOFF使用精简版的protobuf。onnxruntime自带了一个精简的protobuf实现以减小依赖除非你有特殊需求比如需要链接外部的protobuf库否则保持OFF即可。实操心得CMake缓存问题CMake会缓存之前的配置结果。如果你修改了CMake配置选项比如从CPU改为CUDA直接再次运行cmake命令可能不会生效。最稳妥的做法是清空build目录从头开始cd ~/onnxruntime_build rm -rf build mkdir build cd build # 重新运行完整的cmake命令这是避免各种诡异编译错误的最有效方法。4. 编译、安装与验证CMake配置成功后终端会输出一大段总结信息显示哪些功能被启用、哪些被禁用以及依赖库的路径。仔细检查这部分输出确认CUDA、TensorRT等是否被正确找到。4.1 并行编译与资源管理接下来就是调用make进行编译。编译onnxruntime是一个计算和I/O密集型任务非常耗时在我的8核机器上大约需要30-60分钟。使用-j参数指定并行任务数可以大幅缩短时间。通常设置为CPU核心数或核心数的1.5倍。make -j$(nproc)$(nproc)命令会自动获取你系统的CPU核心数。编译过程中终端会滚动输出大量的编译信息。如果遇到错误通常会以红色文字显示。最常见的错误是依赖缺失或版本不兼容需要根据错误信息回头检查环境准备步骤。4.2 安装到系统目录编译完成后build目录下会生成我们需要的库文件如libonnxruntime.so和头文件。我们可以选择将其安装到系统目录如/usr/local方便所有项目链接。sudo make install默认的安装路径是/usr/local。头文件会安装在/usr/local/include/onnxruntime/core/session/等子目录下库文件会安装在/usr/local/lib。你可以通过CMake的-DCMAKE_INSTALL_PREFIX参数来修改安装路径例如-DCMAKE_INSTALL_PREFIX~/my_onnxruntime。注意事项动态库路径如果你选择安装到非标准路径或者不安装而直接使用build目录下的库需要确保系统运行时链接器能找到它。有几种方法将库路径添加到LD_LIBRARY_PATH环境变量export LD_LIBRARY_PATH/path/to/onnxruntime/lib:$LD_LIBRARY_PATH将库路径添加到/etc/ld.so.conf或/etc/ld.so.conf.d/下的一个文件中然后运行sudo ldconfig。在编译你的应用程序时使用-Wl,-rpath,/path/to/onnxruntime/lib链接器选项指定运行时路径。对于生产环境方法2是更规范的做法。4.3 验证安装是否成功安装完成后如何验证C库是否可用呢onnxruntime编译时如果开启了单元测试会生成一个简单的测试程序。我们可以运行一个测试来验证# 在build目录下 ./onnxruntime_test_all --gtest_filter*ModelLoad*CPU*这个命令会运行所有名称包含“ModelLoad”和“CPU”的测试用例。如果看到一堆[ OK ]或[ PASSED ]的输出基本说明库的编译和链接是正常的。更直接的验证方法是我们写一个最简单的C程序来加载一个ONNX模型。首先你需要一个ONNX模型文件。可以从onnxruntime的测试模型里找一个或者用PyTorch、TensorFlow导出一个简单的模型。假设我们有一个名为model.onnx的模型文件和一个验证程序test_ort.cc// test_ort.cc #include onnxruntime/core/session/onnxruntime_cxx_api.h #include iostream #include vector int main() { Ort::Env env(ORT_LOGGING_LEVEL_WARNING, test); Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(1); // 加载模型 Ort::Session session(env, model.onnx, session_options); // 获取模型输入输出信息此处省略具体代码 // ... std::cout ONNX Runtime C library loaded and model session created successfully! std::endl; return 0; }编译这个测试程序g -stdc14 test_ort.cc -o test_ort \ -I/usr/local/include \ -L/usr/local/lib \ -lonnxruntime \ -Wl,-rpath,/usr/local/lib运行./test_ort如果不报错并打印出成功信息那么恭喜你C版的onnxruntime已经成功部署到你的Ubuntu系统上了。5. 集成到CMake项目最佳实践在实际项目中我们很少直接用g命令行编译而是使用CMake来管理。下面是一个极简的CMakeLists.txt示例展示如何在一个C项目中查找并链接onnxruntime。cmake_minimum_required(VERSION 3.18) project(MyOnnxProject) set(CMAKE_CXX_STANDARD 14) # 查找onnxruntime库 find_package(onnxruntime REQUIRED) # 如果find_package找不到可以手动指定路径 # include_directories(/path/to/onnxruntime/include) # link_directories(/path/to/onnxruntime/lib) add_executable(my_app main.cpp) # 链接onnxruntime库 target_link_libraries(my_app PRIVATE onnxruntime::onnxruntime) # 或者手动指定 # target_link_libraries(my_app PRIVATE onnxruntime)为了让find_package能正常工作你需要确保onnxruntime的安装路径在CMake的搜索路径中或者通过-Donnxruntime_DIR/path/to/onnxruntime/lib/cmake/onnxruntime传递给CMake。onnxruntime在安装时会在lib/cmake/目录下生成相应的CMake配置文件。5.1 多配置构建与交叉编译考量如果你需要为不同的构建类型Debug/Release或不同的执行提供程序CPU/GPU准备不同的库最好在编译onnxruntime时就做好规划。一种常见的做法是创建不同的构建目录onnxruntime_build/ ├── cpu-release/ ├── cuda-release/ └── cuda-debug/在每个目录下分别用不同的CMake参数进行配置和编译。在你的主项目中可以通过CMake的生成器表达式来条件化地链接不同的库。对于嵌入式或边缘设备可能涉及到交叉编译。onnxruntime支持通过CMake工具链文件进行交叉编译。你需要准备好目标平台的工具链如aarch64-linux-gnu并在CMake配置时通过-DCMAKE_TOOLCHAIN_FILE指定工具链文件。这个过程更为复杂需要确保所有依赖库如protobuf也为目标平台正确编译。6. 常见问题与排查技巧实录即便按照指南操作你也可能会遇到一些问题。下面是我在多次部署中遇到的一些典型问题及其解决方法。6.1 编译阶段问题问题1CMake配置失败提示找不到protobuf或版本不对。现象Could NOT find Protobuf (missing: Protobuf_LIBRARIES Protobuf_INCLUDE_DIR)排查首先确认libprotobuf-dev已安装。然后onnxruntime默认使用其内置的、精简的protobuf。这个错误有时是因为你设置了-Donnxruntime_USE_FULL_PROTOBUFON但系统没有安装完整版的protobuf开发包。解决最简单的办法是保持-Donnxruntime_USE_FULL_PROTOBUFOFF默认值。如果确实需要外部protobuf确保安装了对应版本的开发包如libprotobuf-dev并且版本与onnxruntime要求的兼容。问题2编译过程中内存不足被系统杀死。现象编译进程突然终止终端显示Killed。排查这通常是因为并行编译任务太多占用了过多内存尤其是链接阶段。解决减少并行编译任务数。将make -j$(nproc)改为make -j2或make -j4。你也可以尝试增加系统的交换空间swap。问题3启用CUDA后编译失败提示CUDA架构相关错误。现象nvcc fatal : Unsupported gpu architecture compute_xx排查你的CUDA工具包版本可能不支持你GPU的计算能力Compute Capability。或者CMake自动检测的GPU架构列表包含了不支持的架构。解决手动指定支持的GPU架构。在CMake命令中添加参数例如对于算力为7.5的GPU如RTX 20系列-Donnxruntime_CUDA_ARCHITECTURES75你可以在NVIDIA官网查询你的GPU算力。对于多GPU架构可以用分号分隔如75;80;86。6.2 链接与运行时问题问题4编译自己的程序时链接失败。现象undefined reference to Ort::xxx...排查这是典型的链接错误说明编译器找到了头文件但链接器没找到库文件或链接的库不匹配。解决检查-L参数指定的路径是否正确libonnxruntime.so文件是否在该路径下。检查链接的库名是否正确动态库一般是-lonnxruntime。确保编译onnxruntime时-Donnxruntime_BUILD_SHARED_LIBON生成.so而你的程序链接的也是动态库。如果onnxruntime编译的是静态库.a你需要链接静态库并且可能需要链接它所有的依赖项这非常复杂不推荐新手尝试。问题5程序运行时崩溃提示GLIBCXX版本错误。现象/usr/lib/x86_64-linux-gnu/libstdc.so.6: version GLIBCXX_3.4.29 not found排查这表示编译onnxruntime时使用的GCC版本及其C标准库比运行环境的版本要新。解决这是部署中的经典问题。有两种思路统一环境在目标运行环境上编译onnxruntime和你自己的程序。静态链接C标准库在编译你自己的程序时可以尝试添加-static-libstdc选项但这会增大二进制文件体积且可能引入其他兼容性问题。更推荐第一种方法。问题6加载模型时出错提示不支持的算子或模型格式错误。现象[E:onnxruntime:, inference_session.cc:1534 onnxruntime::InferenceSession::Initialize] ... Fatal error: ...排查ONNX模型可能包含onnxruntime当前版本不支持的算子OP或者模型文件本身损坏、版本不兼容。解决使用onnx.checkerPython包检查模型文件是否有效。确认导出模型的框架PyTorch/TensorFlow和onnxruntime的版本兼容性。有时需要升级onnxruntime或使用特定版本的模型导出工具。如果是不支持的算子可以考虑在编译onnxruntime时启用更多的执行提供程序如TensorRT或者寻找替代模型。6.3 性能调优与监控成功运行之后你可能会关心性能。onnxruntime提供了会话选项SessionOptions来进行一些基础调优Ort::SessionOptions session_options; // 设置线程数 session_options.SetIntraOpNumThreads(4); // 设置算子内部并行线程数 session_options.SetInterOpNumThreads(2); // 设置并行执行算子的线程数 // 启用内存模式优化对于多次运行相同模型很有用 session_options.EnableCpuMemArena(); // 设置执行模式默认是ORT_SEQUENTIAL session_options.SetExecutionMode(ORT_PARALLEL);对于GPU推理确保你的模型在导出时已经做了适当的优化如使用TensorRT的ONNX导出器并且在创建会话时指定了CUDA或TensorRT执行提供程序。Ort::SessionOptions session_options; OrtCUDAProviderOptions cuda_options{}; // 可以设置CUDA设备ID、CUDA内存限制等 session_options.AppendExecutionProvider_CUDA(cuda_options);最后一个实用的技巧是使用环境变量ORT_LOGGING_LEVEL来获取更详细的运行时日志这对调试性能瓶颈和错误非常有帮助。例如在运行你的程序前执行export ORT_LOGGING_LEVELVERBOSE ./your_onnx_app日志会输出每个算子的执行时间、内存分配等信息是性能分析和优化的宝贵资料。整个过程从系统环境准备到最终集成验证虽然步骤繁多但每一步都有其必要性。理解每一步背后的原因能让你在遇到问题时更快地定位和解决。希望这份详尽的指南能帮你把onnxruntime这个强大的工具稳稳地运行在Ubuntu的C环境里。