4、CPU子系统功耗分析:CPU核心电压频率调节、大小核调度策略、C-State与P-State

好,咱们进入CPU子系统。这部分是功耗优化的核心战场,也是我花时间最多的地方。你想想看,CPU是整个SoC的“大脑”,它吃掉的功耗往往占了大头。搞不定CPU,其他优化都是白搭。

今天咱们就掰开揉碎,聊聊三个关键点:电压频率调节(DVFS)大小核调度,还有C-State与P-State。这三兄弟,说白了就是CPU功耗的“油门”、“变速箱”和“熄火开关”。

4.1 电压频率调节(DVFS)——CPU的“油门”

DVFS,Dynamic Voltage and Frequency Scaling。名字挺唬人,其实道理很简单:CPU干活多,就给它加电压、提频率;活少了,就降压降频。功耗和电压的平方成正比,和频率成正比。所以降压带来的收益,比降频大得多。

我在项目中遇到过一个问题:某款8155平板,跑分时CPU温度直接飙到95度。一查,发现DVFS的调频策略太激进,频率跳变太快,电压没跟上,导致瞬间电流过大。后来我们加了一个调频速率限制器,让频率每10ms只能变化一次,温度立马降了8度。

核心公式(心里有数就行):

P ≈ C × V² × f

其中C是负载电容,V是电压,f是频率。V的平方,你品,你细品。

高通8155的DVFS实现,用的是RPM(Resource Power Manager)来统一管理。CPU核心有自己的电压域,可以独立调压。但要注意,调频调压不是瞬间完成的,有延迟。我建议你在调频前,先读一下cpufreq驱动里的transition_latency参数,心里有个底。

// 查看当前CPU频率和可用频率
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies

// 设置governor为性能模式(慎用,功耗会飙升)
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

我的习惯:在调试阶段,我会把governor设为userspace,手动写一个脚本,按场景动态调频。比如视频播放时锁在1.2GHz,游戏时放开到2.4GHz。这样比内核自带的ondemand或interactive更可控。

4.2 大小核调度策略——让合适的核干合适的活

8155用的是ARM的big.LITTLE架构。大核(Cortex-A76)性能强、功耗高;小核(Cortex-A55)省电、性能弱。调度器的任务,就是把任务分给合适的核。

嗯,这里要注意。调度策略如果搞不好,会出现“大核摸鱼,小核累死”或者“小核卡顿,大核空转”的情况。我见过一个案例:某款车机,后台音乐播放时,调度器把解码线程扔到了大核上,结果大核跑在1.8GHz,功耗多了300mW。后来我们给音乐播放器绑定了小核,功耗直接降了15%。

高通8155的调度策略,主要靠Energy Aware Scheduling(EAS)。EAS会估算每个任务在不同核上的功耗和性能,然后选一个最优解。但EAS不是万能的,它依赖CPU的功耗模型。如果模型不准,调度就会跑偏。

场景 推荐调度策略 说明
后台任务(音乐、通知) 强制绑定小核 用小核跑,频率锁在1.0GHz以下
前台交互(滑动、点击) 优先小核,必要时迁移到大核 响应要快,但别一上来就上大核
游戏/渲染 大核全开,小核辅助 性能优先,但注意温度墙
待机/息屏 所有任务迁移到小核,大核休眠 能省一点是一点

我曾经踩过的坑:在某个项目中,我们为了省电,把所有任务都绑在小核上。结果用户反馈“滑动卡顿”。一查,发现小核的负载已经100%了,但大核在睡大觉。后来我们加了一个负载均衡阈值:小核负载超过80%时,自动把任务迁移到大核。卡顿问题解决,功耗只多了2%。

4.3 C-State与P-State——CPU的“熄火”与“怠速”

这两个概念,很多人容易搞混。我简单说一下:

  • P-State(Performance State):CPU在工作时的状态,对应不同的电压频率组合。说白了就是“怠速”和“高速”的区别。
  • C-State(Core State):CPU在空闲时的状态,对应不同的休眠深度。说白了就是“熄火”和“深度熄火”的区别。

C-State从C0(工作)到C7(深度休眠),数字越大,功耗越低,但唤醒延迟也越大。高通8155支持到C7,但实际项目中,我很少用C7。为什么?因为唤醒延迟太大,有些外设等不及,会导致数据丢失。

// 查看当前C-State统计
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/name
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/time
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/usage

你看,usage字段显示每个C-State被进入的次数。如果C7的usage是0,说明它根本没被用上。我建议你检查一下,是不是某个驱动阻止了深度休眠。

避坑指南:

我曾经遇到一个bug:系统待机时功耗居高不下,有50mW。查了一圈,发现是某个USB驱动在轮询设备,导致CPU无法进入C2以上的状态。后来我们给驱动加了一个autosuspend延迟,待机功耗降到了8mW。

P-State的调优,我建议你关注cpufreqscaling_min_freqscaling_max_freq。不要一味地追求低频,有时候低频反而更费电。为什么?因为任务处理时间变长了,CPU在C0状态待得更久,整体功耗反而更高。这就是所谓的Race to Idle策略:快速干完活,然后赶紧进入深度休眠。

我的经验:对于8155,我一般把最低频率设在800MHz左右。低于这个值,任务处理时间会明显变长,得不偿失。最高频率则根据散热条件动态调整,比如温度超过85度时,强制降到2.0GHz以下。

好了,CPU子系统的功耗分析,咱们就聊到这儿。总结一下:DVFS是油门,大小核调度是变速箱,C-State和P-State是熄火开关。三管齐下,才能把CPU的功耗压到最低。下一章,咱们聊聊GPU子系统的功耗优化,那又是另一番天地了。