news 2026/9/13 18:48:29

ONNX Runtime WebGPU EP ABI 适配器:插件式动态执行提供者的设计与实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ONNX Runtime WebGPU EP ABI 适配器:插件式动态执行提供者的设计与实现解析

ONNX Runtime WebGPU EP ABI 适配器:插件式动态执行提供者的设计与实现解析

【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime

本文以 onnxruntime/core/providers/webgpu/ep/README.md 为核心,深入剖析 ONNX Runtime WebGPU 执行提供者(EP)在“EP ABI 适配器”架构下的设计取舍与源码实现:静态库构建如何保持零改动、动态库构建如何仅依赖 EP ABI 并通过桥接类连接内部实现、导出符号与版本脚本如何收敛 DLL 接口,以及构建系统中版本号的注入方式。读完本文,你将掌握 WebGPU EP 插件化(Plugin EP)的完整机制,包括设备枚举、内存分配器、数据布局协商、图捕获(Graph Capture)等关键 API 的实现路径,并能理解 README 中列出的“缺失部分”当前在源码中的落地状态。

一、定位:ep文件夹是 WebGPU 的 EP ABI 适配层

onnxruntime/core/providers/webgpu/ep/ 目录下的 README 开门见山地说明了该目录的职责:

The current folder contains the implementation of EP ABI adapter for WebGPU.

也就是说,整个 WebGPU EP 的推理内核(program_managerbuffer_manager、wgsl 着色器模板等,见 onnxruntime/core/providers/webgpu/)在静态库构建下直接链接进onnxruntime主库;而在插件化场景下,同一套内核被封装为一个独立的动态库(Windows 下为onnxruntime_providers_webgpu.dll),通过 ONNX Runtime 定义的EP ABI(Execution Provider ABI,即OrtEpFactory/OrtEp函数表结构)与主运行时解耦通信。

目录结构如下,各文件职责清晰:

