Day9_用sysfs实测NPU饱和度与DVFS调频
2026/9/16 9:24:26 网站建设 项目流程

系列上一篇:Day8|多 batch 推理实测:RK3568 的 NPU 真被喂满了吗?
硬件:RK3568(正点原子 EVB1 DDR4 V10 板,1 核 NPU)|模型:yolov5s 640×640 INT8|runtime:librknnrt 1.5.0

一、引子:昨天我下了一个错误结论

Day8 结尾我写:“batch=4 只比单帧快 6%,说明 NPU 已经饱和。”

这句话有一个我没验证的假设——NPU 负载真的到 100% 了吗?

嵌入式工程师的本能应该是:别猜,去问硬件。RK3568 的 NPU 驱动在 sysfs 里暴露了负载计数器:

cat/sys/kernel/debug/rknpu/load# NPU load: 0% <- 空载

于是我写了个采样线程,边推理边读这个节点。结果推翻了昨天的结论,还顺藤摸瓜挖出了三个更深的发现。这一天没有写一行 C++,没有碰一次交叉编译——全部用板端 Python(rknnlite)完成,这也是本文的第二个主题:不依赖工具链的板端基准测试方法。

二、发现一:RK3568 的 NPU 是单核(一个流传很广的误区)

起因是我一开始想测"NPU 多核调度",给 rknnlite 传了 core_mask:

fromrknnlite.apiimportRKNNLite rknn=RKNNLite()rknn.load_rknn('yolov5s.rknn')rknn.init_runtime(core_mask=0x07)# 想用 3 个核

runtime 直接报错:

E The core_mask is only supported by RK3588. Please do not set this parameter on other platforms.

core_mask 多核绑定是 RK3588 独有的。RK3588 有 3 个 NPU 核心(可以 1/2/3 核灵活组合),而 RK3568 只有一颗 NPU 核,设备树节点是fde40000.npu

网上不少资料把"RK35xx 系列 NPU"混着说,“3 核 NPU”"多核并行"的说法很多。看芯片手册 + 实测报错,比看博客靠谱。这是本文的第一个坑:

坑25:RK3568 = 单核 NPU;core_mask参数仅 RK3588 支持,在 RK3568 上设置会 init 失败(ret=-1)。

三、发现二:NPU 负载从来没超过 78%

把负载采样线程跑起来(50ms 间隔),同时用 1/2/3/4 个线程并发跑推理:

并发实例单次推理(ms)总吞吐NPU load 平均/峰值
190.48.1 FPS23.9% / 54%
2130.310.5 FPS47.4% / 73%
3187.110.7 FPS56.0% / 78%
4251.27.3 FPS ⬇53.1% / 77%

两个信息:

  1. NPU 负载峰值只有 78%,从来就没有"饱和"。Day8 的结论要修正。
  2. 多实例并发到 2-3 个时总吞吐有 1.3 倍收益,到 4 个反而退化——说明并发填的是提交间隙(单线程时 CPU/NPU 互相等待的空档),而不是在压榨剩余算力。

顺带说明:Python rknnlite 单实例 90ms vs C++ API 的 60ms,差值主要是 Python 侧输出张量拷贝和封装开销。本文的结论看的是相对关系,不受绝对值影响。

四、发现三:600MHz 是谁钉死的?——DVFS 调频实验

上面所有测试里有个奇怪现象:NPU 频率从头到尾钉在 600MHz 不动。查 devfreq:

cat/sys/class/devfreq/fde40000.npu/available_frequencies# 200000000 297000000 400000000 600000000 700000000 800000000 900000000cat/sys/class/devfreq/fde40000.npu/governor# rknpu_ondemand

7 个档位,最高900MHz,出厂 governor 是rknpu_ondemand——按负载升频。但负载计数器峰值才 78%(governor 的升频阈值估计在更高处),所以永远够不到 900MHz。

写了个 A/B 脚本:基线跑一遍 → 切performance强制 900MHz 再跑一遍 → 自动恢复原设置。

Python(间歇负载)

baseline (rknpu_ondemand, 600MHz) avg 82.91 ms 12.1 FPS performance (900MHz) avg 69.74 ms 14.3 FPS 提升 15.9%

C++(连续负载):有意思的来了——C++ 版在默认 governor 下自己就升到了 900MHz

C++ baseline (rknpu_ondemand→900MHz) NPU avg 50.69 ms 19.7 FPS C++ performance (900MHz) NPU avg 51.06 ms 19.6 FPS

