CPU 策略学习:interactive分析,结合代码

CPU动态调频策略,目前常见的有performance powersave ondemand consertive interactive

以下是基于interactive代码详细分析下的一些见解


从网上搜索的,比较详细的是:

http://bbs.gfan.com/android-976301-2-1.html


Advantages:
+ significantly more responsive to ramp cpu up when required (UI interaction)
+ more consistent ramping, existing governors do their cpu load sampling in a workqueue context, the 'interactive' governor does this in a timer context, which gives more consistent cpu load sampling.
+ higher priority for cpu frequency increase, rt_workqueue is used for scaling up, giving the remaining tasks the cpu performance benefit, unlike existing governors which schedule rampup work to occur after your performance starved tasks have completed.
优点:
此模式在与用户交互的时候,反应速度更快(即是频率调节的速度更快,更能随时对及时处理器的符合作出反应),由此,便可以提供更好地用户体验(conservative模式反应速度慢于此,因此有时候会出现稍卡的体验)
当然,为了达成这一点,interactive有更高的处理器负荷采样率,并且摒弃了上述两种调节方式在高负荷时候处理器频率不满足需求以后才进行调频,interactive保证了更快的反应,保留了频率调节器的高优先级,来更快地处理器负荷高起来的时候将频提高。

那么,我就有如下疑问:

反应速度如何更快?

为何需要更高采样率?更高是指多少的采样率?

举例说明如何升频,如何降频?


结合代码分析策略

cpufreq_interactive_init

初始化对5个全局变量进行赋值

三个参数:

go_hispeed_load = DEFAULT_GO_HISPEED_LOAD;

min_sample_time = DEFAULT_MIN_SAMPLE_TIME;

timer_rate = DEFAULT_TIMER_RATE;

一个队列:

/* No rescuer thread, bind to CPU queuing the work for possibly

   warm cache (probably doesn't matter much). */

down_wq = alloc_workqueue("knteractive_down", 0, 1);

一个任务栈:

up_task = kthread_create(cpufreq_interactive_up_task, NULL,

 "kinteractiveup");

go_hispeed_load:是高频阈值。当系统的负载超过该值,升频,否则降频。具体如何升频、如何降频,后面介绍。

min_sample_time最小采样时间。每次调频结果必须维持至少这个时间。

timer_rate:采样定时器的采样率。

down_wq :降频队列。降频是以队列方式实现,实时性不高,或者说优先级较低。

up_task:升频任务栈。升频是以线程方式实现,只要有升频需求,立即生效,实时性高,优先级非常高。

到这里,对网上解释的“反应速度快”“保留了频率调节器的高优先级”就可以理解了。


下面介绍如何升频、如何降频

调频定时器,定时器时间被固定,每个timer周期完成如下事情:

检查策略有效性,即是否当前策略使能状态

系统在最近一个采样周期内的负载计算,用于决定升降频

根据负载,得出升降频的判定

升频/降频的实施

下面就是该策略的精髓了,如何判定升降频

if (cpu_load >= go_hispeed_load) {
	if (pcpu->policy->cur == pcpu->policy->min)
		new_freq = hispeed_freq;
	else
		new_freq = pcpu->policy->max * cpu_load / 100;
} else {
	new_freq = pcpu->policy->cur * cpu_load / 100;
}

这里

go_hispeed_load = 816MHz

pcpu->policy->min = 252MHz

pcpu->policy->max = 1416MHz

把代码转换成语言描述就是

1、如果系统负载超过负载阈值,而且系统当前频率是系统最低频,那么升频到816MHz

2、如果系统负载超过负载阈值,但是系统当前频率不是最低频率,那么升频到1416MHz*系统负载

3、如果系统负载没有超过负载阈值,那么降频到 系统当前频率*系统负载,至于是否降频还是保持当前频率后面自然会处理

到此,对网上解释“频率调节的速度更快,更能随时对及时处理器的符合作出反应”就很好理解了。


接下来,对于升频或者降频,新频率值的确定是策略的另一部分,且往下看

