onnx、openvino与mnn在CPU推理场景下的性能实测与优化策略

张开发
2026/6/9 14:27:20 15 分钟阅读
onnx、openvino与mnn在CPU推理场景下的性能实测与优化策略
1. 为什么需要关注CPU推理框架的性能在AI模型部署的实际场景中GPU资源往往比较昂贵且有限。很多边缘设备、移动端应用或者成本敏感的项目都需要依赖CPU进行推理。这时候选择一个高效的推理框架就显得尤为重要了。onnx、openvino和mnn是目前主流的三个CPU推理框架它们各有特点性能表现也各不相同。我曾经在一个智能安防项目中遇到过这样的问题模型在训练时表现很好但部署到实际设备上却卡顿严重。后来发现是推理框架选型不当导致的性能瓶颈。通过对比测试最终将推理速度提升了近40%。这个经历让我深刻认识到框架选择的重要性。2. 测试环境与基准模型准备2.1 测试环境配置为了确保测试结果的可靠性我搭建了统一的测试环境硬件Intel Core i7-10700K CPU 3.80GHz操作系统Ubuntu 20.04 LTSPython版本3.8.10框架版本onnxruntime 1.10.0openvino 2022.1.0MNN 1.2.0这里有个小技巧测试前记得关闭其他占用CPU资源的程序最好在性能模式下运行测试。我刚开始测试时就因为后台开了太多程序导致结果波动很大。2.2 基准模型选择我选用了一个典型的目标检测模型ctdet_coco_dlav0_512作为测试基准。这个模型输入尺寸是512x512输出包含目标框和类别信息在智能安防、工业质检等场景都很常见。模型准备时需要注意onnx模型直接从原训练框架导出openvino使用mo.py工具转换onnx模型MNN模型使用转换工具从onnx转换特别提醒为了公平比较三个框架都使用原始精度FP32的模型没有进行量化等优化操作。实际项目中可以根据需求再做量化。3. 三个框架的推理速度实测3.1 基础推理代码实现先来看看三个框架的基础推理代码怎么写。虽然网上有很多示例但实际使用时还是会遇到各种坑。onnxruntime的实现import onnxruntime session onnxruntime.InferenceSession(model_path) inputs {session.get_inputs()[0].name: input_data} outputs session.run(None, inputs)openvino的实现from openvino.inference_engine import IECore ie IECore() net ie.read_network(modelmodel_path) exec_net ie.load_network(networknet, device_nameCPU) res exec_net.infer(inputs{input_blob: input_data})MNN的实现import MNN interpreter MNN.Interpreter(model_path) session interpreter.createSession() input_tensor interpreter.getSessionInput(session) # 需要特别注意数据格式转换 tmp_input MNN.Tensor(input_shape, MNN.Halide_Type_Float, input_data, MNN.Tensor_DimensionType_Tensorflow) input_tensor.copyFrom(tmp_input) interpreter.runSession(session)3.2 实测数据对比在相同环境和模型下我进行了100次推理取平均值结果如下框架平均推理时间(ms)内存占用(MB)onnxruntime78.2520openvino52.4480MNN75.8450从数据可以看出openvino表现最优比onnxruntime快了约33%MNN和onnxruntime速度接近但内存占用更优openvino在Intel CPU上的优化确实很出色这里有个有趣的发现第一次运行时openvino的优势没那么明显但连续运行几次后性能会提升。这是因为openvino有自动优化机制。4. 性能优化实战技巧4.1 openvino的优化策略openvino之所以快主要得益于Intel的深度优化。我们可以进一步调优使用Async模式适合流水线作业exec_net ie.load_network(networknet, device_nameCPU, num_requests2) infer_request exec_net.requests[0] infer_request.async_infer(inputs{input_blob: input_data})调整线程数ie IECore() ie.set_config({CPU_THREADS_NUM: 4}, CPU)使用模型缓存避免每次加载都重新优化ie.set_config({CACHE_DIR: ./cache}, CPU)4.2 onnxruntime的优化方法onnxruntime也可以通过一些技巧提升性能启用并行执行options onnxruntime.SessionOptions() options.execution_mode onnxruntime.ExecutionMode.ORT_PARALLEL session onnxruntime.InferenceSession(model_path, options)优化执行提供者session.set_providers([CPUExecutionProvider])图形优化options.graph_optimization_level onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL4.3 MNN的实用优化MNN虽然默认性能不是最优但在移动端很有优势使用半精度MNN::ScheduleConfig config; config.backendConfig { .precision MNN::BackendConfig::Precision_Low, };调整缓存大小interpreter.setCacheSize(1024*1024*10) # 10MB使用Winograd优化interpreter.setSessionMode(MNN.Interpreter.Session_Resize_Check)5. 框架选型建议经过实测和优化实践我总结了一些选型建议Intel CPU环境优先考虑openvino特别是最新一代处理器跨平台需求onnxruntime是更通用的选择移动端部署MNN可能更适合特别是资源受限的场景模型复杂度简单模型差异不大复杂模型openvino优势明显在实际项目中我通常会这样做先用onnxruntime快速验证模型如果运行在Intel硬件转换为openvino移动端则考虑MNN记得有一次我把一个分类模型从onnxruntime迁移到openvino后推理速度从60ms降到了40msQPS直接提升了50%。这种优化在需要处理大量请求的服务中特别有价值。6. 常见问题与解决方案在长期使用这三个框架的过程中我积累了一些典型问题的解决方法模型转换失败openvino检查opset版本可能需要降级MNN尝试用最新版本的转换工具推理结果不一致检查输入数据预处理是否一致验证各框架的默认计算精度性能波动大确保测试时CPU频率稳定关闭其他占用资源的程序内存泄漏onnxruntime注意session的释放openvino检查infer_request的使用最近遇到一个典型问题客户反馈openvino推理时内存持续增长。后来发现是每次推理都新建session导致的。改为复用session后问题解决。7. 进阶优化思路对于追求极致性能的场景还可以考虑以下方法模型量化将FP32转为INT8通常能获得2-4倍加速算子融合减少计算图中的算子数量内存复用避免频繁申请释放内存批处理优化合理设置batch size以量化为例openvino提供了很方便的量化工具pot -q default -m model.xml -w model.bin --output-dir ./quantized不过要注意量化可能会影响精度需要仔细验证。我在一个人脸识别项目中通过量化将模型从90MB缩小到23MB速度提升3倍而准确率只下降了0.3%。

更多文章