第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_setPriorityANeuralNetworksCompilation_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 本章知识体系

下面这张图,把功耗优化的核心逻辑串起来了。从测量到策略,再到动态调整,是一个闭环。

电池与功耗优化知识体系 功耗分析工具 降低推理功耗策略 动态频率调整 具体方法 • dumpsys batterystats • Battery Historian • 硬件功耗仪 具体方法 • 模型量化/剪枝 • 批量处理/降帧率 • 硬件选择(NPU/DSP) 具体方法 • DVFS自动调频 • setPreference API • 电池状态监听 最终目标:在保证用户体验的前提下,最大化续航 持续迭代优化

你看,整个优化流程是闭环的。先测量,再优化,然后通过动态调整来适应不同场景。没有一步到位的方案,只有不断迭代的过程。

总结一下:功耗优化不是一锤子买卖。工具帮你发现问题,策略帮你解决问题,动态调整帮你适应变化。三者缺一不可。


公众号:蓝海资料掘金营,微信deep3321