0x00 前言
生活离不开手机,安卓占据了手机系统的半壁江山。手机cpu频率是手机性能的重要指标之一,目前顶级梯队的骁龙855芯片中“大核”频率已经达到了2.96GHz,即使它使用7nm工艺制造,运行在如此高频必然需要较高的核心电压,也同时会增加耗电。为了提高能耗比,除了物理上改进cpu设计,软件上优化软件算法……还有一个重要的措施————使用CPU调速器(Governor)。
0x01 cpu如何手动调速
本文实验均在lineageOS进行(关闭高通热插拔与跳频设置)。本地化系统(如miui,flyme,emui等)或高通的热插拔技术可能会干预cpu频率,无法简单地进行手动调频。
//对cpu0进行调整
cd /sys/devices/system/cpu/cpu0/cpufreq/
//调整cpu0最大频率
echo 2200000 > scaling_max_freq
//调整cpu最小频率
echo 1596000 > scaling_min_freq
//若将cpu最大最小频率设置为相同频率,即锁定cpu频率。
0x02 什么是cpu调速器
机器处在相对空闲的状态下,此时CPU不需要运行在高频率上,调速器会降低cpu频率从而省电。当机器需要进行大量计算时,调速器会升高cpu频率以防止应用出现卡顿。广义上讲,AI优化也是一种调速器,因为现在手机厂商所谓AI优化很大一部分优化效果归功于对cpu频率的精准控制(当然AI优化也不全是调速器,还有动态分辨率调整之类的其他技术)。
简单来说,调速器就是根据负载情况自动化调节cpu频率的工具。
0x03 有哪些常见的cpu调速器
【interactive】交互模式(在安卓使用最广泛的调速器)
【conservative】保守模式
【performance】高性能模式
【powersave】省电模式
【Wheatley】惠特利模式
【lionheart】狮心模式
【smartass】聪明模式
【smartassV2】聪明V2模式
0x04 如何用好调速器
(以interactive调速器为例)——调速器参数调整的学问
1.above_hispeed_delay:
核心频率超过hispeed_freq时,升频延迟时间(above_hispeed_delay)(单位uS)
(example:40000 1400000:30000)
hispeed_freq<频率<1.4GHz,且连续40000uS达到目标负载,才进行升频。
频率>1.4GHz,且连续30000uS达到目标负载,才进行升频。
2.boost
:
如果非0则将频率设定为hispeed_freq,用于应对瞬间大量任务。
3.boostpulse_duration
boost时间限制(单位uS)
4.go_hispeed_load
:
example: (90)
在最低频状态,且负载达到 go_hispeed_load
即90%时,CPU直接提频到hispeed_freq。
5.hispeed_freq
:
example:(1728000)(即1728MHz)
设定hispeed_freq
。
6.min_sample_time
:
example: (40000)
需要保持当前频率多久才进行降频(单位uS)。
7.sampling_down_factor
:
example: (1(default))
8.target_loads
:
example: (85
1200000:75
1500000:85)
负载达到85时进行升频。频率为1.2GHz-1.5GHz时负载75%进行升频。频率>1.5GHz时负载85%进行升频。
注:go_hispeed_load
优先级高于target_loads
优先级。
补充:实际案例
用实际案例解释各个调速器参数
安卓手机打开软件时的cpu频率折线图
(单位为0.1s)
- 上图为软件打开时的cpu频率曲线,可以看到cpu瞬间提频至1.7GHz(
hispeed_freq
) - 但此时软件还在加载过程中,负载依然很高,延迟大约200ms(above_hispeed_delay)进行升频。升至最高频率1.98GHz
- 最终软件完全加载完毕,仍然保持当前频率(
min_sample_time
),后降频至300MHz。 - 进入软件后进行滑动,ui绘制需要较多资源,cpu瞬间升至1.7GHz(
hispeed_freq
)。
个人见解:
为了省电:
- 将
hispeed
降低,将target_loads
提高(cpu快速进入一个较低的频率,然后缓慢升频)
- 将
为了性能:
- 一是提高
hispeed
,当负载升高时,cpu快速进入高频,保证响应速度(保证突发性能)。提高target_load
和above_hispeed_delay
防止过快升频,为省电考虑。 - 二是降低
hispeed
,降低target_load
和above_hispeed_delay
缓慢提升频率,保证正常升频。
- 一是提高
拓展:引入前台应用/时间/用户动作(如拿起手机,是否连续触摸)等多维度信息来调整调速器参数
2019-10-21更新
0x05 调速器代码逻辑简析
——从代码理解调速器逻辑(博主能力有限,正在研究代码中,持续更新
综合两文做了一些修改和补充,原文链接:https://blog.csdn.net/xujianqun/article/details/7681011
https://blog.csdn.net/u014089131/article/details/68490573
如何判断跳频(跳到hispeed_freq
的实现)
if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) {
if (pcpu->policy->cur < tunables->hispeed_freq &&
cpu_load <= MAX_LOCAL_LOAD) {//当前频率未达到最大频率,可以直接设置为最大频率
new_freq = tunables->hispeed_freq;
} else {//此时需要选择比hispeed_freq更大的频率
new_freq = choose_freq(pcpu, loadadjfreq);
if (new_freq < tunables->hispeed_freq)
new_freq = tunables->hispeed_freq;
}
} else {//choose_freq 设定新的频率,此时需要降频处理
new_freq = choose_freq(pcpu, loadadjfreq);
}
把代码转换成语言描述就是
1、如果负载超过阈值(或开启boost),就直接升频到hispeed
。
2、如果负载超过阈值,频率也大于hispeed
(或开启boost),就调用choose_freq确定目标频率”最大频率*负载”。
3、如果负载没有超过阈值(未开启boost),需要降频,就调用choose_freq确定目标频率”当前频率负载”,至于判断是否执行降频请继续向下阅读。
如何确定目标频率具体数值?(choose_freq的实现)
注:loadadjfreq算法
cputime_speedadj = (u64)sched_get_busy(data) * pcpu->policy->cpuinfo.max_freq;
loadadjfreq = (unsigned int)cputime_speedadj * 100;//内核不支持浮点运算
/*
choose_freq函数用来选频,使选频后的系统workload小于或等于target load
核心思想是:选择最小的频率来满足target load
loadadjfreq一段时间内工作量
*/
static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu,
unsigned int loadadjfreq)
{
unsigned int freq = pcpu->policy->cur;//当前频率
unsigned int prevfreq, freqmin, freqmax;
unsigned int tl;//target load
int index;
freqmin = 0;
freqmax = UINT_MAX;
do {
prevfreq = freq;
//计算当前频率对应的workload
tl = freq_to_targetload(pcpu->policy->governor_data, freq);
/*
* Find the lowest frequency where the computed load is less
* than or equal to the target load.
*/
//从freq_table中获取最优频率对应的index,取大于等于loadadjfreq / tl (target freq)的最小值
if (cpufreq_frequency_table_target(
pcpu->policy, pcpu->freq_table, loadadjfreq / tl,
CPUFREQ_RELATION_L, &index))
break;
freq = pcpu->freq_table[index].frequency;//**计算出的目标频率,调速器核心之一**
if (freq > prevfreq) {//需要提高频率
/* The previous frequency is too low. */
freqmin = prevfreq;
if (freq >= freqmax) {//如果目标频率高于最大频率,取最大频率
/*
* Find the highest frequency that is less
* than freqmax.
*/
if (cpufreq_frequency_table_target(
pcpu->policy, pcpu->freq_table,
freqmax - 1, CPUFREQ_RELATION_H,
&index))
break;
freq = pcpu->freq_table[index].frequency;
if (freq == freqmin) {//这种情况就是锁频的情况,最大频率==最小频率
/*
* The first frequency below freqmax
* has already been found to be too
* low. freqmax is the lowest speed
* we found that is fast enough.
*/
freq = freqmax;
break;
}
}
} else if (freq < prevfreq) {//需要降低频率
/* The previous frequency is high enough. */
freqmax = prevfreq;
if (freq <= freqmin) {//目标频率比最低频率还小,取最小频率
/*
* Find the lowest frequency that is higher
* than freqmin.
*/
if (cpufreq_frequency_table_target(
pcpu->policy, pcpu->freq_table,
freqmin + 1, CPUFREQ_RELATION_L,
&index))
break;
freq = pcpu->freq_table[index].frequency;
/*
* If freqmax is the first frequency above
* freqmin then we have already found that
* this speed is fast enough.
*/
if (freq == freqmax)//锁频的情况,最大频率==最小频率
break;
}
}
/* If same frequency chosen as previous then done. */
} while (freq != prevfreq);
return freq;
}
待更新: 在应用频率前,调速器还做了那些事?
2019-10-21待更新
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!