文件职责
ep.h / ep.ccEp桥接类,实现OrtEp函数表,连接 EP ABI 与内部WebGpuExecutionProvider
factory.h / factory.ccFactory类,实现OrtEpFactory函数表,负责设备枚举、创建 EP 实例、共享分配器与数据搬运
api.cc动态库仅有的两个导出符号CreateEpFactories/ReleaseEpFactory的实现
symbols.defWindows 导出定义文件(.def
version_script.ldsLinux/Unix 链接器版本脚本,限定导出符号
versioninfo.rcWindows DLL 资源版本信息

二、README 的核心设计约束:静态库零改动,动态库“只用 EP ABI”

README 的 “Design considerations” 一节给出了三条设计决策,这些决策直接决定了源码的组织方式:

  1. 静态库构建不做任何改动(“No changes to static library build. It should still work as before.”)。WebGPU 在静态构建下仍是内置 EP,原有的GetProvider路径继续工作。
  2. 动态库只使用、且仅使用 EP ABI(“use and only use the EP ABI. (no support forGetProvider)”)。插件库不再依赖主库的内部 C++ 符号发现机制,避免 ABI 耦合。
  3. 动态库仍然依赖 onnxruntime 各构建目标(“still depends on onnxruntime targets”),并通过桥接类(bridge)把 EP ABI 的内部类连接起来。

这第 3 点在 cmake/onnxruntime_providers_webgpu.cmake 中有直接印证:动态库目标onnxruntime_providers_webgpu链接了onnxruntime_optimizeronnxruntime_providersonnxruntime_loraonnxruntime_frameworkonnxruntime_graphonnxruntime_utilonnxruntime_commononnxruntime_flatbuffers等内部目标——插件 EP 复用了大量主库代码,只是对外接口收敛到 EP ABI

构建方式上还有两条硬约束(cmake/onnxruntime_providers_webgpu.cmake):共享库构建不支持 build cache,且Emscripten 平台不支持共享库构建(WASM 场景请使用静态库),否则 CMake 会直接报错终止。

三、导出面:整个动态库只有两个 C 符号

无论 Windows 还是 Linux,插件库的导出符号都被严格限定为两个:

  • symbols.def(Windows):
LIBRARY "onnxruntime_providers_webgpu.dll" EXPORTS CreateEpFactories @1 ReleaseEpFactory @2
  • version_script.lds(Linux):
VERS_1.0.0 { global: CreateEpFactories; ReleaseEpFactory; local: *; };

这两个符号正是 README 所说“use and only use the EP ABI”的落地点。cmake/onnxruntime_providers_webgpu.cmake 中按平台分别挂上链接选项:Linux 使用--version-script=.../ep/version_script.lds--gc-sections-rpath=$ORIGIN;Windows 使用-DEF:.../ep/symbols.def;macOS 则仅附加-dead_strip(符号导出依赖 api.cc 中的EXPORT_SYMBOLvisibility 属性)。

3.1CreateEpFactories:插件入口

api.cc 中的CreateEpFactories由 ORT 主库在加载插件动态库时调用,其执行流程为:

  1. 手动初始化 C++ API 层:以ORT_API_MANUAL_INIT模式包含onnxruntime_cxx_api.h,并调用onnxruntime::ep::ApiInit(ort_api_base, ORT_PLUGIN_EP_MIN_ORT_VERSION)。这里传入的最小 ORT 版本宏在初始化失败时(如宿主 ORT 版本过低)会经由一个保守的report_errorlambda 创建OrtStatus——由于此时OrtApi尚未完成初始化,代码用static_assert断言OrtApi::CreateStatus在 v1 API 中的偏移为 0,保证即使只能拿到 v1 表也能安全创建错误对象。
  2. 读取环境变量决定 Factory 行为(见 factory.h 的Config结构):
    • 环境配置项kOrtEnvAllowVirtualDevices(值为"1"时)允许注册一个虚拟 GPU 设备,用于“无设备、仅编译”(device-free compile-only)会话,典型场景是 Win32k-lockdown 沙箱等枚举不到 GPU 的主机;
    • 环境变量ORT_WEBGPU_EP_ALLOW_SOFTWARE_ADAPTER设为"1"时,允许在没有 GPU 硬件设备的情况下把 WebGPU EP 挂到CPU 硬件设备上,使 EP 设备变得可选(Dawn 独立选择软件适配器),开启时还会打印 WARNING 日志。
  3. 初始化全局默认 loggerLoggingManager::CreateDefaultLogger),并构造唯一的Factory实例返回给宿主。

3.2ReleaseEpFactory:清理顺序即“缺失部分”的答案

README 的 “Missing parts” 第一条提到“需要一种方式做 WebGPU 清理(OrtEnv::~OrtEnv()目前在静态库构建下调用webgpu::CleanupWebGpuContexts())”。从当前源码看,动态库构建中这个职责已经落在 api.cc 的ReleaseEpFactory中,其清理链条为固定五步:

  1. deleteFactory实例(其析构函数会释放虚拟硬件设备);
  2. webgpu::CleanupKernelRegistries()清理缓存的 kernel 注册表;
  3. webgpu::CleanupWebGpuContexts()清理 WebGPU 上下文;
  4. LoggingManager::DestroyDefaultLogger()销毁默认日志包装;
  5. google::protobuf::ShutdownProtobufLibrary()关闭 protobuf 库。

这与静态库构建下OrtEnv析构时的清理形成了互补:静态库由OrtEnv生命周期驱动,动态库由插件工厂生命周期驱动。

四、Factory:设备枚举、EP 实例与共享分配器

Factory继承自OrtEpFactory(见 factory.h),构造函数(factory.cc)完成三件事:初始化OrtEpFactory函数表、预构造两组OrtMemoryInfodefault_memory_info_OrtDeviceAllocatorreadonly_memory_info_OrtReadOnlyAllocator,allocator name 均为WEBGPU_BUFFER)、在allow_virtual_devices开启时通过Api().ep.CreateHardwareDevice注册带IsVirtual=1元数据的虚拟 GPU 硬件设备。工厂自报身份为:名字kWebGpuExecutionProvider、厂商"Microsoft"、vendor id0、版本ORT_PLUGIN_EP_VERSION

