第24章 电池与功耗优化:功耗分析工具、降低推理功耗的策略、动态频率调整
做AI应用开发,尤其是跑在手机上的神经网络,功耗是个绕不开的话题。我见过不少团队,模型精度刷得挺高,一上真机,手机烫得能煎鸡蛋,用户直接卸载。嗯,今天我们就来聊聊怎么把功耗降下来。
24.1 功耗分析工具:先搞清楚电都去哪了
优化功耗的第一步,不是瞎调参数,而是先测量。你得知道你的模型在跑的时候,到底消耗了多少电。
24.1.1 Android自带的电池统计
最简单的办法,用dumpsys batterystats。这个命令能告诉你每个应用的耗电情况。我个人习惯在测试前先重置一下:
adb shell dumpsys batterystats --reset
# 跑你的模型
adb shell dumpsys batterystats --charged > battery.txt
打开battery.txt,找到你的包名,能看到CPU唤醒时间、网络流量、传感器使用时长等。不过说实话,这个粒度比较粗,只能看个大概。
24.1.2 更精细的工具:Battery Historian
Google官方的Battery Historian,能把耗电数据可视化。你导出bugreport,丢到网页上,就能看到一条时间轴,哪段时间CPU在忙、哪段时间屏幕亮着、哪段时间GPS在工作,一目了然。
我建议你在跑NNAPI推理前后,分别抓一次数据。对比一下,就能看出推理过程对功耗的影响有多大。
24.1.3 硬件级功耗测量
如果你手头有Monsoon或Power Monitor这类设备,那是最准的。直接串在电池和主板之间,实时电流电压都能看到。我在项目中遇到过,软件统计显示功耗不高,但硬件一测,峰值电流直接超标。嗯,软件统计有时候会“骗人”。
核心观点:功耗优化,数据先行。没有测量,就没有优化。
24.2 降低推理功耗的策略:从模型到硬件,层层优化
搞清楚电都去哪了,接下来就是怎么省。我总结了几条实战经验,从软件到硬件都有。
24.2.1 模型层面的优化
模型越小,计算量越少,功耗自然越低。这是最根本的。
- 量化:FP32转INT8,计算量不变,但内存带宽和功耗能降一半以上。我做过一个图像分类模型,量化后功耗从2.3W降到1.1W,精度只掉了0.3%。
- 剪枝:去掉不重要的权重。有些模型剪掉30%的参数,精度几乎不变。
- 知识蒸馏:用大模型教小模型。小模型推理快,功耗低。
我的经验:量化是最容易上手的。NNAPI对INT8支持很好,你只需要在转换模型时指定量化方式,代码几乎不用改。
24.2.2 运行时策略:别让NPU一直满负荷
模型加载好了,推理的时候怎么省电?
- 批量处理:把多个输入攒到一起,一次推理。减少NPU的启动和停止次数。比如视频帧处理,可以攒5帧一起送进去。
- 降低帧率:不是所有场景都需要30fps。人脸检测,10fps就够用了。帧率降下来,功耗直线下降。
- 异步推理:别在主线程里同步等结果。用
ANeuralNetworksExecution_startCompute异步执行,CPU可以去做别的事。
24.2.3 硬件选择:用NPU还是DSP?
NNAPI支持多种硬件加速器。不同硬件,功耗特性不一样。
| 硬件 | 性能 | 功耗 | 适用场景 |
|---|---|---|---|
| CPU | 低 | 高(通用计算) | 小模型、兼容性优先 |
| GPU | 高 | 中高 | 大模型、高吞吐 |
| NPU | 高 | 低(专用电路) | 定点模型、低功耗 |
| DSP | 中 | 极低 | 始终在线、语音唤醒 |
我个人建议:如果模型已经量化成INT8,优先选NPU。如果模型是FP16,GPU可能更合适。DSP适合那种“一直开着但不用太频繁”的场景,比如语音关键词检测。
24.3 动态频率调整:让硬件按需工作
动态频率调整,说白了就是“用多少电,干多少活”。你想想看,如果模型推理只需要50%的算力,为什么让NPU跑在100%的频率上?
24.3.1 什么是DVFS?
DVFS(Dynamic Voltage and Frequency Scaling)是硬件自带的节能技术。当负载低时,降低电压和频率;负载高时,再提上去。Android系统本身就在用,比如CPU的调频器(governor)。
NNAPI也支持类似机制。你可以通过ANeuralNetworksCompilation_setPriority和ANeuralNetworksCompilation_setPreference来暗示系统:这个推理任务对功耗敏感,还是对延迟敏感。
// 设置编译偏好:优先低功耗
ANeuralNetworksCompilation_setPreference(compilation,
ANEURALNETWORKS_PREFER_LOW_POWER);
// 或者优先低延迟
ANeuralNetworksCompilation_setPreference(compilation,
ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER);
注意:这只是“偏好”,不是强制命令。具体怎么调频,还是硬件驱动说了算。不同厂商的实现差异很大。
24.3.2 手动控制频率?别想了
有些同学可能会问:能不能直接设置NPU频率?嗯,在Android上,普通应用没有这个权限。系统安全模型不允许你直接操作硬件寄存器。你只能通过NNAPI的API来“建议”。
我曾经试过在root过的设备上手动写频率,结果系统直接崩溃。后来我学乖了,老老实实用NNAPI的标准接口。
24.3.3 实战:动态调整策略
在实际项目中,我一般这样用:
- 场景一:应用刚启动,需要快速响应。用
PREFER_FAST_SINGLE_ANSWER,让硬件跑高频。 - 场景二:后台持续处理,比如视频滤镜。用
PREFER_LOW_POWER,牺牲一点延迟,换续航。 - 场景三:电池电量低于20%。强制切换到低功耗模式,甚至降级到CPU推理。
你可以在代码里监听电池状态:
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
int percent = level * 100 / scale;
if (percent < 20) {
// 切换到低功耗模式
setLowPowerMode();
}
}
}, filter);
24.4 本章知识体系
下面这张图,把功耗优化的核心逻辑串起来了。从测量到策略,再到动态调整,是一个闭环。
你看,整个优化流程是闭环的。先测量,再优化,然后通过动态调整来适应不同场景。没有一步到位的方案,只有不断迭代的过程。
总结一下:功耗优化不是一锤子买卖。工具帮你发现问题,策略帮你解决问题,动态调整帮你适应变化。三者缺一不可。