引言

在一般人的观念中,CPU&GPU等设备的高频意味着耗电和发热,降频则是给设备降温的好办法。
这种观念符合人们对耗电发热的基本感知——基于时间的感知。设备连续运行指定的任务相同的时间,频率越高,发热越大,莫得毛病。
直到Sat, 27 Apr 2019 01:06:00 -0700,notable kernel developer Sultan Alsawaf给sultan kernel打上了一个神奇的提交,在引起激烈争论的同时颠覆了不少人的观念。
https://github.com/kerneltoast/android_kernel_google_wahoo/commit/823b3ac123d7b65dd4690cde831155a4c6e00b4f
在这个提交中,Sultan去掉了骁龙835处理器的大量低频,只为小核保留了1900800khz和1824000khz、为大核保留了2457600khz 2323200khz 2265600khz 2112000khz 1958400khz 1804800khz的频率。
而骁龙835的默认最低频,大小核均为300000khz,这个提交给人的第一印象便是:疯了?
而这,正是高频方案的诞生。

为什么?

先说一句,下文说的高频都是指能效最高的频率,而不是10GHz

从理解sultan的原话开始

It turns out that the efficiency (i.e., performance per watt) of each
CPU frequency varies significantly, with the most efficient frequencies
appearing towards the higher end of the spectrum. By using the most
efficient frequencies, work is completed as efficiently as possible,
which then allows the CPU to idle earlier and thus save significant
amounts of power.

这是sultan在提交中写下的原话。这里引入了一个新的概念高效,那么,什么是高效
众所周知,高效意味着事半功倍,翻译到CPU上有两种可能的形式:

单位时间完成更多的任务 (对时间高效)
单位能量完成更多的任务 (对能量高效)

很显然,这里指的是能量吧?(sultan也说了 每瓦性能)如果是指单位时间,那高效就等于高性能了,不太对。

也就是说,sultan选出的这几个频率,使用单位的能量能完成更多的任务,完成单个任务需要的能量也更少,这便是高频方案能省电的基本思路。

而这种思路,与常人的感知并不太一样。正常人的感知都是基于时间的——我玩这么久的手机,耗电了多少?
而来自sultan的高频方案,是基于任务的——我上微信回个消息,耗电了多少?

同样做一件事情,高频方案更省电
同样时间内一直在做事情,低频更省电

再翻译一下
就是
在理想状态下
玩一个游戏
如果不限制任务量 (也就是基于时间考虑)
那么高频能跑90帧 (正好吃饱性能)
低频只能跑30帧 (性能不足)
此时显然低频更省电
但是假如
限制任务量 (也就是基于任务了)
高频和低频都锁30帧
那么理论上,使用能效更高的高频将会更加省电

举了这么多栗子,要是还不懂那就建议Ctrl+F4

CPUidle与C-State

那么新的问题来了,有人可能会问:正常情况下,完成了任务cpu不就会降到低频来省电么?那现在没有了低频,停在高频不就还会接着耗电么?那这还有什么意义?

这就要来科普一下CPU的C-State了

先去抄一段话

为了在CPU空闲的时候降低功耗,CPU可以被命令进入low-power模式。每个CPU都有几种power模式,这些模式被统称为C-states或者C-modes。
lower-power模式最早在486DX4处理器上被引入,到现在,更多power mode被引入和增强,来进一步降低CPU的功耗。
这些模式最基本的思想是通过CPU内部的idle unit切断CPU的clock信号和供电。越多的CPU单元被停止(通过切断时钟),降低电压或者完全关闭,降低的功耗就越多,但同时也需要更多的时间来唤醒CPU, 重新回到满操作模式。
C-States从C0开始,C0是CPU的正常工作模式,CPU处于100%运行状态。C后的数越高,CPU睡眠得越深,CPU的功耗被降低得越多,同时需要更多的时间回到C0模式。