4.1GetSupportedDevices:三层设备暴露策略

factory.cc 按顺序做三件事:

  1. 真实 GPU:遍历宿主传入的OrtHardwareDevice数组,凡类型为OrtHardwareDeviceType_GPU的设备都包装成一个OrtEpDevice,并挂上默认与只读两组 allocator info;
  2. 软件适配器回退allow_software_adapter开启且没有任何 GPU 设备时):找到 CPU 硬件设备,把 WebGPU EP 暴露在其上,使 EP 在纯 CPU 主机上仍可选(实际由 Dawn 选择软件适配器);
  3. 虚拟设备allow_virtual_devices开启时):额外暴露虚拟 GPU EP 设备,且不携带 allocator info——源码注释解释了原因:虚拟设备仅支撑“停止在会话最终化之前的 compile-only 会话”,从不分配内存,若设置内存信息反而会让 ORT 尝试创建没有底层设备的共享 WebGPU 分配器。

4.2CreateEp:把内部WebGpuExecutionProvider包进Ep

factory.cc 的CreateEpImpl是桥接发生的地方:

  • 目前只支持一次一个设备num_devices != 1直接报ORT_INVALID_ARGUMENT);
  • OrtSessionOptions取出全部 session config entries 并灌入ConfigOptions,再经WebGpuProviderFactoryCreator::Create(config_options)+CreateProvider构造出真正的内部 EPWebGpuExecutionProvider——可以看到插件路径下,session 配置项(如webgpu_*选项)的解析逻辑与静态构建完全复用;
  • 虚拟设备 + 非 compile-only 组合会被前置拒绝:若选中的设备元数据标记为虚拟但session.compile_only != 1,直接返回清晰的错误信息,避免 Dawn 后续创建设备时的晦涩失败;
  • 分配器策略:若上下文HasDevice()为 false(即无设备的 compile-only 会话),CreateWebGpuAllocator会构造一个 no-op 分配器;否则构造真正的GpuBufferAllocator。最终Ep::Config持有三个分配器:CPU 分配器(CPUAllocator::DefaultInstance())、设备分配器、以及 initializer 只读设备分配器(指向InitializerBufferManager)。

4.3 共享分配器与数据搬运

  • CreateAllocatorImpl(factory.cc)只接受allocator_type == OrtDeviceAllocatordevice_id == 0、allocator name 为WEBGPU_BUFFEROrtMemoryInfo,底层包装成webgpu::GpuBufferAllocator(绑定到默认 WebGPU 上下文的BufferManager);
  • CreateDataTransferImpl(factory.cc)直接调用OrtWebGpuCreateDataTransfer(),复用 WebGPU EP 的 host↔device 数据搬运实现;
  • IsStreamAware恒为falseCreateSyncStreamForDevice返回ORT_NOT_IMPLEMENTED——WebGPU EP 不是 stream-aware 的 EP。

五、Ep桥接类:EP ABI 函数表的逐项映射

Ep继承自onnxruntime::ep::adapter::Ep(见 ep.h),构造函数(ep.cc)把OrtEp的每个函数表槽位绑定到对应的静态实现,并显式地把不适用的槽位置空

  • Compile = nullptrReleaseNodeComputeInfos = nullptr:per-kernel 型 EP 不使用图级编译;
  • SetDynamicOptions = nullptr:未实现;
  • CreateSyncStreamForDevice = nullptr:非 stream-aware;
  • GetCompiledModelCompatibilityInfo = nullptr:不是 compiled EP。

有实现的槽位逐一映射到内部WebGpuExecutionProvider的方法,形成“EP ABI → 内部类”的一对一桥接:

