已合并
add Pytorch skills #157
gys创建于 7月1日
add Pytorch skills #157
已合并
共 321 个文件变更+43200-0
| @@ -0,0 +1 @@ | |||
| 1 | +../../../graph-log-analyzer/SKILL.md | ||
| @@ -0,0 +1 @@ | |||
| 1 | +../../../graph-mcp-integration/SKILL.md | ||
| @@ -0,0 +1 @@ | |||
| 1 | +../../../graph-mode-diagnostics/SKILL.md | ||
| @@ -0,0 +1 @@ | |||
| 1 | +../../../graph-mode-expert/SKILL.md | ||
| @@ -0,0 +1 @@ | |||
| 1 | +../../../graph-performance-profiling/SKILL.md | ||
| @@ -0,0 +1 @@ | |||
| 1 | +../../../SKILL.md | ||
| @@ -0,0 +1,185 @@ | |||
| 1 | +# NPU Graph Skill — NPU 图模式技能套件 | ||
| 2 | + | ||
| 3 | +面向 Ascend NPU 图模式(NPUGraph/ACLGraph)的 AI Agent 驱动分析工作流。将「依赖个人经验手工排查」转变为「结构化、可追溯、可复用」的标准化流程。 | ||
| 4 | + | ||
| 5 | +5 个子技能统一装在 `NPU-graph-skill/` 下,**一个入口自动路由**,安装时全部注册。 | ||
| 6 | + | ||
| 7 | +## 功能描述 | ||
| 8 | + | ||
| 9 | +torch_npu 的图模式(NPUGraph)对标 CUDA Graphs,通过 capture → compile → replay 三阶段将逐算子 dispatch 替换为单次 ACL 图执行,是 torch.compile 和大模型推理的核心加速手段。调用链跨越 **Python API → C++ 核心 → op-plugin 算子 → ACL 运行时** 四层,问题定位门槛极高。 | ||
| 10 | + | ||
| 11 | +本技能套件核心能力: | ||
| 12 | + | ||
| 13 | +- **知识问答 + 代码导航**:根据代码结构树快速定位涉及文件,解释运行机制 | ||
| 14 | +- **端到端问题诊断**:5 阶段系统诊断,五层架构(L5→L1)定位到具体文件和行号 | ||
| 15 | +- **源码管理**:自动拉取 torch_npu + op-plugin 仓库,按需切换分支 | ||
| 16 | +- **日志分析**:脚本自动解析 TORCH_NPU_LOGS,匹配 16+ 已知异常模式 | ||
| 17 | +- **性能分析**:基准对比、瓶颈定位、ACL Profile 分析 | ||
| 18 | +- **DFX 知识沉淀**:每次诊断后提示记录到知识库,自增长案例库 | ||
| 19 | + | ||
| 20 | +### 支持的问题类型 | ||
| 21 | + | ||
| 22 | +| 类型 | 特征 | 判定方法 | | ||
| 23 | +|------|------|----------| | ||
| 24 | +| 图捕获失败 | `capture_begin` / `aclmdlRICaptureBegin` 报错 | 检查图内不允许的操作、Stream 状态、嵌套捕获 | | ||
| 25 | +| 图重放失败 | `replay` / `aclmdlRIExecuteAsync` 报错 | 检查图编译状态、输入 shape 一致性、LRU 淘汰 | | ||
| 26 | +| 图内 OOM | `NPUCachingAllocator` / `aclrtMalloc` 失败 | 检查捕获期间是否有新分配、内存碎片化、池耗尽 | | ||
| 27 | +| 动态形状 update 失败 | `update_graph` / shape mismatch 报错 | 检查参数合法性、TND+dropout fallback、CPU tensor 要求 | | ||
| 28 | +| 编译 hang/失败 | `torch.compile(backend="npugraphs")` 卡死或报错 | 检查 graph_breaks、Dynamo 异常、图分区逻辑 | | ||
| 29 | +| 性能下降 | 图重放耗时增大、QPS 下降 | P50/P99 延迟对比、ACL Profile 分析、回归检测 | | ||
| 30 | +| 日志异常 | TORCH_NPU_LOGS 出现 ERROR/FATAL | 结构化解析 + 16 个已知模式匹配 | | ||
| 31 | + | ||
| 32 | +### 结论类型 | ||
| 33 | + | ||
| 34 | +| 结论 | 说明 | 后续动作 | | ||
| 35 | +|------|------|----------| | ||
| 36 | +| 用户代码问题 | 图捕获期间有不允许的操作(CPU 同步、条件分支等) | 修改用户代码 | | ||
| 37 | +| 框架 Bug(已定位) | torch_npu 侧代码有误,修改方案明确 | 按修改方案修复 | | ||
| 38 | +| 框架 Bug(待定位) | 问题在 torch_npu 侧但根因未明 | 进入深度诊断 | | ||
| 39 | +| op-plugin 算子问题 | 定位到 op-plugin 侧算子实现有误 | 转 op-plugin 团队 | | ||
| 40 | +| ACL 运行时问题 | 定位到 CANN 驱动层 | 转 CANN 团队 | | ||
| 41 | +| 环境/配置问题 | CANN 版本、Stream 配置、环境变量等 | 修正配置 | | ||
| 42 | +| 设计限制 | 当前 ACL 图不支持的能力(如嵌套捕获) | 记录限制,评估 workaround | | ||
| 43 | + | ||
| 44 | +## 技能总览 | ||
| 45 | + | ||
| 46 | +| 技能 | 职责 | 触发场景 | | ||
| 47 | +|------|------|---------| | ||
| 48 | +| **graph-mode-expert** ★ | 知识问答 + 代码导航 + 仓库管理 + 自验证 | "NPUGraph 怎么工作"/"capture_begin 做了什么" | | ||
| 49 | +| **graph-mode-diagnostics** ★ | 端到端问题诊断(唯一诊断入口) | 捕获/重放/OOM/update 失败、任何报错 | | ||
| 50 | +| graph-performance-profiling | 性能分析:回归检测、瓶颈定位、ACL Profile | "图模式慢"/profile/benchmark/QPS 下降 | | ||
| 51 | +| graph-log-analyzer | 日志结构化解析 + 异常模式匹配 | TORCH_NPU_LOGS/日志分析 | | ||
| 52 | +| graph-mcp-integration | MCP 工具评估、配置、自建 MCP 开发 | 配置 MCP/Context7/Playwright | | ||
| 53 | + | ||
| 54 | +### 技能路由关系 | ||
| 55 | + | ||
| 56 | +``` | ||
| 57 | +用户提问 | ||
| 58 | + │ | ||
| 59 | + ├── 知识问答/代码导航 ──→ graph-mode-expert | ||
| 60 | + │ ├── Phase 0: 仓库准备 | ||
| 61 | + │ ├── Phase 1: 知识问答(关键词→文件→源码→回答) | ||
| 62 | + │ └── Phase 3: 自验证 | ||
| 63 | + │ | ||
| 64 | + ├── 报错/异常/崩溃 ──→ graph-mode-diagnostics | ||
| 65 | + │ ├── Phase 1: 分类(捕获/重放/内存/Update/编译) | ||
| 66 | + │ ├── Phase 2: 分阶段诊断(2A-2E) | ||
| 67 | + │ ├── Phase 3: 模式匹配(14+ known patterns) | ||
| 68 | + │ ├── Phase 4: 深度追踪(源码级根因) | ||
| 69 | + │ └── Phase 5: 结构化报告 | ||
| 70 | + │ | ||
| 71 | + ├── 日志分析 ──→ graph-log-analyzer | ||
| 72 | + │ └── 脚本解析 → 模式匹配 → 时间线重建 → 报告 | ||
| 73 | + │ | ||
| 74 | + ├── 性能问题 ──→ graph-performance-profiling | ||
| 75 | + │ └── 基准建立 → 对比 → 瓶颈定位 → ACL Profile | ||
| 76 | + │ | ||
| 77 | + └── MCP 配置 ──→ graph-mcp-integration | ||
| 78 | + └── 评估 → 配置 → 验证 | ||
| 79 | + | ||
| 80 | +所有会话结束后 ──→ DFX 知识沉淀(提示记录到 knowledge/history.md) | ||
| 81 | +``` | ||
| 82 | + | ||
| 83 | +## 目录结构 | ||
| 84 | + | ||
| 85 | +``` | ||
| 86 | +NPU-graph-skill/ | ||
| 87 | +├── SKILL.md # ★ 总入口(Gate 0 路由 + 中文默认) | ||
| 88 | +├── README.md # ★ 本文件 | ||
| 89 | +├── .claude/skills/ # Claude Code 自动发现(6 个入口) | ||
| 90 | +│ | ||
| 91 | +├── knowledge/ # ★ DFX 知识沉淀 | ||
| 92 | +│ ├── CASE_TEMPLATE.md # 案例录入标准模板 | ||
| 93 | +│ └── history.md # 累积案例库(随使用增长) | ||
| 94 | +│ | ||
| 95 | +├── graph-mode-expert/ # 知识问答 + 代码导航 | ||
| 96 | +│ ├── SKILL.md | ||
| 97 | +│ ├── README.md | ||
| 98 | +│ ├── references/ | ||
| 99 | +│ │ ├── code-structure.md # 完整代码结构树(关键词→文件) | ||
| 100 | +│ │ ├── knowledge-faq.md # 常见问题速查 | ||
| 101 | +│ │ ├── code-repo-setup.md # 仓库准备详细流程 | ||
| 102 | +│ │ ├── architecture.md # 架构设计 | ||
| 103 | +│ │ └── skill-design.md # 技能设计原理 | ||
| 104 | +│ ├── scripts/check_graph_health.py # 环境健康检查 | ||
| 105 | +│ └── templates/analysis-report-template.md | ||
| 106 | +│ | ||
| 107 | +├── graph-mode-diagnostics/ # 端到端问题诊断(唯一入口) | ||
| 108 | +│ ├── SKILL.md | ||
| 109 | +│ ├── references/ | ||
| 110 | +│ │ ├── domain-context.md # 五层架构/源文件/约束 | ||
| 111 | +│ │ ├── known-patterns.md # 14+ 已知问题模式 | ||
| 112 | +│ │ ├── error-code-map.md # ACL 错误码→根因 | ||
| 113 | +│ │ └── diagnostic-checklist.md # 诊断检查清单 | ||
| 114 | +│ └── scripts/ | ||
| 115 | +│ ├── analyze_graph_dump.py | ||
| 116 | +│ └── diagnose_npu_fault.py | ||
| 117 | +│ | ||
| 118 | +├── graph-performance-profiling/ # 性能分析 | ||
| 119 | +│ └── SKILL.md | ||
| 120 | +│ | ||
| 121 | +├── graph-log-analyzer/ # 日志分析 | ||
| 122 | +│ ├── SKILL.md | ||
| 123 | +│ ├── patterns/ (error/timeline/memory) | ||
| 124 | +│ └── scripts/parse_npu_logs.py | ||
| 125 | +│ | ||
| 126 | +└── graph-mcp-integration/ # MCP 集成 | ||
| 127 | + ├── SKILL.md | ||
| 128 | + ├── mcp-configs.json | ||
| 129 | + ├── setup-guide.md | ||
| 130 | + └── references/mcp-evaluation.md | ||
| 131 | +``` | ||
| 132 | + | ||
| 133 | +## 快速开始 | ||
| 134 | + | ||
| 135 | +直接在对话中输入图模式相关问题,自动路由到对应技能: | ||
| 136 | + | ||
| 137 | +``` | ||
| 138 | +NPUGraph 的 capture_begin 内部做了什么? | ||
| 139 | +我的图模式报错了:[贴堆栈] | ||
| 140 | +TORCH_NPU_LOGS 输出 200MB 帮我分析 | ||
| 141 | +图模式比 eager 慢怎么排查? | ||
| 142 | +``` | ||
| 143 | + | ||
| 144 | +### DFX 知识沉淀 | ||
| 145 | + | ||
| 146 | +每次问答/诊断结束后,Agent 会自动提示「是否记录到知识库」。选 Y 后案例按标准模板追加到 `knowledge/history.md`,分类计数自动更新。如果发现新问题模式,还会提示是否同步更新 `known-patterns.md`。 | ||
| 147 | + | ||
| 148 | +## 辅助脚本 | ||
| 149 | + | ||
| 150 | +| 脚本 | 用途 | 位置 | | ||
| 151 | +|------|------|------| | ||
| 152 | +| `parse_npu_logs.py` | 日志结构化解析(模块分布/时间线/异常模式) | graph-log-analyzer/scripts/ | | ||
| 153 | +| `analyze_graph_dump.py` | 图 dump JSON 分析(节点/边/算子分布/基线对比) | graph-mode-diagnostics/scripts/ | | ||
| 154 | +| `check_graph_health.py` | 环境健康检查(CANN/设备/内存/NPUGraph 功能) | graph-mode-expert/scripts/ | | ||
| 155 | + | ||
| 156 | +## 参考文件 | ||
| 157 | + | ||
| 158 | +| 文件 | 内容 | 何时查阅 | | ||
| 159 | +|------|------|----------| | ||
| 160 | +| `graph-mode-expert/references/code-structure.md` | 完整代码结构树(L5→L1,18+ 关键词索引) | 知识问答 / 代码导航 | | ||
| 161 | +| `graph-mode-expert/references/knowledge-faq.md` | 常见问题速查 | 知识问答 | | ||
| 162 | +| `graph-mode-diagnostics/references/domain-context.md` | 五层架构、源文件、使能方式、约束 | 诊断前了解领域 | | ||
| 163 | +| `graph-mode-diagnostics/references/known-patterns.md` | 14+ 已知问题模式 | 诊断 Phase 3 模式匹配 | | ||
| 164 | +| `graph-mode-diagnostics/references/error-code-map.md` | ACL 错误码 → 根因映射 | 看到 ACL 错误码时 | | ||
| 165 | +| `graph-mode-diagnostics/references/diagnostic-checklist.md` | 诊断检查清单 | 诊断全流程 | | ||
| 166 | +| `graph-log-analyzer/patterns/` | E/T/M-Series 异常模式 | 日志分析 | | ||
| 167 | +| `graph-mcp-integration/references/mcp-evaluation.md` | MCP 工具评估与推荐 | 配置 MCP 工具前 | | ||
| 168 | +| `knowledge/history.md` | 累积案例库 | 新诊断前查历史 | | ||
| 169 | +| `knowledge/CASE_TEMPLATE.md` | 案例录入模板 | 记录新案例时 | | ||
| 170 | + | ||
| 171 | +## 维护指南 | ||
| 172 | + | ||
| 173 | +### 新增案例 | ||
| 174 | + | ||
| 175 | +诊断结束后选择记录,Agent 自动追加到 `knowledge/history.md` 对应分类。 | ||
| 176 | + | ||
| 177 | +### 新增已知模式 | ||
| 178 | + | ||
| 179 | +- **问题模式**: `graph-mode-diagnostics/references/known-patterns.md`,按 P-CAP/P-REP/P-MEM/P-UPD/P-COM 系列 | ||
| 180 | +- **日志模式**: `graph-log-analyzer/patterns/` 下的对应文件,按 E/T/M 系列 | ||
| 181 | +- **错误码**: `graph-mode-diagnostics/references/error-code-map.md` | ||
| 182 | + | ||
| 183 | +## 作者 | ||
| 184 | + | ||
| 185 | +YCH | ||
| @@ -0,0 +1,41 @@ | |||
| 1 | +--- | ||
| 2 | +name: npu-graph-skill | ||
| 3 | +description: >- | ||
| 4 | + NPU 图模式技能套件。Use when user asks about NPU graph mode — | ||
| 5 | + NPUGraph, ACLGraph, capture/replay, graph compile, graph OOM, | ||
| 6 | + graph update, FA3 graph, graph performance, graph logs, MCP config. | ||
| 7 | + Covers torch_npu full stack (Python API -> C++ core -> op-plugin -> ACL runtime). | ||
| 8 | +--- | ||
| 9 | + | ||
| 10 | +# NPU Graph Skill | ||
| 11 | + | ||
| 12 | +## 语言 | ||
| 13 | + | ||
| 14 | +**所有回答默认使用中文。** | ||
| 15 | + | ||
| 16 | +--- | ||
| 17 | + | ||
| 18 | +## Gate 0: 按用户意图路由 | ||
| 19 | + | ||
| 20 | +| 用户意图 | 加载技能 | 触发示例 | | ||
| 21 | +|---------|---------|---------| | ||
| 22 | +| 知识问答、概念解释、代码导航 | `npu-graph-mode-expert` | "NPUGraph 怎么工作"/"capture_begin 做了什么" | | ||
| 23 | +| 报错/异常/崩溃/OOM | `npu-graph-mode-diagnostics` | "图捕获报错"/"replay failed"/"OOM" | | ||
| 24 | +| 性能问题/变慢/回归 | `npu-graph-performance-profiling` | "图模式慢"/"QPS下降"/"profile" | | ||
| 25 | +| 日志分析 | `npu-graph-log-analyzer` | "TORCH_NPU_LOGS"/"帮我分析日志" | | ||
| 26 | +| MCP 工具配置 | `npu-graph-mcp-integration` | "配置MCP"/"启用Context7" | | ||
| 27 | + | ||
| 28 | +## 手递手规则 | ||
| 29 | + | ||
| 30 | +``` | ||
| 31 | +expert ───→ diagnostics (报错) → log-analyzer (日志量大) | ||
| 32 | + ├→ profiling (性能) → diagnostics (根因是bug) | ||
| 33 | + └→ log-analyzer (日志) | ||
| 34 | + | ||
| 35 | +log-analyzer → diagnostics (日志指出异常) | ||
| 36 | + └→ profiling (日志指出性能异常) | ||
| 37 | +``` | ||
| 38 | + | ||
| 39 | +## DFX: 知识沉淀 | ||
| 40 | + | ||
| 41 | +所有子技能在会话结束后**自动提示用户**是否将问答/诊断结果记录到 [knowledge/history.md](knowledge/history.md)。详见 [knowledge/CASE_TEMPLATE.md](knowledge/CASE_TEMPLATE.md)。 | ||
| @@ -0,0 +1,97 @@ | |||
| 1 | +--- | ||
| 2 | +name: npu-graph-log-analyzer | ||
| 3 | +description: >- | ||
| 4 | + Use when user provides TORCH_NPU_LOGS output, NPUGraph log files, | ||
| 5 | + or logs containing [NPUGRAPH] tags that need structured analysis. | ||
| 6 | + Use keywords: "TORCH_NPU_LOGS", "日志分析", "parse log", "log analysis", | ||
| 7 | + "解析日志", "模块标签", "NPUGRAPH log". | ||
| 8 | + NOT for diagnosing the root cause from logs — handoff findings to | ||
| 9 | + npu-graph-mode-diagnostics for full diagnosis. | ||
| 10 | +--- | ||
| 11 | + | ||
| 12 | +# Graph Log Analyzer — 图模式日志分析 | ||
| 13 | + | ||
| 14 | +## The Iron Law | ||
| 15 | + | ||
| 16 | +``` | ||
| 17 | +日志分析必须先自动化扫描已知模式,再人工介入异常点。 | ||
| 18 | +禁止未解析就全文阅读——100MB 日志人工读不完,必漏关键信息。 | ||
| 19 | +``` | ||
| 20 | + | ||
| 21 | +--- | ||
| 22 | + | ||
| 23 | +## When to Use | ||
| 24 | + | ||
| 25 | +| 场景 | 输入 | | ||
| 26 | +|------|------| | ||
| 27 | +| 图模式运行后有大量日志 | 日志文件 (`.log`, `.txt`) | | ||
| 28 | +| TORCH_NPU_LOGS 输出需分析 | stdout/stderr 文本 | | ||
| 29 | +| 需还原图执行时间线 | 带时间戳的日志 | | ||
| 30 | +| 周期性错误需模式匹配 | 多次运行的历史日志 | | ||
| 31 | + | ||
| 32 | +--- | ||
| 33 | + | ||
| 34 | +## Phase 1: 结构化解析 | ||
| 35 | + | ||
| 36 | +**用脚本**: | ||
| 37 | +```bash | ||
| 38 | +python scripts/parse_npu_logs.py <log_file> --output json --summary | ||
| 39 | +# 输出: 模块分布 + 日志级别分布 + 时间范围 + 错误/警告列表 + 时间线 | ||
| 40 | +``` | ||
| 41 | + | ||
| 42 | +**手动快速定位**(脚本不可用时): | ||
| 43 | +```bash | ||
| 44 | +grep -E "ERROR|FATAL" <log_file> # 只看错误 | ||
| 45 | +grep "\[NPUGRAPH\]" <log_file> | grep -v DEBUG # 去噪 | ||
| 46 | +grep "\[Capture\]" <log_file> # 只看特定阶段 | ||
| 47 | +``` | ||
| 48 | + | ||
| 49 | +--- | ||
| 50 | + | ||
| 51 | +## Phase 2: 异常模式匹配 | ||
| 52 | + | ||
| 53 | +详见 `patterns/error-patterns.md`、`patterns/timeline-patterns.md`、`patterns/memory-patterns.md` | ||
| 54 | + | ||
| 55 | +**快速索引**: | ||
| 56 | + | ||
| 57 | +| 模式 ID | 描述 | 关键特征 | | ||
| 58 | +|---------|------|----------| | ||
| 59 | +| E-001 | 图捕获中断 | `Capture FAILED` + 无 `capture_end` | | ||
| 60 | +| E-002 | 重放状态异常 | `Replay ERROR: graph not in captured state` | | ||
| 61 | +| E-003 | 内存分配失败 | `Memory ERROR: aclrtMalloc failed` | | ||
| 62 | +| E-007 | 图编译失败 | `Compile ERROR: aclmdl build failed` | | ||
| 63 | +| T-001 | 捕获耗时长 | Capture 时间戳间隔 > 预期 | | ||
| 64 | +| T-002 | 重放耗时长 | Replay 时间戳间隔 > 预期 | | ||
| 65 | +| M-001 | 内存碎片化 | 大量小 alloc + 小 free | | ||
| 66 | +| M-002 | 内存泄漏 | alloc > free + 累积增长 | | ||
| 67 | + | ||
| 68 | +## Phase 3: 时间线重建 | ||
| 69 | + | ||
| 70 | +从日志还原图执行时间线:warmup → capture → compile → replay × N → cache | ||
| 71 | + | ||
| 72 | +**异常检测**:捕获 >10s? / 编译 >30s? / 重放 P99 > P50 × 3? / 首次重放 >> 后续? | ||
| 73 | + | ||
| 74 | +## Phase 4: 跨日志对比 | ||
| 75 | + | ||
| 76 | +```bash | ||
| 77 | +python scripts/parse_npu_logs.py log_v1.txt log_v2.txt --diff | ||
| 78 | +# 对比:模块分布变化 / 错误增减 / 重放耗时分布 / 新模块标签 | ||
| 79 | +``` | ||
| 80 | + | ||
| 81 | +## Phase 5: 报告 | ||
| 82 | + | ||
| 83 | +```markdown | ||
| 84 | +## 日志分析报告 | ||
| 85 | +### 概况: 文件/总行数/时间跨度/错误数/警告数 | ||
| 86 | +### 模块分布: | 模块 | 行数 | 占比 | | ||
| 87 | +### 异常模式: [E-00X] 模式名: N次, L<行号> → 影响 → 建议 | ||
| 88 | +### 结论: 核心问题 + 下一步 | ||
| 89 | +``` | ||
| 90 | + | ||
| 91 | +## Handoff | ||
| 92 | + | ||
| 93 | +| 条件 | 移交技能 | | ||
| 94 | +|------|----------| | ||
| 95 | +| 日志指出捕获/重放/OOM 失败 | `NPU-graph-mode-diagnostics` | | ||
| 96 | +| 日志指出性能异常 | `NPU-graph-performance-profiling` | | ||
| 97 | +| 日志正常但仍有问题 | `NPU-graph-mode-diagnostics` | | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | +# 错误日志模式 — E-Series | ||
| 2 | + | ||
| 3 | +| 模式 ID | 模式名称 | 正则匹配 | 说明 | 关联阶段 | | ||
| 4 | +|---------|---------|---------|------|---------| | ||
| 5 | +| E-001 | 捕获中断 | `Capture.*FAILED\|capture.*error` + 后续无 `capture_end` | 图捕获过程中断 | Capture | | ||
| 6 | +| E-002 | 重放状态异常 | `Replay.*graph not in captured state\|state.*invalid` | 图不在可重放状态 | Replay | | ||
| 7 | +| E-003 | 内存分配失败 | `Memory.*aclrtMalloc failed\|GE_DEVICE_MEMORY_ALLOC` | NPU 内存不足 | Memory | | ||
| 8 | +| E-004 | Update 参数非法 | `Update.*invalid.*param\|shape.*mismatch.*update` | update 参数类型/形状错误 | Update | | ||
| 9 | +| E-005 | 图 LRU 淘汰 | `TreeManager.*evicting.*graph` | LRU 淘汰了正在使用的图 | Tree/Cache | | ||
| 10 | +| E-006 | Stream 不匹配 | `stream.*mismatch\|Stream.*different` | 捕获和重放 Stream 不一致 | Capture/Replay | | ||
| 11 | +| E-007 | 图编译失败 | `Compile.*aclmdl.*failed\|build.*error` | ACL 图编译出错 | Compile | | ||
| 12 | +| E-008 | HCCL UnmapMem | `Memory.*HCCL.*UnmapMem\|HCCL.*free.*crash` | HCCL 通信内存被意外释放 | Memory | | ||
| 13 | +| E-009 | FA3 图不支持 | `FA3.*not supported.*graph\|flash_attn.*fallback` | FA3 graph handler 判定不支持 | Capture | | ||
| 14 | +| E-010 | 注册 fake 缺失 | `register_fake.*not found\|no fake impl` | 自定义 op 缺少 fake tensor 实现 | Compile/Dynamo | | ||
| 15 | + | ||
| 16 | +## 如何使用 | ||
| 17 | + | ||
| 18 | +当 `parse_npu_logs.py` 检测到以上任一模式,会输出: | ||
| 19 | +- 模式 ID | ||
| 20 | +- 匹配行号和上下文 | ||
| 21 | +- 建议的诊断方向 | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | +# 内存异常模式 — M-Series | ||
| 2 | + | ||
| 3 | +| 模式 ID | 模式名称 | 检测条件 | 说明 | | ||
| 4 | +|---------|---------|---------|------| | ||
| 5 | +| M-001 | 内存碎片化 | 大量小 alloc/free (< 1MB),但无大块连续 alloc 成功 | NPUCachingAllocator 池碎片化 | | ||
| 6 | +| M-002 | 内存泄漏 | alloc_count - free_count 随时间单调递增 | 图 replay 后未释放临时内存 | | ||
| 7 | +| M-003 | 跨 Stream 双倍 | 两个 Stream 各自的 peak > 总内存 / 2 | 各 Stream 独立内存池 | | ||
| 8 | +| M-004 | 图捕获时分配 | Capture 期间出现 alloc 事件 | 违反捕获规则 (P-CAP-005) | | ||
| 9 | +| M-005 | 池耗尽 | reserved ≈ total, allocated << total | 池预留大量未使用内存 | | ||
| 10 | +| M-006 | 峰值接近上限 | peak > total × 0.95 | OOM 风险高 | | ||
| 11 | + | ||
| 12 | +## 内存日志分析命令 | ||
| 13 | + | ||
| 14 | +```bash | ||
| 15 | +# 统计 alloc/free 数量 | ||
| 16 | +grep "\[Memory\].*alloc" <log> | wc -l | ||
| 17 | +grep "\[Memory\].*free" <log> | wc -l | ||
| 18 | + | ||
| 19 | +# 统计各 size 范围的 alloc | ||
| 20 | +grep -oP 'alloc \d+ bytes' <log> | awk '{sum[$2]++} END {for (s in sum) print s, sum[s]}' | ||
| 21 | + | ||
| 22 | +# 查看峰值前后日志 | ||
| 23 | +grep -B 20 -A 10 "max_memory\|peak" <log> | ||
| 24 | +``` | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | +# 时间线异常模式 — T-Series | ||
| 2 | + | ||
| 3 | +| 模式 ID | 模式名称 | 检测条件 | 说明 | | ||
| 4 | +|---------|---------|---------|------| | ||
| 5 | +| T-001 | 捕获耗时长 | Capture 时间戳间隔 > 预期 | 模型复杂度正常时为正常现象 | | ||
| 6 | +| T-002 | 重放 P99 异常 | P99 延迟 > P50 × 3 | 间歇性瓶颈(Stream 竞争/调度) | | ||
| 7 | +| T-003 | 编译耗时长 | Compile 跨度 > 30s | ACL 编译异常,检查算子上限/带宽 | | ||
| 8 | +| T-004 | 首次重放显著慢 | replay#1 >> replay#N | 编译在首次重放触发,正常 | | ||
| 9 | +| T-005 | 无 warmup 直接测 | 前几次 replay 偏慢 | 缺少 warmup,正常 | | ||
| 10 | +| T-006 | 重复编译 | Compile 事件出现多次 | 相同 shape 编译了多次(pool miss) | | ||
| 11 | +| T-007 | 重放间隔递增 | replay 时间逐渐变长 | 可能的内存碎片或资源泄漏 | | ||
| 12 | +| T-008 | 捕获-编译间隙大 | capture_end → compile 间隔 > 1s | 编译调度延迟 | | ||
| 13 | + | ||
| 14 | +## 基准参考值 | ||
| 15 | + | ||
| 16 | +以下是基于 Ascend 910B 的经验值,实际可能因模型和 CANN 版本不同: | ||
| 17 | + | ||
| 18 | +| 阶段 | 预期范围 | 告警阈值 | | ||
| 19 | +|------|---------|---------| | ||
| 20 | +| 捕获 | < 模型 eager 前向耗时 × 2 | > 10s | | ||
| 21 | +| 编译 | 取决于图规模 | > 30s | | ||
| 22 | +| 重放 (稳态) | < eager 前向耗时 | > eager × 1.5 | | ||
| @@ -0,0 +1,276 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +""" | ||
| 3 | +NPU 图模式日志解析器 — TORCH_NPU_LOGS 结构化分析 | ||
| 4 | + | ||
| 5 | +用法: | ||
| 6 | + python parse_npu_logs.py <log_file> [--output json|text] [--summary] [--diff <other_log>] | ||
| 7 | + python parse_npu_logs.py --test | ||
| 8 | + | ||
| 9 | +输出: | ||
| 10 | + - 模块分布统计 | ||
| 11 | + - 按日志级别分类 | ||
| 12 | + - 时间线提取 | ||
| 13 | + - 异常模式匹配 | ||
| 14 | +""" | ||
| 15 | + | ||
| 16 | +import re | ||
| 17 | +import sys | ||
| 18 | +import json | ||
| 19 | +import argparse | ||
| 20 | +from collections import defaultdict, Counter | ||
| 21 | +from dataclasses import dataclass, field | ||
| 22 | +from datetime import datetime | ||
| 23 | +from typing import Optional | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +# ── 数据结构 ─────────────────────────────────────────── | ||
| 27 | + | ||
| 28 | +@dataclass | ||
| 29 | +class LogEntry: | ||
| 30 | + line_no: int | ||
| 31 | + timestamp: Optional[str] | ||
| 32 | + module: str | ||
| 33 | + action: str | ||
| 34 | + level: str | ||
| 35 | + message: str | ||
| 36 | + | ||
| 37 | +@dataclass | ||
| 38 | +class LogSummary: | ||
| 39 | + total_lines: int | ||
| 40 | + modules: dict # {module: count} | ||
| 41 | + by_level: dict # {level: count} | ||
| 42 | + errors: list | ||
| 43 | + warnings: list | ||
| 44 | + timeline: list | ||
| 45 | + matched_patterns: list | ||
| 46 | + | ||
| 47 | + | ||
| 48 | +# ── 正则模式 ─────────────────────────────────────────── | ||
| 49 | + | ||
| 50 | +LOG_LINE_RE = re.compile( | ||
| 51 | + r'\[(?P<timestamp>\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}[.\d]*)\]\s*' | ||
| 52 | + r'\[NPUGRAPH\]\[(?P<module>\w+)\]\[(?P<action>\w+)\]\s*' | ||
| 53 | + r'(?P<message>.*)' | ||
| 54 | +) | ||
| 55 | + | ||
| 56 | +# 简化的模块+动作提取(兼容无时间戳格式) | ||
| 57 | +SIMPLE_RE = re.compile( | ||
| 58 | + r'\[NPUGRAPH\]\[(?P<module>\w+)\](?:\[(?P<action>\w+)\])?\s*' | ||
| 59 | + r'(?P<message>.*)' | ||
| 60 | +) | ||
| 61 | + | ||
| 62 | +# 日志级别 | ||
| 63 | +LEVEL_RE = re.compile(r'\b(DEBUG|INFO|WARNING|WARN|ERROR|FATAL)\b', re.IGNORECASE) | ||
| 64 | + | ||
| 65 | +# ── 异常模式定义 ──────────────────────────────────────── | ||
| 66 | + | ||
| 67 | +ERROR_PATTERNS = [ | ||
| 68 | + ('E-001', r'Capture.*(?:FAILED|failed|error)', '图捕获中断'), | ||
| 69 | + ('E-002', r'Replay.*(?:graph not in|captured state|invalid state)', '重放状态异常'), | ||
| 70 | + ('E-003', r'Memory.*(?:aclrtMalloc failed|GE_DEVICE_MEMORY_ALLOC|alloc.*failed)', '内存分配失败'), | ||
| 71 | + ('E-004', r'Update.*(?:invalid.*param|shape.*mismatch|param.*error)', 'Update 参数非法'), | ||
| 72 | + ('E-005', r'TreeManager.*evicting.*graph', '图 LRU 淘汰'), | ||
| 73 | + ('E-006', r'(?:stream|Stream).*(?:mismatch|different|wrong)', 'Stream 不匹配'), | ||
| 74 | + ('E-007', r'Compile.*(?:aclmdl.*failed|build.*error|compilation.*error)', '图编译失败'), | ||
| 75 | + ('E-008', r'(?:HCCL|hccl).*(?:UnmapMem|free.*crash)', 'HCCL UnmapMem'), | ||
| 76 | + ('E-009', r'FA3.*(?:not supported|fallback|unsupported).*graph', 'FA3 图不支持'), | ||
| 77 | + ('E-010', r'register_fake.*(?:not found|no fake impl|missing)', '注册 fake 缺失'), | ||
| 78 | +] | ||
| 79 | + | ||
| 80 | + | ||
| 81 | +# ── 核心逻辑 ──────────────────────────────────────────── | ||
| 82 | + | ||
| 83 | +def parse_log(filepath: str) -> list[LogEntry]: | ||
| 84 | + """解析日志文件,返回 LogEntry 列表""" | ||
| 85 | + entries = [] | ||
| 86 | + with open(filepath, 'r', encoding='utf-8', errors='replace') as f: | ||
| 87 | + for i, line in enumerate(f, 1): | ||
| 88 | + line = line.rstrip() | ||
| 89 | + if not line: | ||
| 90 | + continue | ||
| 91 | + | ||
| 92 | + # 尝试完整格式 | ||
| 93 | + m = LOG_LINE_RE.match(line) | ||
| 94 | + if m: | ||
| 95 | + entries.append(LogEntry( | ||
| 96 | + line_no=i, | ||
| 97 | + timestamp=m.group('timestamp'), | ||
| 98 | + module=m.group('module'), | ||
| 99 | + action=m.group('action'), | ||
| 100 | + level=_extract_level(m.group('message'), m.group('action')), | ||
| 101 | + message=m.group('message'), | ||
| 102 | + )) | ||
| 103 | + continue | ||
| 104 | + | ||
| 105 | + # 尝试简化格式 | ||
| 106 | + m = SIMPLE_RE.match(line) | ||
| 107 | + if m: | ||
| 108 | + entries.append(LogEntry( | ||
| 109 | + line_no=i, | ||
| 110 | + timestamp=None, | ||
| 111 | + module=m.group('module'), | ||
| 112 | + action=m.group('action') or 'UNKNOWN', | ||
| 113 | + level=_extract_level(m.group('message'), m.group('action') or ''), | ||
| 114 | + message=m.group('message'), | ||
| 115 | + )) | ||
| 116 | + return entries | ||
| 117 | + | ||
| 118 | + | ||
| 119 | +def _extract_level(message: str, action: str = '') -> str: | ||
| 120 | + # 优先从 action 字段提取(如 [Memory][ERROR]) | ||
| 121 | + for candidate in (action, message): | ||
| 122 | + m = LEVEL_RE.search(candidate) | ||
| 123 | + if m: | ||
| 124 | + lvl = m.group(1).upper() | ||
| 125 | + return 'WARNING' if lvl == 'WARN' else lvl | ||
| 126 | + return 'INFO' | ||
| 127 | + | ||
| 128 | + | ||
| 129 | +def summarize(entries: list[LogEntry]) -> LogSummary: | ||
| 130 | + """生成摘要""" | ||
| 131 | + modules = Counter(e.module for e in entries) | ||
| 132 | + by_level = Counter(e.level for e in entries) | ||
| 133 | + | ||
| 134 | + errors = [e for e in entries if e.level in ('ERROR', 'FATAL')] | ||
| 135 | + warnings = [e for e in entries if e.level == 'WARNING'] | ||
| 136 | + | ||
| 137 | + # 提取时间线 | ||
| 138 | + timeline = [ | ||
| 139 | + {'line': e.line_no, 'ts': e.timestamp, 'module': e.module, | ||
| 140 | + 'action': e.action, 'level': e.level, 'msg': e.message[:120]} | ||
| 141 | + for e in entries | ||
| 142 | + if e.level in ('ERROR', 'FATAL', 'WARNING') or e.action in ('BEGIN', 'END', 'START', 'STOP') | ||
| 143 | + ] | ||
| 144 | + | ||
| 145 | + # 模式匹配 | ||
| 146 | + matched = [] | ||
| 147 | + for entry in errors + warnings: | ||
| 148 | + full_text = f"[{entry.module}][{entry.action}] {entry.message}" | ||
| 149 | + for pattern_id, pattern_re, desc in ERROR_PATTERNS: | ||
| 150 | + if re.search(pattern_re, full_text, re.IGNORECASE): | ||
| 151 | + matched.append({ | ||
| 152 | + 'pattern_id': pattern_id, | ||
| 153 | + 'description': desc, | ||
| 154 | + 'line': entry.line_no, | ||
| 155 | + 'message': entry.message[:200], | ||
| 156 | + }) | ||
| 157 | + break | ||
| 158 | + | ||
| 159 | + return LogSummary( | ||
| 160 | + total_lines=len(entries), | ||
| 161 | + modules=dict(modules.most_common()), | ||
| 162 | + by_level=dict(by_level), | ||
| 163 | + errors=[{'line': e.line_no, 'msg': e.message[:200]} for e in errors], | ||
| 164 | + warnings=[{'line': e.line_no, 'msg': e.message[:200]} for e in warnings], | ||
| 165 | + timeline=timeline, | ||
| 166 | + matched_patterns=matched, | ||
| 167 | + ) | ||
| 168 | + | ||
| 169 | + | ||
| 170 | +def print_summary(s: LogSummary): | ||
| 171 | + """文本格式输出摘要""" | ||
| 172 | + print(f"=== 日志分析摘要 ===") | ||
| 173 | + print(f"总行数: {s.total_lines}") | ||
| 174 | + print(f"模块分布: {json.dumps(s.modules, ensure_ascii=False)}") | ||
| 175 | + print(f"级别分布: {json.dumps(s.by_level, ensure_ascii=False)}") | ||
| 176 | + | ||
| 177 | + print(f"\n--- 错误 ({len(s.errors)}) ---") | ||
| 178 | + for e in s.errors[:20]: | ||
| 179 | + print(f" L{e['line']}: {e['msg']}") | ||
| 180 | + | ||
| 181 | + print(f"\n--- 警告 ({len(s.warnings)}) ---") | ||
| 182 | + for w in s.warnings[:10]: | ||
| 183 | + print(f" L{w['line']}: {w['msg']}") | ||
| 184 | + | ||
| 185 | + if s.matched_patterns: | ||
| 186 | + print(f"\n--- 匹配到的异常模式 ---") | ||
| 187 | + seen = set() | ||
| 188 | + for m in s.matched_patterns: | ||
| 189 | + if m['pattern_id'] not in seen: | ||
| 190 | + seen.add(m['pattern_id']) | ||
| 191 | + print(f" [{m['pattern_id']}] {m['description']}") | ||
| 192 | + print(f" 位置: L{m['line']}") | ||
| 193 | + print(f" 内容: {m['message'][:150]}") | ||
| 194 | + | ||
| 195 | + if s.timeline: | ||
| 196 | + print(f"\n--- 关键事件时间线 ({len(s.timeline)} 条目) ---") | ||
| 197 | + for t in s.timeline[:30]: | ||
| 198 | + ts = t['ts'] or 'N/A' | ||
| 199 | + print(f" {ts} [{t['module']}][{t['action']}] {t['level']}: {t['msg'][:100]}") | ||
| 200 | + | ||
| 201 | + | ||
| 202 | +# ── 主入口 ────────────────────────────────────────────── | ||
| 203 | + | ||
| 204 | +def main(): | ||
| 205 | + parser = argparse.ArgumentParser(description='NPU 图模式日志解析器') | ||
| 206 | + parser.add_argument('logfile', nargs='?', help='日志文件路径') | ||
| 207 | + parser.add_argument('--output', choices=['json', 'text'], default='text') | ||
| 208 | + parser.add_argument('--summary', action='store_true', default=True) | ||
| 209 | + parser.add_argument('--test', action='store_true', help='自检') | ||
| 210 | + args = parser.parse_args() | ||
| 211 | + | ||
| 212 | + if args.test: | ||
| 213 | + _self_test() | ||
| 214 | + return | ||
| 215 | + | ||
| 216 | + if not args.logfile: | ||
| 217 | + parser.print_help() | ||
| 218 | + sys.exit(1) | ||
| 219 | + | ||
| 220 | + entries = parse_log(args.logfile) | ||
| 221 | + if not entries: | ||
| 222 | + print("警告: 未找到 NPUGRAPH 日志条目。请确认日志格式是否正确。", file=sys.stderr) | ||
| 223 | + print("期望格式: [NPUGRAPH][Module][Action] message", file=sys.stderr) | ||
| 224 | + sys.exit(1) | ||
| 225 | + | ||
| 226 | + summary = summarize(entries) | ||
| 227 | + | ||
| 228 | + if args.output == 'json': | ||
| 229 | + print(json.dumps({ | ||
| 230 | + 'total_lines': summary.total_lines, | ||
| 231 | + 'modules': summary.modules, | ||
| 232 | + 'by_level': summary.by_level, | ||
| 233 | + 'errors': summary.errors, | ||
| 234 | + 'warnings': summary.warnings, | ||
| 235 | + 'matched_patterns': summary.matched_patterns, | ||
| 236 | + }, ensure_ascii=False, indent=2)) | ||
| 237 | + else: | ||
| 238 | + print_summary(summary) | ||
| 239 | + | ||
| 240 | + | ||
| 241 | +def _self_test(): | ||
| 242 | + """自检""" | ||
| 243 | + test_log = """ | ||
| 244 | +[2026-06-10 10:00:00.100] [NPUGRAPH][TreeManager][WARMUP] Warmup iteration start | ||
| 245 | +[2026-06-10 10:00:01.000] [NPUGRAPH][Capture][BEGIN] capture_begin on stream 0x1 | ||
| 246 | +[2026-06-10 10:00:03.500] [NPUGRAPH][Capture][END] capture_end success | ||
| 247 | +[2026-06-10 10:00:03.600] [NPUGRAPH][Compile][START] ACL graph compilation | ||
| 248 | +[2026-06-10 10:00:05.000] [NPUGRAPH][Compile][END] compilation complete | ||
| 249 | +[2026-06-10 10:00:05.100] [NPUGRAPH][Replay][INFO] replay() #1 success, 12.3ms | ||
| 250 | +[2026-06-10 10:00:05.150] [NPUGRAPH][Memory][ERROR] aclrtMalloc failed: device memory insufficient | ||
| 251 | +[2026-06-10 10:00:05.200] [NPUGRAPH][Replay][ERROR] Replay ERROR: graph not in captured state | ||
| 252 | +[2026-06-10 10:00:05.300] [NPUGRAPH][TreeManager][EVICT] evicting graph key=hash_abc123 | ||
| 253 | +""" | ||
| 254 | + import tempfile, os | ||
| 255 | + with tempfile.NamedTemporaryFile(mode='w', suffix='.log', delete=False) as f: | ||
| 256 | + f.write(test_log) | ||
| 257 | + testpath = f.name | ||
| 258 | + | ||
| 259 | + try: | ||
| 260 | + entries = parse_log(testpath) | ||
| 261 | + print(f"[SELF TEST] 解析条目数: {len(entries)} (期望 9)") | ||
| 262 | + assert len(entries) == 9, f"FAIL: 期望 9 条目,实际 {len(entries)}" | ||
| 263 | + | ||
| 264 | + summary = summarize(entries) | ||
| 265 | + assert summary.total_lines == 9 | ||
| 266 | + assert len(summary.errors) == 2 | ||
| 267 | + assert len(summary.matched_patterns) >= 2 | ||
| 268 | + print(f"[SELF TEST] PASSED — 所有断言通过") | ||
| 269 | + print(f" 错误数: {len(summary.errors)}") | ||
| 270 | + print(f" 匹配模式: {[m['pattern_id'] for m in summary.matched_patterns]}") | ||
| 271 | + finally: | ||
| 272 | + os.unlink(testpath) | ||
| 273 | + | ||
| 274 | + | ||
| 275 | +if __name__ == '__main__': | ||
| 276 | + main() | ||
| @@ -0,0 +1,70 @@ | |||
| 1 | +--- | ||
| 2 | +name: npu-graph-mcp-integration | ||
| 3 | +description: >- | ||
| 4 | + Use when user asks about configuring or setting up MCP tools for NPU graph | ||
| 5 | + mode development — Context7, Playwright, Greptile, or custom NPU MCP servers. | ||
| 6 | + Use keywords: "配置MCP", "setup mcp", "启用Context7", "mcp工具", | ||
| 7 | + "add mcp", "MCP server". | ||
| 8 | + NOT for using MCP tools in diagnosis — this skill only handles configuration. | ||
| 9 | +--- | ||
| 10 | + | ||
| 11 | +# Graph MCP Integration — MCP 工具集成 | ||
| 12 | + | ||
| 13 | +## The Iron Law | ||
| 14 | + | ||
| 15 | +``` | ||
| 16 | +MCP 工具是辅助,不是替代。诊断的核心永远是理解 NPUGraph 五层架构,MCP 只是加速信息检索。 | ||
| 17 | +``` | ||
| 18 | + | ||
| 19 | +--- | ||
| 20 | + | ||
| 21 | +## 推荐工具 | ||
| 22 | + | ||
| 23 | +| 优先级 | 工具 | 价值 | | ||
| 24 | +|--------|------|------| | ||
| 25 | +| P0 | **Context7** | 查询 CANN/PyTorch API 文档 | | ||
| 26 | +| P0 | **NPU-Log-Parser** (自建) | 结构化解析日志 | | ||
| 27 | +| P1 | **Playwright** | 捕获 CANN Profiler 页面 | | ||
| 28 | +| P2 | **Graph-Dump-Analyzer** (自建) | 分析 graph.debug_dump() JSON | | ||
| 29 | + | ||
| 30 | +MCP 工具评估详情 → [references/mcp-evaluation.md](references/mcp-evaluation.md) | ||
| 31 | + | ||
| 32 | +--- | ||
| 33 | + | ||
| 34 | +## 快速配置 | ||
| 35 | + | ||
| 36 | +### Context7 (文档查询) | ||
| 37 | + | ||
| 38 | +```json | ||
| 39 | +{ "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } } | ||
| 40 | +``` | ||
| 41 | + | ||
| 42 | +### Playwright (浏览器自动化) | ||
| 43 | + | ||
| 44 | +```json | ||
| 45 | +{ "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] } } } | ||
| 46 | +``` | ||
| 47 | + | ||
| 48 | +### NPU-Log-Parser (自建) | ||
| 49 | + | ||
| 50 | +```json | ||
| 51 | +{ "mcpServers": { "npu-log-parser": { "command": "python", "args": ["-m", "npu_mcp.log_parser"], "cwd": "${workspaceFolder}/skills/NPU-graph-skill/graph-mcp-integration" } } } | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +解析脚本位于 [../graph-log-analyzer/scripts/parse_npu_logs.py](../graph-log-analyzer/scripts/parse_npu_logs.py)。 | ||
| 55 | + | ||
| 56 | +--- | ||
| 57 | + | ||
| 58 | +## 自建 MCP 规范 | ||
| 59 | + | ||
| 60 | +1. **Python stdio 协议** — 兼容 Claude Code MCP 客户端 | ||
| 61 | +2. **JSON 输入输出** — 确保结构化 | ||
| 62 | +3. **错误隔离** — MCP crash 不影响主诊断流程 | ||
| 63 | + | ||
| 64 | +--- | ||
| 65 | + | ||
| 66 | +## 注意事项 | ||
| 67 | + | ||
| 68 | +- NPU 开发环境通常在内网,Context7 等云端 MCP 可能不可用 | ||
| 69 | +- MCP 需要 Python 3.10+ | ||
| 70 | +- 日志可能含敏感信息,MCP 只做本地解析 | ||
| @@ -0,0 +1,46 @@ | |||
| 1 | +{ | ||
| 2 | + "_comment": "NPU 图模式开发推荐 MCP 配置。复制需要的条目到项目根目录 .mcp.json", | ||
| 3 | + "mcpServers": { | ||
| 4 | + "context7": { | ||
| 5 | + "command": "npx", | ||
| 6 | + "args": ["-y", "@upstash/context7-mcp"], | ||
| 7 | + "description": "实时文档查询 — CANN/PyTorch/C++ API" | ||
| 8 | + }, | ||
| 9 | + "playwright": { | ||
| 10 | + "command": "npx", | ||
| 11 | + "args": ["@playwright/mcp@latest"], | ||
| 12 | + "description": "浏览器自动化 — CANN Profiler Web UI" | ||
| 13 | + }, | ||
| 14 | + "greptile": { | ||
| 15 | + "url": "https://api.greptile.com/mcp", | ||
| 16 | + "headers": { | ||
| 17 | + "Authorization": "Bearer ${GREPTILE_API_TOKEN}" | ||
| 18 | + }, | ||
| 19 | + "description": "代码库语义搜索" | ||
| 20 | + }, | ||
| 21 | + "npu-log-parser": { | ||
| 22 | + "command": "python", | ||
| 23 | + "args": ["-m", "npu_mcp.log_parser"], | ||
| 24 | + "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/scripts", | ||
| 25 | + "description": "自建 — TORCH_NPU_LOGS 结构化解析", | ||
| 26 | + "env": { | ||
| 27 | + "PYTHONPATH": "${workspaceFolder}/NPUGRAPH_SKILL/scripts" | ||
| 28 | + } | ||
| 29 | + }, | ||
| 30 | + "graph-dump-analyzer": { | ||
| 31 | + "command": "python", | ||
| 32 | + "args": ["-m", "npu_mcp.graph_dump_analyzer"], | ||
| 33 | + "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/scripts", | ||
| 34 | + "description": "自建 — graph.debug_dump() JSON 分析", | ||
| 35 | + "env": { | ||
| 36 | + "PYTHONPATH": "${workspaceFolder}/NPUGRAPH_SKILL/scripts" | ||
| 37 | + } | ||
| 38 | + }, | ||
| 39 | + "npu-smi": { | ||
| 40 | + "command": "python", | ||
| 41 | + "args": ["-m", "npu_mcp.npu_smi"], | ||
| 42 | + "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/scripts", | ||
| 43 | + "description": "自建 — npu-smi 命令封装,结构化设备状态查询" | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | +} | ||
| @@ -0,0 +1,142 @@ | |||
| 1 | +# MCP 服务器评估报告 — NPU 图模式开发 | ||
| 2 | + | ||
| 3 | +> 评估日期:2026-06-10 | ||
| 4 | +> 评估目标:筛选对 NPU 图模式开发/调试/性能分析最有价值的 MCP 服务器和开源工具 | ||
| 5 | + | ||
| 6 | +--- | ||
| 7 | + | ||
| 8 | +## 1. 当前 MCP 状态 | ||
| 9 | + | ||
| 10 | +| 项目 | 状态 | | ||
| 11 | +|------|------| | ||
| 12 | +| 全局 `claude.json` 中 MCP 配置 | **全部为空** — 未启用任何 MCP 服务器 | | ||
| 13 | +| 项目级 `.mcp.json` | **不存在** | | ||
| 14 | +| 可用但未启用的 MCP 插件 | 15 个(见下方) | | ||
| 15 | +| 远程连接机制 | SSH + openai.yaml(非 MCP,用于连接 NPU 远程服务器) | | ||
| 16 | + | ||
| 17 | +**结论**: 当前 NPU 图模式开发完全依赖 SKILL 文件 + Bash 工具,零 MCP 集成。这是最大的效率提升空间。 | ||
| 18 | + | ||
| 19 | +--- | ||
| 20 | + | ||
| 21 | +## 2. 可用 MCP 服务器评估 | ||
| 22 | + | ||
| 23 | +### 2.1 高价值(建议立即启用) | ||
| 24 | + | ||
| 25 | +| MCP 服务器 | 评分 | 用途 | NPU 图模式场景 | | ||
| 26 | +|------------|------|------|----------------| | ||
| 27 | +| **Context7** | ★★★★★ | 实时文档查询 | 查询 CANN API、PyTorch C++ 文档、ACL 头文件定义。避免手动查网页 | | ||
| 28 | +| **Playwright** | ★★★★☆ | 浏览器自动化 | 捕获 CANN Profiler Web UI 截图、操作 MSCCL 配置页面、导出性能报告 | | ||
| 29 | +| **Greptile** | ★★★★☆ | 代码库语义理解 | 理解 pytorch/ 仓库中 NPUGraph 调用链、追踪 op-plugin 注册关系 | | ||
| 30 | + | ||
| 31 | +### 2.2 中等价值(有条件启用) | ||
| 32 | + | ||
| 33 | +| MCP 服务器 | 评分 | 用途 | NPU 图模式场景 | | ||
| 34 | +|------------|------|------|----------------| | ||
| 35 | +| **GitHub** | ★★★☆☆ | Issue/PR 自动化 | 实际仓库在 gitcode.com,非 GitHub。但上游 PyTorch PR 分析可用 | | ||
| 36 | +| **Serena** | ★★★☆☆ | AI 编码代理 | 辅助重构 NPUGraph 代码、生成测试用例 | | ||
| 37 | +| **Linear** | ★★★☆☆ | 项目管理 | 如果团队用 Linear 跟踪 NPUGraph bug | | ||
| 38 | + | ||
| 39 | +### 2.3 低价值(不推荐) | ||
| 40 | + | ||
| 41 | +| MCP 服务器 | 原因 | | ||
| 42 | +|------------|------| | ||
| 43 | +| GitLab, Asana, Discord, Telegram, iMessage, FakeChat | 与 NPU 图开发无关 | | ||
| 44 | +| Firebase, Terraform, Laravel Boost | 不适用场景 | | ||
| 45 | +| Docker | CANN 环境通常直接安装在 NPU 服务器上,不容器化 | | ||
| 46 | + | ||
| 47 | +--- | ||
| 48 | + | ||
| 49 | +## 3. 推荐的自定义 MCP 设计(高优先级) | ||
| 50 | + | ||
| 51 | +现有通用 MCP 无法覆盖 NPU 图模式的核心需求,建议自建以下专用 MCP: | ||
| 52 | + | ||
| 53 | +### 3.1 NPU-Log-Parser MCP ★★★★★ | ||
| 54 | + | ||
| 55 | +``` | ||
| 56 | +功能:解析 TORCH_NPU_LOGS 输出,提取结构化信息 | ||
| 57 | +输入:日志文件路径 或 直接读取日志流 | ||
| 58 | +输出: | ||
| 59 | + - 按模块(aclgraph/memory/dispatch)分类的事件列表 | ||
| 60 | + - 时间线(capture_begin → capture_end → replay → execute) | ||
| 61 | + - 错误码 → 错误描述映射表 | ||
| 62 | + - 异常模式自动标注(如 "memcpy size mismatch", "aclmdlExecute failed") | ||
| 63 | +实现方式:Python stdio MCP server | ||
| 64 | +核心依赖:正则匹配引擎 + ACL 错误码表 | ||
| 65 | +``` | ||
| 66 | + | ||
| 67 | +### 3.2 Graph-Dump-Analyzer MCP ★★★★★ | ||
| 68 | + | ||
| 69 | +``` | ||
| 70 | +功能:解析 graph.debug_dump(path) 导出的 JSON,结构化分析 | ||
| 71 | +输入:JSON dump 文件路径 | ||
| 72 | +输出: | ||
| 73 | + - 图拓扑结构(节点数、边数、层数) | ||
| 74 | + - 算子类型分布统计 | ||
| 75 | + - 异常结构检测(孤立节点、环、超长链) | ||
| 76 | + - 与上次成功 dump 的 diff | ||
| 77 | +实现方式:Python stdio MCP server | ||
| 78 | +核心依赖:JSON schema 定义 + 图算法(networkx) | ||
| 79 | +``` | ||
| 80 | + | ||
| 81 | +### 3.3 NPU-SMI MCP ★★★★☆ | ||
| 82 | + | ||
| 83 | +``` | ||
| 84 | +功能:封装 npu-smi 命令,提供结构化设备状态查询 | ||
| 85 | +输入:查询类型(memory/process/temperature/power) | ||
| 86 | +输出:JSON 格式的设备状态 | ||
| 87 | +场景:调试 OOM 时自动查询内存占用;性能分析时监控功耗 | ||
| 88 | +实现方式:Python stdio MCP server,封装 npu-smi 命令行 | ||
| 89 | +``` | ||
| 90 | + | ||
| 91 | +### 3.4 CANN-Doc-Query MCP ★★★☆☆ | ||
| 92 | + | ||
| 93 | +``` | ||
| 94 | +功能:查询 CANN 离线文档(HTML/PDF),快速定位 API 说明 | ||
| 95 | +输入:API 名称(如 aclmdlRICaptureBegin) | ||
| 96 | +输出:函数签名、参数说明、返回值、错误码、示例代码 | ||
| 97 | +实现方式:Python MCP + 本地文档索引(meilisearch 或 simple grep) | ||
| 98 | +注意:如果 Context7 已能满足需求,此 MCP 优先级降低 | ||
| 99 | +``` | ||
| 100 | + | ||
| 101 | +--- | ||
| 102 | + | ||
| 103 | +## 4. 开源工具补充 | ||
| 104 | + | ||
| 105 | +这些不是 MCP,但对图模式开发有直接帮助: | ||
| 106 | + | ||
| 107 | +| 工具 | 用途 | 集成方式 | | ||
| 108 | +|------|------|----------| | ||
| 109 | +| **networkx** | 图拓扑分析 | Python 脚本 | | ||
| 110 | +| **rich** | 终端美化输出(日志高亮、进度条) | Python 脚本 | | ||
| 111 | +| **pydantic** | 结构化日志/Analytics 数据验证 | Python 脚本 | | ||
| 112 | +| **watchfiles** | 监控日志文件变化,实时触发分析 | Python 脚本 | | ||
| 113 | +| **graphviz** | 生成图捕获拓扑的 SVG 可视化 | Python 脚本 | | ||
| 114 | + | ||
| 115 | +--- | ||
| 116 | + | ||
| 117 | +## 5. 推荐实施优先级 | ||
| 118 | + | ||
| 119 | +``` | ||
| 120 | +Phase 1 (本周): | ||
| 121 | + ├── 启用 Context7 MCP | ||
| 122 | + └── 创建 NPU-Log-Parser MCP 原型 | ||
| 123 | + | ||
| 124 | +Phase 2 (本月): | ||
| 125 | + ├── 启用 Playwright MCP | ||
| 126 | + ├── 创建 Graph-Dump-Analyzer MCP | ||
| 127 | + └── 创建 NPU-SMI MCP | ||
| 128 | + | ||
| 129 | +Phase 3 (下月): | ||
| 130 | + ├── 启用 Greptile MCP | ||
| 131 | + ├── 评估 CANN-Doc-Query MCP 必要性 | ||
| 132 | + └── 建立 MCP 回归测试 | ||
| 133 | +``` | ||
| 134 | + | ||
| 135 | +--- | ||
| 136 | + | ||
| 137 | +## 6. 风险与注意事项 | ||
| 138 | + | ||
| 139 | +1. **Context7 可能无法访问 CANN 内部文档** — CANN 文档多为离线 HTML,Context7 可能索引不到。需要验证。 | ||
| 140 | +2. **NPU 环境隔离** — MCP server 运行在开发机上,但 NPU 在远程服务器上。需要通过 SSH 转发或 API 代理解决。 | ||
| 141 | +3. **日志量级** — `TORCH_NPU_LOGS=+aclgraph,+dispatch` 输出可达 GB 级,日志解析器需要流式处理。 | ||
| 142 | +4. **安全** — `npu-smi` 查询包含硬件信息,MCP 接口需限制暴露范围。 | ||
| @@ -0,0 +1,84 @@ | |||
| 1 | +# MCP 安装配置指南 | ||
| 2 | + | ||
| 3 | +## 前提条件 | ||
| 4 | + | ||
| 5 | +- Claude Code CLI 已安装 | ||
| 6 | +- Python 3.10+ (自建 MCP 需要) | ||
| 7 | +- Node.js 18+ (Context7, Playwright 需要) | ||
| 8 | + | ||
| 9 | +## 启用通用 MCP | ||
| 10 | + | ||
| 11 | +### Context7 — 推荐最先启用 | ||
| 12 | + | ||
| 13 | +在 Claude Code 对话中: | ||
| 14 | +``` | ||
| 15 | +/add-mcp context7 | ||
| 16 | +``` | ||
| 17 | + | ||
| 18 | +或手动创建项目根目录 `.mcp.json`: | ||
| 19 | +```json | ||
| 20 | +{ | ||
| 21 | + "mcpServers": { | ||
| 22 | + "context7": { | ||
| 23 | + "command": "npx", | ||
| 24 | + "args": ["-y", "@upstash/context7-mcp"] | ||
| 25 | + } | ||
| 26 | + } | ||
| 27 | +} | ||
| 28 | +``` | ||
| 29 | + | ||
| 30 | +验证: | ||
| 31 | +``` | ||
| 32 | +帮我用 Context7 查询 PyTorch torch.cuda.CUDAGraph API | ||
| 33 | +``` | ||
| 34 | + | ||
| 35 | +### Playwright | ||
| 36 | + | ||
| 37 | +``` | ||
| 38 | +/add-mcp playwright | ||
| 39 | +``` | ||
| 40 | + | ||
| 41 | +用于操作 CANN Profiler Web 页面。 | ||
| 42 | + | ||
| 43 | +### Greptile(可选,需要 token) | ||
| 44 | + | ||
| 45 | +需要先注册 https://greptile.com 获取 API token。 | ||
| 46 | + | ||
| 47 | +## 自建 MCP 安装 | ||
| 48 | + | ||
| 49 | +### NPU-Log-Parser | ||
| 50 | + | ||
| 51 | +```bash | ||
| 52 | +# 1. 安装依赖 | ||
| 53 | +pip install mcp | ||
| 54 | + | ||
| 55 | +# 2. 测试运行 | ||
| 56 | +python NPUGRAPH_SKILL/scripts/parse_npu_logs.py --test | ||
| 57 | + | ||
| 58 | +# 3. 配置为 MCP server | ||
| 59 | +# 在 .mcp.json 中添加 | ||
| 60 | +{ | ||
| 61 | + "npu-log-parser": { | ||
| 62 | + "command": "python", | ||
| 63 | + "args": ["-m", "npu_mcp.log_parser"], | ||
| 64 | + "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/MCP" | ||
| 65 | + } | ||
| 66 | +} | ||
| 67 | +``` | ||
| 68 | + | ||
| 69 | +### Graph-Dump-Analyzer | ||
| 70 | + | ||
| 71 | +TODO: v0.2.0 | ||
| 72 | + | ||
| 73 | +### NPU-SMI | ||
| 74 | + | ||
| 75 | +TODO: v0.3.0 | ||
| 76 | + | ||
| 77 | +## 故障排查 | ||
| 78 | + | ||
| 79 | +| 问题 | 解决方案 | | ||
| 80 | +|------|---------| | ||
| 81 | +| `npx` 不可用 | 安装 Node.js 18+ | | ||
| 82 | +| Python `mcp` 库找不到 | `pip install mcp` | | ||
| 83 | +| Context7 查询不到 CANN 文档 | CANN 是离线文档,Context7 可能无法索引 | | ||
| 84 | +| MCP server 超时 | 检查网络代理,MCP 默认 60s 超时 | | ||
| @@ -0,0 +1,166 @@ | |||
| 1 | +--- | ||
| 2 | +name: npu-graph-mode-diagnostics | ||
| 3 | +description: >- | ||
| 4 | + Use when user reports NPU graph mode errors, crashes, or exceptions — | ||
| 5 | + capture failure (aclmdlRICaptureBegin), replay failure (aclmdlRIExecuteAsync), | ||
| 6 | + graph OOM, dynamic shape update failure, graph_breaks, compile hang, | ||
| 7 | + or any error with [NPUGRAPH]/ACLGraph tags. | ||
| 8 | + Covers all four enablement modes (NPUGraph/graph()/make_graphed_callables/torch.compile). | ||
| 9 | + NOT for performance analysis or log-only analysis — handoff to respective skills. | ||
| 10 | +--- | ||
| 11 | + | ||
| 12 | +# Graph Mode Diagnostics — 图模式问题诊断 | ||
| 13 | + | ||
| 14 | +## The Iron Law | ||
| 15 | + | ||
| 16 | +``` | ||
| 17 | +在未完成 L5→L1 层级定位并识别到具体失败点之前,禁止提出修复方案。 | ||
| 18 | +先定位,再修复。永远是先定位,再修复。 | ||
| 19 | +``` | ||
| 20 | + | ||
| 21 | +--- | ||
| 22 | + | ||
| 23 | +## When to Use | ||
| 24 | + | ||
| 25 | +| 症状 | 错误特征 | | ||
| 26 | +|------|----------| | ||
| 27 | +| 图捕获阶段报错 | `aclmdlRICaptureBegin` / `capture_begin()` 失败 | | ||
| 28 | +| 图重放阶段报错 | `aclmdlRIExecuteAsync` / `replay()` 失败 | | ||
| 29 | +| 图内 OOM | `NPUCachingAllocator` / `aclrtMalloc` 失败 | | ||
| 30 | +| 动态形状 update 失败 | `update_graph()` / `NPUGraphUpdateError` | | ||
| 31 | +| graph_breaks 异常多 | Dynamo 图断裂数超出预期 | | ||
| 32 | +| 图编译 hang | `torch.compile(backend="npugraphs")` 卡死 | | ||
| 33 | + | ||
| 34 | +**领域知识(五层架构、源文件、使能方式、约束)→ [domain-context.md](references/domain-context.md)** | ||
| 35 | + | ||
| 36 | +--- | ||
| 37 | + | ||
| 38 | +## Phase 1: 分类 — 确定问题阶段 | ||
| 39 | + | ||
| 40 | +``` | ||
| 41 | +┌─ 错误发生在哪个阶段? | ||
| 42 | +│ | ||
| 43 | +├── 捕获阶段 (Capture) ──→ Phase 2A 特征:aclmdlRICaptureBegin/End 失败 | ||
| 44 | +├── 重放阶段 (Replay) ──→ Phase 2B 特征:aclmdlRIExecuteAsync 失败 | ||
| 45 | +├── 内存阶段 (Memory) ──→ Phase 2C 特征:OOM / allocator 错误 | ||
| 46 | +├── Update 阶段 ──→ Phase 2D 特征:update_graph() 失败 | ||
| 47 | +└── 编译阶段 (Compile) ──→ Phase 2E 特征:torch.compile 卡死 / Dynamo 错误 | ||
| 48 | +``` | ||
| 49 | + | ||
| 50 | +**检查清单**: | ||
| 51 | +- [ ] 记录完整错误栈(Python + C++) | ||
| 52 | +- [ ] 记录 `TORCH_NPU_LOGS` 输出(至少 `aclgraph` 模块) | ||
| 53 | +- [ ] 确认使能方式(1/2/3/4) | ||
| 54 | +- [ ] 确认 CANN 版本和 NPU 型号 | ||
| 55 | + | ||
| 56 | +--- | ||
| 57 | + | ||
| 58 | +## Phase 2: 分阶段诊断 | ||
| 59 | + | ||
| 60 | +### 2A — 捕获阶段 | ||
| 61 | + | ||
| 62 | +**先检查常见原因**:图内 CPU 同步操作(`.item()`, `.cpu()`, `print()`)→ 条件分支 → 嵌套捕获 → Stream 异常 | ||
| 63 | + | ||
| 64 | +**定位层级**: | ||
| 65 | +- L4 (Python): `graphs.py` capture_begin/end 逻辑、LRU cache、graph pool | ||
| 66 | +- L2 (C++): `NPUGraph.cpp` ACLGraph 包装、ACL 状态机 | ||
| 67 | +- L1 (ACL): `aclmdlRICaptureBegin` 返回值、CANN 兼容性 | ||
| 68 | + | ||
| 69 | +**获取诊断信息**: | ||
| 70 | +```bash | ||
| 71 | +export TORCH_NPU_LOGS=+aclgraph,+dispatch | ||
| 72 | +export TORCH_NPU_GRAPHS_LOG_LEVEL=DEBUG | ||
| 73 | +export TORCH_NPUGRAPH_GC=1 | ||
| 74 | +``` | ||
| 75 | + | ||
| 76 | +### 2B — 重放阶段 | ||
| 77 | + | ||
| 78 | +**三连问**:图是否正确捕获?→ 是否需要 update?→ 图是否被意外释放(LRU/reset/跨线程)? | ||
| 79 | + | ||
| 80 | +**输入验证**:检查 shape/stride/dtype/device/contiguous 与捕获时一致。 | ||
| 81 | + | ||
| 82 | +**ACL 诊断**:`g.debug_dump()` 导出图结构,对照 ACL 错误码。 | ||
| 83 | + | ||
| 84 | +### 2C — 内存阶段 | ||
| 85 | + | ||
| 86 | +**四连问**:捕获期间分配了新内存?→ 内存池耗尽?→ 碎片化?→ 跨 Stream 内存访问? | ||
| 87 | + | ||
| 88 | +### 2D — Update 阶段 | ||
| 89 | + | ||
| 90 | +**前提检查**:TND + keep_prob < 1.0 → 必须 fallback。CPU tensor 参数、lvalue 传递。 | ||
| 91 | + | ||
| 92 | +**常见原因**:shape 超出预留空间 → dtype 不匹配 → 非法参数 → 并发竞态。 | ||
| 93 | + | ||
| 94 | +### 2E — 编译阶段 | ||
| 95 | + | ||
| 96 | +```bash | ||
| 97 | +TORCH_LOGS=graph_breaks python script.py # 查看图断裂原因 | ||
| 98 | +torch._dynamo.explain(model)(inputs) # Dynamo 分析 | ||
| 99 | +torch.compile(backend="aot_eager") # 绕过图优化定位 | ||
| 100 | +``` | ||
| 101 | + | ||
| 102 | +--- | ||
| 103 | + | ||
| 104 | +## Phase 3: 模式匹配 | ||
| 105 | + | ||
| 106 | +先查 [known-patterns.md](references/known-patterns.md)(14+ 模式)再深入源码: | ||
| 107 | +- 捕获: 4 个模式 | 重放: 5 个 | 内存: 3 个 | 性能: 2 个 | ||
| 108 | + | ||
| 109 | +## Phase 4: 深度追踪 | ||
| 110 | + | ||
| 111 | +已知模式无法匹配时,按路径深入: | ||
| 112 | +- Python: `graphs.py` → `_graph_tree.py` → `utils/_graph_tree.py` | ||
| 113 | +- C++: `NPUGraph.cpp` → `NPUGraph.h` | ||
| 114 | +- ACL: CANN headers + `/var/log/npu/` 驱动日志 | ||
| 115 | + | ||
| 116 | +## Phase 5: 报告 | ||
| 117 | + | ||
| 118 | +```markdown | ||
| 119 | +## 问题诊断报告 | ||
| 120 | +### 1. 问题概况 | ||
| 121 | +- 错误信息/阶段/层级/严重程度 | ||
| 122 | +### 2. 根因分析 | ||
| 123 | +- 直接原因/触发条件/影响范围 | ||
| 124 | +### 3. 修复建议 | ||
| 125 | +- 短期/长期方案 + 文件定位 | ||
| 126 | +### 4. 验证步骤 | ||
| 127 | +``` | ||
| 128 | + | ||
| 129 | +--- | ||
| 130 | + | ||
| 131 | +## Red Flags | ||
| 132 | + | ||
| 133 | +| 症状 | 误区 | 正确做法 | | ||
| 134 | +|------|------|----------| | ||
| 135 | +| 报错含 "memory" | 直接加 `empty_cache()` | 先查分配栈 | | ||
| 136 | +| 捕获阶段失败 | 直接改 op 实现 | 先确认 op 是否在 allowlist | | ||
| 137 | +| shape mismatch | 直接增大预留空间 | 先确认 update 调用是否正确 | | ||
| 138 | +| 偶尔成功偶尔失败 | 认为是硬件不稳定 | 检查竞态(LRU/多线程/Stream) | | ||
| 139 | +| torch.compile hang | 强行 kill | 先 dump Dynamo 中间状态 | | ||
| 140 | + | ||
| 141 | +## Handoff | ||
| 142 | + | ||
| 143 | +| 条件 | 移交技能 | | ||
| 144 | +|------|----------| | ||
| 145 | +| 确认是缺失功能 | `requirements` | | ||
| 146 | +| 根因确认需修代码 | `implementation` | | ||
| 147 | +| 修复完成需验证 | `verification` | | ||
| 148 | +| 性能相关(非功能 bug) | `NPU-graph-performance-profiling` | | ||
| 149 | +| 日志量太大需自动分析 | `NPU-graph-log-analyzer` | | ||
| 150 | +| MCP 工具配置 | `NPU-graph-mcp-integration` | | ||
| 151 | + | ||
| 152 | +--- | ||
| 153 | + | ||
| 154 | +## DFX: 知识沉淀 | ||
| 155 | + | ||
| 156 | +诊断完成并输出 Phase 5 报告后,**必须执行**: | ||
| 157 | + | ||
| 158 | +1. **问用户**:「是否将此诊断案例记录到知识库?[Y/n]」 | ||
| 159 | +2. 如 Y → 按 [../knowledge/CASE_TEMPLATE.md](../knowledge/CASE_TEMPLATE.md) 格式生成条目,包含: | ||
| 160 | + - 症状原文(从 Phase 1 检查清单提取) | ||
| 161 | + - 层级定位(从 Phase 4 深度追踪提取) | ||
| 162 | + - 根因 + 解决方案(从 Phase 5 报告提取) | ||
| 163 | +3. 追加到 [../knowledge/history.md](../knowledge/history.md) 对应分类下 | ||
| 164 | +4. 更新分类总览计数 | ||
| 165 | +5. 如该案例匹配了 known-patterns.md 中的模式 → 在条目中标注关联模式编号 | ||
| 166 | +6. 如该案例**不匹配**任何已知模式 → 提示用户:「发现新问题模式,是否同步更新 known-patterns.md?」 | ||
| @@ -0,0 +1,70 @@ | |||
| 1 | +# 图模式诊断检查清单 | ||
| 2 | + | ||
| 3 | +> 每次诊断必须完成以下检查项,不可跳过。 | ||
| 4 | + | ||
| 5 | +--- | ||
| 6 | + | ||
| 7 | +## Phase 1: 分类 | ||
| 8 | + | ||
| 9 | +- [ ] 记录完整错误栈(Python traceback + C++ 层错误码) | ||
| 10 | +- [ ] 记录 TORCH_NPU_LOGS 输出片段(至少前后 50 行) | ||
| 11 | +- [ ] 确认使能方式(NPUGraph / graph() / make_graphed_callables / torch.compile) | ||
| 12 | +- [ ] 确认 CANN 版本、PyTorch 版本、NPU 型号 | ||
| 13 | +- [ ] 确定问题阶段(Capture / Replay / Memory / Update / Compile) | ||
| 14 | + | ||
| 15 | +## Phase 2: 阶段诊断 | ||
| 16 | + | ||
| 17 | +### 捕获阶段 | ||
| 18 | +- [ ] 检查图内有无 .item() / .cpu() / print(tensor) | ||
| 19 | +- [ ] 检查有无 data-dependent 条件分支 | ||
| 20 | +- [ ] 检查是否嵌套捕获 | ||
| 21 | +- [ ] 检查 Stream 是否正确 | ||
| 22 | +- [ ] 检查是否首次运行触发 lazy init | ||
| 23 | +- [ ] 用 `TORCH_LOGS=graph_breaks` 确认图完整性 | ||
| 24 | + | ||
| 25 | +### 重放阶段 | ||
| 26 | +- [ ] 检查输入 shape/stride/dtype 与捕获时一致性 | ||
| 27 | +- [ ] 检查设备(device)是否正确 | ||
| 28 | +- [ ] 检查内存连续性(contiguous) | ||
| 29 | +- [ ] 检查是否需要 update_graph() | ||
| 30 | +- [ ] 检查图是否被 LRU 淘汰 | ||
| 31 | +- [ ] 检查图对象生命周期 | ||
| 32 | +- [ ] 检查 Graph Pool 状态 | ||
| 33 | +- [ ] 用 `debug_dump()` 导出图结构 | ||
| 34 | + | ||
| 35 | +### 内存阶段 | ||
| 36 | +- [ ] 检查 `torch.npu.memory_allocated()` 和 `max_memory_allocated()` | ||
| 37 | +- [ ] 检查 `memory_reserved()` vs `memory_allocated()` | ||
| 38 | +- [ ] 检查 NPUCachingAllocator 统计信息 | ||
| 39 | +- [ ] 确认 empty_cache() 是否会影响 HCCL | ||
| 40 | +- [ ] 检查跨 Stream 内存占用 | ||
| 41 | + | ||
| 42 | +### Update 阶段 | ||
| 43 | +- [ ] 检查 TND + Dropout 是否已 fallback | ||
| 44 | +- [ ] 检查 CPU Tensor 参数正确性 | ||
| 45 | +- [ ] 检查 seed/offset shape (1x1) | ||
| 46 | +- [ ] 检查 C++ 宏参数是否 lvalue | ||
| 47 | + | ||
| 48 | +### 编译阶段 | ||
| 49 | +- [ ] 检查 graph_breaks 数量和是否合理 | ||
| 50 | +- [ ] 用 `torch._dynamo.explain()` 分析 | ||
| 51 | +- [ ] 尝试 `backend="aot_eager"` 对比 | ||
| 52 | + | ||
| 53 | +## Phase 3: 模式匹配 | ||
| 54 | + | ||
| 55 | +- [ ] 检查 [known-patterns.md](known-patterns.md) 找到匹配模式 | ||
| 56 | +- [ ] 检查 [error-code-map.md](error-code-map.md) 定位错误码 | ||
| 57 | + | ||
| 58 | +## Phase 4: 深度追踪 | ||
| 59 | + | ||
| 60 | +- [ ] Python 层:`graphs.py` / `_graph_tree.py` 源码 | ||
| 61 | +- [ ] C++ 层:`NPUGraph.cpp` 源码 | ||
| 62 | +- [ ] ACL 层:CANN 文档 | ||
| 63 | +- [ ] 检查是否有相关 issue/PR | ||
| 64 | + | ||
| 65 | +## Phase 5: 报告 | ||
| 66 | + | ||
| 67 | +- [ ] 填写问题概况(阶段、层级、严重程度) | ||
| 68 | +- [ ] 明确根因(直接原因 + 触发条件) | ||
| 69 | +- [ ] 提出修复方案(短期 + 长期) | ||
| 70 | +- [ ] 编写验证步骤 | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | +# 图模式领域知识 | ||
| 2 | + | ||
| 3 | +> 从 `SKILL.md` Domain Context 拆分,诊断时按需查阅。 | ||
| 4 | + | ||
| 5 | +## 五层架构 | ||
| 6 | + | ||
| 7 | +``` | ||
| 8 | +L5 用户代码 model.forward(), 训练循环, torch.compile 包装 | ||
| 9 | +L4 Python API npu/graphs.py, npu/_graph_tree.py, utils/_graph_tree.py | ||
| 10 | +L3 Graph Tree NPUGraphTreeManager, NPUGraphNode, NPUWarmupNode | ||
| 11 | +L2 C++ 核心 NPUGraph.cpp/h, NPUCachingAllocator, aclmdlRICaptureBegin/End | ||
| 12 | +L1 ACL 运行时 ACL DVPP, ACL Runtime, CANN 驱动层 | ||
| 13 | +L0 NPU 硬件/驱动 Ascend 910B/910B2/910C 硬件, 固件, HCCL | ||
| 14 | +``` | ||
| 15 | + | ||
| 16 | +## 关键源文件 | ||
| 17 | + | ||
| 18 | +| 文件 | 路径(相对 pytorch/torch_npu/) | | ||
| 19 | +|------|-------------------------------| | ||
| 20 | +| NPUGraph Python API | `npu/graphs.py` | | ||
| 21 | +| Graph Tree 管理 | `npu/_graph_tree.py` | | ||
| 22 | +| Graph Tree 工具 | `utils/_graph_tree.py` | | ||
| 23 | +| C++ NPUGraph 核心 | `csrc/core/npu/NPUGraph.h`, `NPUGraph.cpp` | | ||
| 24 | +| C++ 内存管理 | `csrc/core/npu/NPUCachingAllocator.h` | | ||
| 25 | +| FA3 图处理 | `npu/_npugraph_handlers/_fa3_graph_handler.py` | | ||
| 26 | +| Inductor 图分区 | `_inductor/npu_fusion_attention_graph.py` | | ||
| 27 | + | ||
| 28 | +## 四种使能方式 | ||
| 29 | + | ||
| 30 | +``` | ||
| 31 | +方式1: torch_npu.npu.NPUGraph → 底层 API,手动 capture_begin/end | ||
| 32 | +方式2: torch_npu.npu.graph(g) → 上下文管理器,自动 Stream 同步 | ||
| 33 | +方式3: torch_npu.npu.make_graphed_callables → 高级封装,安全子图 | ||
| 34 | +方式4: torch.compile(backend="npugraphs") → Dynamo + NPUGraph Tree | ||
| 35 | +``` | ||
| 36 | + | ||
| 37 | +## 关键约束速查 | ||
| 38 | + | ||
| 39 | +1. **图捕获期间不能有 CPU-GPU 同步**(`.item()`, `.cpu()`, `print(tensor)`) | ||
| 40 | +2. **动态形状**需要 update 机制,非所有 op 支持 | ||
| 41 | +3. **TND + Dropout** (`keep_prob < 1.0`) 必须 fallback | ||
| 42 | +4. **捕获中禁止分配新 NPU 内存**,必须预分配 | ||
| 43 | +5. **Stream 隔离**:捕获和重放必须在同一 Stream | ||
| @@ -0,0 +1,40 @@ | |||
| 1 | +# ACL 错误码 → 根因映射 | ||
| 2 | + | ||
| 3 | +> 快速查询 ACL 运行时错误码的含义和最可能根因 | ||
| 4 | +> 基于 CANN 7.0+ 错误码表 | ||
| 5 | + | ||
| 6 | +--- | ||
| 7 | + | ||
| 8 | +## ACL Runtime 错误 (ACL_ERROR_RT_*) | ||
| 9 | + | ||
| 10 | +| 错误码 | 宏 | 含义 | NPU 图模式常见场景 | | ||
| 11 | +|--------|-----|------|-------------------| | ||
| 12 | +| `0x100000` | `ACL_ERROR_RT_MODEL_EXECUTE_FAILED` | 模型执行失败 | 图重放失败(最常见),检查图是否已正确编译 | | ||
| 13 | +| `0x100001` | `ACL_ERROR_RT_INVALID_DEVICE` | 无效设备 | device_id 错误,或 NPU 未就绪 | | ||
| 14 | +| `0x100002` | `ACL_ERROR_RT_MEMORY_ALLOC_FAILED` | 内存分配失败 | 图内 OOM | | ||
| 15 | +| `0x100003` | `ACL_ERROR_RT_STREAM_CONTEXT` | Stream 上下文错误 | 捕获和重放 Stream 不一致 | | ||
| 16 | +| `0x100004` | `ACL_ERROR_RT_MODEL_NOT_INIT` | 模型未初始化 | 图未编译就 replay | | ||
| 17 | + | ||
| 18 | +## ACL Graph 错误 (ACL_ERROR_GE_*) | ||
| 19 | + | ||
| 20 | +| 错误码 | 宏 | 含义 | NPU 图模式常见场景 | | ||
| 21 | +|--------|-----|------|-------------------| | ||
| 22 | +| `0x200000` | `ACL_ERROR_GE_DEVICE_MEMORY_ALLOC_FAILED` | GE 设备内存分配失败 | 图编译时内存不足 | | ||
| 23 | +| `0x200001` | `ACL_ERROR_GE_EXECUTOR_NOT_INIT` | 执行器未初始化 | 图编译未完成就尝试执行 | | ||
| 24 | +| `0x200002` | `ACL_ERROR_GE_GRAPH_NOT_INIT` | 图未初始化 | capture 未完成就调用 replay | | ||
| 25 | +| `0x200003` | `ACL_ERROR_GE_PARAM_INVALID` | 参数无效 | update 时 shape/stride 参数非法 | | ||
| 26 | + | ||
| 27 | +## 快速查询流程 | ||
| 28 | + | ||
| 29 | +``` | ||
| 30 | +看到错误码 → | ||
| 31 | + 1. 查本表找到对应宏 | ||
| 32 | + 2. 查看 NPU 图模式常见场景列 | ||
| 33 | + 3. 如匹配 → 跳转到 graph-mode-diagnostics 对应 Phase | ||
| 34 | + 4. 如不匹配 → 查 CANN 官方文档 | ||
| 35 | +``` | ||
| 36 | + | ||
| 37 | +## 备注 | ||
| 38 | + | ||
| 39 | +- 错误码会随 CANN 版本变化,以运行环境的 `acl.h` 为准 | ||
| 40 | +- 完整错误码表参见 CANN 安装目录 `include/acl.h` 和 `include/ge/ge_error_codes.h` | ||
| @@ -0,0 +1,212 @@ | |||
| 1 | +# 已知问题模式 — NPU 图模式 | ||
| 2 | + | ||
| 3 | +> 继承自 `.npugraphs/skills/debugging/known-patterns.md`,扩展为 14+ 个模式 | ||
| 4 | +> 最后更新: 2026-06-10 | ||
| 5 | + | ||
| 6 | +--- | ||
| 7 | + | ||
| 8 | +## 捕获阶段 (Capture Phase) | ||
| 9 | + | ||
| 10 | +### P-CAP-001: 图内 CPU 同步操作 | ||
| 11 | + | ||
| 12 | +**症状**: `capture_begin()` 后某个 op 触发 `RuntimeError: Cannot .item() during graph capture` | ||
| 13 | + | ||
| 14 | +**根因**: `.item()`, `.cpu()`, `print(tensor)`, `.numpy()` 等触发 CPU-GPU/NPU 同步,ACLGraph 不允许。 | ||
| 15 | + | ||
| 16 | +**检测**: | ||
| 17 | +```python | ||
| 18 | +# 在 capture 代码块中搜索 | ||
| 19 | +grep -n "\.item()\|\.cpu()\|print(" model.py | ||
| 20 | +``` | ||
| 21 | + | ||
| 22 | +**修复**: 将同步操作移到图外,或用图内等效操作替代。 | ||
| 23 | + | ||
| 24 | +--- | ||
| 25 | + | ||
| 26 | +### P-CAP-002: 嵌套捕获 | ||
| 27 | + | ||
| 28 | +**症状**: `RuntimeError: Capture already in progress` | ||
| 29 | + | ||
| 30 | +**根因**: ACLGraph 不支持嵌套捕获。在已开始捕获的 Stream 上再次 `capture_begin()`。 | ||
| 31 | + | ||
| 32 | +**常见触发**: | ||
| 33 | +- `make_graphed_callables` 内部嵌套 | ||
| 34 | +- torch.compile 递归触发图捕获 | ||
| 35 | +- 多线程同时捕获 | ||
| 36 | + | ||
| 37 | +--- | ||
| 38 | + | ||
| 39 | +### P-CAP-003: 图内条件分支 | ||
| 40 | + | ||
| 41 | +**症状**: graph_breaks 异常多,或捕获结果与预期不符 | ||
| 42 | + | ||
| 43 | +**根因**: `if x > 0: y = f(x) else: y = g(x)` 在图捕获中只走一个分支,后续重放不会切换。 | ||
| 44 | + | ||
| 45 | +**检测**: | ||
| 46 | +```bash | ||
| 47 | +TORCH_LOGS=graph_breaks python script.py | ||
| 48 | +``` | ||
| 49 | + | ||
| 50 | +--- | ||
| 51 | + | ||
| 52 | +### P-CAP-004: Stream 不匹配 | ||
| 53 | + | ||
| 54 | +**症状**: `capture_begin()` 后 op 不在同一 Stream 执行 | ||
| 55 | + | ||
| 56 | +**根因**: 多 Stream 场景下,capture 在 Stream A 开始,但某个 op 在 Stream B 运行。 | ||
| 57 | + | ||
| 58 | +--- | ||
| 59 | + | ||
| 60 | +### P-CAP-005: 首次运行触发的 lazy init | ||
| 61 | + | ||
| 62 | +**症状**: 捕获阶段触发了 NPU 内存分配 | ||
| 63 | + | ||
| 64 | +**根因**: 某些 op 首次调用时做 lazy initialization(如 weight init, kernel cache build),触发 `aclrtMalloc`。ACLGraph 在捕获期间禁止新内存分配。 | ||
| 65 | + | ||
| 66 | +**修复**: 在捕获前先 eager 跑一次触发所有 lazy init。 | ||
| 67 | + | ||
| 68 | +--- | ||
| 69 | + | ||
| 70 | +## 重放阶段 (Replay Phase) | ||
| 71 | + | ||
| 72 | +### P-REP-001: 输入 Shape 不匹配 | ||
| 73 | + | ||
| 74 | +**症状**: `RuntimeError: Graph input shape mismatch` | ||
| 75 | + | ||
| 76 | +**根因**: 重放时输入的 shape/stride 与捕获时不一致,且未调用 `update_graph()`。 | ||
| 77 | + | ||
| 78 | +**检测**: | ||
| 79 | +```python | ||
| 80 | +# 对比捕获时和重放时的 shape | ||
| 81 | +print(g.captured_input_meta) | ||
| 82 | +print(x.shape, x.stride()) | ||
| 83 | +``` | ||
| 84 | + | ||
| 85 | +--- | ||
| 86 | + | ||
| 87 | +### P-REP-002: 图已被 LRU 淘汰 | ||
| 88 | + | ||
| 89 | +**症状**: `RuntimeError: Graph has been evicted` 或 `aclmdlRIExecuteAsync failed with error 0x...` | ||
| 90 | + | ||
| 91 | +**根因**: Graph pool 有 LRU 限制,长期不用的图被淘汰。 | ||
| 92 | + | ||
| 93 | +**检测**: 日志中出现 `[NPUGRAPH][TreeManager] evicting graph key=<hash>` | ||
| 94 | + | ||
| 95 | +--- | ||
| 96 | + | ||
| 97 | +### P-REP-003: 图生命周期结束 | ||
| 98 | + | ||
| 99 | +**症状**: `RuntimeError: Graph object has been destroyed` | ||
| 100 | + | ||
| 101 | +**根因**: Python 端 NPUGraph 对象被 GC/del,但 C++ 端 ACLGraph 仍在使用。 | ||
| 102 | + | ||
| 103 | +--- | ||
| 104 | + | ||
| 105 | +### P-REP-004: 并发重放冲突 | ||
| 106 | + | ||
| 107 | +**症状**: 偶尔成功偶尔失败,无固定模式 | ||
| 108 | + | ||
| 109 | +**根因**: 多线程同时 replay 同一个图,ACLGraph 不支持并发重放。 | ||
| 110 | + | ||
| 111 | +--- | ||
| 112 | + | ||
| 113 | +### P-REP-005: ACL 执行超时 | ||
| 114 | + | ||
| 115 | +**症状**: `aclmdlRIExecuteAsync` 超时(hang) | ||
| 116 | + | ||
| 117 | +**根因**: | ||
| 118 | +- 图内有同步等待(如 HCCL allreduce 未完成) | ||
| 119 | +- ACL 任务调度死锁 | ||
| 120 | +- NPU 硬件异常 | ||
| 121 | + | ||
| 122 | +--- | ||
| 123 | + | ||
| 124 | +## 内存阶段 (Memory Phase) | ||
| 125 | + | ||
| 126 | +### P-MEM-001: 图内内存分配 | ||
| 127 | + | ||
| 128 | +**症状**: 捕获阶段 `aclrtMalloc` 失败 | ||
| 129 | + | ||
| 130 | +**根因**: 参见 P-CAP-005(lazy init) | ||
| 131 | + | ||
| 132 | +--- | ||
| 133 | + | ||
| 134 | +### P-MEM-002: 内存碎片化 | ||
| 135 | + | ||
| 136 | +**症状**: `memory_allocated` 不大但 `aclrtMalloc` 失败 | ||
| 137 | + | ||
| 138 | +**根因**: NPUCachingAllocator 池碎片化,大块连续内存不足。 | ||
| 139 | + | ||
| 140 | +**检测**: | ||
| 141 | +```python | ||
| 142 | +print(torch.npu.memory_stats()) | ||
| 143 | +``` | ||
| 144 | + | ||
| 145 | +--- | ||
| 146 | + | ||
| 147 | +### P-MEM-003: 跨 Stream 内存不共享 | ||
| 148 | + | ||
| 149 | +**症状**: 两个 Stream 上的图各自占用大量内存,总量接近上限 | ||
| 150 | + | ||
| 151 | +**根因**: NPU 内存池默认按 Stream 隔离。 | ||
| 152 | + | ||
| 153 | +--- | ||
| 154 | + | ||
| 155 | +### P-MEM-004: empty_cache + HCCL UnmapMem 崩溃 | ||
| 156 | + | ||
| 157 | +**症状**: `torch.npu.empty_cache()` 后程序 SIGSEGV | ||
| 158 | + | ||
| 159 | +**根因**: 详见 `docs/analysis/empty_cache_hccl_unmap_crash.md`。HCCL 通信内存被 empty_cache 释放后,HCCL 仍尝试访问。 | ||
| 160 | + | ||
| 161 | +--- | ||
| 162 | + | ||
| 163 | +## Update 阶段 (Update Phase) | ||
| 164 | + | ||
| 165 | +### P-UPD-001: TND + Dropout Fallback 缺失 | ||
| 166 | + | ||
| 167 | +**症状**: FA3 图 update 失败,`keep_prob < 1.0` | ||
| 168 | + | ||
| 169 | +**根因**: Training dropout 场景下 TND 不支持 ACLGraph update。 | ||
| 170 | + | ||
| 171 | +--- | ||
| 172 | + | ||
| 173 | +### P-UPD-002: CPU Tensor 参数错误 | ||
| 174 | + | ||
| 175 | +**症状**: `actual_seq_qlen`/`actual_seq_kvlen` 不在 CPU 上 | ||
| 176 | + | ||
| 177 | +**根因**: Update 参数要求 CPU Tensor 传入。 | ||
| 178 | + | ||
| 179 | +--- | ||
| 180 | + | ||
| 181 | +### P-UPD-003: Dropout Seed/Offset shape 错误 | ||
| 182 | + | ||
| 183 | +**症状**: `seed`/`offset` 参数 shape 不是 `[1]` 或 `[1, 1]` | ||
| 184 | + | ||
| 185 | +**根因**: ACL FA 接口要求 1x1 Tensor。 | ||
| 186 | + | ||
| 187 | +--- | ||
| 188 | + | ||
| 189 | +## 编译阶段 (Compile Phase) | ||
| 190 | + | ||
| 191 | +### P-COM-001: Inductor 图分区异常 | ||
| 192 | + | ||
| 193 | +**症状**: `torch.compile(backend="npugraphs")` 分区结果不合理 | ||
| 194 | + | ||
| 195 | +**根因**: Inductor 的 `npu_fusion_attention_graph.py` 分区逻辑与模型不匹配。 | ||
| 196 | + | ||
| 197 | +--- | ||
| 198 | + | ||
| 199 | +### P-COM-002: Dynamo graph_break 过多 | ||
| 200 | + | ||
| 201 | +**症状**: 期望单个子图的模型,实际产生 20+ 个子图 | ||
| 202 | + | ||
| 203 | +**根因**: 模型中有 Dynamo 不支持的 Python 结构(data-dependent control flow 等)。 | ||
| 204 | + | ||
| 205 | +--- | ||
| 206 | + | ||
| 207 | +## 使用说明 | ||
| 208 | + | ||
| 209 | +1. 先按阶段(Capture / Replay / Memory / Update / Compile)缩小范围 | ||
| 210 | +2. 再按症状匹配模式 ID | ||
| 211 | +3. 匹配到模式后,直接按"修复"步骤操作 | ||
| 212 | +4. 未匹配的,进入 `graph-mode-diagnostics` Phase 4 深度追踪 | ||
| @@ -0,0 +1,155 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +""" | ||
| 3 | +图 Dump 分析器 — 解析 graph.debug_dump() 导出的 ACL 图 JSON | ||
| 4 | + | ||
| 5 | +用法: | ||
| 6 | + python analyze_graph_dump.py <dump.json> [--baseline <baseline.json>] [--output json|text] | ||
| 7 | + | ||
| 8 | +功能: | ||
| 9 | + - 节点/边统计 | ||
| 10 | + - 算子类型分布 | ||
| 11 | + - 与基线对比(regression 检测) | ||
| 12 | + - 结构异常检测(孤立节点/环/超长链) | ||
| 13 | +""" | ||
| 14 | + | ||
| 15 | +import json | ||
| 16 | +import sys | ||
| 17 | +import argparse | ||
| 18 | +from collections import Counter | ||
| 19 | +from typing import Optional | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +def load_dump(filepath: str) -> dict: | ||
| 23 | + with open(filepath, 'r') as f: | ||
| 24 | + return json.load(f) | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +def analyze(dump: dict) -> dict: | ||
| 28 | + """分析单个 dump""" | ||
| 29 | + nodes = dump.get('nodes', dump.get('graph', {}).get('nodes', [])) | ||
| 30 | + edges = dump.get('edges', dump.get('graph', {}).get('edges', [])) | ||
| 31 | + | ||
| 32 | + op_types = Counter() | ||
| 33 | + for n in nodes: | ||
| 34 | + op_type = n.get('type', n.get('op_type', 'Unknown')) | ||
| 35 | + op_types[op_type] += 1 | ||
| 36 | + | ||
| 37 | + return { | ||
| 38 | + 'node_count': len(nodes), | ||
| 39 | + 'edge_count': len(edges), | ||
| 40 | + 'op_type_distribution': dict(op_types.most_common(20)), | ||
| 41 | + 'unique_op_count': len(op_types), | ||
| 42 | + 'density': len(edges) / max(len(nodes) * (len(nodes) - 1), 1), | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +def detect_anomalies(dump: dict) -> list[dict]: | ||
| 47 | + """检测结构异常""" | ||
| 48 | + nodes = dump.get('nodes', dump.get('graph', {}).get('nodes', [])) | ||
| 49 | + edges = dump.get('edges', dump.get('graph', {}).get('edges', [])) | ||
| 50 | + anomalies = [] | ||
| 51 | + | ||
| 52 | + node_ids = {n.get('id', n.get('name', '')) for n in nodes} | ||
| 53 | + sources = {e.get('source', e.get('src', '')) for e in edges} | ||
| 54 | + targets = {e.get('target', e.get('dst', '')) for e in edges} | ||
| 55 | + | ||
| 56 | + # 孤立节点(无入边也无出边) | ||
| 57 | + connected = sources | targets | ||
| 58 | + isolated = node_ids - connected - {'input', 'output', 'INPUT', 'OUTPUT'} | ||
| 59 | + if isolated: | ||
| 60 | + anomalies.append({ | ||
| 61 | + 'type': 'isolated_nodes', | ||
| 62 | + 'severity': 'WARNING', | ||
| 63 | + 'description': f'发现 {len(isolated)} 个孤立节点(非输入/输出)', | ||
| 64 | + 'nodes': list(isolated)[:10], | ||
| 65 | + }) | ||
| 66 | + | ||
| 67 | + # 无出边节点(非输出) | ||
| 68 | + no_output = node_ids - sources - {'output', 'OUTPUT'} | ||
| 69 | + if len(no_output) > 1: | ||
| 70 | + anomalies.append({ | ||
| 71 | + 'type': 'dead_end_nodes', | ||
| 72 | + 'severity': 'INFO', | ||
| 73 | + 'description': f'发现 {len(no_output)} 个无出边节点', | ||
| 74 | + }) | ||
| 75 | + | ||
| 76 | + # 节点数异常(过大图可能有问题) | ||
| 77 | + if len(nodes) > 10000: | ||
| 78 | + anomalies.append({ | ||
| 79 | + 'type': 'large_graph', | ||
| 80 | + 'severity': 'WARNING', | ||
| 81 | + 'description': f'图中有 {len(nodes)} 节点,可能需要分区优化', | ||
| 82 | + }) | ||
| 83 | + | ||
| 84 | + return anomalies | ||
| 85 | + | ||
| 86 | + | ||
| 87 | +def diff_dumps(current: dict, baseline: dict) -> dict: | ||
| 88 | + """对比两个 dump""" | ||
| 89 | + ca = analyze(current) | ||
| 90 | + ba = analyze(baseline) | ||
| 91 | + | ||
| 92 | + cur_ops = set(ca['op_type_distribution'].keys()) | ||
| 93 | + bas_ops = set(ba['op_type_distribution'].keys()) | ||
| 94 | + | ||
| 95 | + return { | ||
| 96 | + 'node_diff': ca['node_count'] - ba['node_count'], | ||
| 97 | + 'edge_diff': ca['edge_count'] - ba['edge_count'], | ||
| 98 | + 'new_ops': list(cur_ops - bas_ops), | ||
| 99 | + 'removed_ops': list(bas_ops - cur_ops), | ||
| 100 | + 'op_counts_current': ca['op_type_distribution'], | ||
| 101 | + 'op_counts_baseline': ba['op_type_distribution'], | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + | ||
| 105 | +def main(): | ||
| 106 | + parser = argparse.ArgumentParser(description='图 Dump 分析器') | ||
| 107 | + parser.add_argument('dumpfile', help='graph.debug_dump() 导出的 JSON 文件') | ||
| 108 | + parser.add_argument('--baseline', help='基线 dump JSON(用于对比)') | ||
| 109 | + parser.add_argument('--output', choices=['json', 'text'], default='text') | ||
| 110 | + args = parser.parse_args() | ||
| 111 | + | ||
| 112 | + try: | ||
| 113 | + dump = load_dump(args.dumpfile) | ||
| 114 | + except (json.JSONDecodeError, FileNotFoundError) as e: | ||
| 115 | + print(f"错误: 无法加载 {args.dumpfile}: {e}", file=sys.stderr) | ||
| 116 | + sys.exit(1) | ||
| 117 | + | ||
| 118 | + result = analyze(dump) | ||
| 119 | + anomalies = detect_anomalies(dump) | ||
| 120 | + | ||
| 121 | + if args.output == 'json': | ||
| 122 | + output = {'analysis': result, 'anomalies': anomalies} | ||
| 123 | + if args.baseline: | ||
| 124 | + baseline = load_dump(args.baseline) | ||
| 125 | + output['diff'] = diff_dumps(dump, baseline) | ||
| 126 | + print(json.dumps(output, ensure_ascii=False, indent=2)) | ||
| 127 | + else: | ||
| 128 | + print("=== 图 Dump 分析 ===") | ||
| 129 | + print(f"节点数: {result['node_count']}") | ||
| 130 | + print(f"边数: {result['edge_count']}") | ||
| 131 | + print(f"图密度: {result['density']:.4f}") | ||
| 132 | + print(f"唯一算子类型: {result['unique_op_count']}") | ||
| 133 | + print(f"\n算子类型分布 (Top-10):") | ||
| 134 | + for op, count in list(result['op_type_distribution'].items())[:10]: | ||
| 135 | + print(f" {op}: {count}") | ||
| 136 | + | ||
| 137 | + if anomalies: | ||
| 138 | + print(f"\n结构异常:") | ||
| 139 | + for a in anomalies: | ||
| 140 | + print(f" [{a['severity']}] {a['type']}: {a['description']}") | ||
| 141 | + | ||
| 142 | + if args.baseline: | ||
| 143 | + baseline = load_dump(args.baseline) | ||
| 144 | + diff = diff_dumps(dump, baseline) | ||
| 145 | + print(f"\n与基线对比:") | ||
| 146 | + print(f" 节点变化: {diff['node_diff']:+d}") | ||
| 147 | + print(f" 边变化: {diff['edge_diff']:+d}") | ||
| 148 | + if diff['new_ops']: | ||
| 149 | + print(f" 新增算子: {diff['new_ops']}") | ||
| 150 | + if diff['removed_ops']: | ||
| 151 | + print(f" 移除算子: {diff['removed_ops']}") | ||
| 152 | + | ||
| 153 | + | ||
| 154 | +if __name__ == '__main__': | ||
| 155 | + main() | ||
| @@ -0,0 +1,202 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +""" | ||
| 3 | +NPU 单卡故障诊断 — 纯被动模式,不触发算子下发,不分配 GPU 内存 | ||
| 4 | +所有诊断信息存入 device_logs/<device_id>/ 子文件夹 | ||
| 5 | +""" | ||
| 6 | + | ||
| 7 | +import os | ||
| 8 | +import sys | ||
| 9 | +import subprocess | ||
| 10 | +import json | ||
| 11 | +from datetime import datetime | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
| 15 | +LOG_ROOT = os.path.join(SCRIPT_DIR, "..", "device_logs") | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +def run_cmd(cmd: list, timeout: int = 10) -> str: | ||
| 19 | + try: | ||
| 20 | + r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) | ||
| 21 | + return r.stdout.strip() or r.stderr.strip() | ||
| 22 | + except subprocess.TimeoutExpired: | ||
| 23 | + return "TIMEOUT" | ||
| 24 | + except FileNotFoundError: | ||
| 25 | + return "CMD_NOT_FOUND" | ||
| 26 | + except Exception as e: | ||
| 27 | + return f"ERROR: {e}" | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +def save_log(log_dir: str, filename: str, content: str): | ||
| 31 | + """保存单个日志文件""" | ||
| 32 | + if not content: | ||
| 33 | + return | ||
| 34 | + filepath = os.path.join(log_dir, filename) | ||
| 35 | + with open(filepath, "w", encoding="utf-8", errors="replace") as f: | ||
| 36 | + f.write(content) | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +def save_json(log_dir: str, filename: str, data): | ||
| 40 | + """保存 JSON 格式文件""" | ||
| 41 | + filepath = os.path.join(log_dir, filename) | ||
| 42 | + with open(filepath, "w", encoding="utf-8") as f: | ||
| 43 | + json.dump(data, f, ensure_ascii=False, indent=2, default=str) | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +def save_log_lines(log_dir: str, filename: str, lines: list): | ||
| 47 | + """保存行列表""" | ||
| 48 | + if not lines: | ||
| 49 | + return | ||
| 50 | + filepath = os.path.join(log_dir, filename) | ||
| 51 | + with open(filepath, "w", encoding="utf-8", errors="replace") as f: | ||
| 52 | + f.write("\n".join(str(l) for l in lines)) | ||
| 53 | + | ||
| 54 | + | ||
| 55 | +def diagnose_card(device_id: int, log_dir: str): | ||
| 56 | + """诊断单卡,所有结果写入 log_dir""" | ||
| 57 | + | ||
| 58 | + # ─── 1. npu-smi ─────────────────────────────────── | ||
| 59 | + smi_dir = os.path.join(log_dir, "npu_smi") | ||
| 60 | + os.makedirs(smi_dir, exist_ok=True) | ||
| 61 | + | ||
| 62 | + save_log(smi_dir, "health.txt", | ||
| 63 | + run_cmd(["npu-smi", "info", "-t", "health", "-i", str(device_id)])) | ||
| 64 | + save_log(smi_dir, "error_counters.txt", | ||
| 65 | + run_cmd(["npu-smi", "info", "-t", "error", "-i", str(device_id)])) | ||
| 66 | + save_log(smi_dir, "board_info.txt", | ||
| 67 | + run_cmd(["npu-smi", "info", "-t", "board", "-i", str(device_id)])) | ||
| 68 | + save_log(smi_dir, "processes.txt", | ||
| 69 | + run_cmd(["npu-smi", "info", "-t", "proc", "-i", str(device_id)])) | ||
| 70 | + save_log(smi_dir, "firmware.txt", | ||
| 71 | + run_cmd(["npu-smi", "info", "-t", "firmware", "-i", str(device_id)])) | ||
| 72 | + save_log(smi_dir, "temperature.txt", | ||
| 73 | + run_cmd(["npu-smi", "info", "-t", "temp", "-i", str(device_id)])) | ||
| 74 | + save_log(smi_dir, "power.txt", | ||
| 75 | + run_cmd(["npu-smi", "info", "-t", "power", "-i", str(device_id)])) | ||
| 76 | + | ||
| 77 | + # ─── 2. 驱动故障日志 ────────────────────────────── | ||
| 78 | + log_src = f"/var/log/npu/device-{device_id}" | ||
| 79 | + driver_dir = os.path.join(log_dir, "driver_logs") | ||
| 80 | + if os.path.isdir(log_src): | ||
| 81 | + os.makedirs(driver_dir, exist_ok=True) | ||
| 82 | + error_keywords = ("error", "fault", "exception", "timeout", | ||
| 83 | + "reset", "fail", "abnormal", "hang", "ecc", "assert") | ||
| 84 | + | ||
| 85 | + for fname in sorted(os.listdir(log_src)): | ||
| 86 | + fpath = os.path.join(log_src, fname) | ||
| 87 | + try: | ||
| 88 | + with open(fpath, "r", errors="replace") as f: | ||
| 89 | + lines = f.readlines() | ||
| 90 | + # 完整日志 | ||
| 91 | + save_log_lines(driver_dir, f"{fname}.full", [l.strip() for l in lines[-500:]]) | ||
| 92 | + # 过滤后的关键行 | ||
| 93 | + filtered = [l.strip() for l in lines | ||
| 94 | + if any(kw in l.lower() for kw in error_keywords)] | ||
| 95 | + save_log_lines(driver_dir, f"{fname}.errors", filtered[-200:]) | ||
| 96 | + except Exception as e: | ||
| 97 | + save_log(driver_dir, f"{fname}.error", str(e)) | ||
| 98 | + else: | ||
| 99 | + save_log(log_dir, "driver_logs_missing.txt", f"directory not found: {log_src}") | ||
| 100 | + | ||
| 101 | + # ─── 3. 内核日志 ────────────────────────────────── | ||
| 102 | + dmesg_out = run_cmd(["dmesg"], timeout=5) | ||
| 103 | + if dmesg_out and dmesg_out != "CMD_NOT_FOUND": | ||
| 104 | + lines = dmesg_out.split("\n") | ||
| 105 | + save_log_lines(log_dir, "dmesg_full.txt", [l.strip() for l in lines[-1000:]]) | ||
| 106 | + filtered = [l.strip() for l in lines | ||
| 107 | + if any(kw in l.lower() for kw in | ||
| 108 | + ("npu", "ascend", "hisi", "davinci", "pcie", "hbm", "ts", "aicore"))] | ||
| 109 | + save_log_lines(log_dir, "dmesg_errors.txt", filtered[-200:]) | ||
| 110 | + | ||
| 111 | + # ─── 4. cron 巡检日志 ───────────────────────────── | ||
| 112 | + cron_log = "/var/log/npu/cron_npu.log" | ||
| 113 | + if os.path.isfile(cron_log): | ||
| 114 | + try: | ||
| 115 | + with open(cron_log, "r", errors="replace") as f: | ||
| 116 | + lines = f.readlines() | ||
| 117 | + # 完整日志 | ||
| 118 | + save_log_lines(log_dir, "cron_full.txt", | ||
| 119 | + [l.strip() for l in lines[-1000:]]) | ||
| 120 | + # 过滤当前设备的关键行 | ||
| 121 | + filtered = [l.strip() for l in lines | ||
| 122 | + if str(device_id) in l and | ||
| 123 | + any(kw in l.lower() for kw in | ||
| 124 | + ("error", "fault", "fail", "reset", "timeout"))] | ||
| 125 | + save_log_lines(log_dir, "cron_errors.txt", filtered[-200:]) | ||
| 126 | + except Exception as e: | ||
| 127 | + save_log(log_dir, "cron_error.txt", str(e)) | ||
| 128 | + | ||
| 129 | + # ─── 5. Python 侧只读查询 ───────────────────────── | ||
| 130 | + py_info = {"device_id": device_id} | ||
| 131 | + try: | ||
| 132 | + import torch | ||
| 133 | + import torch_npu | ||
| 134 | + | ||
| 135 | + py_info["device_count"] = torch.npu.device_count() | ||
| 136 | + if device_id < torch.npu.device_count(): | ||
| 137 | + py_info["visible"] = True | ||
| 138 | + py_info["device_name"] = torch.npu.get_device_name(device_id) | ||
| 139 | + py_info["capability"] = torch.npu.get_device_capability(device_id) | ||
| 140 | + props = torch.npu.get_device_properties(device_id) | ||
| 141 | + py_info["total_memory_mb"] = props.total_memory / 1024**2 | ||
| 142 | + | ||
| 143 | + torch.npu.set_device(device_id) | ||
| 144 | + py_info["mem_allocated_mb"] = torch.npu.memory_allocated() / 1024**2 | ||
| 145 | + py_info["mem_reserved_mb"] = torch.npu.memory_reserved() / 1024**2 | ||
| 146 | + py_info["max_mem_allocated_mb"] = torch.npu.max_memory_allocated() / 1024**2 | ||
| 147 | + | ||
| 148 | + stats = torch.npu.memory_stats(device_id) | ||
| 149 | + if stats: | ||
| 150 | + py_info["alloc_count"] = stats.get("allocation.all.allocated", "N/A") | ||
| 151 | + py_info["free_count"] = stats.get("allocation.all.freed", "N/A") | ||
| 152 | + py_info["segment_count"] = stats.get("segment.all.current", "N/A") | ||
| 153 | + else: | ||
| 154 | + py_info["visible"] = False | ||
| 155 | + except ImportError: | ||
| 156 | + py_info["visible"] = "torch_npu not importable" | ||
| 157 | + except Exception as e: | ||
| 158 | + py_info["python_error"] = str(e) | ||
| 159 | + | ||
| 160 | + save_json(log_dir, "python_info.json", py_info) | ||
| 161 | + | ||
| 162 | + # ─── 6. 诊断摘要 ────────────────────────────────── | ||
| 163 | + summary = { | ||
| 164 | + "device_id": device_id, | ||
| 165 | + "timestamp": datetime.now().isoformat(), | ||
| 166 | + "log_directory": log_dir, | ||
| 167 | + "files": [], | ||
| 168 | + } | ||
| 169 | + for root, dirs, files in os.walk(log_dir): | ||
| 170 | + for fname in files: | ||
| 171 | + rel = os.path.relpath(os.path.join(root, fname), log_dir) | ||
| 172 | + size = os.path.getsize(os.path.join(root, fname)) | ||
| 173 | + summary["files"].append({"file": rel, "size_bytes": size}) | ||
| 174 | + | ||
| 175 | + save_json(log_dir, "summary.json", summary) | ||
| 176 | + | ||
| 177 | + return summary | ||
| 178 | + | ||
| 179 | + | ||
| 180 | +def print_summary(summary: dict): | ||
| 181 | + """命令行输出摘要""" | ||
| 182 | + print(f"\n=== 诊断完成 ===") | ||
| 183 | + print(f"设备: NPU {summary['device_id']}") | ||
| 184 | + print(f"时间: {summary['timestamp']}") | ||
| 185 | + print(f"输出: {summary['log_directory']}") | ||
| 186 | + print(f"文件数: {len(summary['files'])}") | ||
| 187 | + print(f"\n目录结构:") | ||
| 188 | + for f in summary["files"]: | ||
| 189 | + size_kb = f["size_bytes"] / 1024 | ||
| 190 | + print(f" {f['file']} ({size_kb:.1f} KB)") | ||
| 191 | + | ||
| 192 | + | ||
| 193 | +if __name__ == "__main__": | ||
| 194 | + device_id = int(sys.argv[1]) if len(sys.argv) > 1 else 0 | ||
| 195 | + | ||
| 196 | + log_dir = os.path.join(LOG_ROOT, f"device_{device_id}") | ||
| 197 | + os.makedirs(log_dir, exist_ok=True) | ||
| 198 | + | ||
| 199 | + print(f"诊断 NPU {device_id},输出目录: {log_dir}") | ||
| 200 | + | ||
| 201 | + summary = diagnose_card(device_id, log_dir) | ||
| 202 | + print_summary(summary) | ||
| @@ -0,0 +1,77 @@ | |||
| 1 | +# Graph Mode Expert — 图模式专家技能 | ||
| 2 | + | ||
| 3 | +> NPU 图模式全能技能:知识问答 + 问题诊断 + 修复方案 + 自验证 | ||
| 4 | + | ||
| 5 | +## 这个技能能做什么 | ||
| 6 | + | ||
| 7 | +1. **拉取代码仓** — 自动拉取 torch_npu 和 op-plugin 仓库,支持本地源码和远程 clone | ||
| 8 | +2. **知识问答** — 回答图模式任何概念问题,根据代码结构树快速定位涉及文件 | ||
| 9 | +3. **问题诊断** — 5 步系统分析法:概要定位→堆栈流程树→根因→方案→报告 | ||
| 10 | +4. **自验证** — 环境可用时自行测试验证修复方案 | ||
| 11 | + | ||
| 12 | +## 快速使用 | ||
| 13 | + | ||
| 14 | +### 知识问答 | ||
| 15 | + | ||
| 16 | +``` | ||
| 17 | +"NPUGraph 的 capture_begin 内部做了什么?" | ||
| 18 | +"图 tree 是怎么管理多图的?" | ||
| 19 | +"FA3 update 机制有什么限制?" | ||
| 20 | +"为什么捕获期间不能分配新内存?" | ||
| 21 | +``` | ||
| 22 | + | ||
| 23 | +### 问题分析 | ||
| 24 | + | ||
| 25 | +``` | ||
| 26 | +"我的图模式报错了,日志在 xxx.log,帮我分析" | ||
| 27 | +"torch.compile(backend='npugraphs') hang 住了" | ||
| 28 | +"图重放阶段 OOM,怎么排查?" | ||
| 29 | +``` | ||
| 30 | + | ||
| 31 | +### 分析报告 | ||
| 32 | + | ||
| 33 | +分析结果会输出到 `analysis/<问题简述>_analysis.md`,包含: | ||
| 34 | +- 问题概要定位(一句话) | ||
| 35 | +- 报错定位堆栈流程树(精确到文件:函数:行号) | ||
| 36 | +- 根因总结 | ||
| 37 | +- 修改方案 | ||
| 38 | +- 测试建议 | ||
| 39 | + | ||
| 40 | +## 文件结构 | ||
| 41 | + | ||
| 42 | +``` | ||
| 43 | +graph-mode-expert/ | ||
| 44 | +├── SKILL.md # 技能主体(Phase 0-3) | ||
| 45 | +├── README.md # 本文件 | ||
| 46 | +├── references/ | ||
| 47 | +│ └── code-structure.md # ★ 完整代码结构树 (关键词→文件索引) | ||
| 48 | +└── templates/ | ||
| 49 | + └── analysis-report-template.md # 分析报告模板 | ||
| 50 | +``` | ||
| 51 | + | ||
| 52 | +## 关键设计 | ||
| 53 | + | ||
| 54 | +| 特性 | 说明 | | ||
| 55 | +|------|------| | ||
| 56 | +| **代码仓准备** | 先问本地路径,没有再拉取;询问 torch_npu 分支 | | ||
| 57 | +| **代码结构树** | 18+ 个关键词索引,覆盖 L4→L1 全链路 | | ||
| 58 | +| **5 步分析** | 概要→堆栈树→根因→方案→报告 | | ||
| 59 | +| **分析输出** | 放入 `analysis/` 文件夹的结构化 Markdown 报告 | | ||
| 60 | +| **自验证** | 环境可用时自动测试 | | ||
| 61 | + | ||
| 62 | +## 依赖 | ||
| 63 | + | ||
| 64 | +- 参考 `NPU-graph-mode-diagnostics` 的已知模式和错误码表 | ||
| 65 | +- 使用 `scripts/check_graph_health.py` 做环境健康检查 | ||
| 66 | +- 使用 `../graph-log-analyzer/scripts/parse_npu_logs.py` 做日志分析 | ||
| 67 | + | ||
| 68 | +## Checklist | ||
| 69 | + | ||
| 70 | +- [x] 代码仓拉取逻辑 | ||
| 71 | +- [x] 知识问答流程 | ||
| 72 | +- [x] 问题诊断流程(5步) | ||
| 73 | +- [x] 分析报告模板 | ||
| 74 | +- [x] 代码结构树(关键词索引) | ||
| 75 | +- [x] 自验证流程 | ||
| 76 | +- [x] 与 NPU-graph-mode-diagnostics 互补 | ||
| 77 | +- [x] Red Flags 和 Handoff 规则 | ||
| @@ -0,0 +1,114 @@ | |||
| 1 | +--- | ||
| 2 | +name: npu-graph-mode-expert | ||
| 3 | +description: >- | ||
| 4 | + Use when user asks conceptual questions about NPU graph mode — | ||
| 5 | + how NPUGraph/ACLGraph works, capture/replay lifecycle, graph tree management, | ||
| 6 | + update mechanism, FA3 graph integration, super kernel, memory model. | ||
| 7 | + Also use when user needs code navigation — locating specific files/functions | ||
| 8 | + in torch_npu graph mode codebase. | ||
| 9 | + NOT for error/crash diagnosis — handoff to npu-graph-mode-diagnostics. | ||
| 10 | +--- | ||
| 11 | + | ||
| 12 | +# Graph Mode Expert — 图模式知识专家 | ||
| 13 | + | ||
| 14 | +## The Iron Law | ||
| 15 | + | ||
| 16 | +``` | ||
| 17 | +分析问题前必须拿到两样东西: | ||
| 18 | + 1. 完整报错堆栈(Python traceback + C++ error code) | ||
| 19 | + 2. 复现用例(能跑的最小脚本) | ||
| 20 | + | ||
| 21 | +没有这两样不开始分析。绝不凭空猜测根因。 | ||
| 22 | +``` | ||
| 23 | + | ||
| 24 | +**报错/异常/崩溃 → 立即 handoff 到 `NPU-graph-mode-diagnostics`,本技能只做知识问答和代码导航。** | ||
| 25 | + | ||
| 26 | +--- | ||
| 27 | + | ||
| 28 | +## Phase 0: 代码仓准备 | ||
| 29 | + | ||
| 30 | +涉及代码分析时,先确认源码可用。详细步骤 → [code-repo-setup.md](references/code-repo-setup.md)。 | ||
| 31 | + | ||
| 32 | +核心原则:先问用户本地路径,没有再拉取;拉取前确认分支。 | ||
| 33 | + | ||
| 34 | +--- | ||
| 35 | + | ||
| 36 | +## Phase 1: 知识问答 | ||
| 37 | + | ||
| 38 | +### 处理流程 | ||
| 39 | + | ||
| 40 | +``` | ||
| 41 | +Step 1: 从关键词提取技术点 | ||
| 42 | + ↓ 使用 code-structure.md 的「关键词→文件」索引 | ||
| 43 | +Step 2: 定位涉及的文件(精确到文件+关键函数) | ||
| 44 | + ↓ | ||
| 45 | +Step 3: 读取关键源码 | ||
| 46 | + ↓ | ||
| 47 | +Step 4: 回答,包含: | ||
| 48 | + - 要点概述 (2-3句话) | ||
| 49 | + - 涉及的代码层级和文件 | ||
| 50 | + - 关键调用链/数据流 | ||
| 51 | + - 相关约束和注意事项 | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +常见问题的回答要点见 [knowledge-faq.md](references/knowledge-faq.md)。 | ||
| 55 | + | ||
| 56 | +--- | ||
| 57 | + | ||
| 58 | +## Phase 3: 自验证 | ||
| 59 | + | ||
| 60 | +仅当环境有 NPU 设备且修改方案已实施时使用。 | ||
| 61 | + | ||
| 62 | +``` | ||
| 63 | +Step 1: python scripts/check_graph_health.py → 确认环境健康 | ||
| 64 | +Step 2: python <repro_script.py> → 确认问题不再复现 | ||
| 65 | +Step 3: 运行相关测试套件 | ||
| 66 | +Step 4: 输出 [PASS/FAIL] 验证结论 + 性能影响 | ||
| 67 | +``` | ||
| 68 | + | ||
| 69 | +--- | ||
| 70 | + | ||
| 71 | +## 知识库索引 | ||
| 72 | + | ||
| 73 | +| 参考文档 | 路径 | 用途 | | ||
| 74 | +|---------|------|------| | ||
| 75 | +| 完整代码结构树 | [code-structure.md](references/code-structure.md) | 关键词→文件快速定位 | | ||
| 76 | +| 常见问题速查 | [knowledge-faq.md](references/knowledge-faq.md) | 知识问答参考 | | ||
| 77 | +| 代码仓准备 | [code-repo-setup.md](references/code-repo-setup.md) | clone/分支切换详细流程 | | ||
| 78 | +| 已知问题模式 | [known-patterns.md](../graph-mode-diagnostics/references/known-patterns.md) | 14+ 已知模式匹配 | | ||
| 79 | +| ACL 错误码映射 | [error-code-map.md](../graph-mode-diagnostics/references/error-code-map.md) | 错误码→根因 | | ||
| 80 | +| 分析报告模板 | [analysis-report-template.md](templates/analysis-report-template.md) | 标准化报告输出 | | ||
| 81 | + | ||
| 82 | +## Handoff | ||
| 83 | + | ||
| 84 | +| 条件 | 移交技能 | 原因 | | ||
| 85 | +|------|---------|------| | ||
| 86 | +| 报错/异常/崩溃/OOM | `NPU-graph-mode-diagnostics` | 进入端到端诊断流程 | | ||
| 87 | +| 性能问题/变慢 | `NPU-graph-performance-profiling` | 进入性能分析 | | ||
| 88 | +| 日志量太大需自动分析 | `NPU-graph-log-analyzer` | 进入日志分析 | | ||
| 89 | +| MCP 工具配置 | `NPU-graph-mcp-integration` | 进入 MCP 集成 | | ||
| 90 | + | ||
| 91 | +--- | ||
| 92 | + | ||
| 93 | +## Red Flags | ||
| 94 | + | ||
| 95 | +| 误区 | 正确做法 | | ||
| 96 | +|------|---------| | ||
| 97 | +| 看到 "memory" 就加 `empty_cache()` | 先查分配栈,确认是泄漏还是不足 | | ||
| 98 | +| 看到 "shape mismatch" 就改 padding | 先确认 update 机制是否正确调用 | | ||
| 99 | +| 看到 graph_break 就改模型 | 先确认是否是真的问题(有的断裂是预期的) | | ||
| 100 | +| 看到 ACL 错误码不查表 | 先查 error-code-map.md | | ||
| 101 | +| 没看到复现用例就分析 | 先要复现用例,否则分析无意义 | | ||
| 102 | +| 看到报错就开始诊断 | handoff 到 diagnostics,不要自己分析 | | ||
| 103 | + | ||
| 104 | +--- | ||
| 105 | + | ||
| 106 | +## DFX: 知识沉淀 | ||
| 107 | + | ||
| 108 | +每次问答/诊断结束后,**必须执行**: | ||
| 109 | + | ||
| 110 | +1. **问用户**:「是否将此问题记录到知识库?[Y/n]」 | ||
| 111 | +2. 如 Y → 按 [../knowledge/CASE_TEMPLATE.md](../knowledge/CASE_TEMPLATE.md) 格式生成条目 | ||
| 112 | +3. 追加到 [../knowledge/history.md](../knowledge/history.md) 对应分类下 | ||
| 113 | +4. 更新分类总览计数 | ||
| 114 | +5. 如发现新的问题模式 → 提示用户:「是否同步更新 known-patterns.md?」 | ||
| @@ -0,0 +1,147 @@ | |||
| 1 | +# NPUGRAPH_SKILL 架构设计 | ||
| 2 | + | ||
| 3 | +> 版本: v0.1.0 | 日期: 2026-06-10 | 状态: 设计阶段 | ||
| 4 | + | ||
| 5 | +--- | ||
| 6 | + | ||
| 7 | +## 1. 设计目标 | ||
| 8 | + | ||
| 9 | +在现有 `.npugraphs/` 技能体系基础上,构建一个**专注于图模式问题诊断与解决**的技能增强层。核心差异化: | ||
| 10 | + | ||
| 11 | +| 维度 | .npugraphs/ (现有) | NPUGRAPH_SKILL (本系统) | | ||
| 12 | +|------|-------------------|------------------------| | ||
| 13 | +| **定位** | 通用 NPUGraph 开发全流程 | 图模式**问题诊断**专项 | | ||
| 14 | +| **粒度** | 粗粒度(debugging/requirements 覆盖所有) | 细粒度(捕获/重放/内存/性能 分技能) | | ||
| 15 | +| **工具链** | 依赖 Bash 手动执行 | MCP 自动化 + Python 脚本 | | ||
| 16 | +| **日志** | 手动 grep | 结构化解析 + 模式匹配 | | ||
| 17 | +| **性能** | 不在 scope 内 | 独立性能分析技能 | | ||
| 18 | +| **输出** | Markdown 报告 | 结构化报告 + Timeline 可视化 | | ||
| 19 | + | ||
| 20 | +## 2. 技能体系 | ||
| 21 | + | ||
| 22 | +``` | ||
| 23 | +NPUGRAPH_SKILL/ | ||
| 24 | +│ | ||
| 25 | +├── NPUGRAPH_SKILL_DOC.md # 总入口文档(本文件同级) | ||
| 26 | +│ | ||
| 27 | +├── DOC/ # 设计文档 | ||
| 28 | +│ ├── architecture.md # 本文件:架构设计 | ||
| 29 | +│ ├── mcp-evaluation.md # MCP 服务器评估 | ||
| 30 | +│ └── skill-design.md # 技能设计原理 | ||
| 31 | +│ | ||
| 32 | +├── SKILL/ # ===== 技能定义 ===== | ||
| 33 | +│ │ | ||
| 34 | +│ ├── graph-mode-diagnostics/ # ★ 核心技能:图模式问题诊断 | ||
| 35 | +│ │ ├── SKILL.md # 主技能文件 | ||
| 36 | +│ │ └── references/ | ||
| 37 | +│ │ ├── known-patterns.md # 已知问题模式(扩展版) | ||
| 38 | +│ │ ├── error-code-map.md # ACL 错误码 → 根因映射 | ||
| 39 | +│ │ └── diagnostic-checklist.md # 诊断检查清单 | ||
| 40 | +│ │ | ||
| 41 | +│ ├── graph-performance-profiling/ # 性能分析技能 | ||
| 42 | +│ │ ├── SKILL.md | ||
| 43 | +│ │ └── references/ | ||
| 44 | +│ │ ├── profiling-methodology.md | ||
| 45 | +│ │ └── baseline-metrics.md | ||
| 46 | +│ │ | ||
| 47 | +│ ├── graph-log-analyzer/ # 日志分析技能 | ||
| 48 | +│ │ ├── SKILL.md | ||
| 49 | +│ │ └── patterns/ | ||
| 50 | +│ │ ├── error-patterns.md # 错误日志模式 | ||
| 51 | +│ │ ├── timeline-patterns.md # 时间线异常模式 | ||
| 52 | +│ │ └── memory-patterns.md # 内存异常模式 | ||
| 53 | +│ │ | ||
| 54 | +│ └── graph-mcp-integration/ # MCP 工具集成 | ||
| 55 | +│ └── SKILL.md # 如何配置和使用 MCP | ||
| 56 | +│ | ||
| 57 | +├── MCP/ # MCP 配置 | ||
| 58 | +│ ├── mcp-configs.json # 所有相关 MCP server 定义 | ||
| 59 | +│ └── setup-guide.md # MCP 安装配置指南 | ||
| 60 | +│ | ||
| 61 | +└── scripts/ # 实用脚本 | ||
| 62 | + ├── parse_npu_logs.py # 日志解析器 | ||
| 63 | + ├── analyze_graph_dump.py # 图 dump 分析器 | ||
| 64 | + └── check_graph_health.py # 图健康检查 | ||
| 65 | +``` | ||
| 66 | + | ||
| 67 | +## 3. 技能触发规则 | ||
| 68 | + | ||
| 69 | +``` | ||
| 70 | +用户问题 → 关键词匹配 → 路由到对应技能 | ||
| 71 | +│ | ||
| 72 | +├── "图捕获失败" / "capture failed" / "aclmdlRICaptureBegin" | ||
| 73 | +│ → graph-mode-diagnostics (捕获阶段) | ||
| 74 | +│ | ||
| 75 | +├── "重放出错" / "replay error" / "aclmdlRIExecuteAsync failed" | ||
| 76 | +│ → graph-mode-diagnostics (重放阶段) | ||
| 77 | +│ | ||
| 78 | +├── "OOM" / "内存泄漏" / "memory" / "allocator" | ||
| 79 | +│ → graph-mode-diagnostics (内存阶段) | ||
| 80 | +│ | ||
| 81 | +├── "慢" / "性能下降" / "regression" / "profile" / "耗时" | ||
| 82 | +│ → graph-performance-profiling | ||
| 83 | +│ | ||
| 84 | +├── "日志分析" / "parse log" / "TORCH_NPU_LOGS" | ||
| 85 | +│ → graph-log-analyzer | ||
| 86 | +│ | ||
| 87 | +├── "配置MCP" / "setup mcp" / "mcp工具" | ||
| 88 | +│ → graph-mcp-integration | ||
| 89 | +│ | ||
| 90 | +└── 模糊/综合问题 → graph-mode-diagnostics (全流程) | ||
| 91 | +``` | ||
| 92 | + | ||
| 93 | +## 4. 与现有 .npugraphs/ 的关系 | ||
| 94 | + | ||
| 95 | +``` | ||
| 96 | +.npugraphs/skills/ NPUGRAPH_SKILL/SKILL/ | ||
| 97 | +├── debugging/ ←→ graph-mode-diagnostics/ (互补增强) | ||
| 98 | +├── requirements/ ← 独立(需求分析不在此系统 scope) | ||
| 99 | +├── implementation/ ← 独立(编码实现不在此系统 scope) | ||
| 100 | +├── verification/ ←→ graph-performance-profiling/ (验证阶段增强) | ||
| 101 | +├── torch-npu-logs-registration/ ←→ graph-log-analyzer/ (日志分析互补) | ||
| 102 | +└── graph-issue-solver/ ←→ graph-mode-diagnostics/ (问题单求解互补) | ||
| 103 | +``` | ||
| 104 | + | ||
| 105 | +**关键原则**:不重复 `.npugraphs/` 已有能力,只做**增强和特化**。 | ||
| 106 | + | ||
| 107 | +## 5. 数据流 | ||
| 108 | + | ||
| 109 | +``` | ||
| 110 | +┌──────────────┐ | ||
| 111 | +│ 用户报告问题 │ | ||
| 112 | +└──────┬───────┘ | ||
| 113 | + ▼ | ||
| 114 | +┌──────────────────┐ MCP: Context7 ┌─────────────────┐ | ||
| 115 | +│ graph-mode- │◄───查询文档──────────►│ CANN/PyTorch 文档│ | ||
| 116 | +│ diagnostics │ └─────────────────┘ | ||
| 117 | +└──────┬───────────┘ | ||
| 118 | + │ 需要日志分析? | ||
| 119 | + ▼ | ||
| 120 | +┌──────────────────┐ scripts/ ┌─────────────────┐ | ||
| 121 | +│ graph-log- │───parse_npu_logs.py──►│ 结构化日志报告 │ | ||
| 122 | +│ analyzer │ └─────────────────┘ | ||
| 123 | +└──────┬───────────┘ | ||
| 124 | + │ 需要性能分析? | ||
| 125 | + ▼ | ||
| 126 | +┌──────────────────┐ MCP: Playwright ┌─────────────────┐ | ||
| 127 | +│ graph- │───Profiler截图────────►│ 性能分析报告 │ | ||
| 128 | +│ performance- │ └─────────────────┘ | ||
| 129 | +│ profiling │ | ||
| 130 | +└──────┬───────────┘ | ||
| 131 | + │ | ||
| 132 | + ▼ | ||
| 133 | +┌──────────────────┐ | ||
| 134 | +│ 综合诊断报告 │ | ||
| 135 | +│ + 修复建议 │ | ||
| 136 | +│ + 验证步骤 │ | ||
| 137 | +└──────────────────┘ | ||
| 138 | +``` | ||
| 139 | + | ||
| 140 | +## 6. 扩展计划 | ||
| 141 | + | ||
| 142 | +| 版本 | 内容 | | ||
| 143 | +|------|------| | ||
| 144 | +| v0.1.0 | 核心技能定义 + MCP 评估 + 脚本原型 | | ||
| 145 | +| v0.2.0 | MCP 实际集成(Context7 + NPU-Log-Parser) | | ||
| 146 | +| v0.3.0 | Graph-Dump-Analyzer MCP + 性能分析脚本完善 | | ||
| 147 | +| v1.0.0 | 全量测试覆盖 + 与 .npugraphs/ 无缝集成 | | ||
| @@ -0,0 +1,30 @@ | |||
| 1 | +# 代码仓准备详细流程 | ||
| 2 | + | ||
| 3 | +> 从 `SKILL.md` Phase 0 拆分。 | ||
| 4 | + | ||
| 5 | +## 规则 | ||
| 6 | + | ||
| 7 | +每次对话开始时,如果涉及代码分析,必须先确认源码可用: | ||
| 8 | + | ||
| 9 | +1. 先问用户:是否有本地 torch_npu 和 op-plugin 源码?路径是? | ||
| 10 | +2. 如果用户提供了本地路径: | ||
| 11 | + - 验证路径存在且包含关键文件(torch_npu/npu/graphs.py 等) | ||
| 12 | + - 记录为 `$TORCH_NPU_ROOT` 和 `$OP_PLUGIN_ROOT` | ||
| 13 | +3. 如果用户没有本地源码: | ||
| 14 | + - 告知用户将拉取以下仓库: | ||
| 15 | + - `git clone https://gitcode.com/Ascend/pytorch.git` (torch_npu) | ||
| 16 | + - `git clone https://gitcode.com/Ascend/op-plugin.git -b master` (op-plugin) | ||
| 17 | + - 询问用户:torch_npu 需要特定分支吗?[默认: master] | ||
| 18 | + - 拉取后按分支切换 | ||
| 19 | + - op-plugin 默认 master 分支 | ||
| 20 | +4. 拉取完成后,验证关键文件存在 | ||
| 21 | + | ||
| 22 | +## 仓库验证检查清单 | ||
| 23 | + | ||
| 24 | +``` | ||
| 25 | +□ torch_npu/npu/graphs.py — 主 API 文件 | ||
| 26 | +□ torch_npu/npu/_graph_tree.py — 图树管理 | ||
| 27 | +□ torch_npu/csrc/core/npu/NPUGraph.cpp — C++ 核心 | ||
| 28 | +□ torch_npu/csrc/npu/Graph.cpp — pybind 绑定 | ||
| 29 | +□ op-plugin/ops/ — 算子实现 | ||
| 30 | +``` | ||
| @@ -0,0 +1,336 @@ | |||
| 1 | +# NPU 图模式完整代码结构树 | ||
| 2 | + | ||
| 3 | +> 从 torch_npu Python API → C++ 核心 → op-plugin 的完整文件索引 | ||
| 4 | +> 用于问题快速定位:根据关键词/报错信息定位到具体文件和函数 | ||
| 5 | + | ||
| 6 | +--- | ||
| 7 | + | ||
| 8 | +## 一、总览架构图 | ||
| 9 | + | ||
| 10 | +``` | ||
| 11 | +┌──────────────────────────────────────────────────────────────────┐ | ||
| 12 | +│ 用户代码 (L5) │ | ||
| 13 | +│ model.forward() / train loop / torch.compile(backend="npugraphs")│ | ||
| 14 | +└──────────────────────────┬───────────────────────────────────────┘ | ||
| 15 | + │ | ||
| 16 | +┌──────────────────────────▼───────────────────────────────────────┐ | ||
| 17 | +│ Python API 层 (L4) │ | ||
| 18 | +│ │ | ||
| 19 | +│ npu/graphs.py (1189行) npu/npugraph_ex/ (55行) │ | ||
| 20 | +│ ├── NPUGraph (继承 C++_NPUGraph) ├── inference/ │ | ||
| 21 | +│ ├── graph (上下文管理器) └── scope/ │ | ||
| 22 | +│ ├── make_graphed_callables() │ | ||
| 23 | +│ ├── _GraphDispatchMode │ | ||
| 24 | +│ └── _GraphDispatchRecord │ | ||
| 25 | +│ │ | ||
| 26 | +│ npu/__init__.py ← 导出: NPUGraph, graph, make_graphed_callables │ | ||
| 27 | +└──────────────────────────┬───────────────────────────────────────┘ | ||
| 28 | + │ | ||
| 29 | +┌──────────────────────────▼───────────────────────────────────────┐ | ||
| 30 | +│ Graph Tree 层 (L3) │ | ||
| 31 | +│ │ | ||
| 32 | +│ npu/_graph_tree.py (2560行) utils/_graph_tree.py (380行) │ | ||
| 33 | +│ ├── NPUGraphTreeManager ├── NpugraphsBackend │ | ||
| 34 | +│ ├── NPUGraphNode ├── npugraphify() │ | ||
| 35 | +│ ├── NPUWarmupNode ├── npugraphify_impl() │ | ||
| 36 | +│ ├── GraphID ├── _apply_npugraph_tree_methods │ | ||
| 37 | +│ ├── OutputAliasInfo └── check_for_skip() │ | ||
| 38 | +│ ├── TreeManagerContainer │ | ||
| 39 | +│ └── ExecutionState / CompilationMode │ | ||
| 40 | +│ │ | ||
| 41 | +│ Handler 层: npu/_npugraph_handlers/ (5文件, 582行) │ | ||
| 42 | +│ ├── __init__.py ← 自动注册入口 │ | ||
| 43 | +│ ├── npugraph_handler.py ← NpuGraphOpHandler 基类 + 注册表 │ | ||
| 44 | +│ ├── _fa3_graph_handler.py ← FA3 前后向 handler │ | ||
| 45 | +│ ├── ifa_handler.py ← IFA v1/v2 handler │ | ||
| 46 | +│ └── simple_handler.py ← 默认 handler │ | ||
| 47 | +└──────────────────────────┬───────────────────────────────────────┘ | ||
| 48 | + │ | ||
| 49 | +┌──────────────────────────▼───────────────────────────────────────┐ | ||
| 50 | +│ C++ 核心层 (L2) │ | ||
| 51 | +│ │ | ||
| 52 | +│ core/npu/NPUGraph.h (134行) core/npu/NPUGraph.cpp (371行) │ | ||
| 53 | +│ ├── class NPUGraph ├── capture_begin/end 实现 │ | ||
| 54 | +│ ├── MempoolId_t ├── replay() 实现 │ | ||
| 55 | +│ ├── NPUTaskGroupHandle ├── reset() 实现 │ | ||
| 56 | +│ └── 图状态 + 生成器状态管理 └── 图内存池管理 │ | ||
| 57 | +│ │ | ||
| 58 | +│ core/npu/NPUGraphsUtils.h/cpp ← CaptureStatus 枚举 + 流状态 │ | ||
| 59 | +│ core/npu/interface/AclInterface ← aclmdlRI* 动态加载 │ | ||
| 60 | +│ core/npu/interface/SkInterface ← aclsk* 超级内核 API │ | ||
| 61 | +│ │ | ||
| 62 | +│ core/npu/NPUCachingAllocator.h/cpp ← 图内存池 (PrivatePool) │ | ||
| 63 | +│ ├── beginAllocateToPool() ↔ 对应 capture_begin │ | ||
| 64 | +│ ├── endAllocateToPool() ↔ 对应 capture_end │ | ||
| 65 | +│ ├── releasePool() ↔ 对应 reset() │ | ||
| 66 | +│ └── graph_pools / graph_pools_freeable │ | ||
| 67 | +│ │ | ||
| 68 | +│ aten/NPUGeneratorImpl.h/cpp ← RNG 状态图安全 │ | ||
| 69 | +│ ├── capture_prologue/epilogue │ | ||
| 70 | +│ └── replay_prologue │ | ||
| 71 | +│ │ | ||
| 72 | +│ csrc/npu/Graph.cpp (834行) ← pybind11 绑定 │ | ||
| 73 | +│ ├── THNPGraph_init() ← 所有 Python 绑定入口 │ | ||
| 74 | +│ └── _NPUGraph / _graph_pool_handle / _graph_task_group_* │ | ||
| 75 | +└──────────────────────────┬───────────────────────────────────────┘ | ||
| 76 | + │ | ||
| 77 | +┌──────────────────────────▼───────────────────────────────────────┐ | ||
| 78 | +│ ACL 运行时层 (L1) │ | ||
| 79 | +│ │ | ||
| 80 | +│ libascendcl.so: │ | ||
| 81 | +│ ├── aclmdlRICaptureBegin / GetInfo / End │ | ||
| 82 | +│ ├── aclmdlRIExecuteAsync │ | ||
| 83 | +│ ├── aclmdlRIDestroy │ | ||
| 84 | +│ ├── aclmdlRICaptureTaskGrpBegin/End │ | ||
| 85 | +│ ├── aclmdlRICaptureTaskUpdateBegin/End │ | ||
| 86 | +│ └── aclmdlRIDebugPrint / DebugJsonPrint │ | ||
| 87 | +│ │ | ||
| 88 | +│ libascendsk.so: │ | ||
| 89 | +│ ├── aclskOptimize │ | ||
| 90 | +│ ├── aclskScopeBegin / ScopeEnd │ | ||
| 91 | +│ └── aclskOption 结构体 │ | ||
| 92 | +└──────────────────────────┬───────────────────────────────────────┘ | ||
| 93 | + │ | ||
| 94 | +┌──────────────────────────▼───────────────────────────────────────┐ | ||
| 95 | +│ NPU 硬件/驱动层 (L0) │ | ||
| 96 | +│ Ascend 910B / 910B2 / 910C / 310B1 系列 │ | ||
| 97 | +└──────────────────────────────────────────────────────────────────┘ | ||
| 98 | +``` | ||
| 99 | + | ||
| 100 | +--- | ||
| 101 | + | ||
| 102 | +## 二、完整文件清单(按层级) | ||
| 103 | + | ||
| 104 | +### L4 — Python API 层 | ||
| 105 | + | ||
| 106 | +| 文件 | 路径 | 行数 | 核心符号 | 用途 | | ||
| 107 | +|------|------|------|----------|------| | ||
| 108 | +| `graphs.py` | `torch_npu/npu/graphs.py` | 1189 | `NPUGraph`, `graph`, `_GraphDispatchMode`, `make_graphed_callables`, `graph_pool_handle`, `super_kernel_scope_begin/end` | **主 API 文件**:三种使能方式 | | ||
| 109 | +| `__init__.py` | `torch_npu/npu/__init__.py` | — | 导出 `NPUGraph`, `graph`, `make_graphed_callables` | 公开 API 导出 | | ||
| 110 | +| `npugraph_ex/__init__.py` | `torch_npu/npu/npugraph_ex/__init__.py` | 33 | `compile_fx()`, `register_replacement()` | npugraph_ex 后端入口 | | ||
| 111 | +| `inference/__init__.py` | `torch_npu/npu/npugraph_ex/inference/__init__.py` | 16 | `cache_compile()` | 推理缓存编译 | | ||
| 112 | +| `scope/__init__.py` | `torch_npu/npu/npugraph_ex/scope/__init__.py` | 6 | `limit_core_num()` | 图编译 scope | | ||
| 113 | + | ||
| 114 | +### L3 — Graph Tree + Handler 层 | ||
| 115 | + | ||
| 116 | +| 文件 | 路径 | 行数 | 核心符号 | 用途 | | ||
| 117 | +|------|------|------|----------|------| | ||
| 118 | +| `_graph_tree.py` | `torch_npu/npu/_graph_tree.py` | 2560 | `NPUGraphTreeManager`, `NPUGraphNode`, `NPUWarmupNode`, `GraphID`, `OutputAliasInfo`, `TreeManagerContainer`, `ExecutionState`, `CompilationMode` | **最大文件**:多图树管理 | | ||
| 119 | +| `_graph_tree.py` | `torch_npu/utils/_graph_tree.py` | 380 | `NpugraphsBackend`, `npugraphify()`, `check_for_skip()` | Dynamo 后端桥接 | | ||
| 120 | +| `__init__.py` | `torch_npu/npu/_npugraph_handlers/__init__.py` | 32 | 导入所有 handler 触发注册 | Handler 自动注册 | | ||
| 121 | +| `npugraph_handler.py` | `torch_npu/npu/_npugraph_handlers/npugraph_handler.py` | 212 | `NpuGraphOpHandler`, `register_npu_graph_handler`, `_NPU_GRAPH_OP_HANDLERS` | Handler 基类 + 注册表 | | ||
| 122 | +| `_fa3_graph_handler.py` | `torch_npu/npu/_npugraph_handlers/_fa3_graph_handler.py` | 184 | `FA3ForwardHandler`, `FA3BackwardHandler`, `_FA3TensorListOutHandler` | FA3 图 handler | | ||
| 123 | +| `ifa_handler.py` | `torch_npu/npu/_npugraph_handlers/ifa_handler.py` | 122 | `_IFAv1DefaultHandler`, `_IFAv2DefaultHandler` | IFA handler | | ||
| 124 | +| `simple_handler.py` | `torch_npu/npu/_npugraph_handlers/simple_handler.py` | 32 | `_SimpleGraphHandler` | 默认 handler | | ||
| 125 | + | ||
| 126 | +### L3 — Inductor 图编译层 | ||
| 127 | + | ||
| 128 | +| 文件 | 路径 | 行数 | 核心符号 | 用途 | | ||
| 129 | +|------|------|------|----------|------| | ||
| 130 | +| `graph.py` | `torch_npu/_inductor/graph.py` | 550 | `patch_codegen_with_cpp_wrapper()` | Inductor 代码生成补丁 | | ||
| 131 | +| `npu_fusion_attention_graph.py` | `torch_npu/_inductor/fx_passes/pattern_match/npu_fusion_attention_graph.py` | 256 | `NpuGraphAttentionFunction`, `npu_fusion_attention_graph()` | FA 图融合 pass | | ||
| 132 | +| `ascend_graph_pass.py` | `torch_npu/_inductor/fx_passes/ascend_custom_passes/ascend_graph_pass.py` | 2483 | `AscendCustomPostPass` | 图优化 passes 集合 | | ||
| 133 | +| `joint_graph.py` | `torch_npu/_inductor/fx_passes/joint_graph.py` | 14 | `patch_constant_fold_uniform_value()` | 常量折叠补丁 | | ||
| 134 | +| `graph_match_pass.py` | `torch_npu/_inductor/fx_passes/graph_match_pass.py` | 17 | — | 图模式匹配 | | ||
| 135 | +| `graph_build.py` | `torch_npu/_inductor/dvm/graph_build.py` | 173 | `DvmCodegenInterpreter` | DVM 图构建 | | ||
| 136 | +| `graph_fusion.py` | `torch_npu/_inductor/dvm/graph_fusion.py` | 447 | `DvmGraphFusionPatch`, `dvm_graph_fusion()` | DVM 图融合 | | ||
| 137 | +| `graph_fusion.py` | `torch_npu/_inductor/mfusion/graph_fusion.py` | 963 | `MFusionPatch`, `mfusion_graph_fusion()` | MFusion 图融合 | | ||
| 138 | +| `subgraph_registry.py` | `torch_npu/_inductor/mfusion/subgraph_registry.py` | 67 | `register()`, `get()`, `pop()` | MFusion 子图注册 | | ||
| 139 | + | ||
| 140 | +### L2 — C++ 核心层 | ||
| 141 | + | ||
| 142 | +| 文件 | 路径 | 行数 | 核心符号 | 用途 | | ||
| 143 | +|------|------|------|----------|------| | ||
| 144 | +| `NPUGraph.h` | `torch_npu/csrc/core/npu/NPUGraph.h` | 134 | `class NPUGraph`, `MempoolId_t`, `NPUTaskGroupHandle` | C++ 图类声明 | | ||
| 145 | +| `NPUGraph.cpp` | `torch_npu/csrc/core/npu/NPUGraph.cpp` | 371 | `capture_begin/end`, `replay`, `reset` 实现 | C++ 图类实现 | | ||
| 146 | +| `NPUGraphsUtils.h` | `torch_npu/csrc/core/npu/NPUGraphsUtils.h` | 107 | `CaptureId_t`, `CaptureStatus`, `currentStreamCaptureStatus` | 图工具函数 | | ||
| 147 | +| `NPUGraphsUtils.cpp` | `torch_npu/csrc/core/npu/NPUGraphsUtils.cpp` | — | 流状态查询实现 | 图工具实现 | | ||
| 148 | +| `AclInterface.h` | `torch_npu/csrc/core/npu/interface/AclInterface.h` | ~300 | `AclmdlRICaptureBegin/End`, `AclmdlRIExecuteAsync` 等 | ACL API C++ 包装声明 | | ||
| 149 | +| `AclInterface.cpp` | `torch_npu/csrc/core/npu/interface/AclInterface.cpp` | ~1200 | 动态加载 aclmdlRI* 函数 | ACL API 动态加载 | | ||
| 150 | +| `SkInterface.h` | `torch_npu/csrc/core/npu/interface/SkInterface.h` | — | `AclskOptimize`, `AclskScopeBegin/End` | 超级内核 API 声明 | | ||
| 151 | +| `SkInterface.cpp` | `torch_npu/csrc/core/npu/interface/SkInterface.cpp` | — | 动态加载 aclsk* 函数 | 超级内核 API 加载 | | ||
| 152 | +| `NPUCachingAllocator.h` | `torch_npu/csrc/core/npu/NPUCachingAllocator.h` | — | `MemPool`, `MemPoolContext`, `beginAllocateToPool` 等 | 图内存池声明 | | ||
| 153 | +| `NPUCachingAllocator.cpp` | `torch_npu/csrc/core/npu/NPUCachingAllocator.cpp` | — | `PrivatePool`, `graph_pools` | 图内存池实现 | | ||
| 154 | +| `NPUGeneratorImpl.h` | `torch_npu/csrc/aten/NPUGeneratorImpl.h` | — | `NPUGeneratorState`, `capture_prologue/epilogue` | RNG 图安全管理 | | ||
| 155 | +| `NPUGeneratorImpl.cpp` | `torch_npu/csrc/aten/NPUGeneratorImpl.cpp` | — | 生成器状态管理实现 | RNG 图安全实现 | | ||
| 156 | +| `Graph.h` | `torch_npu/csrc/npu/Graph.h` | — | `THNPGraph_init()` 声明 | pybind 绑定声明 | | ||
| 157 | +| `Graph.cpp` | `torch_npu/csrc/npu/Graph.cpp` | 834 | `THNPGraph_init()` 实现, `_NPUGraph` 绑定 | **pybind 绑定入口** | | ||
| 158 | +| `OpCommand.cpp` | `torch_npu/csrc/framework/OpCommand.cpp` | — | aclop 捕捉检查 | 图内 aclop 拦截 | | ||
| 159 | +| `NPUWorkspaceAllocator.cpp` | `torch_npu/csrc/core/npu/NPUWorkspaceAllocator.cpp` | — | 工作空间分配器 | 图感知工作空间 | | ||
| 160 | +| `ProcessGroupHCCL.cpp` | `torch_npu/csrc/distributed/ProcessGroupHCCL.cpp` | — | HCCL 捕捉状态检查 | 分布式图兼容 | | ||
| 161 | + | ||
| 162 | +### op-plugin 层 | ||
| 163 | + | ||
| 164 | +| 文件 | 路径 | 用途 | | ||
| 165 | +|------|------|------| | ||
| 166 | +| RNG 算子 | `op_plugin/ops/opapi/*KernelNpuOpApi.cpp` | 图感知的随机数算子 | | ||
| 167 | +| FA 算子 | `op_plugin/ops/opapi/FlashAttentionKernelNpuOpApi.cpp` | 图感知的 FlashAttention | | ||
| 168 | +| 惰性融合 | `op_plugin/ops/dvm/lazy_fusion_ops.cpp` | DVM 图融合算子 | | ||
| 169 | +| ATB 创建 | `op_plugin/utils/custom_functions/atb/AtbCommon.h/cpp` | 图感知 ATB 算子创建 | | ||
| 170 | +| ACL 图示例 | `third_party/op-plugin/examples/kernel_extension_aclgraph/` | ACL 图扩展参考实现 | | ||
| 171 | + | ||
| 172 | +### Dynamo 补丁和配置 | ||
| 173 | + | ||
| 174 | +| 文件 | 路径 | 用途 | | ||
| 175 | +|------|------|------| | ||
| 176 | +| `dynamo_patches.py` | `torch_npu/_init/patches/dynamo_patches.py` | `apply_npugraph_tree_patch()` | | ||
| 177 | +| `__init__.py` | `torch_npu/dynamo/__init__.py` | `NPUGRAPH_EX_BACKEND` 注册 | | ||
| 178 | +| `config.py` | `torch_npu/_inductor/config.py` | cudagraph 兼容性引用 | | ||
| 179 | +| `utils.py` | `torch_npu/_inductor/utils.py` | `get_first_incompatible_cudagraph_node` | | ||
| 180 | +| `transfer_to_npu.py` | `torch_npu/contrib/transfer_to_npu.py` | `CUDAGraph` → `NPUGraph` 映射 | | ||
| 181 | + | ||
| 182 | +### 测试文件 | ||
| 183 | + | ||
| 184 | +| 文件 | 路径 | 行数 | 用途 | | ||
| 185 | +|------|------|------|------| | ||
| 186 | +| `test_graph_tree.py` | `test/npu/test_graph_tree.py` | 1210 | 图树核心测试 | | ||
| 187 | +| `test_npugraph_ex.py` | `test/dynamo/test_npugraph_ex.py` | 192 | npugraph_ex 测试 | | ||
| 188 | +| `test_npugraph_handler.py` | `test/npu/test_npugraph_handler.py` | 60 | Handler 测试 | | ||
| 189 | +| `test_aclgraph_update.py` | `test/npu/test_aclgraph_update.py` | 594 | ACL 图 update 测试 | | ||
| 190 | +| `test_aclgraph_super_kernel.py` | `test/npu/test_aclgraph_super_kernel.py` | 396 | 超级内核测试 | | ||
| 191 | +| `test_fav3_aclgraph_update.py` | `test/npu/test_fav3_aclgraph_update.py` | 234 | FA3 update 测试 | | ||
| 192 | +| `test_aclgraph_dfx.py` | `test/npu/test_aclgraph_dfx.py` | 244 | DFX 测试 | | ||
| 193 | +| `test_aclgraph_multi_stream.py` | `test/npu/test_aclgraph_multi_stream.py` | 135 | 多流图测试 | | ||
| 194 | +| `test_compile_aclgraph_update.py` | `test/_inductor/test_compile_aclgraph_update.py` | 143 | Inductor 图 update | | ||
| 195 | + | ||
| 196 | +--- | ||
| 197 | + | ||
| 198 | +## 三、关键调用链 | ||
| 199 | + | ||
| 200 | +### 3.1 图捕获流程 | ||
| 201 | + | ||
| 202 | +``` | ||
| 203 | +用户代码: g.capture_begin() | ||
| 204 | + │ | ||
| 205 | + ▼ | ||
| 206 | +graphs.py: NPUGraph.capture_begin() | ||
| 207 | + │ 参数校验(auto_dispatch, mode, super_kernel等) | ||
| 208 | + │ 调用 C++ _capture_begin() | ||
| 209 | + ▼ | ||
| 210 | +Graph.cpp (pybind): _NPUGraph.capture_begin() | ||
| 211 | + │ 解析 Python 参数 → C++ CaptureMode 枚举 | ||
| 212 | + │ 调用 cpp_obj->capture_begin() | ||
| 213 | + ▼ | ||
| 214 | +NPUGraph.cpp: NPUGraph::capture_begin() | ||
| 215 | + │ 1. 检查 TASK_QUEUE_ENABLE | ||
| 216 | + │ 2. apply_cache_op_info(stream) — 设置 Stream 属性 | ||
| 217 | + │ 3. 注册默认生成器状态(capture_prologue) | ||
| 218 | + │ 4. 选择/创建 MemPool | ||
| 219 | + │ 5. NPUCachingAllocator::beginAllocateToPool() | ||
| 220 | + │ 6. 等待 HCCL 事件查询 | ||
| 221 | + │ 7. AclmdlRICaptureBegin() → 进入 ACL 运行时 | ||
| 222 | + │ 8. AclmdlRICaptureGetInfo() → 确认状态 | ||
| 223 | + ▼ | ||
| 224 | +AclInterface.cpp: AclmdlRICaptureBegin() | ||
| 225 | + │ 调用动态加载的 libascendcl.so 中的 aclmdlRICaptureBegin | ||
| 226 | + ▼ | ||
| 227 | +ACL 运行时: aclmdlRICaptureBegin | ||
| 228 | + │ 进入图捕获模式 | ||
| 229 | + │ 后续所有 ACL 调用被记录而非立即执行 | ||
| 230 | +``` | ||
| 231 | + | ||
| 232 | +### 3.2 图重放流程 | ||
| 233 | + | ||
| 234 | +``` | ||
| 235 | +用户代码: g.replay() | ||
| 236 | + │ | ||
| 237 | + ▼ | ||
| 238 | +graphs.py: NPUGraph.replay() | ||
| 239 | + │ 调用 C++ replay() | ||
| 240 | + ▼ | ||
| 241 | +NPUGraph.cpp: NPUGraph::replay() | ||
| 242 | + │ 1. 设置 device guard | ||
| 243 | + │ 2. replay_prologue() — 更新生成器状态 | ||
| 244 | + │ 3. AclmdlRIExecuteAsync() → 异步执行 | ||
| 245 | + │ 4. 如果阻塞模式:同步等待 | ||
| 246 | + ▼ | ||
| 247 | +ACL 运行时: aclmdlRIExecuteAsync | ||
| 248 | + │ 在 NPU 上执行捕获的图 | ||
| 249 | +``` | ||
| 250 | + | ||
| 251 | +### 3.3 Graph Tree 流程 (torch.compile) | ||
| 252 | + | ||
| 253 | +``` | ||
| 254 | +torch.compile(backend="npugraphs") | ||
| 255 | + │ | ||
| 256 | + ▼ | ||
| 257 | +Dynamo → FX Graph → AOT Autograd | ||
| 258 | + │ | ||
| 259 | + ▼ | ||
| 260 | +utils/_graph_tree.py: NpugraphsBackend | ||
| 261 | + │ npugraphify() — 将编译后的模块注册到树管理器 | ||
| 262 | + ▼ | ||
| 263 | +npu/_graph_tree.py: NPUGraphTreeManager | ||
| 264 | + │ 1. 创建 NPUGraphNode (每个动态形状变体) | ||
| 265 | + │ 2. NPUWarmupNode → 预热运行 | ||
| 266 | + │ 3. 触发 capture_begin() → 用户代码 → capture_end() | ||
| 267 | + │ 4. 编译图 → replay 使用 | ||
| 268 | + │ 5. LRU 管理缓存图 | ||
| 269 | +``` | ||
| 270 | + | ||
| 271 | +### 3.4 FA3 Update 流程 | ||
| 272 | + | ||
| 273 | +``` | ||
| 274 | +用户代码: model(input_with_new_shape) | ||
| 275 | + │ | ||
| 276 | + ▼ | ||
| 277 | +npu/_graph_tree.py: NPUGraphTreeManager | ||
| 278 | + │ 检测 shape 变化 | ||
| 279 | + │ 调用 update_graph() | ||
| 280 | + ▼ | ||
| 281 | +graphs.py: _GraphDispatchMode | ||
| 282 | + │ 查找 NpuGraphOpHandler | ||
| 283 | + ▼ | ||
| 284 | +_fa3_graph_handler.py: FA3ForwardHandler | ||
| 285 | + │ update_args() — 更新 TND/形状参数 | ||
| 286 | + │ 检查 TND + dropout fallback | ||
| 287 | + ▼ | ||
| 288 | +C++: graph_task_update_begin/end → aclmdlRICaptureTaskUpdateBegin/End | ||
| 289 | +``` | ||
| 290 | + | ||
| 291 | +--- | ||
| 292 | + | ||
| 293 | +## 四、关键词 → 文件快速索引 | ||
| 294 | + | ||
| 295 | +| 关键词/报错 | 第一优先级文件 | 第二优先级 | | ||
| 296 | +|-------------|---------------|-----------| | ||
| 297 | +| `capture_begin` / `capture_end` | `graphs.py` → `NPUGraph.cpp` | `AclInterface.cpp` | | ||
| 298 | +| `replay` / `ExecuteAsync` | `graphs.py` → `NPUGraph.cpp` | `AclInterface.cpp` | | ||
| 299 | +| `aclmdlRICaptureBegin` | `AclInterface.h/.cpp` | `NPUGraph.cpp` | | ||
| 300 | +| `aclmdlRIExecuteAsync` | `AclInterface.h/.cpp` | `NPUGraph.cpp` (replay) | | ||
| 301 | +| `graph tree` / `TreeManager` | `_graph_tree.py` | `utils/_graph_tree.py` | | ||
| 302 | +| `NPUGraphTreeManager` | `npu/_graph_tree.py` | — | | ||
| 303 | +| `make_graphed_callables` | `graphs.py` (L889) | `NPUGraph.cpp` | | ||
| 304 | +| `super_kernel` / `aclsk` | `SkInterface.h/.cpp` | `graphs.py`, `Graph.cpp` | | ||
| 305 | +| `OOM` / `allocator` | `NPUCachingAllocator.cpp` | `NPUGraph.cpp` (pool 管理) | | ||
| 306 | +| `Memory pool` / `mempool` | `NPUCachingAllocator.cpp` | `NPUGraph.h` (MempoolId_t) | | ||
| 307 | +| `NpuGraphOpHandler` / handler | `_npugraph_handlers/npugraph_handler.py` | `_fa3_graph_handler.py` | | ||
| 308 | +| `FA3` / `flash attention` | `_fa3_graph_handler.py` | `npu_fusion_attention_graph.py` | | ||
| 309 | +| `IFA` / `infer attention` | `ifa_handler.py` | `FlashAttentionKernelNpuOpApi.cpp` | | ||
| 310 | +| `update_graph` / `update` | `graphs.py` (_GraphDispatchMode) | `_fa3_graph_handler.py` | | ||
| 311 | +| `graph_task_group` | `Graph.cpp` (pybind) | `NPUGraph.cpp`, `AclInterface.cpp` | | ||
| 312 | +| `graph_task_update` | `Graph.cpp` (pybind) | `NPUGraph.cpp` | | ||
| 313 | +| `Generator` / `RNG` / `seed` | `NPUGeneratorImpl.h/.cpp` | `graphs.py` | | ||
| 314 | +| `register_fake` / `fake tensor` | Dynamo 注册 | `issues/custom-op-register-fake/` | | ||
| 315 | +| `HCCL` / `ProcessGroup` | `ProcessGroupHCCL.cpp` | `NPUGraphsUtils.h` | | ||
| 316 | +| `Stream` capture status | `NPUGraphsUtils.h/.cpp` | `graphs.py` | | ||
| 317 | +| `debug_dump` / `dump` | `NPUGraph.cpp` | `Graph.cpp` (pybind) | | ||
| 318 | +| `npu_fusion_attention_graph` | `npu_fusion_attention_graph.py` | `ascend_graph_pass.py` | | ||
| 319 | +| `graph_breaks` / Dynamo | — | `utils/_graph_tree.py` | | ||
| 320 | +| `npugraph_ex` | `npugraph_ex/__init__.py` | `dynamo/__init__.py` | | ||
| 321 | +| `is_current_stream_capturing` | `NPUGraphsUtils.cpp` | `graphs.py` | | ||
| 322 | +| `empty_cache` crash | `NPUCachingAllocator.cpp` | `docs/analysis/empty_cache_hccl_unmap_crash.md` | | ||
| 323 | +| `save_npugraph_tensor` / `print` | `graphs.py` (调试工具) | — | | ||
| 324 | +| `compile` / inductor graph | `graph.py`, `ascend_graph_pass.py` | `dvm/graph_fusion.py` | | ||
| 325 | + | ||
| 326 | +--- | ||
| 327 | + | ||
| 328 | +## 五、各使能方式的文件路径差异 | ||
| 329 | + | ||
| 330 | +| 使能方式 | 入口文件 | Tree层 | Handler层 | C++核心 | | ||
| 331 | +|---------|---------|--------|-----------|---------| | ||
| 332 | +| 1. `NPUGraph()` 底层 | `graphs.py` (NPUGraph类) | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` | | ||
| 333 | +| 2. `graph(g)` 上下文 | `graphs.py` (graph类) | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` | | ||
| 334 | +| 3. `make_graphed_callables` | `graphs.py` (L889) | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` | | ||
| 335 | +| 4. `torch.compile(backend="npugraphs")` | `utils/_graph_tree.py` | `_graph_tree.py` | `_npugraph_handlers/` | `NPUGraph.cpp` | | ||
| 336 | +| 5. `torch.compile(backend="npugraph_ex")` | `npugraph_ex/` | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` | | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | +# 知识领域速查 | ||
| 2 | + | ||
| 3 | +> 从 `SKILL.md` 拆分,知识问答时按需查阅。 | ||
| 4 | + | ||
| 5 | +## 常见问题→回答要点→涉及文件 | ||
| 6 | + | ||
| 7 | +| 用户问 | 回答要点 | 涉及文件 | | ||
| 8 | +|--------|---------|---------| | ||
| 9 | +| "NPUGraph 怎么工作" | 四种使能方式 + capture→compile→replay 生命周期 | `graphs.py`, `NPUGraph.cpp` | | ||
| 10 | +| "图捕获期间发生了什么" | Stream 属性设置 → 内存池切换 → ACL capture begin → 记录算子 | `NPUGraph.cpp: capture_begin()` | | ||
| 11 | +| "图重放和直接执行的差异" | ACL 图一次性编译 → 后续重放跳过逐算子 dispatch | `NPUGraph.cpp: replay()` | | ||
| 12 | +| "update 机制是什么" | 动态形状时更新指针/形状参数,不重新捕获 | `_fa3_graph_handler.py`, `graphs.py:_GraphDispatchMode` | | ||
| 13 | +| "graph tree 怎么管理多图" | warmup→capture→compile→cache,LRU 淘汰 | `_graph_tree.py` | | ||
| 14 | +| "为什么会有 graph_breaks" | Dynamo 图断裂原因:data-dependent control flow 等 | `utils/_graph_tree.py: check_for_skip()` | | ||
| 15 | +| "FA3 的图模式有什么特殊" | TND + dropout fallback、CPU tensor 参数限制 | `_fa3_graph_handler.py` | | ||
| 16 | +| "图和内存的关系" | 图捕获期间禁止新分配,专用 MemPool | `NPUCachingAllocator.cpp` | | ||
| 17 | +| "super kernel 是什么" | aclsk 编译器优化编译图,多算子融合 | `SkInterface.cpp`, `Graph.cpp` | | ||
| 18 | +| "怎么调试图内数据" | save_npugraph_tensor / print_npugraph_tensor / debug_dump | `graphs.py` (调试工具) | | ||
| @@ -0,0 +1,89 @@ | |||
| 1 | +# 技能设计原理 | ||
| 2 | + | ||
| 3 | +> 记录 NPUGRAPH_SKILL 中每个技能的设计决策和权衡。 | ||
| 4 | + | ||
| 5 | +--- | ||
| 6 | + | ||
| 7 | +## 1. 设计原则 | ||
| 8 | + | ||
| 9 | +### PR-001: 不重复原则 | ||
| 10 | + | ||
| 11 | +**规则**: 如果 `.npugraphs/` 已有该能力,NPUGRAPH_SKILL 不做。 | ||
| 12 | + | ||
| 13 | +**举例**: | ||
| 14 | +- `.npugraphs/` 已有 `implementation` (TDD 编码) → NPUGRAPH_SKILL 不设编码技能 | ||
| 15 | +- `.npugraphs/` 已有 `code-analysis` (源码分析) → NPUGRAPH_SKILL 不设源码分析技能 | ||
| 16 | +- `.npugraphs/` 已有 `requirements` (需求设计) → NPUGRAPH_SKILL 不设需求分析技能 | ||
| 17 | + | ||
| 18 | +### PR-002: 单文件核心 + 参考资料分离 | ||
| 19 | + | ||
| 20 | +**规则**: 每个技能的核心逻辑在 `SKILL.md` 中自包含,参考资料放到 `references/`。 | ||
| 21 | + | ||
| 22 | +**原因**: 跟从 `.npugraphs/` 的已证明模式。Claude Code 加载 `SKILL.md` 到上下文,按需读取 `references/`。 | ||
| 23 | + | ||
| 24 | +### PR-003: 脚本优先于手动操作 | ||
| 25 | + | ||
| 26 | +**规则**: 能自动化的操作提供 Python 脚本,在技能中标注 `python scripts/xxx.py` 而非 `grep | awk | sort`。 | ||
| 27 | + | ||
| 28 | +**原因**: Bash 管道易出错且不好维护。Python 脚本可测试、可复用、可被 MCP 包装。 | ||
| 29 | + | ||
| 30 | +### PR-004: 结构化输出 | ||
| 31 | + | ||
| 32 | +**规则**: 技能输出带有明确的字段(Phase、时间线、错误码),JSON 可序列化。 | ||
| 33 | + | ||
| 34 | +**原因**: 方便后续 MCP 集成和跨技能数据传递。 | ||
| 35 | + | ||
| 36 | +--- | ||
| 37 | + | ||
| 38 | +## 2. 技能设计决策 | ||
| 39 | + | ||
| 40 | +### graph-mode-diagnostics | ||
| 41 | + | ||
| 42 | +| 决策点 | 选择 | 原因 | | ||
| 43 | +|--------|------|------| | ||
| 44 | +| 诊断流程 | 5 阶段(分类→定位→模式→追踪→报告) | 继承自 `.npugraphs/debugging`,保持一致 | | ||
| 45 | +| 阶段划分 | Capture / Replay / Memory / Update / Compile | 对标图执行生命周期 | | ||
| 46 | +| 是否含修复 | 否(handoff 到 implementation) | 遵循 Iron Law:先定位再修复 | | ||
| 47 | + | ||
| 48 | +### graph-performance-profiling | ||
| 49 | + | ||
| 50 | +| 决策点 | 选择 | 原因 | | ||
| 51 | +|--------|------|------| | ||
| 52 | +| 指标选用 | P50/P99 而非 mean/std | 性能分析的标准实践,P99 更反映长尾 | | ||
| 53 | +| 对比方式 | 同脚本两次运行再对比 | 避免跨环境差异(CANN 版本等) | | ||
| 54 | +| warmup 次数 | 默认 10 次 | 足够消除首次编译开销 | | ||
| 55 | + | ||
| 56 | +### graph-log-analyzer | ||
| 57 | + | ||
| 58 | +| 决策点 | 选择 | 原因 | | ||
| 59 | +|--------|------|------| | ||
| 60 | +| 模式命名 | E-/T-/M- 三系列 | E=错误, T=时间线, M=内存,可以无限扩展 | | ||
| 61 | +| 解析器实现 | Python 而非 Bash | 支持流式处理、JSON 输出、可 MCP 化 | | ||
| 62 | +| 异常检测 | 正则匹配 + 后续 ML | v0.1.0 用正则足够,v2.0 可升级 ML | | ||
| 63 | + | ||
| 64 | +### graph-mcp-integration | ||
| 65 | + | ||
| 66 | +| 决策点 | 选择 | 原因 | | ||
| 67 | +|--------|------|------| | ||
| 68 | +| MCP 启用顺序 | Context7 → NPU-Log-Parser → Playwright → 其他 | 先启用最快的(文档查询),再自建核心工具 | | ||
| 69 | +| 自建 MCP | Python stdio 协议 | 开发简单,与 Claude Code MCP 客户端兼容 | | ||
| 70 | + | ||
| 71 | +--- | ||
| 72 | + | ||
| 73 | +## 3. 与 `.npugraphs/` 的差异 | ||
| 74 | + | ||
| 75 | +| 维度 | .npugraphs/ | NPUGRAPH_SKILL | | ||
| 76 | +|------|------------|----------------| | ||
| 77 | +| 语言 | 英文为主 | 中文为主(面向中文团队) | | ||
| 78 | +| 触发方式 | `/command` slash 命令 | 关键词 + 上下文匹配 | | ||
| 79 | +| MCP 集成 | 无 | 4 个 MCP 推荐 + 3 个自建 | | ||
| 80 | +| 脚本 | 无独立脚本目录 | `scripts/` 含 3 个 Python 脚本 | | ||
| 81 | +| 分发方式 | 复制 `.npugraphs/` 目录 | 需额外复制 `NPUGRAPH_SKILL/` 和 `NPUGRAPH_SKILL_DOC.md` | | ||
| 82 | + | ||
| 83 | +--- | ||
| 84 | + | ||
| 85 | +## 4. 待决策事项 | ||
| 86 | + | ||
| 87 | +1. **是否需要独立 git 仓库?** `.npugraphs/` 是独立仓库,NPUGRAPH_SKILL 目前放在 `wtd/` 下。如果要独立分发,需要拆出来。 | ||
| 88 | +2. **是否需要安装脚本?** 目前手动创建目录。如果有 Cursor rule 安装需求,参考 `.npugraphs/install.sh` 模式。 | ||
| 89 | +3. **MCP 自建优先级** — NPU-Log-Parser MCP 是否要先于 Playwright? | ||
| @@ -0,0 +1,189 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +""" | ||
| 3 | +图模式健康检查 — 检查环境配置是否正确,快速诊断常见配置问题 | ||
| 4 | + | ||
| 5 | +用法: | ||
| 6 | + python check_graph_health.py [--verbose] | ||
| 7 | + | ||
| 8 | +检查项: | ||
| 9 | + 1. CANN 环境完整性 | ||
| 10 | + 2. NPU 设备状态 | ||
| 11 | + 3. torch_npu 版本 | ||
| 12 | + 4. NPUGraph 功能正常性 | ||
| 13 | + 5. 日志环境变量 | ||
| 14 | + 6. 图 Pool 状态 | ||
| 15 | +""" | ||
| 16 | + | ||
| 17 | +import os | ||
| 18 | +import sys | ||
| 19 | +import argparse | ||
| 20 | +import subprocess | ||
| 21 | +from datetime import datetime | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +def check(label: str, passed: bool, detail: str = '', critical: bool = False) -> dict: | ||
| 25 | + return { | ||
| 26 | + 'label': label, | ||
| 27 | + 'passed': passed, | ||
| 28 | + 'detail': detail, | ||
| 29 | + 'critical': critical, | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +def run_checks(verbose: bool = False) -> list[dict]: | ||
| 34 | + results = [] | ||
| 35 | + | ||
| 36 | + # 1. CANN 环境 | ||
| 37 | + cann_home = os.environ.get('ASCEND_HOME', os.environ.get('ASCEND_TOOLKIT_HOME', '')) | ||
| 38 | + results.append(check( | ||
| 39 | + 'ASCEND_HOME 已设置', | ||
| 40 | + bool(cann_home), | ||
| 41 | + cann_home or '未设置——CANN 环境可能不完整', | ||
| 42 | + critical=True, | ||
| 43 | + )) | ||
| 44 | + | ||
| 45 | + # 2. Python 环境 | ||
| 46 | + try: | ||
| 47 | + import torch | ||
| 48 | + torch_ver = torch.__version__ | ||
| 49 | + results.append(check('PyTorch 已安装', True, f'版本: {torch_ver}')) | ||
| 50 | + except ImportError: | ||
| 51 | + results.append(check('PyTorch 已安装', False, 'torch 未安装', critical=True)) | ||
| 52 | + return results | ||
| 53 | + | ||
| 54 | + try: | ||
| 55 | + import torch_npu | ||
| 56 | + npu_ver = getattr(torch_npu, '__version__', 'unknown') | ||
| 57 | + results.append(check('torch_npu 已安装', True, f'版本: {npu_ver}')) | ||
| 58 | + except ImportError: | ||
| 59 | + results.append(check('torch_npu 已安装', False, 'torch_npu 未安装', critical=True)) | ||
| 60 | + return results | ||
| 61 | + | ||
| 62 | + # 3. NPU 设备 | ||
| 63 | + try: | ||
| 64 | + device_count = torch.npu.device_count() | ||
| 65 | + if device_count > 0: | ||
| 66 | + device_name = torch.npu.get_device_name(0) | ||
| 67 | + results.append(check( | ||
| 68 | + 'NPU 设备可用', | ||
| 69 | + True, | ||
| 70 | + f'设备数: {device_count}, 型号: {device_name}', | ||
| 71 | + critical=True, | ||
| 72 | + )) | ||
| 73 | + else: | ||
| 74 | + results.append(check('NPU 设备可用', False, '未检测到 NPU 设备', critical=True)) | ||
| 75 | + except Exception as e: | ||
| 76 | + results.append(check('NPU 设备可用', False, str(e), critical=True)) | ||
| 77 | + | ||
| 78 | + # 4. NPUGraph 导入 | ||
| 79 | + try: | ||
| 80 | + from torch_npu.npu import NPUGraph, graph, make_graphed_callables | ||
| 81 | + results.append(check('NPUGraph API 可用', True, 'NPUGraph/graph/make_graphed_callables')) | ||
| 82 | + except ImportError as e: | ||
| 83 | + results.append(check('NPUGraph API 可用', False, str(e))) | ||
| 84 | + | ||
| 85 | + # 5. 日志环境变量 | ||
| 86 | + log_vars = { | ||
| 87 | + 'TORCH_NPU_LOGS': os.environ.get('TORCH_NPU_LOGS', '(未设置)'), | ||
| 88 | + 'TORCH_NPU_GRAPHS_LOG_LEVEL': os.environ.get('TORCH_NPU_GRAPHS_LOG_LEVEL', 'INFO(默认)'), | ||
| 89 | + 'TORCH_NPU_GRAPHS_DEBUG': os.environ.get('TORCH_NPU_GRAPHS_DEBUG', '0(默认)'), | ||
| 90 | + 'TORCH_NPUGRAPH_GC': os.environ.get('TORCH_NPUGRAPH_GC', '0(默认)'), | ||
| 91 | + } | ||
| 92 | + results.append(check( | ||
| 93 | + '日志环境变量', | ||
| 94 | + True, | ||
| 95 | + '; '.join(f'{k}={v}' for k, v in log_vars.items()), | ||
| 96 | + )) | ||
| 97 | + | ||
| 98 | + # 6. NPUGraph 基础功能测试 | ||
| 99 | + if device_count > 0: | ||
| 100 | + try: | ||
| 101 | + x = torch.randn(10, 10, device='npu') | ||
| 102 | + g = NPUGraph() | ||
| 103 | + g.capture_begin() | ||
| 104 | + y = x + 1 | ||
| 105 | + g.capture_end() | ||
| 106 | + g.replay() | ||
| 107 | + torch.npu.synchronize() | ||
| 108 | + results.append(check('NPUGraph 基础捕获/重放', True, '简单图正常')) | ||
| 109 | + except Exception as e: | ||
| 110 | + results.append(check('NPUGraph 基础捕获/重放', False, str(e))) | ||
| 111 | + | ||
| 112 | + try: | ||
| 113 | + with graph() as g: | ||
| 114 | + y = x * 2 | ||
| 115 | + torch.npu.synchronize() | ||
| 116 | + results.append(check('context manager graph() 可用', True, '上下文管理器模式正常')) | ||
| 117 | + except Exception as e: | ||
| 118 | + results.append(check('context manager graph() 可用', False, str(e))) | ||
| 119 | + | ||
| 120 | + # 7. 内存信息 | ||
| 121 | + try: | ||
| 122 | + allocated = torch.npu.memory_allocated() / 1024**2 | ||
| 123 | + reserved = torch.npu.memory_reserved() / 1024**2 | ||
| 124 | + total = torch.npu.get_device_properties(0).total_memory / 1024**2 | ||
| 125 | + results.append(check( | ||
| 126 | + '内存状态', | ||
| 127 | + True, | ||
| 128 | + f'已分配: {allocated:.0f}MB / 已预留: {reserved:.0f}MB / 总容量: {total:.0f}MB', | ||
| 129 | + )) | ||
| 130 | + except Exception as e: | ||
| 131 | + results.append(check('内存状态', False, str(e))) | ||
| 132 | + | ||
| 133 | + # 8. CANN 版本 | ||
| 134 | + try: | ||
| 135 | + result = subprocess.run( | ||
| 136 | + ['npu-smi', 'info', '-t', 'board', '-i', '0'], | ||
| 137 | + capture_output=True, text=True, timeout=10 | ||
| 138 | + ) | ||
| 139 | + results.append(check( | ||
| 140 | + 'npu-smi 可用', | ||
| 141 | + result.returncode == 0, | ||
| 142 | + result.stdout.strip()[:200] if result.returncode == 0 else result.stderr.strip()[:200], | ||
| 143 | + )) | ||
| 144 | + except FileNotFoundError: | ||
| 145 | + results.append(check('npu-smi 可用', False, 'npu-smi 命令未找到')) | ||
| 146 | + except Exception as e: | ||
| 147 | + results.append(check('npu-smi 可用', False, str(e))) | ||
| 148 | + | ||
| 149 | + return results | ||
| 150 | + | ||
| 151 | + | ||
| 152 | +def print_report(results: list[dict], verbose: bool = False): | ||
| 153 | + """打印健康报告""" | ||
| 154 | + passed = sum(1 for r in results if r['passed']) | ||
| 155 | + failed = sum(1 for r in results if not r['passed']) | ||
| 156 | + critical_failed = sum(1 for r in results if not r['passed'] and r['critical']) | ||
| 157 | + | ||
| 158 | + print(f"=== NPU 图模式健康检查 ===") | ||
| 159 | + print(f"时间: {datetime.now().isoformat()}") | ||
| 160 | + print(f"结果: {passed} 通过, {failed} 失败 ({critical_failed} 严重)") | ||
| 161 | + print() | ||
| 162 | + | ||
| 163 | + for r in results: | ||
| 164 | + icon = 'PASS' if r['passed'] else ('CRIT' if r['critical'] else 'FAIL') | ||
| 165 | + print(f" [{icon}] {r['label']}") | ||
| 166 | + if (not r['passed'] or verbose) and r['detail']: | ||
| 167 | + print(f" {r['detail']}") | ||
| 168 | + | ||
| 169 | + print() | ||
| 170 | + if critical_failed > 0: | ||
| 171 | + print("结论: 环境存在严重问题,需优先解决 CRIT 项") | ||
| 172 | + sys.exit(1) | ||
| 173 | + elif failed > 0: | ||
| 174 | + print("结论: 存在非严重问题,建议解决 FAIL 项") | ||
| 175 | + else: | ||
| 176 | + print("结论: 环境健康,NPUGraph 功能正常") | ||
| 177 | + | ||
| 178 | + | ||
| 179 | +def main(): | ||
| 180 | + parser = argparse.ArgumentParser(description='NPU 图模式健康检查') | ||
| 181 | + parser.add_argument('--verbose', '-v', action='store_true', help='显示详细信息') | ||
| 182 | + args = parser.parse_args() | ||
| 183 | + | ||
| 184 | + results = run_checks(verbose=args.verbose) | ||
| 185 | + print_report(results, verbose=args.verbose) | ||
| 186 | + | ||
| 187 | + | ||
| 188 | +if __name__ == '__main__': | ||
| 189 | + main() | ||