#define CPUFREQ_RELATION_L 0  /* lowest frequency at or above target */

#define CPUFREQ_RELATION_H 1  /* highest frequency below or at target */

这两个声明的作用是,当我们用上面负载乘以频率得到的频率值,在频率列表中查找对应频率点时,CPUFREQ_RELATION_L 是取上限值,CPUFREQ_RELATION_H 是取下限值。


对于上面去上限值还是下限值如果不理解,或者有疑问,看下代码

struct cpufreq_frequency_table optimal = {
	.index = ~0,
	.frequency = 0,
};
struct cpufreq_frequency_table suboptimal = {
	.index = ~0,
	.frequency = 0,
};

第一步:初始化两个结构体,索引号很大,频率为0

switch (relation) {
case CPUFREQ_RELATION_H:
	suboptimal.frequency = ~0;
	break;
case CPUFREQ_RELATION_L:
	optimal.frequency = ~0;
	break;
}

第二步:这一步要结合下面一起看

for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
	//循环频率列表,寻找需要的频率值
	unsigned int freq = table[i].frequency;
	if (freq == CPUFREQ_ENTRY_INVALID)
		continue;
	if ((freq < policy->min) || (freq > policy->max))
		continue;
	//有效性检查结束
	switch (relation) {
	case CPUFREQ_RELATION_H:
	//如果是H,在上一步中suboptimal.frequency = ~0,optimal.frequency = 0
		if (freq <= target_freq) {
		//当前频率比目标频率小,记录当前频率作为下限
			if (freq >= optimal.frequency) {
				optimal.frequency = freq;
				optimal.index = i;
			}
		} else {//当前频率比目标频率大,记录当前频率作为上限
			if (freq <= suboptimal.frequency) {
				suboptimal.frequency = freq;
				suboptimal.index = i;
			}
		}
	break;
	case CPUFREQ_RELATION_L:
		if (freq >= target_freq) {
			if (freq <= optimal.frequency) {
				optimal.frequency = freq;
				optimal.index = i;
			}
		} else {
			if (freq >= suboptimal.frequency) {
				suboptimal.frequency = freq;
				suboptimal.index = i;
			}
		}
	break;
}

第三步:找到了目标频率对应在那个频率段,并且知道了上下限频率值

if (optimal.index > i) {//如果下限频率是最高频
	if (suboptimal.index > i)
	return -EINVAL;
	*index = suboptimal.index;//如果已经是最后一档的频率,取最高频
} else
	*index = optimal.index;//正常情况,取下限频率

举个例子:

当前系统频率504MHz,系统负载阈值80,当前系统负载90

索引列表是 

252 504 816 1008 1200 1272 1416

由系统负载算出新频率(目标频率)是

1416 * 90% , 介于12721416之间,取1272MHz


Interactive策略考虑的比较细,因为当系统在最低频,例如252MHz时,稍微的任务启动,都会使得系统负载时100%,如果按照以上算法,必定跑到系统最高频,

例如1416MHz

所以对于系统最低频有特殊处理

当系统处于最低频时,负载超过负载阈值,直接升频至已经实现指定的go_hispeed_load ,例如816MHz


以上是升频的算法,接下来看看如何决定降频值

当系统负载没有达到负载阈值,就决定要降频了,不同于升频的是,降频是用系统当前频率,也就是系统自身频率值,乘以负载百分比,得到新频率,即目标频率,同样在频率索引列表中,寻找合适的频率值,也是取下限频率。

举个例子:

当前系统频率504MHz,系统负载阈值80,当前系统负载60

索引列表是 

252 504 816 1008 1200 1272 1416

由系统负载算出新频率(目标频率)是

504* 60% 介于252504之间,取252MHz

调频和调压,就是动态调压调频DVFS

频率确定后,对应的armlogic电压也就确定

如果是升频,先升压,后升频

如果是降频,先降频,后降压


你可能感兴趣的:(timer,struct,table,performance,Go,代码分析)