mingw下使用msvc的onnxruntime库
mingw下使用msvc的onnxruntime库
- 背景
- 方法1:直接下载release版——失败
- 方法2:使用mingw编译onnx——失败
- 方法3:mingw下调用msvc库
- 3.1 直接调用已有onnxruntime.dll——失败
- 3.2 动态加载onnxruntime.dll——失败
- 3.3 静态库——失败
- 方法4:VS编译一个不依赖msvc库的onnxruntime.dll——成功
- 安装Visual Studio2022
- 启动VS命令行端
- 启动编译
背景
我有个算法A.dll,为了兼容多平台使用了mingw环境编译Windows版本。
A需要用到onnx,所以需要链接onnxruntime.dll。
赶时间直接去到方法4。不赶时间建议全部看完,能了解其中的原理。
方法1:直接下载release版——失败
你是不是和我一样想到直接下载:https://github.com/microsoft/onnxruntime/releases
恭喜你,第一个坑,release的Windows版本的dll,是用Visual Studio编译,基于msvc运行时库的,而你的A.dll是基于mingw运行时库的。
当你的A.dll加载onnxruntime.dll时,会依赖如msvcp140.dll、vcruntime140.dll这些库,就会崩溃!
方法2:使用mingw编译onnx——失败
经过上面折腾,你就想:那我就自己编译onnx呗,恭喜你,进入第二个坑:onnxruntime在Windows上只支持Visual Studio编译
给你看看github上的留言:
人家已经明确不支持了。当然我也没直接放弃,也是挣扎过的,遇到了很多问题,包括:
- 报错protoc Error -1073741515
- flatbuffers版本不对
- 报错: #pragma warning
- 报错: sal.h重复定义
坑太深,就先不折腾了,建议兄弟姐妹们能把它解决,毕竟这才是正道!
我是这样编译的:
打开mingw64命令行,进入目录:onnxruntime-1.22.0/cmake/build_mingw
cmake .. -G "MinGW Makefiles" \-DCMAKE_BUILD_TYPE=Release \-Donnxruntime_BUILD_SHARED_LIB=ON \-Donnxruntime_BUILD_UNIT_TESTS=OFF \-Donnxruntime_ENABLE_PYTHON=OFF \-DCMAKE_INSTALL_PREFIX=`pwd`/install \-Donnxruntime_USE_FULL_PROTOBUF=OFF \-Donnxruntime_USE_CUDA=OFF
编译
mingw32-make.exe
方法3:mingw下调用msvc库
既然只有msvc库,那就研究下什么办法可以调用呗
(这里就得吐槽下CSDN下搜到相关的文章,都是要收费的,这么缺钱吗?不懂得分享和互助吗?建议去google搜索,很多资料)
这里总结一下:就是你的msvc下编译的dll,需要封装出C接口,不能提供c++接口,因为msvc和mingw的库函数符号命名有差异,无法调用c++的接口。刚好我们的onnxruntime库已经提供了c接口了onnxruntime_c_api.h
可是这些文章没提到一个根源,请往下看
3.1 直接调用已有onnxruntime.dll——失败
我的A.dll直接连接使用onnxruntime.dll,运行时崩溃,原因上面说过,onnxruntime.dll会依赖msvcp140.dll这些,就会崩溃
3.2 动态加载onnxruntime.dll——失败
就是不在编译阶段link,在代码里面动态load,实际也是会依赖msvcp140.dll,依旧崩溃
3.3 静态库——失败
我就想既然onnxruntime.dll会依赖msvcp140.dll,那我把这个动态库变成静态库不就好咯?搜索了一下找到下面方案
gendef.exe onnxruntime.dll #从MSVC的DLL中提取导出符号列表,生成.def 文件
dlltool -d onnxruntime.def -l libonnxruntime.a -D onnxruntime.dll #通过 dlltool 将 .def 转换为 .a 文件
开始我以为是生成libonnxruntime.a静态库咯,还依赖个毛线其它库呀,谁知道上面操作实际是这样的:
-
编译阶段
MinGW 链接 libonnxruntime.a,解析符号引用。
.a 文件中的符号指向 onnxruntime.dll 的导出函数地址(通过 .def 定义)。 -
运行阶段
A.dll 加载时,操作系统自动绑定到 onnxruntime.dll。
函数调用通过 DLL 的导出表正确跳转。
也就是说,最后还是调用onnxruntime.dll,白搞。
方法4:VS编译一个不依赖msvc库的onnxruntime.dll——成功
经过上面方法3你也应该知道问题的根因了,就是官方release的onnxruntime.dll会依赖msvc运行时库。所以只要我们编译一个不依赖msvc运行时库的onnxruntime.dll就可以了。
也就是:
- 使用Visual Studio,按照官方方法编译,静态连接msvc库,编译出onnxruntime.dll
- mingw程序正常使用这个onnxruntime.dll
安装Visual Studio2022
安装向导里面,只需要选择 C++的桌面开发,勾选
- C++ MSVC 工具链
- Windows sdk 11
启动VS命令行端
我的路径是:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2022\Visual Studio Tools\VC
有4个
x64 Native Tools Command Prompt for VS 2022
x64_x86 Cross Tools Command Prompt for VS 2022
x86 Native Tools Command Prompt for VS 2022
x86_x64 Cross Tools Command Prompt for VS 2022
命名规则:目标平台——宿主平台,所以x86_x64意思是在64位机器上编译32位程序,所以我们应该选择x64 Native Tools
启动编译
根据自己需要调整
python ./tools/ci_build/build.py ^--cmake_generator "Visual Studio 17 2022" ^--build_dir ./target/ ^--config Release ^--parallel 8 ^--enable_msvc_static_runtime ^--skip_tests ^--skip_submodule_sync ^--build_shared_lib
编译出来的onnxruntime.dll,可以mingw直接编译链接/调用,不会报错了。