先说一个反直觉的结论
模型在 RK3588 上推理出了正确结果,不代表它在 NPU 上跑。
算子不被 NPU 支持时,RKNN runtime 会静默回退到 CPU 执行:程序不报错、结果是对的、只有速度慢得莫名其妙。更麻烦的是,官方模型仓库(model zoo)里的模型一般都很干净,真正会回退的恰恰是大家自己改过结构、加过算子的模型——网上现成的经验帖帮不上忙,只能自己查。
这篇写我实际用的查法:不靠估算,直接调 librknnrt 的 PERF 接口读硬件侧的逐算子账本,再按 CPU 时间占比给结论。包含一个差点让我得出错误结论的坑,和两个模型的真机实测数据。测试环境:RK3588,Ubuntu 22.04,rknpu 驱动 0.9.8,librknnrt 2.3.2。
第一步:坑——PERF 数据会静默为空
查回退的正道是 rknn_query(RKNN_QUERY_PERF_DETAIL),它返回每个算子的耗时明细。但有个巨坑:
rknn_init 时必须带 RKNN_FLAG_COLLECT_PERF_MASK(值 8),否则 PERF_DETAIL 返回空字符串,而且不报错。
// 错误:flags 传 0,PERF_DETAIL 永远是空的
rknn_init(&ctx, model, size, 0, NULL);
// 正确:带上 PERF 采集标志
rknn_init(&ctx, model, size, RKNN_FLAG_COLLECT_PERF_MASK, NULL); // 8
我第一次测就是栽在这里:明细是空的,第一反应是「运行时版本不对」或者「这个模型没在跑」,差点顺着错误方向查一下午。结论先行:如果你拿到的 PERF_DETAIL 是空的,先检查 init 的 flags,别怀疑模型。
拿到明细之后是文本格式的逐算子列表,每行一个算子:名称、耗时(µs)、执行单元。剩下的工作就是把这份账算清楚。
第二步:判定口径——看 CPU 时间占比,不看算子个数
逐算子账本到手后,怎么判定「回退严不严重」?我用的口径是按 CPU 时间占总推理时间的比例,而不是数有几个算子落在 CPU 上,原因有两个:
- 有两类算子天生在 CPU 侧,属于结构性开销,不算回退:
InputOperator和OutputOperator——数据进出 NPU 的搬运,每个模型都有。 - 一个算子占总耗时 15%,和十个算子各占 0.01%,性质完全不同。数个数会把前者漏掉、把后者放大。
实际判定阈值(我自己的工具里用的口径,供参考):
| CPU 时间占比 | 判定 | 含义 |
|---|---|---|
| < 1%,且无单个 CPU 算子 ≥ 100µs | NPU_ONLY | 干净,结构性开销除外 |
| 1% – 20% | MIXED | 部分回退,看占大头的算子是谁 |
| > 20% | CPU_HEAVY | CPU 在扛主要计算,先修这个 |
看到 MIXED 别慌,先看榜首:回退榜第一名通常是某个 Reshape / Transpose / 自定义算子单独吃掉一大块,对着它改模型结构(或者换等价写法)比什么都管用。
第三步:实测——两个官方模型的真实账本
方法对不对,要拿真模型验。以下是在上面那块板子上用 ctypes 直调 librknnrt 跑的(没有用厂商的 benchmark 工具,PERF 数据来自 runtime 自己的计数):
YOLOv8n(检测)
- 算子总数:108
- p50 延迟:40.9 ms
- CPU 侧总耗时:203 µs,全部是 Input/OutputOperator 的结构性搬运
- 判定:NPU_ONLY
YOLOv8s-seg(分割)
- p50 延迟:约 106 ms
- 判定:NPU_ONLY(账本结构同上,CPU 侧只有结构性算子)
两个模型都干净,和预感一致:官方 zoo 的模型是按 NPU 的算子集精挑细选过的,转换链路也成熟。所以这套查法的目标从来不是官方模型,是你自己改的那个:动过骨干、换过激活、加了后处理算子的模型,转完之后请务必逐算子验一遍——回退不会报错,只会体现在帧率上。
第四步:延迟数字的口径,顺带提醒
逐算子账本解决「在哪跑」,延迟数字解决「跑多快」,后者有个测量口径的坑值得单独说:板子闲置一段时间后,第一个进程的推理整体会慢约 15%(频率调度爬升期的影响),拿单次跑分比模型、比板子,很容易得出错误结论。我的做法是进程级多轮重复、取中位数,并且把测量时的系统负载一并记进报告——脱离负载谈延迟,数字没法复现。
小结
- PERF_DETAIL 为空,先查
rknn_init有没有带RKNN_FLAG_COLLECT_PERF_MASK; - 判定回退看 CPU 时间占比,Input/OutputOperator 的结构性耗时要剔除;
- 官方模型普遍干净,自己改过的模型才是回退重灾区,转完必验;
- 延迟只认多轮中位数,单次跑分会骗人。
我把这套流程(逐算子回退诊断 + 板卡体检 + RTSP 流验收 + ONNX 转 RKNN 前的预检)做成了一个零依赖的开源小工具 rk-edge-doctor,判定口径就是文中这套,仓库里有全部真机报告样本可对照:
如果你有自定义模型查出 MIXED 或 CPU_HEAVY 的报告,欢迎到仓库提 issue——真实回退样本是目前最缺的数据。