( https://blog.csdn.net/chdhust/article/details/80675569 )

然后呢?这段话又关我手机什么事呢?

在ARM芯片上也存在着类似的机制,比如wfi指令就可以令CPU关闭时钟速度(说白了就是0mhz),进入休眠状态,从而达到极低的功耗 ( https://github.com/xzr467706992/android_kernel_oneplus_sm8250/blob/5c3a3d09eb747395579e016e7ce8fb8c927c9fa7/drivers/cpuidle/lpm-levels.c#L1307

从上面那个链接里就已经可以看出,休眠由cpuidle驱动负责。
cpuidle是和cpufreq(动态调频)相并列的一套系统,专门负责把无所事事的cpu丢进低功耗状态(以下简称idle)

那么,cpuidle在何时被调用呢?
答案是随时,就像cpu随时都在调频一样

那我为什么看不到cpu频率变成0mhz呢?
因为看到的cpu频率是cpufreq驱动回报的值,而cpuidle驱动并没有和它串通,你当然看不到。

在理想状态下,假如上一秒的CPU负载为50%
那就意味着在上一秒中
有半秒,cpu在以指定频率处理工作
还有半秒,cpu进入了idle状态,低功耗休眠
即使你看到cpu一直在高频运行,但实际上,它在没有负载的时候也处在一个”超低频休眠状态“

(当然,是 理!想!状!态!,实际干扰因素会很多)

总结一下高频方案的想法

只以最高效的方式干事,干完直接以功耗最低的方式休眠,岂不美哉?

对高频方案的亿点点怀疑

怎么算的最高效频率?

算法1:性能/总功耗

kdrag0n和sultan都在使用性能/总功耗的算法
把比值相对较大的频率作为高效频率
但是这里存在一个非常严重的问题
那就是总功耗中包含了屏幕、基带等外围开销
在这张图中,展示了我使用sultan bench测试得出的数据使用性能/总功耗算法来进行计算的结果 (图上虽然写了去空载,但是空载我设为0了,因此就是性能/总功耗

可以看到,最佳频率为2.1GHz

但是假如我们对功耗进行全局偏移(所有数据+1w),来模拟屏幕激发亮度状态的额外耗电

可以看到,最佳能效频率变成了2.3GHz😅

所以说这种算法得到的结果只能代表当时状态下的最佳频率,不能推而广之 其实是有一定规律的,外围功耗越大,性能除以总功耗得出的高能效频率越高,而外围功耗归零时,得出的就是方法2算出的最佳频率(有点点双勾函数内味)

算法2:去空载(性能/功耗增量)

https://www.akr-developers.com/d/355

这种算法得到了一个非常有趣的结论:
从高通指定的最低频率开始,各频率能效单调递减
(小核给的默认最低频是691200khz )
(另外,你可能会说为什么小核最佳能效在614400khz而不是691200khz。这似乎是测量误差范围,稍微偏移一下数据就变了 )

如果高通真的就是这么确定各核心的最低频率的,那是不是可以反向印证方案1寻找最佳能效频率思路存在一些问题😅

cpuidle的效率

这个也在上面提到过了,高频方案的最佳效果需要cpuidle运行在理想状态下。但是实际上。。emmm。。真的不好说。cpu不会在任务处理完立即进入idle(还有一系列调度过程),同时idle状态也会受到各种影响(比如内核侧的pm_qos框架就能阻止cpu进入不同层次的idle模式)

用户的感知

高频方案的省电建立在完成指定量任务的前提下,但是,现实中,谁会遵守这个原则?
当cpu完成了指定的任务之后,用户将可以继续操作手机去干别的事情,从而填补idle时间。
举个栗子,
低频下,指定时间内,你只能开个微信
高频方案下,指定时间内,你只用了一半的时间就打开了微信
如果剩下的一半时间你啥事也不干,让cpu进入idle状态,那么高频方案会比低频方案省电
但是,谁会这么干?
打开微信把消息看完,随手又开了个QQ也是很正常的事情。
此时再说高频比低频省电,不是想peech的事情?
这就意味着,想让高频方案的亮屏时间比低频方案长几乎是不可能的事情,除非你一直在亮屏禅定。

总结

高频方案是一个很好的思路,但是,现实也许有一些残酷。
高频方案也许能让你用一电池的电做成更多的事情,但是,它并不能让亮屏时间的纸面数据变得更好看。
不过,它还有一个非常非常大的优点——绝致的流畅度和极低的掉帧量。

但是,这里可以得出另一个非常有意义的结论:超低频不省电
对A55核心来说,用百分之二十几的功耗增加换取两倍的性能提升(300mhz vs 691mhz),从而赢得更多idle时间来省电,何乐而不为?
何况,强行拉低最低频很容易导致cpu频率反复震荡,从而导致额外无用开销。