为什么 C++ 能升频而 Python 不能?因为 C++ 的推理是背靠背连续的,负载计数器读数高;Python 每次推理之间有 ~28ms 的宿主侧空隙,负载计数被稀释——governor 看到的是"半闲的 NPU",于是不给最高频

这里藏着一个对做基准测试的人很重要的教训:

坑26:DVFS 设备上做 benchmark,必须先echo performance > .../governor钉死频率。否则你测的不是算法/硬件的性能,而是"调度器觉得你配用多少频率"。同一份代码,负载形态不同(连续 vs 间歇),实测性能差 16%。

另外顺手验证了/sys/kernel/debug/rknpu/load这个计数器本身也不可靠:900MHz 满速推理时它平均只报 9.7%,跑完空闲时反而报 75%。采样的负载值只能当参考,别当证据;证据是墙钟时间。

五、终极实验:频率 x 并发的完整矩阵

最后把 performance 档下的多实例并发也跑了,凑齐 2×4 矩阵:

并发实例@600MHz 单次/总吞吐@900MHz 单次/总吞吐
190.4ms / 8.166.2ms / 9.2
2130.3ms /10.5(1.30x)112.7ms /10.6(1.15x)
3187.1ms /10.7(1.32x)161.7ms /10.5(1.14x)
4251.2ms / 7.3 ⬇205.8ms / 8.6 ⬇

这张表里最有价值的是加粗的那一列:总吞吐的天花板在 600MHz 和 900MHz 下都是 ~10.5 FPS,纹丝不动。

把频率提高 50%,单实例延迟明显改善(90→66ms),但总吞吐一动不动——这说明多实例并发的瓶颈根本不在 NPU 计算单元,而在 DDR 带宽或驱动层的串行化。yolov5s 每次推理要在 DDR 里搬运约 30MB 的权重+激活值,多实例并发时大家抢的是同一条内存总线。

六、结论修正:Day8 那句话应该怎么说

Day8 的说法(错)Day9 修正后(对)
单帧 60ms 的本质“NPU 饱和了”这是本 BSP 上该管线的延迟地板(900MHz、C++ 连续负载),与 NPU 利用率无关
batch=4 只 +6%“NPU 没有摊销空间”batch 摊销掉的是提交间隙,但带宽压力不降,所以收益有限
正确的优化方向单路追延迟:锁频+连续提交;多路追吞吐:上限 ~10.5 FPS 等效,撞的是内存墙

这套"用实验修正自己昨天结论"的过程,比结论本身更值钱。面试时它对应的是一类高频问题——"你怎么定位性能瓶颈?"标准答案不是背名词,而是这样的方法论:

“先找到硬件的观测接口(sysfs 计数器、devfreq、trace),让数据说话;发现矛盾(load 78% 但性能不再提升)就设计对照实验(锁频 A/B);最后用’改 X 不改 Y,Y 没动’的方式归因(频率+50% 吞吐不动 → 带宽瓶颈)。”

七、复现方法(板端直接跑,无需交叉编译)

三个脚本都在仓库 python/ 目录,scp 上板即可:

python3 bench_npu_core.py /root/yolov5s.rknn20# 单实例延迟统计(p50/p95/min/max)python3 bench_npu_satur.py /root/yolov5s.rknn15# 1-4 实例并发 + load/freq 采样python3 bench_npu_gov.py30# governor A/B 对比(自动恢复原设置)

bench_npu_gov.py的安全设计值得抄:跑之前先读出原 governor/min_freq/max_freq 存起来,实验完无条件写回,异常退出也不至于把板子留在 performance 档白耗电。

八、30 秒面试话术

「我在 RK3568 上做过一次 NPU 饱和度的系统排查:最初多 batch 只提升 6%,我判断是 NPU 饱和;后来用 sysfs 负载采样发现峰值只有 78%,继续查 devfreq 发现出厂 governor 把频率钉在 600MHz(最高 900MHz)。锁 performance 档后单实例提速 16%,但多实例总吞吐在两个频点下都卡在同一个值——说明并发瓶颈是 DDR 带宽不是 NPU 算力。这件事让我形成了一个习惯:DVFS 设备上做基准测试先锁频,性能归因必须用对照实验,不能拿一个反常数据直接下结论。」

写在最后

  • 明天后计划做 RKNN 多路视频流的工程化(既然并发吞吐上限是 10.5 FPS 等效,多路场景就要认真做跳帧/降分辨率权衡了)。
  • 本系列所有代码与数据都在 GitHub 仓库,欢迎取用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询