关于 ascend-profiling-anomaly
用于分析 Ascend NPU 性能分析数据的技能,面向性能工程师。它通过设备空闲间隙检测、结构分解和模型架构分析,分别呈现异常证据与可能归因,并生成独立的架构报告。
主要功能
- 01分解步骤、结构、区块与算子
- 02合并设备区间并检测空闲间隙
- 03结合主机事件标注潜在归因
- 04汇总异常标签与周期性指标
- 05生成独立的架构分析报告
适用场景
- 01定位训练或推理步骤中的设备空闲时间
- 02检查层边界及结构内部的间隙
- 03依据主机事件评估同步或通信风险
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ascend/ agent-skills --skill ascend-profiling-anomaly安装后在 Claude Code 中输入/ascend-profiling-anomaly确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/architecture_report_template.mdkernel_data_guide.mdrulebook.mdschema.jsonscripts/reference_host_gap_branch.py详细介绍
Ascend Profiling 异常发现技能
用途
通过三个并行流水线分析 Ascend NPU 的 Profiling 数据:
- 结构分解:step → structure(层)→ block / side → op → PMU 判定 — 回答时间花在哪里。
- 异常发现:step → 设备忙碌区间并集 → 空泡检测 → 异常标签 → 软归因 — 回答哪些情况看起来不自然,隐藏问题可能在哪里。
- 模型架构分析:FIA 时间线 → pass 边界 → 层分类 → 每层子结构 → 通信流水线 → 架构摘要 — 回答这是什么模型,以及各组件如何执行。生成单独的 Markdown 报告文件。
核心理念是关注点分离:“存在异常”是基于设备时间区间得出的硬事实;“异常为何存在”是软归因,可能需要额外证据。即使 Profiling 配置较弱(无调用栈、无形状信息、主机事件稀疏),该技能仍必须可靠地发现设备空闲空泡和风险标签。
参考文件 — 何时阅读
开始分析前请先阅读以下文件:
| 文件 | 阅读时机 | 内容 |
|---|---|---|
references/kernel_data_guide.md | 始终阅读 — 首先阅读 | kernel_details.csv、op_summary、trace_view.json 的原始数据列模式;step → structure → block/side → op 层级;如何在各层级解析、筛选和分配 kernel;多流处理;各层级的耗时聚合 |
references/rulebook.md | 始终阅读 | 异常阈值、标记规则、判定表、软归因规则、AICPU 分类、等待锚点规则 |
references/architecture_report_template.md | 始终阅读 — 生成架构报告前阅读 | 独立 Markdown 架构报告的完整模板:必需章节、格式规则、层分类分析技术、通信重叠测量、每层耗时分解 |
references/schema.json | 生成结构化 JSON 输出时 | anomaly_discovery 输出对象的完整 JSON 模式 |
scripts/reference_host_gap_branch.py | 编写分析脚本时 | 区间合并、空泡指标、软归因、等待锚点评分的 Python 参考实现 |
流水线概览
完整状态机:
INGEST → INVENTORY → FACT_EXTRACTION → CANDIDATE_STEP_DETECTION →
STEP_GROUPING → MACRO_STEP_RESOLUTION → SEGMENTATION →
CLOCK_ACCOUNTING → ANOMALY_DISCOVERY → PERF_JUDGEMENT →
RECOMMENDATION → RENDER → DONE
↘
ARCHITECTURE_ANALYSIS → ARCH_REPORT_RENDER → ARCH_REPORT_SAVE
ANOMALY_DISCOVERY 位于 CLOCK_ACCOUNTING 之后、PERF_JUDGEMENT 之前。它接收已分段的 step 和 structure,并在其基础上运行空泡检测流水线。
ARCHITECTURE_ANALYSIS 与 PERF_JUDGEMENT 并行运行,使用来自 CLOCK_ACCOUNTING 的相同分段数据以及 FIA 时间线分析。它会生成单独的 Markdown 报告文件,并将其保存在 Profiling 数据旁。
数据层级:Step → Structure → Block/Side → Op
理解原始 kernel 数据如何映射到各个层级至关重要。完整列模式和解析细节请阅读 references/kernel_data_guide.md。以下是概念概览:
第 0 层:原始 Kernel
kernel_details.csv 中的每一行表示一次设备 kernel 执行 — 一次 AI Core 任务、AI CPU 任务或 HCCL 通信任务调用。关键字段:Name、Task Type、Start Time(us)、Duration(us)、Wait Time(us)、Accelerator Core、Stream ID、Input Shapes、Output Shapes。
第 1 层:Step
一个 step 表示一次训练/推理迭代。Step 由 trace_view.json 中的 ProfilerStep#N 用户标注或 Iteration#N 标记识别。每个 step 定义一个服务窗口 [S_i, S_{i+1})。所有开始时间落在此窗口内的 kernel 都属于 step i。
在 step 层级,计算:
- 总服务时间、设备忙碌区间并集、供给不足比例
- 启动前间隙、尾部间隙、内部空泡
- 每个 step 的异常标签和软根因标签
Level 2:结构(层)
在一个步骤内,内核会形成重复的结构,通常对应模型层(例如 Transformer 块、注意力层、MLP 块)。分段通过以下方式识别这些结构:
- 内核时间线中重复的名称模式序列
- 内核组之间显著的时间间隔
- 标记层边界的用户注释(如果存在)
每个结构都包含步骤窗口内一段连续的内核。
在结构级别,计算:
- 结构墙钟时间,即结构跨度内设备忙碌时间的并集
- 结构时间占步骤总时间的比例
- 每个结构的气泡指标(气泡是在此结构内,还是位于结构之间?)
Level 3:块 / 辅助操作
在每个结构内,内核分为:
- 块(主要计算路径):构成此层前向或反向传播过程的主导 AI Core 内核链。这些内核在主计算流上执行。
- 辅助操作(辅助运算):其他所有内容,包括小型逐元素运算、通信(HCCL)、AI CPU 回退运算、内存复制和同步事件。这些操作可能在单独的流上执行。
在块 / 辅助操作级别,同时维护四种计时视角:
wall_ms— 从第一个内核开始到最后一个内核结束的墙钟时间busy_union_ms— 合并后的设备忙碌时间(计入多流重叠)kernel_sum_ms— 所有内核持续时间的算术总和(忽略重叠)total_cost_ms— 所有内核的duration + wait总和
仅依据一个指标得出的结论并不完整。块的 kernel_sum 较高而 wall 较低,意味着流并行度较高。辅助操作的 total_cost 较高而 duration 较低,则意味着存在 wait-anchor 虚假热点风险。
Level 4:算子(单个算子)
最细粒度。每个算子可能产生一个或多个设备内核。算子级分析包括:
- 按总成本排名的顶级算子与按内核持续时间排名的顶级算子(这两种排名往往不同)
- Wait-anchor 检测:
wait_ratio > 0.95且duration极短、但total_cost较高的算子 - AICPU 分类:根据
masked_ratio对运行在 AI CPU 而非 AI Core 上的算子进行分类 - 小算子的初步判断:判断单个小算子究竟是真正的低效之处,还是噪声
异常发现流程(详细说明)
Phase 1:BUILD_DEVICE_INTERVALS
对于每个步骤,收集 kernel_details.csv 中所有设备内核区间:
device_intervals = []
for each kernel row where Start_Time_us is within step window:
s = max(step_start_us, row.Start_Time_us)
e = min(step_end_us, row.Start_Time_us + row.Duration_us)
if e > s:
device_intervals.append(Interval(s, e))
关键规则:
- 将内核区间裁剪到步骤窗口边界内
- 在进行区间统计之前应用通信去重规则(参见
references/kernel_data_guide.md中关于通信去重的章节) - 纳入 AI_CORE、AI_CPU 和 HCCL 任务——这些都计为“设备忙碌”
- 存在多个流时,将所有流的区间收集到同一集合中
Phase 2:MERGE_INTERVALS
按开始时间对区间排序,并合并重叠区间:
merged = merge(device_intervals) # see scripts/reference_host_gap_branch.py
busy_union = sum of merged segment durations
由此得到合并后的忙碌时间段,所有气泡指标均据此计算。
Phase 3:GAP_CLASSIFICATION
根据合并后的时间段,计算每个步骤的指标:
service_ms= 步骤窗口时长device_busy_union_ms= 合并后时间段的持续时间总和underfeed_ms= service − busy_unionunderfeed_ratio= underfeed / serviceprelaunch_gap_ms= start(first_merged_segment) − step_starttail_gap_ms= step_end − end(last_merged_segment)internal_bubble_total_ms= 相邻合并时间段之间的间隔总和largest_internal_bubble_ms= 相邻合并时间段之间的最大间隔bubble_count= 时间段之间的间隔数量
Phase 4:HOST_EVIDENCE_COLLECTION
对于每个气泡窗口(合并片段之间的间隙,或启动前/末尾间隙),在 trace_view.json 中扫描相同时间范围内的主机事件:
要收集的主机事件类别:
cpu_op/python_function/user_annotation— 一般主机活动AscendCL@*— ACL 运行时事件HostToDevice/torch_to_npu/aclrtMemcpy*/aclrtSynchronize*— 同步/拷贝标记c10d/Hccl/hcom/StreamWaitEvent/Notify_Wait— 通信标记
计算重叠比:
host_visible_coverage_ratio= 任意主机事件覆盖的气泡比例sync_marker_overlap_ratio= 同步/拷贝标记覆盖的比例comm_marker_overlap_ratio= 通信标记覆盖的比例
Phase 5:SOFT_ATTRIBUTION
根据重叠比,为每个显著气泡分配概率级别的标签:
| 条件 | 标签 |
|---|---|
| sync_overlap ≥ 0.20 | possible_sync_or_h2d |
| comm_overlap ≥ 0.20 | possible_comm_wait |
| host_coverage < 0.05 | possible_untraced_host_blocking |
| host_coverage ≥ 0.10,但同步/通信并非主要因素 | possible_host_launch_lag |
| host_parallelism < 1.2,且以上情况均不适用 | possible_python_serialization_or_lock |
| 均不适用 | insufficient_evidence |
可以同时存在多个标签。这些标签明确不代表唯一的根因。
Phase 6:ANOMALY_TAGGING
根据 references/rulebook.md 决策表应用异常标签。核心标签:
气泡严重程度:DEVICE_IDLE_GAP_HEAVY、PRELAUNCH_GAP_HEAVY、TAIL_GAP_HEAVY、INTERNAL_BUBBLE_HEAVY
风险标签:HOST_ORIGINATED_RISK、COMM_SYNC_RISK、WAIT_POLLUTION_RISK、WAIT_ANCHOR_FALSE_HOTSPOT、AICPU_EXPOSED_RISK、UNTRACED_HOST_BLOCKING_RISK、PARTIAL_CAPTURE_BOUNDARY、VARIABLE_SHAPE_SAME_TEMPLATE
Phase 7:WAIT_ANCHOR_SCAN
在算子层级扫描虚假热点:
wait_ratio = wait_us / (duration_us + wait_us)
if wait_ratio > 0.95 and duration_us < 10.0 and total_cost_rank <= 10:
tag WAIT_ANCHOR_FALSE_HOTSPOT
这些算子会吸收空闲等待时间,因此看起来耗时较长,但其内核执行时间可以忽略不计。请在根因排序中降低它们的优先级。
Phase 8:GROUP_AGGREGATION
按 step_group_id 汇总步骤级指标:
- 为每个气泡指标计算平均值、中位数、P90、P95
- 如果组内 ≥60% 的步骤满足
bubble_count > 0,则recurring_bubble_pattern= true dominant_idle_pattern= 占比最高的类型:prelaunch / internal_bubble / tail
Phase 9:PRODUCE_OUTPUT
将异常结果与结构拆分结果合并。报告必须包含:
隐藏问题发现部分
- 主要步骤/组的气泡指标(service、busy_union、underfeed_ratio)
- 原始内核证据表:对于每个排名靠前的气泡窗口,列出间隙前后紧邻的内核及其名称、任务类型、耗时和流,以便专家在时间线上定位准确位置
- 前 5 个气泡窗口的时间戳、范围和主机证据
- 步骤组中的气泡周期性统计
- 主机证据覆盖情况评估
- 带证据链的软根因标签
- 后续采样建议
气泡优先摘要(固定的 5 个问题模板)
- 是否存在显著的设备空闲气泡?
- 它们主要集中在哪种步骤类型/组?
- 它们主要属于启动前/末尾/内部/步骤间气泡中的哪种?
- 是否存在显著的主机来源风险?
- 证据是否足以确定根因?如果不足,请明确说明。
结构级气泡深入分析
对于主要步骤,按结构拆分气泡贡献:
- 哪些结构包含最大的内部气泡?
- 气泡集中在结构边界(层与层之间),还是结构内部?
- 对于存在大气泡的结构,其周边内核的名称和任务类型是什么?
模型架构分析流程(单独的 Markdown 报告)
此流程会生成一份独立的 Markdown 报告文件,记录根据性能分析数据逆向还原的模型架构。完整模板、格式规则和分析技术请参阅 references/architecture_report_template.md。
报告将保存为 model_architecture_report_<profiling_dir_name>.md,位于性能分析目录或输出目录中。
何时生成架构报告
始终生成。 每次性能分析都必须在输出异常发现结果的同时生成此报告。架构报告提供了必要的背景信息,有助于理解异常发现结果。
架构分析阶段
阶段 A1:FIA_TIMELINE_ANALYSIS
使用 FusedInferAttentionScore(FIA)调用作为主要结构标记:
- 从
kernel_details.csv中提取所有 FIA 内核(名称中包含FusedInferAttentionScore) - 按
Start Time(us)排序 - 将每个 FIA 分类为 prefill(持续时间 > 10ms)或 decode(持续时间 < 1ms)
- 确定 pass 数量:
num_passes = total_FIA / FIA_per_pass - 根据 prefill 和 decode FIA 簇之间的时间戳间隔,识别阶段转换
阶段 A2:PASS_BOUNDARY_DETECTION
对每个前向 pass,确定:
- FIA 索引范围(例如 #0–#94)
- 时间跨度(绝对时间戳)
- 从第一个内核到最后一个内核的墙钟时间
- 平均 prefill FIA 持续时间
- 内核总数
应记录跨 pass 的差异(FIA 持续时间、墙钟时间);这些差异可能揭示 KV 缓存增长或内存压力的影响。
阶段 A3:LAYER_CLASSIFICATION
对每一层(由相邻的 FIA 调用界定),提取内核序列并进行分类:
| 分类内核 | 层类型 |
|---|---|
| 没有 MoE 标记(无 MoeGatingTopK、无 DFC、无 GroupedMatmul) | Dense |
| 存在 DispatchFFNCombine | MoE+DFC |
| 存在 GroupedMatmul 和 alltoallv(无 DFC) | MoE+GMM |
| 存在采样操作(ArgMax、rejection_*) | 包含 decode/采样逻辑 |
构建汇总表:层类型 | 层范围 | 数量 | 特征
阶段 A4:CROSS_VERIFICATION
统计每个 pass 中的关键操作数,并验证其与层分类相符:
- FIA 数量应等于层数
- DispatchFFNCombine 数量应与 MoE+DFC 层数相符
- GroupedMatmul 数量应与 MoE+GMM 层数加 decode 层数相符
- MoeGatingTopK 数量应与所有 MoE 层(DFC + GMM + decode)相符
差异表明分类可能有误;请在继续之前解决这些问题。
阶段 A5:PER_LAYER_SUBSTRUCTURE
对每种不同的层类型,分析内核执行序列:
- 按功能角色对内核分组(attention、projection、TP 通信、MoE 路由、专家分发、专家 FFN、MoE 后归一化、下一层准备、EP 通信、采样)
- 测量每个功能组的墙钟时间
- 确定每个组运行所在的流
- 计算时间分解:组件 | 墙钟时间 | 占层的比例
- 记录该层类型特有的异常(第 0 层的预热开销、转换层中的额外内核、应在 AI_CORE 上运行的 AICPU 操作)
以带时间标注和流标签的内核序列树形式呈现。确切的树形表示格式请参阅模板。
阶段 A6:DECODE_ANALYSIS
Decode 层需要单独分析,因为其成本分布有本质区别:
- FIA 持续时间大幅下降(prefill 28ms → decode 0.2ms)
- 通信(尤其是 EP 的 all-to-all)通常占主导
- 专家计算从融合形式(DFC)转为显式形式(GroupedMatmul)
生成主要成本表,并解释其成本分布为何不同于 prefill。
阶段 A7:COMMUNICATION_PIPELINE
记录多流重叠策略:
- 说明每个流的用途(主计算、AI_CPU 通信、HCCL、alltoall)
- 测量各流之间的重叠比例
- 绘制 ASCII 流水线图,展示通信如何隐藏在计算之后
- 计算总通信量中被隐藏(重叠)的比例
- 解释 kernel_sum >> wall 的关系
阶段 A8:ARCH_REPORT_RENDER
按照 references/architecture_report_template.md 中的模板,将所有发现整理成 Markdown 报告。必须包含以下 10 个部分:
- 配置上下文
- 模型架构判定(证据链表)
- 前向传播边界(逐次传递表)
- 层分类(类型表)
- 交叉验证表
- 逐层子结构(核函数序列树 + 每种层类型的计时明细)
- 解码阶段分析(主要开销 + 预填充与解码对比)
- 通信流水线结构(流表 + ASCII 流水线图)
- 层间差异(对比表)
- 模型架构摘要(ASCII 模型图 + 执行时间线)
阶段 A9:ARCH_REPORT_SAVE
将报告保存为 model_architecture_report_<profiling_dir_name>.md。告知用户文件所在位置。
关键约束
- **绝不能因根本原因不明确而跳过异常发现。**气泡事实是确定性结论;根因标签是非确定性结论。两者都必须始终报告。
- 未检查等待锚点风险前,绝不能将高等待、短时长的操作称为真正的热点。
- 即使整体步骤受设备限制,也绝不能忽略局部气泡。
- 绝不能只使用一种计时指标——始终在块/侧级别进行双时钟核算(wall_ms + busy_union_ms + kernel_sum_ms + total_cost_ms)。
- 主机端证据稀少时,始终输出
insufficient_evidence或possible_untraced_host_blocking——绝不能默默省略异常部分。 - 始终提供已报告气泡附近的原始核函数上下文——每个气泡间隙前后的核函数名称、任务类型、时长和流 ID。
- 使用分层确定性措辞:事实使用陈述句;根因使用
possible / probable / insufficient evidence。 - 始终将模型架构 Markdown 报告单独生成为一个文件——绝不能只将其合并到异常输出中。
- **绝不能跳过逐层子结构分析。**每种不同的层类型都必须有自己的核函数序列树、计时明细和流注释。
- 架构报告中始终包含证据链表——读者必须能看到如何根据原始 FIA 数据确定层数和传递结构。
- 定稿架构报告前始终进行交叉验证,核对操作数量与层分类是否一致。必须解决差异,或明确注明。
优雅降级
| 缺失数据 | 影响 | 操作 |
|---|---|---|
record_shapes=false | 无法检测形状变化 | 气泡检测继续;跳过 VARIABLE_SHAPE_SAME_TEMPLATE 标签 |
with_stack=false | 软归因的具体程度降低 | 降低置信度;不影响气泡检测 |
| 主机事件稀疏 | 无法缩小根因类别范围 | UNTRACED_HOST_BLOCKING_RISK,requires_host_followup=true |
| 捕获边界截断 | 边缘间隙可能是伪影 | 在边界相邻间隙上标记 PARTIAL_CAPTURE_BOUNDARY |
没有 communication.json | 无法评估通信等待 | 跳过通信重叠分析,并在证据缺口中说明。架构报告省略通信流水线带宽统计,但仍记录流的角色 |
| 没有步骤标记 | 无法定义步骤窗口 | 回退到将全局捕获范围作为单个伪步骤 |
仅有 op_summary(没有 kernel_details) | 粒度较粗 | 改用算子级区间;在局限性中说明。架构报告使用算子计数进行层分类,但无法生成逐层内核序列树 |
| 未检测到 FIA 内核 | 无法通过 FIA 确定层边界 | 架构报告回退到其他结构标记(例如重复的内核模式、通信边界)。说明层数的置信度降低 |
| 捕获了单次前向传播 | 无法交叉验证各次传播的一致性 | 架构报告记录单次传播;说明无法分析不同传播之间的变化 |
| 未检测到解码 FIA | 仅捕获推理或预填充 | 架构报告省略解码阶段分析部分;说明捕获范围的局限性 |
输出约定摘要
每次分析必须生成两项输出:
1. 异常发现输出
顶层对象 anomaly_discovery,包含:enabled、dominant_group_id、global_device_gap_analysis、step_group_anomalies、bubble_windows、wait_anchor_ops、soft_root_cause_summary、requires_host_followup、confidence。
每个步骤结果必须包含气泡指标、异常标签和软根因标签。
完整的 JSON 架构请参阅 references/schema.json。
2. 模型架构报告(Markdown 文件)
生成独立的 Markdown 文件,并保存为 model_architecture_report_<profiling_dir_name>.md,其中包含架构模板要求的全部 10 个章节。完整规范请参阅 references/architecture_report_template.md。
报告至少必须包含:
- 用于证明层数和传播结构的证据链
- 包含所有不同层类型的层分类表
- 每种层类型的逐层内核序列树及计时细分
- 包含流重叠示意图的通信流水线结构
- 模型架构 ASCII 摘要图和逐次传播执行时间线
架构报告是理解模型结构的主要交付物。它必须独立完整,读者无需参考异常输出即可了解模型的完整执行过程。
建议
每条建议必须包含 scope(global/step_group/structure/side/op)、followup_required、evidence_gap 和 priority(P0–P3)。
常见后续处理模式:
- 气泡多且缺少堆栈 → 使用
with_stack=true重新分析 - 缺少形状信息且分组不稳定 → 设置
record_shapes=true - 主机风险高且证据不足 → 进行主机侧采样 / 查看线程视图
- 等待污染严重 → 检查通信和同步路径
- 结构间气泡较大 → 检查主机侧层调度延迟