ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【Bug已解决】[WebGPU EP] Meta-Llama-3.1-8B inference crash on QNN environments 解决方案

【Bug已解决】[WebGPU EP] Meta-Llama-3.1-8B inference crash on QNN environments 解决方案 【Bug已解决】[WebGPU EP] Meta-Llama-3.1-8B inference crash on QNN environments 解决方案一、现象长什么样在启用了 QNNQualcomm Neural Network高通 NPU 执行提供方的环境里用 ONNX Runtime 的 WebGPU EP 跑 Meta-Llama-3.1-8B 推理时进程直接崩溃segfault / 访问空指针而不是优雅报错或正常出结果。现象# 现象 A初始化或首步推理时 segfault # SIGSEGV at webgpu_ep_kernel.cc: ptr-Dispatch() —— ptr 为 nullptr # 因为 QNN 环境下某个 WebGPU 资源如 pipeline / shader module没创建成功 # 现象 B只在 WebGPU EP QNN 环境组合触发 # 纯 WebGPU非 QNN正常纯 QNN不用 WebGPU EP正常 # 一旦 WebGPU EP 跑在 QNN 后端之上就崩 # 现象 C崩在“跨 EP 资源假设”处 # WebGPU EP 假设自己独占 GPU 设备、能创建 compute pipeline # 但 QNN 环境下 GPU 设备已被 NPU 栈部分占用/抽象pipeline 创建返回空 # WebGPU EP 没判空直接用 - 崩最坑的是现象 A直接 segfault 没有可捕获的错误信息且只在特定硬件组合QNN 高通设备出现普通 CI 覆盖不到用户只能拿 crash dump 反推。二、背景ONNX Runtime 支持多个 EP 组合WebGPU EP用浏览器/系统的 WebGPU 算力和 QNN EP用高通 NPU。理论上它们可以共存——比如一部分图用 QNN 在 NPU 上跑一部分用 WebGPU 在 GPU 上跑。但实际中WebGPU EP 的初始化假设“我能拿到一个有效的 WebGPUGPUDevice并创建所有需要的GPUComputePipeline/GPUShaderModule”。在 QNN 环境下系统的图形/计算栈可能被高通驱动做了特殊抽象WebGPU 的requestAdapter()/createShaderModule()在某些情况下返回null而非抛异常或者返回的 device 能力子集与 WebGPU EP 假设不符。WebGPU EP 的代码对“创建结果可能为 null”没有判空直接用这个 null pipeline 去Dispatch于是 segfault。这是多 EP 共存 / 跨后端审查里典型的坑某 EP 假设自己独占且一定能成功创建底层资源对“创建返回 null”的异常后端环境没有防御性判空。三、根因WebGPU 资源创建未判空createShaderModule/createComputePipeline在 QNN 环境下可能返回nullptrWebGPU EP 直接解引用 → segfault现象 A。跨 EP 设备假设过强WebGPU EP 假设自己能独占并完整初始化 GPUDevice没考虑 QNN 已占用/抽象了部分栈导致初始化在中间步骤“静默失败但返回非空句柄、内部资源却为 null”。缺少 QNN 环境的崩溃回归测试CI 没有“WebGPU EP 跑在 QNN 后端”的组合测试null 路径从未被触发和防御。本质是WebGPU EP 对底层资源创建结果未做防御性判空且跨 EP 设备假设过强在 QNN 异常后端下 segfault且缺组合测试。四、最小可运行复现下面用 Python 模拟“WebGPU 资源创建返回 nullEP 未判空直接解引用崩溃”class MockWebGpuDevice: def create_compute_pipeline(self, desc): # QNN 环境下返回 None创建失败但没抛异常 return None def dispatch_buggy(device, shader_desc): buggy: 不判空直接用 pipeline。 pipeline device.create_compute_pipeline(shader_desc) pipeline.dispatch() # ← pipeline 是 None - AttributeError类比 segfault def dispatch_fixed(device, shader_desc): fixed: 创建失败显式报错绝不拿 null 去 dispatch。 pipeline device.create_compute_pipeline(shader_desc) if pipeline is None: raise RuntimeError( WebGPU compute pipeline creation failed (likely unsupported backend such as QNN); cannot dispatch) pipeline.dispatch() dev MockWebGpuDevice() try: dispatch_buggy(dev, {}) except AttributeError: print(REPRO A - segfault equivalent: null pipeline dereferenced) dispatch_fixed(dev, {}) # 优雅报错不崩buggy在pipeline.dispatch()处因None报错类比 C segfaultfixed优雅抛错。五、解决方案第一层最小直接修复最小修复WebGPU EP 在所有底层资源创建后判空为空则抛清晰错误而非解引用// 修正创建 compute pipeline 后判空 auto pipeline device.CreateComputePipeline(pipeline_desc); if (!pipeline) { return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, WebGPU compute pipeline creation failed. This backend may not support the required WebGPU features (e.g. running on QNN env).); } // 只有非空才 dispatch pipeline-Dispatch(workgroup_count);这一层改动最小每处创建后加判空 清晰报错segfault 变可捕获错误。但依赖“每处创建都加判空”下看第二层。六、解决方案第二层结构性改进把“WebGPU EP 所有底层资源创建必须判空、失败即优雅报错”固化成单一事实来源。下面这个 dataclass 集中管理资源创建契约用一个守卫统一处理from dataclasses import dataclass, field from typing import Any, Callable, Optional dataclass class WebGpuLlamaCrashPolicy: 单一事实来源WebGPU EP 资源创建的判空与失败契约。 _created: list field(default_factorylist) def create_or_fail(self, name: str, factory: Callable[[], Any]) - Any: 统一守卫创建失败返回 None即抛清晰错误绝不返回 None 给调用方。 resource factory() if resource is None: raise RuntimeError( fWebGPU resource {name} creation failed (backend may be funsupported, e.g. QNN env). Aborting before dispatch.) self._created.append(name) return resource def assert_no_null_resource(self, name: str, resource: Any) - None: if resource is None: raise AssertionError(fnull WebGPU resource {name} would crash)用法policy WebGpuLlamaCrashPolicy() pipeline policy.create_or_fail( llama_attention_pipeline, lambda: device.create_compute_pipeline(desc)) # 返回 None 时直接报错这一层的关键收益统一判空所有资源创建走create_or_fail不可能返回 None 给 dispatch清晰错误失败时说明“可能是 QNN 等不支持的后端”便于定位单一事实来源所有 WebGPU 资源创建约定收口在WebGpuLlamaCrashPolicy。七、解决方案第三层断言 / CI 守护把第二层钉成 pytest挂进 CI覆盖 QNN 式 null 创建路径import pytest from your_package.webgpu_llama_crash import WebGpuLlamaCrashPolicy def test_null_pipeline_raises(): # 断言 1创建返回 None 必须优雅报错不崩 p WebGpuLlamaCrashPolicy() with pytest.raises(RuntimeError): p.create_or_fail(pipeline, lambda: None) def test_valid_pipeline_returns(): # 断言 2创建成功返回资源不报错 p WebGpuLlamaCrashPolicy() res p.create_or_fail(pipeline, lambda: object()) assert res is not None def test_no_null_dispatched(): # 断言 3任何 null 资源都不允许进入 dispatchassert 守卫 p WebGpuLlamaCrashPolicy() with pytest.raises(AssertionError): p.assert_no_null_resource(shader, None) def test_qnn_env_clean_abort(): # 断言 4模拟 QNN 环境多资源创建失败首处失败即中止不 segfault p WebGpuLlamaCrashPolicy() calls {n: 0} def factory(): calls[n] 1 return None # 模拟 QNN 下所有创建都失败 with pytest.raises(RuntimeError): p.create_or_fail(p0, factory) # 第一个就失败不会继续 dispatch assert calls[n] 1四条断言从“null 报错”“有效返回”“不 dispatch null”“QNN 干净中止”四面把 segfault 回归钉死在 CI。八、排查清单WebGPU EP 在 QNN/特殊后端上 segfault 时crash 在pipeline-Dispatch()或 shader 解引用几乎肯定是创建返回了 null 没判空现象 A。是否只在“WebGPU EP 某特殊后端”组合触发是就确认该后端下createXxx返回 null 而非抛异常。所有底层资源创建是否都判空没判空的都会在异常后端下 segfault。用第二层WebGpuLlamaCrashPolicy统一create_or_fail守卫失败即优雅报错。加第三层 pytest断言“null 报错、有效返回、不 dispatch null、QNN 干净中止”。多 EP 共存时每个 EP 都必须防御“后端部分不可用”不能假设独占成功。九、小结WebGPU EP 在 QNN 环境跑 Llama-3.1-8B 的崩溃 bug 本质是WebGPU EP 假设底层资源compute pipeline / shader module一定能创建成功对异常后端QNN下返回null的创建结果没有防御性判空直接解引用去Dispatch导致 segfault且 CI 没有“WebGPU EP QNN”组合测试null 路径从未防御。修复分三层——第一层所有资源创建后判空、为空抛清晰错误第二层用WebGpuLlamaCrashPolicy这个 dataclass 把“创建判空 失败优雅报错”收口成统一守卫第三层用四条 pytest 把“null 报错、有效返回、不 dispatch null、QNN 干净中止”钉死在 CI。核心心法任何 EP 对底层资源的创建结果都必须判空异常后端下创建可能返回 null 而非抛异常未判空即 segfault。
返回列表