ONNX Runtime CUDA Plugin EP Python 包:从 build_wheel.py 模板构建到运行时注册的完整剖析 ONNX Runtime CUDA Plugin EP Python 包从 build_wheel.py 模板构建到运行时注册的完整剖析【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime本文以 plugin-ep-cuda/python/README.md 为骨架系统讲解onnxruntime-ep-cuda12/onnxruntime-ep-cuda13两个 Python 分发包的构建方式与运行时用法为什么必须通过 build_wheel.py 而非pip install直接构建、四个命令行参数各自的作用与校验逻辑、模板渲染与 auditwheel 修复在打包流程中的具体行为以及构建产物onnxruntime_ep_cuda模块在 ONNX Runtime 中注册 CUDA Plugin EP、发现设备并执行推理的完整调用链。一、包定位独立于 onnxruntime 主包的 CUDA EP 插件plugin-ep-cuda/README.md 说明了 CUDA Plugin Execution Provider 的定位它作为一个独立分发的插件工件可插入已安装的 ONNX Runtime 运行环境中而不是编译进主onnxruntime二进制。Python 目录正是这一工件的打包源头产出两个分发名不同的 wheelonnxruntime-ep-cuda12面向 CUDA 12.x 构建onnxruntime-ep-cuda13面向 CUDA 13.x 构建关键点在于两个分发安装的是同一个导入模块onnxruntime_ep_cuda。也就是说 CUDA 版本只体现在分发名distribution name与内嵌的原生库构建产物上Python 侧 API 完全一致。目录内各文件的分工plugin-ep-cuda/ 顶层与python/子目录共同构成打包链路文件作用MIN_ONNXRUNTIME_VERSION记录最低兼容的onnxruntime核心版本当前仓库为1.24.4是所有包共享的唯一事实来源VERSION_NUMBER插件包自身版本号当前为0.2.0paths.txt指定与 CUDA EP 相关的源码/构建目录如onnxruntime/core/providers/cuda、cmake/onnxruntime_providers_cuda_plugin.cmake及多个 CI 流水线用于在生成 release notes 时筛选相关提交_packaging_utils.py共享的模板渲染工具gen_file_from_template供 Python 与 C# 打包脚本复用python/build_wheel.py构建 wheel 的主脚本本文核心python/pyproject.toml.in构建描述模板注意是.in模板而非实体文件python/setup.py最小的 setuptools 入口负责打平台特定的 wheel tagpython/onnxruntime_ep_cuda/init.py安装后暴露给用户的 API 模块python/test/test_cuda_plugin_ep.py冒烟测试值得强调的是 README 中的一句话设计决策这些包不声明对某个特定onnxruntime版本的硬依赖而是把MIN_ONNXRUNTIME_VERSION中的版本字符串在构建/打包时注入每个包的 README并由原生插件 EP 代码在注册时自行校验兼容性。这样 pip 不会因依赖解析失败而阻止安装兼容性问题被推迟到真正加载库的运行时暴露。二、为什么不能直接pip install模板渲染机制python/README.md 明确写道由于源码树中包含的是pyproject.toml.in模板而不是具体的pyproject.toml直接对该目录执行pip install或pip wheel不受支持必须经由build_wheel.py。这个设计可以从源码中得到完整印证。模板文件 pyproject.toml.in 的内容为[build-system] requires [setuptools68.0, wheel] build-backend setuptools.build_meta [project] name package_name version version description ONNX Runtime CUDA Plugin Execution Provider readme onnxruntime_ep_cuda/README.md license {text MIT} requires-python 3.11 [tool.setuptools.packages.find] include [onnxruntime_ep_cuda*] [tool.setuptools.package-data] onnxruntime_ep_cuda [*.dll, *.so, *.so.*]其中package_name与version两个占位符决定了同一个模板能渲染出cuda12与cuda13两个不同的分发——这是一套源码、两个分发包得以成立的基础。另外两个细节requires-python 3.11wheel 仅面向 Python 3.11 及以上package-data声明了*.dll、*.so、*.so.*这保证后续拷入包目录的原生库会被打进 wheel而不是被当作源码文件忽略。模板渲染由 _packaging_utils.py 中的gen_file_from_template完成。它用正则(\w)匹配占位符并做字符串替换且默认运行在strict 模式下模板中出现的变量集合与调用方传入的键集合必须完全一致否则抛出ValueError并打印仅存在于模板与仅存在于替换参数两侧的差集。这为构建脚本提供了防呆能力——模板新增变量而脚本忘记传值时会立即失败而不是产出带残留占位符的损坏文件。三、build_wheel.py四步构建流程与参数详解3.1 命令与参数README 给出的标准用法python build_wheel.py \ --binary_dir path-to-built-binaries \ --version PEP-440-version \ --package_name onnxruntime-ep-cuda12-or-onnxruntime-ep-cuda13 \ --output_dir output-directory其职责如 README 所述将预构建的 CUDA 插件 EP 二进制与包源码组合产出一个平台特定的 wheel。四个参数的含义与源码中的校验逻辑对照如下参数必填作用源码校验--binary_dir是存放已构建插件 EP 原生库的目录必须存在且为目录否则抛FileNotFoundError--version是写入pyproject.toml的包版本要求 PEP 440 格式直接透传给模板渲染--package_name是写入pyproject.toml的 Python 分发名只能是onnxruntime-ep-cuda12或onnxruntime-ep-cuda13必须匹配正则[A-Za-z0-9][A-Za-z0-9._-]*否则抛ValueError--output_dir是最终 wheel 的输出目录不存在会自动创建无在 build_wheel.py 的main中四个步骤全部在一个tempfile.TemporaryDirectory(prefixort_cuda_wheel_)临时目录内完成先在package/子目录准备 staging 源码树在wheels/子目录产出 wheel再做Linux 下的auditwheel 修复最后把产物拷入--output_dir。3.2 步骤一prepare_staging_dir组装临时源码树prepare_staging_dirbuild_wheel.py做了四件事拷贝setup.py与整个onnxruntime_ep_cuda/包目录到 staging 区拷入原生库只按BINARY_PATTERNS白名单匹配——BINARY_PATTERNS [ onnxruntime_providers_cuda.dll, libonnxruntime_providers_cuda.so, ]即 Windows 侧的.dll或 Linux 侧的.so。若--binary_dir中一个都没找到脚本直接抛FileNotFoundError错误信息中会列出查找过的模式名。这保证了一个平台上只会有恰好一种平台库进入包内该约束在运行时模块中同样被强制见下文注入最低 ORT 版本读取 MIN_ONNXRUNTIME_VERSION当前为1.24.4若为空则报错随后用gen_file_from_template对 staging 区内包 README 做原地替换把其中的min_onnxruntime_version换成具体版本号。用户通过 PyPI 看到的安装说明中那句 version1.24.4or later 正是这样生成的见 onnxruntime_ep_cuda/README.md渲染pyproject.toml用{package_name: ..., version: ...}两个键从pyproject.toml.in生成实体pyproject.toml。3.3 步骤二build_wheel调用 pip 打 wheelbuild_wheelbuild_wheel.py执行的命令为python -m pip wheel staging-dir --wheel-dir wheels-dir --no-deps --no-build-isolation两个关键 flag 的含义--no-deps不解析并下载依赖这个包本身没有运行时第三方依赖--no-build-isolation不复刻隔离的构建环境直接使用当前环境中的 setuptools/wheel——这也要求构建环境里已装好build-system声明的setuptools68.0与wheel对应 CI 中先安装requirements-build-wheel.txt的做法。而最终的 wheel 之所以是平台特定的如py3-none-manylinux.../py3-none-win_amd64靠的是 setup.py 里的两个类PlatformBdistWheel重写get_tag强制返回(py3, none, 平台)BinaryDistribution让has_ext_modules()返回True——后者正是触发平台 tag而非纯py3-none-any的标准手段。3.4 步骤三auditwheel_repair仅 Linuxauditwheel_repairbuild_wheel.py在非 Linux 平台直接返回在 Linux 上对每个原始 wheel 执行python -m auditwheel repair wheel --wheel-dir repaired-dir --exclude libcuda.so.1 --exclude libcublas.so.12 ...其中--exclude列表来自脚本顶部的AUDITWHEEL_EXCLUDE覆盖了两代 CUDA 运行时的共享库AUDITWHEEL_EXCLUDE [ libcuda.so.1, libcublas.so.12, libcublas.so.13, libcublasLt.so.12, libcublasLt.so.13, libcudart.so.12, libcudart.so.13, libcudnn.so.9, libcufft.so.11, libcufft.so.12, libnvJitLink.so.12, libnvJitLink.so.13, libnvrtc.so.12, libnvrtc.so.13, libnvrtc-builtins.so.12, libnvrtc-builtins.so.13, ]这里体现的核心策略是CUDA/cuDNN 运行时库不打进 wheel。auditwheel 默认行为是把外部依赖.so复制到 wheel 内部并改写动态链接路径但对 CUDA 库来说这是错误的——GPU 驱动libcuda.so.1根本不应被打包cuBLAS/cuDNN 等则应由用户按自己的 CUDA 版本提供。通过--exclude声明这些库为允许缺失auditwheel 只会修复真正可自包含的系统依赖CUDA 库保持对外部环境的动态引用。若 repair 后没有产出任何 wheel脚本抛RuntimeError成功则用修复后的 wheel 替换原件。3.5 步骤四collect_wheels收集产物collect_wheels按package_name-*.whl-与.均替换为_得到名字前缀如onnxruntime_ep_cuda12在 wheel 目录中收集产物拷贝到--output_dir并打印Built wheel: path若一个都没有则抛RuntimeError。3.6 CI 中的真实调用上述流程在打包流水线中被这样调用见 plugin-win-cuda-stage.ymlWindows 阶段python -m pip install .../plugin-ep-cuda/python/requirements-build-wheel.txt python .../plugin-ep-cuda/python/build_wheel.py --binary_dir $(Build.BinariesDirectory)\plugin_artifacts\bin --version $(PluginPythonPackageVersion) --package_name ${{ parameters.python_package_name }} --output_dir $(Build.ArtifactStagingDirectory)\python可以看到--binary_dir指向的是流水线前一步下载好的插件构建产物目录--package_name由流水线参数注入onnxruntime-ep-cuda12或onnxruntime-ep-cuda13与本文第 3.1 节的参数说明完全对应。Linux 侧则由 plugin-linux-cuda-stage.yml 执行相同脚本并额外受益于 auditwheel 修复路径。四、构建产物onnxruntime_ep_cuda模块的运行时 APIwheel 安装后用户得到onnxruntime_ep_cuda包其init.py 只导出三个函数__all__ [ get_ep_name, get_ep_names, get_library_path, ]get_ep_name()返回插件 EP 的名称固定为CUDAExecutionProviderget_ep_names()返回[get_ep_name()]即本插件提供的 EP 名列表get_library_path()在包目录下依次探测onnxruntime_providers_cuda.dll与libonnxruntime_providers_cuda.so要求恰好命中一个——若一个都没有或两者同时存在抛出带目录内容信息的RuntimeError。这条防御与构建脚本BINARY_PATTERNS的单平台单库约定相互呼应。4.1 安装与版本前提包内 README构建后版本占位符已被替换给出的安装方式pip install onnxruntime1.24.4 pip install onnxruntime-ep-cuda12 # CUDA 13.x 则安装 onnxruntime-ep-cuda13前提是单独安装onnxruntime核心包且版本不低于MIN_ONNXRUNTIME_VERSION注入的最低版本当前仓库为1.24.4。若运行时安装的 ONNX Runtime 不兼容插件 EP 会在其库被注册时报告错误——这是不声明硬依赖、运行时校验策略的兜底。4.2 注册、设备发现与会话创建的完整调用链两个 READMEpython/README.md 与包内 onnxruntime_ep_cuda/README.md给出同一段最小可用代码import onnxruntime as ort import onnxruntime_ep_cuda as cuda_ep # 1. 把插件 EP 动态库注册进 ONNX Runtime ort.register_execution_provider_library(cuda_ep.get_ep_name(), cuda_ep.get_library_path()) # 2. 枚举所有 EP 设备筛出 CUDA 插件 EP 的设备 devices [d for d in ort.get_ep_devices() if d.ep_name cuda_ep.get_ep_name()] # 3. 将设备绑定到会话选项并创建推理会话 sess_options ort.SessionOptions() sess_options.add_provider_for_devices(devices, {}) session ort.InferenceSession(model.onnx, sess_optionssess_options)调用链逐行拆解ort.register_execution_provider_library(name, lib_path)将get_library_path()返回的.dll/.so加载为插件库name这里是CUDAExecutionProvider成为该 EP 在 ORT 内的注册名。插件原生代码位于主仓库 onnxruntime/core/providers/cuda/plugin/ 目录包含cuda_ep.cc、cuda_plugin_ep.cc、cuda_arena.cc、cuda_memcpy_plugin.cc等实现了 EP 工厂、内存池分配器、数据传输等插件侧能力ort.get_ep_devices()枚举当前进程中所有已注册 EP含插件 EP暴露的设备过滤ep_name cuda_ep.get_ep_name()得到 CUDA 设备列表sess_options.add_provider_for_devices(devices, {})把指定设备以空配置字典挂到SessionOptions上——插件 EP 走按设备绑定而非传统append_execution_provider的路径ort.InferenceSession(...)此时 CUDA EP 参与图分区与算子执行。五、测试验证test_cuda_plugin_ep.py冒烟测试plugin-ep-cuda/python/test/test_cuda_plugin_ep.py 是对构建产物的端到端冒烟测试共四段验证目标包导入与库路径解析test_import_and_library_path导入onnxruntime_ep_cuda断言get_library_path()指向一个真实存在的文件、get_ep_name()返回CUDAExecutionProvider、get_ep_names()返回[CUDAExecutionProvider]EP 注册、设备发现与推理test_registration_and_inference用注册名cuda_plugin_test调用ort.register_execution_provider_library随后发现 CUDA 插件设备若无设备则打印SKIP并优雅跳过适配纯 CPU 构建代理有设备时设置session.disable_cpu_ep_fallback 1确保不会静默回落到 CPU动态构造一个两输入Mul算子的 ONNX 模型create_mul_modelopset 13 / ir_version 7执行推理并用np.testing.assert_allclose(rtol1e-5, atol1e-5)校验数值finally块中调用ort.unregister_execution_provider_library释放注册诊断辅助设置环境变量ORT_TEST_VERBOSE1时开启ort.set_default_logger_severity(0)并打印 Python 版本、平台、架构、ORT 版本与相关环境变量过滤onnx/ort/gpu/cuda/nv/path/ld_library关键词Windows 动态库加载修复Python 3.8 在 Windows 上不再搜索PATH加载 DLL脚本开头遍历PATH中的目录并调用os.add_dll_directory确保 CI 里通过流水线注入PATH的 CUDA/cuDNN 库在LoadLibraryExW时可见——这是该测试跨平台可运行的前提。这段测试同时是插件 EP 使用方式的活文档它与 README 的三步调用链一一对应并额外演示了注册/注销配对、CPU 回退禁用与无 GPU 环境的跳过策略。六、小结与相关文件索引plugin-ep-cuda/python/这套打包体系的关键设计可归纳为三点模板化构建描述pyproject.toml.in 严格模式变量替换一套源码渲染出 cuda12/cuda13 两个分发、二进制与源码分离预构建的libonnxruntime_providers_cuda.so/onnxruntime_providers_cuda.dll由build_wheel.py按白名单拷入CUDA 运行时库经 auditwheel 排除保持外部依赖、软性版本约束最低 ORT 版本写入 README、由插件注册期校验。延伸阅读均位于当前仓库打包总入口与文件职责plugin-ep-cuda/README.md构建脚本plugin-ep-cuda/python/build_wheel.py、模板 pyproject.toml.in、入口 setup.py运行时模块与包内安装文档onnxruntime_ep_cuda/init.py、onnxruntime_ep_cuda/README.md冒烟测试python/test/test_cuda_plugin_ep.py插件 EP 原生实现onnxruntime/core/providers/cuda/plugin/设计文档docs/cuda_plugin_ep/cuda_plugin_ep_design.md、docs/cuda_plugin_ep/QUICK_START.md【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考