编译日志基础:日志级别、格式解析与关键字段

做NPU算子映射,说白了就是跟编译日志打交道。我刚开始接触这个领域时,面对满屏的日志信息,说实话有点懵。后来踩的坑多了,慢慢就摸出了门道。今天咱们就聊聊编译日志的那些事儿。

日志级别:DEBUG/INFO/WARN/ERROR

NPU编译器输出的日志,通常分为四个级别。每个级别都有它存在的意义,咱们一个一个说。

级别 含义 我什么时候用
DEBUG 调试信息 排查算子映射细节时必开
INFO 普通信息 日常编译,了解流程进展
WARN 警告信息 发现潜在问题,但不影响编译
ERROR 错误信息 编译失败,必须处理

DEBUG级别:这是我最常用的级别。说实话,平时编译用INFO就够了,但一旦算子映射出了问题,我第一件事就是把日志级别调到DEBUG。为什么?因为DEBUG会输出每个算子的映射细节、中间表示的变化、甚至每条指令的生成过程。我曾经遇到一个卷积算子映射后精度不对,打开DEBUG日志才发现是量化参数传递时丢了一位。

我的习惯:调试阶段一律开DEBUG,确认没问题后再切回INFO。别嫌日志多,关键时候它能救命。

INFO级别:正常编译时用这个。它会告诉你编译进行到哪一步了,比如「开始解析模型」、「开始算子映射」、「生成NPU指令」等等。我一般用INFO来确认编译流程是否按预期走。

WARN级别:警告信息,说白了就是「有问题但还能忍」。比如某个算子的映射不是最优的,或者某些参数被自动调整了。我建议别忽略WARN,尤其是生产环境。我记得有一次线上模型推理变慢,查了半天,最后发现是编译时有个WARN提示某个算子走了fallback路径,我没在意,结果性能直接掉了30%。

ERROR级别:这个不用多说,编译失败了。ERROR日志会告诉你哪个算子映射失败了,原因是什么。嗯,这里要注意:ERROR信息往往只告诉你「哪里错了」,但「为什么错」需要结合上下文来看。

日志格式解析

不同NPU编译器的日志格式大同小异。我以最常见的格式为例,给大家拆解一下。

[2024-01-15 14:23:45.678] [INFO] [OperatorMapper] [Conv2D] Start mapping operator, input shape: [1, 3, 224, 224]
[2024-01-15 14:23:45.679] [DEBUG] [OperatorMapper] [Conv2D] Kernel shape: [64, 3, 3, 3], stride: [2, 2]
[2024-01-15 14:23:45.680] [WARN] [Optimizer] [Conv2D] Im2col optimization skipped due to input size mismatch
[2024-01-15 14:23:45.681] [ERROR] [CodeGen] [Conv2D] Failed to allocate tensor for output, insufficient NPU local memory

这个日志格式,我拆成几个部分来看:

  • 时间戳:精确到毫秒,方便定位问题发生的时间点。我排查问题时,经常用时间戳来确认事件发生的先后顺序。
  • 日志级别:就是上面说的DEBUG/INFO/WARN/ERROR。
  • 模块名:比如OperatorMapper、Optimizer、CodeGen。这告诉你是哪个环节出了问题。我个人习惯先看模块名,再细看内容。
  • 算子名:比如Conv2D。这个很关键,直接告诉你哪个算子出了问题。
  • 消息内容:具体的描述信息。

小技巧:我一般用grep命令过滤日志。比如只看ERROR级别的日志:grep "\[ERROR\]" compile.log。或者只看某个算子的日志:grep "\[Conv2D\]" compile.log。这样能快速定位问题。

关键字段含义

日志里有些字段,你得多留意。我挑几个重点说说。

算子名称:这个不用多解释。但要注意,同一个算子在不同框架里名字可能不一样。比如PyTorch里的Conv2D,在ONNX里可能叫Conv。我遇到过有人拿着PyTorch的算子名去查ONNX的文档,结果查了半天没找到。

输入输出张量信息:包括形状、数据类型、内存地址等。这些信息对排查精度问题和内存问题特别有用。举个例子,如果输出张量的形状跟你预期的不一样,那八成是映射逻辑有问题。

映射状态:比如「映射成功」、「映射失败」、「使用fallback路径」等。我建议重点关注「fallback路径」这个状态。为什么?因为fallback意味着这个算子没有用NPU的原生指令,而是用通用指令模拟的,性能通常差很多。

性能相关字段:比如计算量、内存访问量、预估延迟等。这些信息在性能调优时特别有用。我记得有一次优化模型,发现某个算子的计算量不大,但内存访问量特别高,后来通过调整数据排布,性能提升了将近一倍。

避坑指南:我曾经遇到过日志里显示「映射成功」,但实际推理结果不对。后来发现是日志输出有缓存,真正的错误信息被淹没了。所以,别完全相信日志的「成功」状态,一定要结合推理结果来验证。

知识体系结构图

下面这张图,是我自己总结的编译日志分析框架。你想想看,拿到一份编译日志,应该从哪几个维度去分析?

编译日志分析 日志级别 日志格式 关键字段 DEBUG INFO WARN ERROR 时间戳 模块名 算子名 消息内容 算子名称 张量信息 映射状态 性能字段 图:编译日志分析知识体系 日志级别 日志格式 关键字段

这张图把编译日志分析分成了三个维度。我个人习惯先看日志级别,快速过滤出ERROR和WARN;然后看日志格式,确认是哪个模块、哪个算子出了问题;最后看关键字段,获取具体的错误原因和性能数据。三步走下来,大部分问题都能定位到。

好了,关于编译日志的基础知识就聊到这儿。这些东西看似简单,但真正用好它们,能帮你省下不少排查问题的时间。

可可精品资料库