EP ABI 函数桥接目标行为说明
GetNameFactory::GetName返回kWebGpuExecutionProvider
GetCapability见 5.1 节决定哪些节点交给 WebGPU
GetKernelRegistryWebGpuExecutionProvider::GetKernelRegistryImpl()委托内部注册表(ep.cc)
GetPreferredDataLayout/ShouldConvertDataLayoutForOpGetPreferredLayout()/ShouldConvertDataLayoutForOp()内部DataLayout枚举与OrtEpDataLayout一一对应(NCHW=0,NHWC=1),未知 op 返回-1表示未表态
OnRunStart/OnRunEndWebGpuExecutionProvider::OnRunStart/OnRunEndOnRunStart目前只透传kOrtRunOptionsConfigCudaGraphAnnotationgpu_graph_id)这一条 run option(ep.cc)
IsConcurrentRunSupportedfalse不支持并发运行
IsGraphCaptureEnabled/IsGraphCaptured/ReplayGraph/ReleaseCapturedGraph/GetGraphCaptureNodeAssignmentPolicy同名内部方法直通内部 WebGPU 图捕获实现(ep.cc)
CreateAllocatoradapter::Allocator包装OrtReadOnlyAllocator映射到 initializer 分配器,其余映射到设备分配器(ep.cc)

5.1GetCapability:节点分配与 CPU 回退逻辑

GetCapabilityImpl(ep.cc)是插件路径下的图分区入口,其判定流程为:

  1. 取图中全部节点,EP 名已标注为 WebGPU 的节点直接进入候选集;
  2. 已标注为其他非 CPU EP的节点跳过(拒绝重分配);
  3. 对未标注或标注 CPU 的节点,先用EpGraphSupportInfo_LookUpKernel查询 kernel 注册表——查不到 kernel 即回退 CPU,并打 INFO 日志(webgpu kernel not found in registries for Op type: ...);
  4. 命中ForceCpuNodeNames强制 CPU 名单的节点回退 CPU;
  5. 针对com.microsoft域的Attention算子做细粒度能力检查:不支持mask_index(input[3])、past(input[4])、past_seq_len(input[6])输入,不支持present(output[1])输出,且past_present_share_buffer属性非 0 时一律回退 CPU(借助FALLBACK_TO_CPU_IF_EXIST_INPUT/OUTPUT宏实现,ep.cc);
  6. 最后调用onnxruntime::ep::GetCpuPreferredNodes(头文件ep/get_capability_utils.h)做 CPU 偏好节点判定,只有不在cpu_preferred_nodes中的候选节点才通过EpGraphSupportInfo_AddSingleNode正式划归 WebGPU。

这套逻辑保证了插件路径与静态路径的节点分配语义一致:注册表查不到 kernel 或能力不满足时,节点自然留在 CPU 上执行,而不是让整个会话失败。

六、构建与版本治理:版本号如何进入插件 DLL

插件 EP 的版本信息有两条注入链,均在 cmake/onnxruntime_providers_webgpu.cmake 中完成:

  1. 插件自身版本:若未显式定义onnxruntime_PLUGIN_EP_VERSION,默认取${ORT_VERSION}-dev,编译为ORT_PLUGIN_EP_VERSION宏,供Factory::GetVersionImpl返回;
  2. 最小兼容 ORT 版本:以 plugin-ep-webgpu/MIN_ONNXRUNTIME_VERSION 文件(当前内容为1.24.4,格式要求严格的MAJOR.MINOR.PATCH)为唯一事实来源,读入并编译为ORT_PLUGIN_EP_MIN_ORT_VERSION宏——该宏在CreateEpFactories中传给ApiInit,从而在插件加载期就强制校验宿主 ORT 版本是否足够新;文件缺失或为空时 CMake 直接FATAL_ERROR

