ONNX Runtime 中的依赖项管理
本文档在 CMake 的 “使用依赖项指南” 的基础上,重点介绍了 ONNX Runtime 的相关信息。ONNX Runtime 使用了许多开源 C++ 库,例如 abseil、protobuf、re2、onnx 等。构建 ONNX Runtime 主要有以下三种方式来获取这些库:
- 使用 VCPKG (推荐)
- 从源代码构建所有依赖项
- 使用预安装的软件包 (适用于高级用户)
以下是一个快速比较
支持网络隔离1 | 支持二进制缓存2 | 支持交叉编译 | 开发状态 | 漏洞管理 | |
---|---|---|---|---|---|
VCPKG | 是 | 是 | 良好 | 进行中 | 由 ONNX Runtime 团队提供 |
从源代码构建所有依赖项 | 部分支持3 | 否 | 可行4 | 完全支持 | 由 ONNX Runtime 团队提供 |
使用预安装的软件包 | 是 | 是 | 难以设置 | 某些软件包无法通过此方式处理 | 由您的软件包管理器提供 |
如果您的软件需要符合美国总统关于改进国家网络安全的行政命令 (EO) 14028,我们强烈建议使用 VCPKG。
VCPKG
什么是 VCPKG?
VCPKG 是由 Microsoft 和 C++ 社区维护的免费开源 C/C++ 软件包管理器。它主要由 Microsoft 的 Visual Studio 团队开发。它帮助开发人员以简单和声明式的方式管理 C++ 依赖项。它基于 CMake,可以集成到您的 CMake 项目中,或在构建前单独使用。ONNX Runtime 使用前一种方法,称为清单模式 (manifest mode)。
使用 VCPKG 的先决条件
请参阅 VCPKG 文档以了解支持的主机系统:https://github.com/microsoft/vcpkg-docs/blob/main/vcpkg/concepts/supported-hosts.md。例如,在 Ubuntu 上,您需要安装以下软件包:apt-get install git curl zip unzip pkgconfig ninja-build
如何使用 VCPKG 构建 ONNX Runtime
只需在您的构建命令中添加 “–use_vcpkg” 标志。构建脚本 (build.py) 会将一个新的 vcpkg 仓库检出到您的构建目录中,并启动 vcpkg 工具。如果您遇到任何错误,可能需要按照以下步骤手动获取 VCPKG:
- 安装 Git 并运行 “git clone https://github.com/microsoft/vcpkg.git”
- 导航到 VCPKG 目录并运行启动脚本
- 在 Windows 上:运行 bootstrap-vcpkg.bat
- 在其他系统上:运行 bootstrap-vcpkg.sh
如果脚本无法找到某些先决条件,请安装缺失的软件并重试。
- 将环境变量 VCPKG_INSTALLATION_ROOT 设置为 VCPKG 目录,然后返回 ONNX Runtime 源代码文件夹并再次运行构建脚本。更多详细信息请参阅:https://github.com/microsoft/vcpkg-docs/blob/main/vcpkg/get_started/includes/setup-vcpkg.md。如果您在启动 VCPKG 时遇到问题,请联系 VCPKG 团队寻求支持。
VCPKG ports、triplets 和 toolchains
VCPKG 中的一个包称为 VCPKG port。port 的构建脚本可以在 http://github.com/microsoft/vcpkg/tree/master/ports 找到。ONNX Runtime 也有一些自定义的 ports,托管在 https://github.com/microsoft/onnxruntime/tree/main/cmake/vcpkg-ports。自定义 ports 的优先级高于官方 ports。port 目录中的文件包含特定于该 port 的配置。例如,是否启用 CUDA。
Triplet 是一个 cmake 文件,其中包含应用于当前构建中所有 ports 的配置。例如,是否启用 C++ 异常。它仅用于构建依赖项,不会影响 ONNX Runtime 源代码的构建标志。tools/ci_build/build.py 中设置的编译器标志和 cmake 变量仅适用于 ONNX Runtime,不适用于 vcpkg ports。因此,我们需要使用自定义 triplet 文件来保持设置一致。
Toolchain file 用于设置编译器/链接器等,功能更强大。除了 WebAssembly 构建,ONNX Runtime 通常使用标准的 vcpkg toolchain files。
独特功能
与本页列出的其他解决方案相比,VCPKG 提供了一些我们非常需要的独特功能
VCPKG 对交叉编译提供了更好的支持。例如,ONNX Runtime 依赖于 ONNX。ONNX 的源代码有一些 *.proto 文件。从源代码构建 ONNX 时,我们需要使用 protoc 从 *.proto 文件生成 C++ 源文件。因此,我们需要为宿主操作系统构建 protoc 和 protoc 的依赖项。例如,如果我们在 x64 机器上构建 arm64 包,我们需要为 x64 构建 protoc,而不是 arm64。而且由于 protoc 依赖于 libprotobuf,我们必须为每个 CPU 架构构建 libprotobuf 两次。无论是否使用 vcpkg,都需要构建两次。CMake 不处理这种情况,这给我们的构建系统增加了额外的复杂性。现在我们可以使用 vcpkg 来解决这个问题。它可以直接正常工作,无需额外配置。
使用 VCPKG,我们只需声明根依赖项。在转向 VCPKG 之前,为了满足网络隔离要求(以便我们轻松找到所有下载 URL)和 组件检测 要求,我们需要将所有传递性依赖项添加到 cmake/deps.txt 以及 cmake/external 文件夹下的 cmake 文件中。现在这不再需要了,因为 VCPKG 内置支持资产缓存和 SBOM 生成。
VCPKG 强制要求同一个库只能有一个版本。例如,onnxruntime_provider_openvino.dll 和 onnxruntime.dll 使用的 protobuf 库必须完全相同。虽然这比必要的更严格,但它有助于防止 ODR 违规问题。它带来的好处多于处理使用同一库多个版本可能产生的潜在冲突和不一致性。
限制
目前对 vcpkg 的支持仍在开发中。它不支持以下场景:
- 最小化构建
- iOS 构建
- Windows WebGPU 本机构建
并且某些依赖项尚未由 VCPKG 管理。例如,Dawn。
为 webassembly 构建时,它假定始终设置 “–enable_wasm_simd” 标志和 “–enable_wasm_threads” 标志。它支持的构建变体比第二种模式(从源代码构建所有依赖项)少得多。
此外,在此模式下,用于运行 tools/ci_build/build.py 的 Python 解释器可能与用于构建 VCPKG ports 的解释器不同。这种不一致可能导致问题。因此,如果您安装了多个 Python 版本,我们建议将所需的版本添加到 PATH
的开头,以便将其设置为默认版本。
它尚不支持设置 VC 工具集版本或 Windows SDK 版本。
对 Windows ARM64EC(包括 ARM64X)的支持尚处于实验阶段,尚未经过充分测试。
虽然标准的 cmake 有 4 种不同的构建类型(Debug、Release、RelWithDebInfo 和 MinSizeRel),但 vcpkg 只支持两种。因此,当构建 ONNX Runtime 的 RelWithDebInfo 或 MinSizeRel 版本时,您可能会看到二进制文件大小增加。这个问题可以通过在自定义 triplet 文件中进行更多定制来解决。
我是一名 EP 开发者。我是否需要将所有依赖项转换为 vcpkg ports?
如果依赖项被 Microsoft 发布的 ONNX Runtime 发行版包使用,那么肯定需要。否则我们可以逐个案例讨论。
更新 VCPKG port 的流程
首先,请检查该 port 是否是 cmake/vcpkg/vcpkg-ports
目录中的自定义 port。如果是,您需要在此处更新它。至少需要更新两个地方:vcpkg.json 文件中的版本号,以及 ports.cmake 文件中的 SHA512 哈希值。如果您不知道应该填写哪个 SHA512 值,可以稍微修改当前的哈希值(翻转几个字节),然后使用 “–use_vcpkg” 标志构建 ONNX Runtime(不要使用额外的标志来启用任何资产缓存),然后 vcpkg 会生成错误消息,告诉您它期望的实际哈希值。您可能还需要更新 patch 文件。您可以克隆库的仓库,然后检出您要更新到的新版本,然后应用旧的 patch 文件,解决冲突,然后使用 “git diff” 生成新的 patch 文件来覆盖现有的。
如果依赖项来自 VCPKG 的官方注册表(在 https://github.com/microsoft/vcpkg 中),那会容易得多。只需打开 vcpkg-configuration.json 并将 baseline commit id 更新为最新的 vcpkg commit id。
然后创建一个 PR(Pull Request)。然后 ONNX Runtime 开发团队的成员将审查您的更改,将依赖项复制到内部位置并触发拉取请求管道。如果一切顺利,我们将合并您的更改。
从源代码构建所有依赖项
在您的构建命令中添加 “–cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER”。当未启用 VCPKG 时,我们使用 CMake 的 FetchContent 来管理依赖项。所有此类依赖项都列在 cmake/deps.txt 中,允许您自定义版本和下载 URL。这对于满足网络隔离要求或升级/降级库版本非常有用。在 ONNX Runtime 的 CMake 文件中声明依赖项时,如果提供了 FIND_PACKAGE 参数,FetchContent 将使用 CMake 的 FindPackage 模块从系统位置查找依赖项。添加 –cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER 可以禁用此行为。
使用预安装的软件包
这是从源代码构建 ONNX Runtime 时的默认模式。如果构建命令中既没有 ‘–use_vcpkg’ 也没有 ‘–cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER’,则将使用此模式。这是因为 FetchContent 是各种 CMake 依赖提供程序的一个包装器。默认情况下,如果为依赖项提供了 FIND_PACKAGE 参数,它优先使用 find_package。如果您将 ONNX Runtime 集成到软件包管理器(如 dnf)中,则需要使用此方法。
然而,它有一些注意事项:
- ONNX Runtime 对依赖项有本地补丁 (patch),这些补丁不会应用于您预安装的库。大多数补丁对于基本功能不是必需的。
- 如果您安装的库版本与 ONNX Runtime 期望的版本不同,构建脚本无法警告您。这可能导致奇怪的构建失败。
- 每个库都可以通过不同的方式构建。例如,ONNX Runtime 期望 ONNX 使用 “-DONNX_DISABLE_STATIC_REGISTRATION=ON” 进行构建。如果您从某个地方获得了预构建的 ONNX 库,很可能不是以这种方式构建的。
因此我们说它适用于高级用户。