共计 3133 个字符,预计需要花费 8 分钟才能阅读完成。
为什么选择 ONNX Runtime?
在 C ++ 项目中直接使用 PyTorch 或 TensorFlow 进行模型推理时,开发者常遇到以下痛点:

- 依赖复杂 :原生框架需要完整的 Python 环境或庞大的 C ++ 库支持
- 性能损失 :Python 接口调用存在额外开销,多线程支持不完善
- 跨平台难 :不同框架的模型格式互不兼容,部署时需要重复适配
ONNX(Open Neural Network Exchange)通过标准化模型表示解决了这些问题。而 ONNX Runtime 作为专门优化的推理引擎,相比原生框架有以下优势:
- 轻量高效 :专注于推理环节,去除了训练相关冗余代码
- 多后端支持 :可灵活选择 CPU/GPU/DSP 等计算设备
- 跨平台 :统一模型格式避免重复适配
技术选型对比
与其他推理引擎对比时,ONNX Runtime 的定位非常明确:
| 引擎 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| ONNX Runtime | 通用性强,支持最广的算子集 | 极致性能稍逊专业引擎 | 多框架模型统一部署 |
| TensorRT | NVIDIA 显卡优化极致性能 | 仅限 NVIDIA 硬件 | 高吞吐量云端推理 |
| OpenVINO | Intel 处理器深度优化 | 对非 Intel 硬件支持有限 | 边缘设备部署 |
如果项目需要:
– 部署来自不同训练框架的模型
– 支持异构计算设备
– 快速迭代验证
ONNX Runtime 通常是最佳选择。
实战:从模型导出到推理实现
模型导出注意事项
以 PyTorch 为例,导出 ONNX 模型时需要特别注意:
-
设置模型为 eval 模式:
model.eval() -
提供正确的输入张量示例:
dummy_input = torch.randn(1, 3, 224, 224) # 与实际输入维度一致 -
指定动态维度(如需支持可变输入):
torch.onnx.export( model, dummy_input, "model.onnx", dynamic_axes={'input': {0: 'batch_size'}, # 批处理维度可变 'output': {0: 'batch_size'} } )
C++ 环境配置
推荐使用 vcpkg 管理依赖:
-
安装 ONNX Runtime:
vcpkg install onnxruntime -
CMake 项目配置示例:
find_package(onnxruntime CONFIG REQUIRED) target_link_libraries(your_target PRIVATE onnxruntime::onnxruntime )
核心推理实现
线程安全的推理会话管理:
#include <onnxruntime_cxx_api.h>
class ONNXInference {
public:
ONNXInference(const std::string& model_path) {
// 1. 初始化环境
env_ = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "Default");
// 2. 配置会话选项
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1); // 每会话独占线程
// 3. 创建会话
session_ = Ort::Session(env_, model_path.c_str(), session_options);
// 4. 获取输入输出信息
auto memory_info = Ort::MemoryInfo::CreateCpu(
OrtAllocatorType::OrtArenaAllocator,
OrtMemType::OrtMemTypeDefault);
}
std::vector<float> Infer(const std::vector<float>& input) {
Ort::RunOptions run_options;
// 准备输入 Tensor
std::vector<int64_t> input_shape = {1, 3, 224, 224};
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
memory_info_,
const_cast<float*>(input.data()),
input.size(),
input_shape.data(),
input_shape.size());
// 执行推理
auto outputs = session_.Run(
run_options,
input_names_.data(),
&input_tensor,
1,
output_names_.data(),
1);
// 处理输出...
}
private:
Ort::Env env_;
Ort::Session session_;
Ort::MemoryInfo memory_info_;
};
性能优化实战
后端选择策略
根据硬件环境选择最优执行提供者:
// CPU 加速(适用于大多数场景)session_options.AppendExecutionProvider_CPU(enable_cpu_mem_arena);
// CUDA 加速(NVIDIA 显卡)OrtCUDAProviderOptions cuda_options;
session_options.AppendExecutionProvider_CUDA(cuda_options);
// DirectML(Windows 平台通用 GPU)OrtDmlProviderOptions dml_options;
session_options.AppendExecutionProvider_DML(dml_options);
Benchmark 数据参考
测试环境:
– CPU: Intel i7-11800H @ 2.30GHz
– GPU: NVIDIA RTX 3060 Laptop
– 模型: ResNet50
| 后端 | 吞吐量 (images/sec) | 延迟 (ms) | 内存占用 (MB) |
|---|---|---|---|
| CPU | 45.2 | 22.1 | 320 |
| CUDA | 210.5 | 4.7 | 780 |
| TensorRT | 285.3 | 3.5 | 650 |
内存池配置
通过 arena 配置优化内存复用:
OrtArenaCfg arena_cfg{0, // 初始内存大小 (0= 自动)
-1, // 最大内存大小 (-1= 无限制)
0 // 内存释放策略 (0= 立即释放)
};
session_options.EnableCpuMemArena(&arena_cfg);
常见问题解决
模型转换错误
典型错误:”Unsupported ONNX opset version 15″
解决方案:
1. 检查 ONNX Runtime 版本支持的 opset
2. 导出时指定兼容的 opset 版本:
torch.onnx.export(..., opset_version=12)
输入输出对齐
常见现象:输出形状与预期不符
调试方法:
// 打印输入输出元数据
auto input_info = session_.GetInputTypeInfo(0);
auto output_info = session_.GetOutputTypeInfo(0);
进阶优化方向
- 模型量化 :使用 ONNX 的量化工具将 FP32 转为 INT8
- 算子融合 :通过 ONNX Runtime 的图优化 pass 合并连续操作
- 自定义算子 :为特定操作注册优化实现
完整示例项目可参考:
https://github.com/microsoft/onnxruntime-inference-examples
实践心得
经过多个项目的实际验证,ONNX Runtime 确实在保持易用性的同时提供了不错的性能。特别是在需要快速验证不同模型架构时,ONNX 的标准格式避免了重复的适配工作。
建议初次使用时:
1. 从 CPU 后端开始验证功能正确性
2. 逐步启用 GPU 加速和内存优化
3. 最终通过量化获得额外性能提升
这种渐进式优化路径能有效降低调试复杂度。
