3、日志文件定位:编译产物目录结构、日志文件命名规则、如何快速找到目标日志
说实话,很多刚接触NPU开发的同事,最头疼的不是写算子,而是找日志。
编译报错了,日志在哪?哪个文件才是关键信息?我见过有人盯着屏幕翻了几百行,结果发现看的是中间产物,根本不是报错日志。
这一章,我就带你把这个痛点彻底解决掉。
3.1 编译产物目录结构——你得知道东西放哪了
不同芯片厂商的NPU工具链,目录结构大同小异。我以最通用的模型说,你理解了这套逻辑,换平台也能快速上手。
一次完整的算子编译,产物目录大概长这样:
build/
├── ir/ # 中间表示层
│ ├── xxx_ir.txt # 原始IR
│ └── xxx_opt_ir.txt # 优化后的IR
├── schedule/ # 调度方案
│ ├── default.sched # 默认调度
│ └── tuned.sched # 调优后调度
├── codegen/ # 代码生成
│ ├── kernel.s # 汇编代码
│ └── kernel.c # C代码(部分平台)
├── log/ # 日志目录(重点)
│ ├── compile.log # 主编译日志
│ ├── pass.log # 各pass执行日志
│ └── error.log # 错误汇总
└── output/ # 最终产物
├── model.npu # NPU可执行文件
└── model.json # 模型描述文件
我个人习惯,先看 log/ 目录。为什么?因为这里记录了编译的全过程,从IR解析到代码生成,每一步都有痕迹。
核心原则:先找 log 目录,再看 error.log,最后翻 compile.log。这个顺序能帮你节省至少一半时间。
3.2 日志文件命名规则——名字里藏着信息
日志文件不是随便命名的。每个名字都包含了关键信息:时间戳、编译阶段、算子名称、错误级别。
举个例子:
20250321_143205_conv2d_fp16_ERROR.log
│ │ │ │ └── 日志级别
│ │ │ └──────── 数据类型
│ │ └──────────────── 算子名称
│ └────────────────────────── 时间戳(时分秒)
└──────────────────────────────────── 日期
我在项目中遇到过一种情况:同事找了半天没找到某个算子的日志,结果发现是因为算子名称写错了。命名规则里用的是 conv2d,他搜的是 conv2D,大小写不匹配。
嗯,这里要注意:算子名称严格区分大小写,而且通常使用小写+下划线的格式。
| 日志文件名模式 | 含义 | 典型场景 |
|---|---|---|
*_INFO.log |
普通信息日志 | 编译进度、pass执行成功 |
*_WARN.log |
警告日志 | 性能可能下降、精度可能损失 |
*_ERROR.log |
错误日志 | 编译失败、算子不支持 |
*_FATAL.log |
致命错误日志 | 工具链崩溃、内存溢出 |
*_DEBUG.log |
调试日志 | 需要详细跟踪时开启 |
小技巧:如果你不确定日志级别,直接搜 ERROR 或 FATAL 关键字。我一般用 grep -rn "ERROR" log/ 一把梭,比一个个翻文件快得多。
3.3 如何快速找到目标日志——实战方法论
你想想看,一个项目编译下来,日志文件可能有几十个。怎么快速定位到你要的那个?
我总结了三个步骤,你照着做,基本不会跑偏。
3.3.1 第一步:按算子名过滤
大多数NPU工具链支持按算子名生成独立日志。编译命令里加个参数就行:
# 假设你的工具链叫 npucc
npucc --target-op=conv2d --log-dir=./logs model.onnx
这样只会生成 conv2d 相关的日志,其他算子的日志不生成。省事吧?
我曾经在一个大模型项目里,编译一次生成200多个日志文件。用了这个参数后,直接降到3个。找问题快多了。
3.3.2 第二步:按时间戳排序
如果日志文件很多,按时间戳排序是最靠谱的。因为编译过程是顺序执行的,越晚生成的文件,越接近出错点。
ls -lt log/ # 按修改时间倒序排列
或者用 find 命令:
find log/ -name "*ERROR*" -exec ls -lt {} \;
这样你一眼就能看到最新的错误日志是哪个。
3.3.3 第三步:看关键行号
日志里通常会标注出错的行号。比如:
[ERROR] In file kernel.s, line 128: operand type mismatch
这个信息太重要了。你直接去 codegen/kernel.s 的第128行看,问题基本就锁定了。
注意:行号可能因为优化pass而偏移。比如IR优化后,代码行号会变。我建议你同时看优化前后的两份日志,对比着找。
3.4 知识体系总览
下面这张图,我把日志定位的核心逻辑画出来了。你一看就明白。
3.5 避坑指南
最后,分享几个我踩过的坑:
- 日志被覆盖:多次编译默认会覆盖同名日志。我建议每次编译前加个时间戳目录,比如
--log-dir=./logs_$(date +%Y%m%d_%H%M%S)。 - 日志级别不对:默认只输出 INFO 及以上级别。如果你需要 DEBUG 日志,记得加
--log-level=DEBUG。 - 跨平台路径问题:Windows 和 Linux 的路径分隔符不同。我建议统一用正斜杠
/,避免转义问题。
我的习惯:每次编译完,第一件事就是 ls -lt log/*ERROR*。如果有 ERROR 文件,直接打开看。没有的话,再去看 compile.log。这个习惯帮我省了无数时间。
说白了,日志定位就是个熟练活。你按我这套方法练几次,以后看到编译报错,心里就有底了。
想要更多资料,联系可可