这与 plugin-ep-webgpu/ 目录下的打包工程(VERSION_NUMBERMIN_ONNXRUNTIME_VERSION、Python/C# 插件包)构成同一套版本治理体系:DLL 内自报的版本和最低宿主版本,与发布物元数据保持一致。

七、README “Missing parts” 的现状盘点

README 最后列出了两项尚未完成的能力,结合当前源码可以做如下对照:

  1. WebGPU 清理的入口:静态库下依赖OrtEnv::~OrtEnv()调用webgpu::CleanupWebGpuContexts();动态库下该职责已由ReleaseEpFactory的五步清理链承接(见 3.2 节)。从源码结构看,动态库路径的清理问题已基本闭环,README 该条目反映的是插件化早期状态。
  2. WebGPU “默认配置”的设置途径:README 希望在 ORT C API 上提供类似SetCurrentGpuDeviceId的全局状态入口,并进一步泛化为:
ORT_API2_STATUS(SetEpDefaultConfig, _In_ const char* ep_name, _In_ const char* key, _In_ const char* value);

该 API 截至目前未在仓库中出现。当前的替代手段是:通过 session options 配置项(在CreateEpImpl中从OrtSessionOptions读取并灌入ConfigOptions)以及环境变量ORT_WEBGPU_EP_ALLOW_SOFTWARE_ADAPTER、环境配置项allow_virtual_devices来控制插件行为。换言之,面向“进程级默认配置”的通用 API 仍是 WebGPU EP ABI 适配层待补齐的一块

八、小结

onnxruntime/core/providers/webgpu/ep/ 这套 EP ABI 适配器用极小的对外接口面(CreateEpFactories/ReleaseEpFactory两个符号 + ep.cc、factory.cc 两层桥接类)实现了 WebGPU EP 的插件化:静态库构建零改动继续走内置 EP 路径;动态库构建下,设备枚举(含软件适配器回退与虚拟设备)、内存分配、数据布局协商、运行生命周期与图捕获全部经由 EP ABI 函数表转发到既有的WebGuExecutionProvider内部实现(WebGpuExecutionProvider)。版本兼容性由MIN_ONNXRUNTIME_VERSION单一事实来源 + 编译期宏注入在加载期强制校验。对需要自行构建或排查 WebGPU 插件行为的开发者,建议按“README 设计约束 →api.cc入口 →factory.cc设备/分配器 →ep.cc函数表映射”的顺序阅读源码,即可完整还原插件从加载到执行的全链路。

【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 18:46:29

JavaWeb成绩管理系统课设拆解:Servlet+JDBC+MySQL全链路实战

简介:这是一套基于JavaWeb与MySql的学生成绩管理系统完整项目,适用于计算机、通信、人工智能、自动化等专业的课程设计、期末大作业或毕业设计。项目中包含前端JSP页面、后端Java控制层与业务层代码、数据库SQL脚本及项目配置文件,覆盖了学生…

作者头像 李华
网站建设 2026/9/13 18:45:01

FOC电流采样全解析:单/双/三电阻拓扑与中心对齐PWM的ADC触发要点

做FOC调试这些年,我最深的感受是:炸机不可怕,可怕的是不知道为什么炸。而十次炸机,有七八次都跟电流采样脱不了干系。你可能已经把SVPWM扇区推导背得滚瓜烂熟,PI参数也算得头头是道,但只要电流采样在这个链…

作者头像 李华
网站建设 2026/9/13 18:43:59

固态变压器电气安全监测六大维度与同步采样实践

1. 为什么说“电气安全”才是SST落地真正的硬骨头? 固态变压器(SST)这词最近在电力电子、智能配网和新型能源站圈子里被反复提起,但凡聊到“大规模落地”,几乎所有人都会先谈拓扑——SiC器件选型、多电平结构对比、软开…

作者头像 李华
网站建设 2026/9/13 18:43:30

Kilo Code AI 编码代理上手指南:从五文件重构到无人值守跑测试

Kilo Code AI 编码代理上手指南:从五文件重构到无人值守跑测试 【免费下载链接】kilocode Kilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent. 项目地址: https://gitcode.…

作者头像 李华