
1. 为什么还要折腾源码编译从二进制包到自编译的取舍很多人第一次接触 OpenVINO 的时候第一反应是去官网找安装包或者直接apt install一把梭。这当然没错官方提供的预编译包覆盖了绝大多数常见场景装完就能跑推理。但只要你稍微往深里走一步就会撞上预编译包的天花板比如你手头是一台老旧的工控机系统还停留在某个已经停止维护的 Ubuntu 版本比如你需要针对特定 CPU 指令集做裁剪把体积压到最小再比如你要把 OpenVINO 嵌进一个自研的中间件里必须自己控制编译选项和依赖版本。这时候源码编译就成了绕不开的一环。这篇内容聊的是 Ubuntu 环境下 OpenVINO 2021.4 LTS 以及更早版本的编译安装方法。2021.4 是一个长期支持版本很多存量项目还锁在这个版本上尤其是那些依赖旧版推理引擎 API 的工程。而旧版本这三个字背后往往意味着更复杂的依赖关系、更挑剔的编译器版本以及官方文档里语焉不详的补丁说明。我前后在几台不同配置的机器上折腾过这套流程踩过的坑足够写满一页纸这里把完整的思路和操作链路摊开讲适合有一定 Linux 基础、需要自己掌控构建过程的开发者参考。先说清楚一个前提源码编译不是更高级的安装方式它只是一种更可控的方式。预编译包是别人替你做了一堆决策的结果源码编译是你自己重新做一遍这些决策。决策做得好收益是体积、性能和可移植性决策做得糙收益就是一堆链接错误和运行时崩溃。所以动手之前先想清楚你到底需不需要自编译。如果你的需求只是跑个 demo 看看效果老老实实用预编译包别给自己找麻烦。2. 编译前的环境盘点那些预编译包替你隐藏的依赖2.1 系统版本与编译器版本的匹配关系OpenVINO 2021.4 对编译器和系统库是有明确要求的这一点在官方 release notes 里写得比较含蓄但实际编译时会以各种报错的形式提醒你。核心的约束在于 C 标准库和 GCC 版本。2021.4 这一代代码大量使用了 C11 特性部分模块涉及 C14官方推荐 GCC 7.5 及以上。如果你在 Ubuntu 18.04 上默认 GCC 是 7.5刚好卡在及格线上Ubuntu 16.04 默认 GCC 5.4直接编译大概率会在模板展开阶段报一堆看不懂的错误。我的建议是不管你的系统默认编译器是什么版本都显式安装一个 GCC 8 或 GCC 9然后通过update-alternatives切换。这里有个细节不要只切gcc和gcc和c这两个软链也要一并处理因为 CMake 在探测编译器时会优先读CC和CXX环境变量而某些第三方依赖的构建脚本又直接调cc。我见过一次诡异的现象主工程用 GCC 9 编译通过但某个子模块用cc调到了 GCC 5链接阶段符号版本对不上报了一堆undefined reference排查了大半天才发现是软链没切干净。sudo apt install gcc-9 g-9 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 sudo update-alternatives --install /usr/bin/g g /usr/bin/g-9 90 sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-9 90 sudo update-alternatives --install /usr/bin/c c /usr/bin/g-9 90切换完之后用gcc --version和cc --version双重确认两个输出必须一致。这一步看着琐碎但能省掉后面至少一半的玄学报错。2.2 CMake 版本这道坎2021.4 要求 CMake 3.13 以上旧版本比如 2020.x 系列要求 3.10 以上。Ubuntu 18.04 自带的 CMake 是 3.10.2装 2021.4 就不够了。这里不建议用apt装系统源里的 CMake版本往往偏旧直接从官方渠道下二进制包解压到/opt下然后把bin目录加进PATH。这样做的好处是不污染系统包管理将来要换版本直接改软链就行。wget https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0-linux-x86_64.tar.gz sudo tar -zxvf cmake-3.20.0-linux-x86_64.tar.gz -C /opt/ sudo ln -s /opt/cmake-3.20.0-linux-x86_64/bin/cmake /usr/local/bin/cmake装完cmake --version确认一下。这里有个小提醒如果你之前用apt装过旧版 CMake/usr/bin/cmake可能还在而/usr/local/bin在PATH里的优先级通常更高所以软链放这里能覆盖掉旧版。但保险起见还是which cmake看一眼实际调用的是哪个。2.3 依赖库清单与安装顺序OpenVINO 的依赖分两类构建期依赖和运行期依赖。构建期最容易被忽略的是libusb-1.0-0-dev和libudev-dev这两个是给神经计算棒等硬件插件用的如果你明确不需要这些硬件支持可以在 CMake 阶段关掉对应开关省掉这两个依赖。但如果你不确定将来会不会用建议还是装上反正体积不大。完整的依赖安装命令大致如下sudo apt install -y build-essential cmake git pkg-config \ libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev \ libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev \ libusb-1.0-0-dev libudev-dev python3-dev python3-numpy \ libprotobuf-dev protobuf-compiler这里重点说两个libtbb2和libtbb-dev。OpenVINO 的并行计算后端依赖 TBB2021.4 默认链接的是系统 TBB。如果你系统里装的是 TBB 2021 之后的版本可能会遇到 ABI 不兼容的问题表现为运行时崩溃或者推理结果异常。稳妥的做法是确认系统 TBB 版本在 2020 系列或者干脆在 CMake 里指定使用 OpenVINO 自带的 TBB通过-DENABLE_TBB_RELEASE_ONLYOFF之类的开关控制具体看版本。protobuf则是模型转换工具链需要的版本太低会导致 IR 模型解析失败。3. 源码获取与分支选择LTS 和旧版本的区别在哪3.1 仓库结构与分支命名规律OpenVINO 的源码托管在公开的代码仓库上分支命名有一套自己的规律。2021.4 LTS 对应的分支标签通常是2021.4.2这种形式而更早的版本比如 2020.4、2019.3 也各有对应标签。这里有个容易踩的坑不要直接 clone 默认分支然后 checkout 标签因为默认分支的 submodule 引用可能已经指向了新版本的依赖切到旧标签后 submodule 状态对不上编译时会报找不到某些头文件。正确的做法是 clone 时直接指定分支git clone --branch 2021.4.2 --depth 1 https://github.com/openvinotoolkit/openvino.git cd openvino git submodule update --init --recursive--depth 1是为了加快速度但要注意如果你后续需要切到别的标签做对比浅克隆会有点麻烦得重新fetch。如果磁盘空间不紧张去掉--depth 1更省心。git submodule update这一步千万别省OpenVINO 依赖了多个第三方库作为 submodule包括 oneDNN、mkl-dnn 等缺了任何一个都会在 CMake 配置阶段报错。3.2 旧版本的特殊处理补丁与兼容性开关如果你编译的是 2020.x 甚至更早的版本有几个额外的坑要提前知道。第一旧版本的 CMake 脚本里对 Python 的探测逻辑比较粗暴如果你系统里同时有 Python 2 和 Python 3它可能默认抓到 Python 2导致后面 Python 绑定编译失败。解决办法是在 CMake 命令里显式指定-DPYTHON_EXECUTABLE/usr/bin/python3。第二旧版本对 GCC 9 的支持不完整某些头文件里用了已经被移除的std::特性编译时会报错。这时候要么降级到 GCC 8要么手动打补丁。补丁的内容通常是把#include memory之类的缺失头文件补上具体看报错信息。我个人的经验是编译 2020.x 系列时优先用 GCC 8兼容性最好2021.4 用 GCC 9 没问题。如果你非要在 GCC 9 上编 2020.x做好手动改几处源码的心理准备改动量不大但得有耐心看报错。3.3 目录规划与磁盘空间预估源码编译 OpenVINO 是个吃磁盘的活。完整 clone 加编译产物轻松超过 15GB。建议单独挂一块盘或者至少留出 30GB 的余量。目录结构上我习惯把源码放在~/workspace/openvino-src构建目录放在~/workspace/openvino-build安装目录放在~/workspace/openvino-install。源码和构建目录分离是 CMake 的标准做法out-of-source build好处是构建失败时直接删掉构建目录重来不用重新 clone。mkdir -p ~/workspace/openvino-build ~/workspace/openvino-install cd ~/workspace/openvino-build4. CMake 配置阶段每一个开关背后的意图4.1 核心开关的取舍逻辑CMake 配置是整个编译过程中最需要动脑子的环节。OpenVINO 提供了几十个开关全开全关都不现实得按需裁剪。下面这张表是我常用的几个关键开关及其影响开关默认值作用建议CMAKE_BUILD_TYPE空构建类型设为Release否则性能极差ENABLE_PYTHONONPython 绑定需要 Python 接口就开否则关掉省时间ENABLE_SAMPLESON示例程序首次编译建议开方便验证ENABLE_OPENCVON内置 OpenCV已有系统 OpenCV 可关掉避免冲突ENABLE_MYRIADON神经计算棒支持不用硬件就关掉省依赖ENABLE_TESTSOFF单元测试除非要改源码否则别开编译时间翻倍THREADINGTBB并行后端默认 TBBOMP 作为备选CMAKE_BUILD_TYPE这一项必须显式设为Release。我见过有人忘了设编出来跑推理比预编译包慢好几倍还以为是源码编译本身的问题。实际上 CMake 默认不带优化-O0编出来的东西当然慢。设成Release后会自动加-O3 -DNDEBUG性能才正常。ENABLE_OPENCV这个开关值得单独说。OpenVINO 源码里捆绑了一个精简版 OpenCV用于示例程序和图像预处理。如果你系统里已经装了完整版 OpenCV两个版本同时存在容易在链接时打架报重复符号。我的做法是如果只是编译推理引擎核心库把ENABLE_OPENCV关掉示例程序也不编最干净如果要跑示例验证就开着内置的别去链接系统 OpenCV。4.2 一个可复用的 CMake 命令模板综合上面的取舍我常用的配置命令是这样的cmake -DCMAKE_BUILD_TYPERelease \ -DCMAKE_INSTALL_PREFIX~/workspace/openvino-install \ -DENABLE_PYTHONON \ -DPYTHON_EXECUTABLE/usr/bin/python3 \ -DENABLE_SAMPLESON \ -DENABLE_OPENCVOFF \ -DENABLE_MYRIADOFF \ -DENABLE_TESTSOFF \ -DENABLE_GNAOFF \ -DTHREADINGTBB \ ..ENABLE_GNA是语音加速硬件的支持绝大多数场景用不上关掉能省不少编译时间。THREADINGTBB是默认值写出来是为了明确意图将来换 OMP 时改这一处就行。配置阶段如果报错重点看CMakeError.log和CMakeOutput.log里面会记录具体的探测失败原因。最常见的两类错误一是找不到某个依赖库二是编译器特性探测失败。前者补装依赖后者基本就是编译器版本问题回到第 2 节的处理方案。4.3 配置成功后的自检清单CMake 配置跑完别急着make先看一眼输出末尾的摘要信息。重点确认几项Python 绑定是否启用、目标架构是否正确x86_64 还是 ARM、TBB 是否找到、安装路径是否如你所愿。如果摘要里显示Python bindings: NO而你明明开了ENABLE_PYTHON多半是 Python 开发头文件没装或者路径不对回去检查python3-dev是否安装、PYTHON_EXECUTABLE是否指向正确的解释器。5. 编译与安装并行度、内存与中断恢复5.1 并行编译的度怎么把握make -jN里的 N 不是越大越好。OpenVINO 编译时单个编译单元的内存占用不低尤其是涉及模板展开的模块峰值能到 2GB 以上。如果你的机器是 8GB 内存-j8很可能触发 OOM表现为编译器进程被系统杀掉报internal compiler error: Killed。这个报错很有迷惑性看起来像编译器 bug实际是内存不够。经验公式是N 取 CPU 核心数和内存 GB 数除以 2 之后的较小值。比如 8 核 16GB取-j8没问题4 核 8GB取-j4就悬建议-j2或-j3。编译过程中可以用htop盯着内存占用如果 swap 开始频繁读写说明并行度高了中断重来调小 N。make -j$(nproc) 21 | tee build.log把输出同时写到日志文件里方便编译失败后回溯。tee这个习惯强烈建议养成尤其是编译旧版本时报错信息刷屏很快终端回滚缓冲区不够用。5.2 编译中断后的恢复策略编译到一半因为各种原因中断断电、误触、OOM不用从头再来。make本身支持增量编译重新执行同样的命令它会跳过已经完成的目标。但有个例外如果你在中断后改过 CMake 配置或者动过源码最好把构建目录清空重来因为增量编译的依赖追踪可能不准编出来的东西状态不一致运行时出玄学问题。判断要不要清空重来的简单标准只中断没改动直接续编改过任何配置或源码rm -rf *重来。后者多花点时间但省掉排查玄学问题的时间划算。5.3 安装与运行时库路径配置编译完成后执行make install产物会装到之前指定的CMAKE_INSTALL_PREFIX下。装完之后还有关键一步让系统能找到这些库。有两种方式一是把安装目录的lib子目录加进LD_LIBRARY_PATH二是写进/etc/ld.so.conf.d/然后ldconfig。前者只对当前 shell 生效适合临时测试后者全局生效适合长期使用。echo ~/workspace/openvino-install/lib | sudo tee /etc/ld.so.conf.d/openvino.conf sudo ldconfigPython 绑定还需要把python子目录加进PYTHONPATH或者在虚拟环境里装一个.pth文件指向该目录。我倾向于用.pth文件干净且不依赖环境变量echo ~/workspace/openvino-install/python $(python3 -c import site; print(site.getsitepackages()[0]))/openvino.pth配完之后用python3 -c import openvino; print(openvino.__version__)验证能打印出版本号就说明绑定正常。6. 验证与排错从编译成功到真正跑通6.1 最小验证程序编译安装成功不等于能用。最稳妥的验证方式是跑一个最小推理程序加载一个简单模型走完整个前向计算。OpenVINO 安装目录里通常带了示例模型和示例程序直接跑samples下的可执行文件即可。如果示例能跑通说明核心链路没问题如果示例报错根据报错定位是库路径问题还是模型解析问题。我习惯自己写一个十行左右的 Python 脚本做验证加载一个 IR 模型打印输入输出形状然后随便喂一组数据跑一次infer。这个脚本的好处是可控报错信息直接指向具体 API比示例程序的黑盒输出好排查。6.2 常见报错与对应根因编译和运行阶段的高频报错我整理了一张对照表报错现象根因处理方式undefined reference to xxx链接库版本不匹配或软链未切检查 GCC 版本一致性确认cc/c软链internal compiler error: Killed并行编译内存不足降低-j数值ImportError: libxxx.so not found运行时库路径未配置配置LD_LIBRARY_PATH或ldconfigCMake Error: Could not find PythonPython 开发头文件缺失安装python3-dev指定PYTHON_EXECUTABLE推理结果异常非报错TBB 版本 ABI 不兼容换用匹配的 TBB 版本或内置 TBBprotobuf相关解析失败protobuf 版本过低升级 protobuf 到 3.6 以上最后一行那个推理结果异常最阴险因为它不报错只是结果不对。我遇到过一次模型加载正常、推理不崩但输出全是 NaN。排查了半天最后发现是系统 TBB 版本太新和 OpenVINO 2021.4 链接的 TBB ABI 不兼容换回旧版 TBB 后一切正常。这类问题没有报错信息可循只能靠版本对照表逐个排除。6.3 旧版本编译的额外注意事项编译 2019.x 这类更老的版本时还有两个额外的坑。一是这些版本的 CMake 脚本可能引用了已经下线的下载地址配置阶段卡在下载依赖上。解决办法是手动把依赖包下好放到指定目录或者改 CMake 脚本里的 URL。二是老版本的 Python 绑定对 Python 3.8 以上支持不好PyEval_InitThreads之类的 API 在新 Python 里行为变了编译能过但运行时可能崩。如果非要在新 Python 上用老版本 OpenVINO建议用虚拟环境锁一个 Python 3.6 或 3.7。7. 我在这套流程里攒下的几条实操心得折腾源码编译这件事流程本身是死的但细节里的坑是活的。分享几条我认为最有价值的经验都是文档里不会写、只有实际动手才会撞上的。第一条先编最小配置再逐步加开关。第一次编译时把所有非必要开关全关掉只编核心推理引擎确认能编能跑之后再逐个打开 Python 绑定、示例程序等。这样一旦出问题你能立刻定位是哪个开关引入的而不是面对一个全开的配置猜谜。我早期图省事一次性全开结果报错信息混在一起排查成本翻了好几倍。第二条构建日志一定要留。tee build.log这个动作花不了几秒钟但编译失败时它就是你的救命稻草。尤其是并行编译时终端输出交错报错信息可能被淹没日志文件里按顺序记录grep 一下就能定位。我习惯在日志文件名里带上日期和配置摘要比如build-2021.4-release-gcc9.log将来对比不同配置的编译结果时一目了然。第三条别迷信最新版本。OpenVINO 的版本迭代很快新版本往往修了旧版的 bug但也可能引入新的依赖要求。如果你的项目锁在某个旧版本上就老老实实按那个版本的依赖清单来别想着用新版依赖应该也能编。ABI 兼容性这东西差一个小版本就可能出问题尤其是 TBB 和 protobuf 这两个。第四条安装目录别和系统目录混。把 OpenVINO 装到/usr/local看似方便但将来要换版本或者卸载时很麻烦容易残留文件污染系统。装到用户目录下的独立路径配合LD_LIBRARY_PATH或ldconfig配置切换版本时改个软链就行干净利落。这套流程走下来从零到跑通大概需要一两个小时其中编译占大头。机器配置好的话半小时能编完老机器可能要两三个小时。时间花得值不值取决于你对可控这件事的需求有多强。如果只是跑个推理看看效果预编译包十分钟搞定没必要折腾但如果你的项目要长期维护、要嵌入到别人的系统里、要对体积和性能负责那这套自编译的流程就是必须掌握的基本功。