RK3588 上模型「能跑但很慢」:逐算子查 CPU 回退的完整方法与实测

先说一个反直觉的结论

模型在 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 返回空字符串,而且不报错。

c 复制代码
// 错误: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 上,原因有两个:

  1. 有两类算子天生在 CPU 侧,属于结构性开销,不算回退:InputOperator 和 OutputOperator------数据进出 NPU 的搬运,每个模型都有。
  2. 一个算子占总耗时 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%(频率调度爬升期的影响),拿单次跑分比模型、比板子,很容易得出错误结论。我的做法是进程级多轮重复、取中位数,并且把测量时的系统负载一并记进报告------脱离负载谈延迟,数字没法复现。

小结

  1. PERF_DETAIL 为空,先查 rknn_init 有没有带 RKNN_FLAG_COLLECT_PERF_MASK;
  2. 判定回退看 CPU 时间占比,Input/OutputOperator 的结构性耗时要剔除;
  3. 官方模型普遍干净,自己改过的模型才是回退重灾区,转完必验;
  4. 延迟只认多轮中位数,单次跑分会骗人。

我把这套流程(逐算子回退诊断 + 板卡体检 + RTSP 流验收 + ONNX 转 RKNN 前的预检)做成了一个零依赖的开源小工具 rk-edge-doctor,判定口径就是文中这套,仓库里有全部真机报告样本可对照:

github.com/anguoyang/r...

如果你有自定义模型查出 MIXED 或 CPU_HEAVY 的报告,欢迎到仓库提 issue------真实回退样本是目前最缺的数据。

相关推荐
xbronze5 小时前
STM32+RC522读取MIFARE 1K卡排坑实录
嵌入式
La Pulga5 小时前
【FreeRTOS源码学习笔记】空闲任务与阻塞延时
笔记·mcu·学习·嵌入式·freertos·实时操作系统
小李不想当小白6 小时前
WDG看门狗(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
La Pulga1 天前
【FreeRTOS源码学习笔记】任务切换
单片机·mcu·学习·嵌入式·freertos·实时操作系统
Sakura_j_h_1 天前
51 单片机驱动四位数码管:动态扫描、限流电阻与消影的取舍
单片机·嵌入式硬件·51单片机·嵌入式·数码管·动态扫描·限流电阻
用户8351414049492 天前
const的四种位置,含义完全不同
嵌入式·源码
Zevalin爱灰灰3 天前
Linux入门第二部分(ARM裸机篇) 第二章 Cortex-A7 MPCore架构与ARM汇编基础
linux·汇编·arm开发·嵌入式
Zwawa3 天前
串口命令如何从字符串长成协议:帧头、长度、校验和与状态机
c语言·单片机·嵌入式
小李不想当小白3 天前
BKP备份寄存器与RTC实时时钟(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式