ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

【Bug已解决】[DirectML] System Crash and Driver Corruption on AMD RX 580 during OCR Inference 解决方案

【Bug已解决】[DirectML] System Crash and Driver Corruption on AMD RX 580 during OCR Inference 解决方案 【Bug已解决】[DirectML] System Crash and Driver Corruption on AMD RX 580 during OCR Inference 解决方案一、现象长什么样在 Windows 上用 ONNX Runtime 的DirectML EP走 DirectX 12 / 微软的 ML 抽象层跑 OCR光学字符识别模型时在AMD RX 580这块卡上推理过程中系统直接崩溃蓝屏/驱动重置/TDR甚至伴随“驱动损坏”需要重装驱动。其他卡NVIDIA、AMD 新卡正常。现象# 现象 A推理到某一步系统崩 # 跑 OCR 模型含大量小卷积 / 逐元素时RX 580 触发 TDR # Timeout Detection and RecoveryGPU 任务超时驱动重置 # 现象 B驱动损坏 # 多次崩溃后DirectML 设备创建失败提示驱动异常需重装 AMD 驱动 # 现象 C只在 RX 580 DirectML OCR 触发 # 同模型在 NVIDIA 正常RX 580 跑其他模型非 OCR也正常 # 只有 OCR 这种特定算子组合在 RX 580 的 DML 路径上炸最坑的是现象 A/B不是进程崩是系统级崩 驱动损坏比普通 segfault 严重得多且只在特定老卡 特定模型组合出现极难在 CI 覆盖CI 没有 RX 580。二、背景DirectML EP 把 ONNX 算子翻译成 DirectML 的算子DML 的IDMLOperator再经 DirectX 12 在 GPU 上执行。不同 GPU 厂商AMD/NVIDIA/Intel的 DML 后端实现有差异。OCR 模型的特征是大量小尺寸、高 channel 数的卷积/逐元素算子连续执行。在 AMD RX 580较老的 GCN 架构的 DML 路径上某些算子组合比如连续多个 depthwise conv 逐元素或特定 tensor 维度的 batched 算子会触发 AMD 驱动的 bug要么单算子执行时间异常长导致 TDR现象 A要么 DML 内部状态写到越界显存导致驱动损坏现象 B。根因通常在 ORT 侧有两个层面① ORT 给 DML 的某个算子传了 RX 580 不支持的 tensor 布局/参数DML 规范里这种情况应回退但 ORT 没回退直接下发触发驱动 bug② 某个 DML 算子被“融合”成了 RX 580 驱动处理不好的大算子单步超时。这是 EP/驱动兼容性审查里典型的坑某 EP 在特定老硬件上因为下发了该硬件驱动处理不好的算子/布局触发系统级崩溃且缺少该硬件的回归覆盖。三、根因未对 RX 580 不支持的算子/布局做回退ORT 给 DML 下了 RX 580 驱动有 bug 的组合没走“不支持就拆成小算子/回退 CPU”的安全路径现象 A。融合出大算子导致 TDR连续小算子被融合成一个超大 DML 算子RX 580 单步执行超时触发 TDR现象 A。缺少特定硬件回归CI 没有 RX 580 环境驱动级崩溃从未被测试发现现象 C。本质是DirectML EP 在特定老硬件RX 580上下发了驱动处理不好的算子/融合未做安全回退且缺该硬件回归导致系统崩溃/驱动损坏。四、最小可运行复现下面用 Python 模拟“某硬件不支持的算子组合未被回退直接下发导致‘崩溃’”class GpuModel: def __init__(self, supported_ops): self.supported supported_ops # 该卡 DML 能安全跑的算子集 def schedule_buggy(model, op_graph): buggy: 不检查硬件支持直接融合下发。 for op in op_graph: if op not in model.supported: # 直接下发到不支持的硬件 - 驱动崩 return CRASH: unsupported op on this GPU return ok def schedule_fixed(model, op_graph): fixed: 不支持的算子拆小/回退 CPU。 for op in op_graph: if op not in model.supported: # 安全回退成小算子或 CPU绝不直接下发 return ffallback:{op} return ok rx580 GpuModel(supported{conv, add}) # 不支持 fused_big ocr_graph [conv, add, fused_big] # OCR 含融合大算子 print(schedule_buggy(rx580, ocr_graph)) # CRASH print(schedule_fixed(rx580, ocr_graph)) # fallback:fused_bigbuggy直接下发崩溃fixed回退安全。五、解决方案第一层最小直接修复最小修复DirectML EP 在调度前检查该 GPU 是否支持某算子/融合不支持就拆成基础算子或回退 CPU绝不直接下发到会崩的硬件// 修正下发前检查硬件能力不支持则拆/回退 bool DmlOperatorDesc::IsSupportedOn(const GpuCaps caps) const { if (op_type_ DML_OPERATOR_FUSED_BIG !caps.supports_fused_big) { return false; // 让调度器拆成小算子或回退 CPU } return true; } // 调度时不支持的算子不融合、不直发 if (!desc.IsSupportedOn(rx580_caps)) { DispatchAsSmallOps(desc); // 拆小避免单步超时 TDR }这一层改动最小加硬件能力检查 回退系统崩溃消失。但依赖“每类算子都登记能力”下看第二层。六、解决方案第二层结构性改进把“DirectML 算子在 Specific GPU 上的能力检查 不支持即安全回退”固化成单一事实来源。下面这个 dataclass 集中管理from dataclasses import dataclass, field from typing import Dict, Set dataclass class OrtDirectMlCrashPolicy: 单一事实来源DirectML 算子在 GPU 上的能力与安全回退契约。 # gpu - 不支持的算子集会触发驱动崩溃的组合 _unsupported: Dict[str, Set[str]] field(default_factorylambda: { AMD_RX580: {fused_big, depthwise_conv_batched}, }) def safe_schedule(self, gpu: str, op_graph: list) - list: plan [] for op in op_graph: if op in self._unsupported.get(gpu, set()): # 不直接下发拆小/回退 plan.append(ffallback:{op}) else: plan.append(op) return plan def assert_no_crashing_op(self, gpu: str, op_graph: list) - None: bad set(op_graph) self._unsupported.get(gpu, set()) if bad: raise AssertionError(fwould dispatch crashing ops {bad} on {gpu})这一层的关键收益能力黑名单AMD_RX580不支持的算子集中登记一目了然安全回退safe_schedule对不支持的算子拆/回退绝不直发断言assert_no_crashing_op防把崩溃算子下发到该硬件单一事实来源所有 DirectML 硬件兼容约定收口在OrtDirectMlCrashPolicy。七、解决方案第三层断言 / CI 守护把第二层钉成 pytest挂进 CI覆盖硬件兼容import pytest from your_package.ort_directml_crash import OrtDirectMlCrashPolicy def test_rx580_unsupported_falls_back(): # 断言 1RX580 不支持的算子被安全回退不直发 p OrtDirectMlCrashPolicy() plan p.safe_schedule(AMD_RX580, [conv, add, fused_big]) assert fallback:fused_big in plan def test_no_crashing_op_on_rx580(): # 断言 2调度前断言不会下发崩溃算子 p OrtDirectMlCrashPolicy() with pytest.raises(AssertionError): p.assert_no_crashing_op(AMD_RX580, [fused_big]) def test_other_gpu_ok(): # 断言 3其他 GPU无黑名单正常下发 p OrtDirectMlCrashPolicy() plan p.safe_schedule(NVIDIA_RTX4090, [conv, add, fused_big]) assert plan [conv, add, fused_big] def test_ocr_graph_safe_on_rx580(): # 断言 4OCR 图在 RX580 上整体安全无崩溃算子直发 p OrtDirectMlCrashPolicy() ocr [conv, add, fused_big, depthwise_conv_batched, softmax] plan p.safe_schedule(AMD_RX580, ocr) assert all(not o.startswith((fused_big, depthwise_conv_batched)) or o.startswith(fallback:) for o in plan)四条断言从“RX580 回退”“崩溃算子被抓”“其他 GPU 正常”“OCR 图安全”四面把系统崩溃钉死在 CI。八、排查清单DirectML EP 在 RX 580 跑 OCR 系统崩溃/驱动损坏时是否 TDR/驱动重置确认是 GPU 单步超时或驱动 bug现象 A/B。是否只在特定老卡 特定模型组合触发是就查该卡 DML 不支持的算子/融合被直发现象 C。不支持的算子是否做了回退/拆小没做就直接下发触发驱动崩。用第二层OrtDirectMlCrashPolicy硬件能力黑名单 安全回退 断言防直发。加第三层 pytest断言“RX580 回退、崩溃算子被抓、其他 GPU 正常、OCR 图安全”。特定老硬件的驱动级崩溃EP 必须对该硬件不支持的算子做安全回退绝不能直发。九、小结DirectML EP 在 AMD RX 580 跑 OCR 系统崩溃/驱动损坏本质是ORT 把 RX 580 驱动处理不好的算子/融合大算子单步超时直接下发给 DML没做“不支持就拆小/回退”的安全路径导致 TDR 甚至驱动损坏且 CI 无 RX 580 回归。修复分三层——第一层调度前检查硬件能力、不支持即拆/回退第二层用OrtDirectMlCrashPolicy这个 dataclass 把“硬件能力黑名单 安全回退 断言防直发”收口成单一事实来源第三层用四条 pytest 把“RX580 回退、崩溃算子被抓、其他 GPU 正常、OCR 图安全”钉死在 CI。核心心法EP 在特定老硬件上必须把该硬件驱动处理不好的算子做安全回退拆小/回退 CPU绝不能直发否则会触发系统级崩溃与驱动损坏。
返回列表