CANN/cannbot-skills NPU Profiling采集技能
【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skillsname: model-infer-profiling description: | NPU 性能分析数据采集技能。用于华为昇腾NPU上的 PyTorch 模型性能分析。触发场景用户需要采集 NPU 性能数据用户提到 profiling、性能分析、tensorboard用户需要分析模型推理性能瓶颈用户使用 torch_npu.profiler用户遇到 profiler 解析失败或 JSON 截断问题关键要求必须用 ExperimentalConfig(Level1 PipeUtilization)否则 kernel_details.csv 只有 9 列、op_statistic/api_statistic 不生成。采集前先判断仓库有无轻框架内置 profenable_profiler有则用框架只调参数、无则注入。NPU Profiling 采集技能本技能帮助你在华为昇腾 NPU 上用torch_npu.profiler正确采集 PyTorch 模型性能数据产出与 Ascend 官方 TensorBoard/Profile 插件兼容的ASCEND_PROFILER_OUTPUT目录。采集有两条路径框架内置仓库已经把 profiler 接进推理循环你只改参数和注入采集仓库没有 profiler你自己写采集代码。第一步是 Step 0 路由判断用哪条。Step 0路由 —— 框架内置 vs 注入采集最先做怎么判断有没有框架内置 prof—— 满足任一即视为有# 1) 有没有 profiler 封装cann-recipes-infer 的标志 ls cann-recipes-infer/executor/utils/profiler_context.py 2/dev/null # 2) 推理配置/循环里有没有 enable_profiler 开关 grep -rn enable_profiler cann-recipes-infer/executor/ cann-recipes-infer/models/ 2/dev/null | head命中 →路径 A优先。本仓库cann-recipes-infer就属于这种profiler 已经接进推理引擎只需开开关 调参数。都没命中 →路径 B注入。通用判据迁到别的仓库时搜torch_npu.profiler、tensorboard_trace_handler、enable_profiler/profile配置项。已经接进推理循环的就走 A只有零散 API 的就走 B。采集前确认采集范围路由后、动手前先问用户判定走哪条路径后、真正动手前先问用户两件事有 AskUserQuestion 就用它给选项没有就直接文字提问。默认值见下要问的选项默认用户没特别要求时采哪个阶段prefill/decode/both两个都采both —— 默认采完整数据decode 采几步例如 3 / 10 / 30 / 全程足够覆盖稳态的步数建议 ≥ 10要完整数据就把步数设大补充约定避免采到无效数据prefill 通常每个 request 只有 1 步所以 prefill 的active固定为 1。第一次图编译 / JIT 必须在采集窗口外预热见路径 B「关键 3」否则采到的是编译时间不是推理时间。采完整数据 两个阶段都采 decode 步数取大而不是把 schedule 的warmup调大。路径 A使用框架内置采集cann-recipes-infer框架已经把torch_npu.profiler接进推理引擎你的工作只是开开关 → 按用户选择调参数 → 验收产物不要再注入新的采集代码。A.1 怎么开启在 YAML 的model_config下把开关打开model_config: enable_profiler: True # 默认 False然后照常bash cann-recipes-infer/executor/scripts/infer.sh --model model --yaml yaml跑。A.2 框架默认采了什么实测enable_profiler: True时框架的ProfilerManagercann-recipes-infer/executor/utils/profiler_context.py在推理引擎里自动干这些事阶段schedule 参数默认输出目录prefillactive1, skip_first0, warmup0, repeat1res_path/prof/prefill/decodeactive3, skip_first3, warmup0, repeat1res_path/prof/decode/配置已经是ExperimentalConfig(Level1 PipeUtilization)与本 skill 的硬性要求一致不用动。res_pathcann-recipes-infer/models/model/res/date/case/与日志log_0.log同级。prefill 和 decode各产一份prof/phase/.../ASCEND_PROFILER_OUTPUT/实测两个目录都会生成。注意prefill profiler 是active1每 request 只采 1 步 prefillprefill 本就只有 1 步设计如此实测能产出有效的 47 列 CSV。要更细的 prefill 分析多步/自定义窗口用注入路径模式 B。多卡时每个 rank 各采一份同一个enable_profiler开关控制。调用链execution_engine.py持有ProfilerManager每个 forward 调set_status(is_prefill)step()。model_runner.py里另有一套define_profilerprefillactive1、decodeactive10/skip_first10但离线 infer 实际走的是ProfilerManager这条调参数认profiler_context.py。A.3 怎么调采集参数只改这里框架的采集参数是硬编码在cann-recipes-infer/executor/utils/profiler_context.py的YAML 只暴露了enable_profiler总开关。要改采几步 / 从第几步开始 / 采哪个阶段改下面这些调用点的入参你想改的参数改哪里profiler_context.py语义实测decode 采几步activecreate_profiler默认参数decode 分支不传吃默认active3真正记录的步数。要完整数据就调大例如 30decode 从第几步开始skip_first同上默认skip_first3跳过最前面 N 个 decode step 不计入采集跳过启动抖动。从第skip_first1步进入采集周期prefill 采几步activeset_status的 prefill 分支写死active1prefill 每 request 只有 1 步保持 1只采 decode / 只采 prefill——在set_status里把不想采的那个分支的 profiler 换成FakeContextManager()或加判断跳过框架默认两个阶段都采要单采就短路掉另一个分支warmup/repeatwarmup/repeatcreate_profiler一般保持warmup0, repeat1清窗口规则改active/skip_first后务必满足decode 的prof.step()调用次数 ≈ 实际 decode 步数受max_new_tokens控制必须≥ skip_first warmup active精确边界才能跨出采集窗口实践再多 1 步收尾更稳。框架 decode 默认需要 ≈3 0 3 6步max_new_tokens32远够把active/skip_first调大时记得把 YAML 的scheduler_config.max_new_tokens同步调大并留余量否则窗口没跨出去、解析会出问题。改完直接重跑infer.sh不需要改 YAML 之外的推理代码。A.4 采完必须验收产物关键不能跳框架开了enable_profiler不等于拿到了可用数据。解析阶段CANNmsprof离线分析可能失败 —— 产物只剩一个被截断的trace_view.json、CSV 全缺。最常见的原因是 profiling 输出目录所在的文件系统/盘详见 A.5不是框架代码、不是权限、不是版本。所以采完立刻验收REScann-recipes-infer/models/model/res/date/case # 与 log_0.log 同级 # 1) 解析有没有报错最快的信号 grep -c run failed $RES/log_0.log # 期望 0非 0 解析失败 # 2) 你采的每个阶段都要查默认 both 就两个都查只采一个就只查那个 for PHASE in prefill decode; do [ -d $RES/prof/$PHASE ] || { echo WARN: prof/$PHASE 没生成该阶段未落盘 视同失败; continue; } OUT$(find $RES/prof/$PHASE -name ASCEND_PROFILER_OUTPUT -type d | head -1) echo $PHASE: ${OUT:-无 ASCEND_PROFILER_OUTPUT} ls $OUT 2/dev/null # 期望含 kernel_details.csv op_statistic.csv api_statistic.csv if [ -f $OUT/kernel_details.csv ]; then head -1 $OUT/kernel_details.csv | tr , \n | wc -l # 期望 ~47只有 9 没开 Level1PipeUtilization else echo kernel_details.csv 缺失 解析失败, 走 A.5 fi python3 -c import json;json.load(open($OUT/trace_view.json));print( trace_view VALID) 2/dev/null \ || echo trace_view.json 截断/无效 解析失败 done验收项期望不符合时log_0.log里run failed计数0非 0 → 解析阶段失败见 A.5先换输出目录你采的每个阶段都有prof/phase/.../ASCEND_PROFILER_OUTPUT/存在缺失比如默认 both 却没prof/prefill→ 该阶段没落盘视同失败、走 A.5kernel_details.csv列数~47缺失/打印1→ 解析失败走 A.5只有 9 → 配置没带Level1PipeUtilization框架默认带正常不会发生op_statistic.csv/api_statistic.csv存在缺失 → 同run failed走 A.5trace_view.json合法 JSON、以]结尾截断/报错 → 解析失败走 A.5A.5 验收不过怎么办先换输出目录再考虑注入框架enable_profiler的离线解析是 CANN 的msprof工具在跑。当出现所有 view 解析任务run failed 没有任何 CSV trace_view.json被截断这组症状时根因已实测定位最常见的原因不是框架代码、不是目录权限、不是torch_npu/CANN 版本而是profiling 输出目录所在的文件系统/盘。某些挂载数据盘 / 网络盘 / 特定 bind-mount会让 CANNmsprof的离线分析产不出 CSV而把输出换到另一个文件系统尤其本地盘就完全正常。实测同一份采集数据输出在原目录 → 全部 parser run failed、无 CSV、trace_view 截断只把输出目录换到另一个盘 → 框架原样产出kernel_details.csv47 列 全套 CSV、零 run failed。验证方法把那份失败的*_ascend_pt原始数据cp到另一个盘再torch_npu.profiler.profiler.analyse(dir)能解析出 CSV 就坐实是输出目录的问题。修复步骤按顺序首选 —— 把 profiling 输出换到另一个目录/文件系统优先本地盘重跑后重新走 A.4 验收。 profiler 输出路径 os.getenv(WORK_DIR)/os.getenv(RES_PATH)/prof/...execution_engine.py:83。注意function.sh会把WORK_DIR写成cann-recipes-infer/models/model直接export WORK_DIR...会被覆盖、不生效。可靠做法是把res目录软链到本地盘mkdir -p /tmp/recipes_res # 若 cann-recipes-infer/models/model/res 已是非空真实目录, 先移走/备份再软链 ln -sfn /tmp/recipes_res cann-recipes-infer/models/model/res bash cann-recipes-infer/executor/scripts/infer.sh --model model --yaml enable_profiler:True 的 yaml # 重新跑 A.4run failed 应为 0、kernel_details.csv 应 ~47 列多换几个盘定位哪个文件系统可用。换了几个盘都还不行再回退到路径 B 注入采集scripts/profile_template.py兜底把模型加载与单步推理填进去、自己起 profiler。一句话判定grep -c run failed log_0.log非 0 或kernel_details.csv缺失 →先把输出目录换到另一个本地盘重试多个盘都不行才走路径 B。路径 B注入 torch_npu.profiler 采集代码仓库没有框架内置 profiler或框架产物验收不过时自己写采集代码。下面三条关键是注入采集的核心。核心原理NPU profiler 能不能拿到有用数据取决于三件事ExperimentalConfig决定能采到哪些字段最关键schedule 决定 profiler 在每一步处于什么状态首次图编译 / JIT 必须放窗口外prefill / decode 按需选择采或不采关键 1ExperimentalConfig决定 kernel_details 是 47 列还是 9 列torch_npu 默认不开详细 AIC/AIV 流水 metric必须显式传ExperimentalConfigexperimental_config torch_npu.profiler._ExperimentalConfig( profiler_leveltorch_npu.profiler.ProfilerLevel.Level1, aic_metricstorch_npu.profiler.AiCMetrics.PipeUtilization, )产物带ExperimentalConfig不带默认kernel_details.csv列数47列含Stream ID、Input/Output Shapes、aic_mac_ratio、aic_mte2_ratio、aiv_vec_ratio…9列基本时间/dur/block_dimop_statistic.csv/api_statistic.csv✅ 生成❌ 缺失operator_details.csv、step_trace_time.csv、trace_view.json生成生成communication*.json仅多卡生成仅多卡生成不带ExperimentalConfig 拿不到 pipeline utilization、算子 shape 和 stream 归属。kernel_details.csv只有 9 列就是这个原因直接回来补 config。参数可选值说明profiler_levelLevel0/Level1/Level2默认 Level0 只收基本 traceLevel1 是推荐默认Level2 追加更细 SoC 计数、体积更大aic_metricsPipeUtilization/ArithmeticUtilization/Memory/MemoryL0/MemoryUB/ResourceConflictRatio/L2Cache只能选一组PipeUtilization定位 cube/vector/mte 流水 bound关心 HBM/L2 带宽选Memory/L2Cache绝大多数优化任务用Level1 PipeUtilization就够。关键 2schedule 要让最后一次step()跨出 active 窗口wait → warmup → active → (下一轮 / NONE)状态在每次prof.step()时转换。循环结束时若 profiler 还停在 RECORD没跨出 activetorch_npu 打[WARNING] Incorrect schedule: Stop profiler while current state is RECORD which may result in incomplete parsed data.规则prof.step()总调用次数 ≥skip_first warmup active 1。多出的 1 步让 schedule 跨出 active、触发收尾回调profiler 干净退出。写法 AMAX_STEPS 调够大WARMUP, ACTIVE 2, 30 MAX_STEPS 33 # ≥ warmup active 1 with profiler as prof: for step in range(MAX_STEPS): run_one_step(); prof.step()写法 B循环后补一次prof.step()推荐采样区间正好是你关心的 N 步with profiler as prof: for step in range(MAX_STEPS): run_one_step(); prof.step() prof.step() # ← 收尾只推进 schedule不跑推理老文档里MAX_STEPS warmup active是误传那样配 profiler 一定停在 active 中间。新版 CANN 会补 JSON但 warning 照打。关键 3首次图编译放窗口外prefill / decode 按需选择把第一次torch.compile/ torchair 图构建 / JIT放进窗口trace 大头会变成编译时间。这类 framework overhead 必须在 profiler 启动前预热一遍。prefill 本身是真实负载kernel 分布、attention layout、MC2/all-to-all 调度都和 decode 不同采不采看你想分析什么想分析采哪些阶段备注decode TPOT、decode kernel 瓶颈只采 decode最常见prefill 离群步会拉偏统计prefill TTFT、长序列 prefill kernel只采 prefilldecode 前无需跑一条时间线对照 prefill vs decode一次采两者trace_view清晰展示 prefill 单步 后续 decode分开对照、不混时间线两次采两个 save_path每个 phase 一个ASCEND_PROFILER_OUTPUT四种模式完整代码在scripts/profile_template.py# 模式 A只采 decode默认 run_prefill(...); warm_one_decode(...) # 预热不采 with create_profiler(./prof_decode, warmup0, activeN) as prof: for _ in range(N): model.decode(...); torch.npu.synchronize(); prof.step() prof.step() # 收尾 # 模式 B只采 prefillprefill 通常 1 步active1 warm_compile(...) # 预热第一次 prefilldecode 的图构建 with create_profiler(./prof_prefill, warmup0, active1) as prof: run_prefill(...); torch.npu.synchronize(); prof.step(); prof.step() # 模式 C一次采 prefill decode同一 timeline with create_profiler(./prof_both, warmup0, active1N) as prof: run_prefill(...); torch.npu.synchronize(); prof.step() # step 1: prefill for _ in range(N): model.decode(...); torch.npu.synchronize(); prof.step() # steps 2..N1: decode prof.step() # 收尾 # 模式 D两次独立采集两个 save_path分别对比标准配置模板注入import os, torch, torch_npu def create_profiler(save_path, warmup0, active30): Level1 PipeUtilization 拿 47 列 kernel_details。 warmup 推荐 0图编译/JIT 预热应已在 profiler 外做掉。 os.makedirs(save_path, exist_okTrue) experimental_config torch_npu.profiler._ExperimentalConfig( profiler_leveltorch_npu.profiler.ProfilerLevel.Level1, aic_metricstorch_npu.profiler.AiCMetrics.PipeUtilization, ) return torch_npu.profiler.profile( activities[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], with_stackFalse, record_shapesFalse, profile_memoryFalse, experimental_configexperimental_config, scheduletorch_npu.profiler.schedule( wait0, warmupwarmup, activeactive, repeat1, skip_first0), on_trace_readytorch_npu.profiler.tensorboard_trace_handler(save_path), ) # 用法见上面四种模式完整可运行模板带 PHASE 开关见 scripts/profile_template.py完整模板见scripts/profile_template.pyPHASE prefill|decode|both|separate对应上面用户选择。产物自检清单两条路径通用采完立刻对ASCEND_PROFILER_OUTPUT/自检python3 - PY import json, glob, os prof glob.glob(./**/ASCEND_PROFILER_OUTPUT, recursiveTrue)[0] kd f{prof}/kernel_details.csv if os.path.exists(kd): with open(kd) as f: cols f.readline().strip().split(,); n sum(1 for _ in f) print(fkernel_details: {n} rows, {len(cols)} cols (OK if len(cols) 40 else WRONG — missing ExperimentalConfig?)) else: print(kernel_details.csv MISSING — 解析失败(框架路径走A.5回退) 或没开 Level1PipeUtilization) try: json.load(open(f{prof}/trace_view.json)); print(trace_view: valid JSON) except Exception as e: print(ftrace_view INVALID/截断: {e}) for fn in [op_statistic.csv,api_statistic.csv,operator_details.csv,step_trace_time.csv]: print(f{fn}: {found if os.path.exists(f{prof}/{fn}) else MISSING}) PY检查项期望不符合kernel_details.csv列数479 → 没带ExperimentalConfig缺失 → 解析失败框架路径走 A.5op_statistic.csv/api_statistic.csv存在缺失 → 同上trace_view.json合法 JSON、以]结尾截断 → 解析失败 / 收尾 step 漏调step_trace_time.csv行数≈ active明显偏少 → 循环内prof.step()漏调communication*.json仅多卡生成单卡没有是正常的输出结构prof/ └── hostname_pid_timestamp_ascend_pt/ ├── ASCEND_PROFILER_OUTPUT/ │ ├── trace_view.json # 时间线TensorBoard / chrome://tracing │ ├── kernel_details.csv # 47 列算子执行明细最重要 │ ├── op_statistic.csv # 按算子类型聚合 │ ├── api_statistic.csv # host 侧 API 耗时 │ ├── operator_details.csv # 按 PyTorch op 聚合 │ ├── step_trace_time.csv # 每步 compute/communication 耗时 │ ├── *.db # DB 版摘要 │ └── communication*.json # 仅多卡生成 ├── FRAMEWORK/ PROF_*/ logs/ profiler_{info,metadata}.json查看结果tensorboard --logdir./prof # 需 pip install torch_tb_profiler切 PyTorch Profiler tab # 或 chrome://tracing 加载 trace_view.json # 或 pandas 读 kernel_details.csv 做定制分析拆解到模型结构 用 perf-breakdown-skill参考文档scripts/profile_template.py— 路径 B 可直接改用的完整模板references/troubleshooting.md— 版本差异、框架解析失败、具体报错、排查流程torch_npu Profiler 文档【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考