【DVFS】【 Governor】 【Interactive】【choose_freq()函数解析】

转自:https://blog.youkuaiyun.com/chongyang198999/article/details/49451587

一.choose_freq函数概述

在interative策略中,choose_freq函数用来选择一个合适的频率,使选频后系统的workload小于或等于target load. 其核心思想是:选取一个最小的频率,使在这个频率中的target load达到理想状态。

 

1. 影响选频的核心因素有两个:

1.两次统计时间内系统频率的平均频率loadadjfreq,
2.系统设定好的target load,在interative初始化的时候设定,tunables->target_loads = default_target_loads;

 

2. 先大概描述下两个核心因素的情况:

1)根据cpufreq_interactive_timer中对cpu当前负载的计算我们知道:

      Cur_load = loadadjfreq / cur_freq 

而在choose_freq函数中,计算target freq时:

      target_freq = loadadjfreq / t1

其中t1为系统设定好的target load,默认为90。

因此,综合上述两个式子,可以得到:

      Target_freq = cur_freq * (cur_load / target_load)  ;

也就是说,如果当前cpu负载大于90%,期望从频率表选择一个大于当前的频率,从而减少负载,如果负载小于90%,则期望从频率表选择一个频率以提高当前负载。

2)在interative初始化时,会为每个cpu频率设定一个理想的target load,以负载+频率的键值对方式,保存在cpufreq_interactive_tunables结构中的target_loads[]数组,ntarget_loads记录一共有多少个键值对。比如,我们可以以下面的方式,指定对应cpu频率下,理想的cpu负载。

Target_loads[] = {90, 800,  85 ,900,  95, 1000};

意思是,如果目标频率在区间[800, 900)之间,则理想cpu负载应在90%左右,如果当前目标频率在区间[900, 100),理想负载在85%左右,从访问方式可以看出,数组中频率最好以递增方式安排。

freq_to_targetload中获取target load:

    for (i = 0; i < tunables->ntarget_loads - 1 &&

    freq >= tunables->target_loads[i+1]; i += 2)

;

ret = tunables->target_loads[i];

在interative中只设定为90,因此对于所有频段,target的cpu负载都是90%。

 

二.choose_freq程序流程

 

三.结合具体样例分析频率的选择

例1

假如当前频率为800Mz,target_load默认为90,且有如下频率表:

{800Mz,  900Mz,  1000Mz, 1100Mz}

当前负载为91,根据Target_freq = cur_freq * (cur_load / target_load)

    Target_freq = 800Mz * (91 / 90) = 808Mz.

根据cpufreq_frequency_table_target()传入参数,从频率表取出比target_freq大的最小频率,也就是900M。此时,freq > prevfreq,则fremin=prefreq,且freq不大于freqmax,因此会重新进入while循环。因为target_load默认值固定为90,因此第二次循环,从频率表取出来的频率不变,freq = prevfreq,循环结束,返回查找出来的900Mz。

 

例2

假如当前频率为1100Mz,target_load默认为90,有如下频率表:

{800Mz,  900Mz,  1000Mz, 1100Mz}

当前负载为82,根据Target_freq = cur_freq * (cur_load / target_load)

    Target_freq = 1100Mz * (82 / 90) = 1002Mz.

从频率表选择比target_freq(1002Mz)大的最小频率,也就是1100Mz。同理,最后返回1100Mz。

 

由此可见,如果当前负载超过90,且频率不是最大值,很容易升频,而当负载为81时,才可能降频(仅限于前面两个例子),可能为了性能考量。

 

Freqmin和freqmax等变量,是为了多target load准备的,本程序中target load固定90的,但我们也可以考虑下,以体会此处算法的精巧。

 

例3

如果当前频率800Mhz,intravite初始化时,系统设定的负载与频率表的对应关系如下:

Target_loads[] = {90, 800,  85 ,900,  95, 1000,  88, 1100};

当前负载为98,根据Target_freq1 = cur_freq * (cur_load / target_load)

Target_freq1 = 800Mhz * (95 / 90)  =  844Mhz

从频率表选出900Mhz作为当前freq,因为prevfreq < freq < freqmax, 此时freqmin = prevfreq = 800Mhz,然后重新进入while循环

 

此时prevfreq = freq = 900Mhz, 计算

     target_freq2 = 800Mhz * ( 95 / 85) = 894Mhz

因此从频率表选出频率为freq = 900Mhz。因为900与prevfreq相等。因此返回900Mhz。算法这样设计,主要是防止频率跨度太大,或频率下降太快。由于需要调整各种target_load和频率表,比较麻烦,如果感兴趣可以自己更改相关参数。

/*
 * If increasing frequencies never map to a lower target load then
 * choose_freq() will find the minimum frequency that does not exceed its
 * target load given the current load.
 */
static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq)
{
	//freq的初始数值是pcpu->policy->cur 
	unsigned int freq = pcpu->policy->cur;
	//prevfreq保存着上次的freq,freq表示本次选频的结果,当二者相等时,就表示已经达到了最佳的选频结果.
	unsigned int prevfreq, freqmin, freqmax;
	unsigned int tl;//其中t1为系统设定好的target load,默认为90
	int index;

	freqmin = 0;
	freqmax = UINT_MAX;

	do {
		prevfreq = freq;
		//通过freq_to_targetload得到target load——tl 
		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.
		 */
		//CPUFREQ_RELATION_L,表示要取大于等于target的最小值 
		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) {// cur_load小于target_load
			/* The previous frequency is too low. */
			freqmin = prevfreq;

			if (freq >= freqmax) {
				/*
				 * Find the highest frequency that is less
				 * than freqmax.
				 */
				 //CPUFREQ_RELATION_H,表示要取小于等于target的最大值
				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) {// cur_load大于target_load
			/* The previous frequency is high enough. */
			freqmax = prevfreq;

			if (freq <= freqmin) {
				/*
				 * Find the lowest frequency that is higher
				 * than freqmin.
				 */
				 //CPUFREQ_RELATION_L,表示要取大于等于target的最小值 
				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;
}


// 系统设置的load,一般默认值是90
static unsigned int freq_to_targetload(
	struct cpufreq_interactive_tunables *tunables, unsigned int freq)
{
	int i;
	unsigned int ret;
	unsigned long flags;

	spin_lock_irqsave(&tunables->target_loads_lock, flags);

	for (i = 0; i < tunables->ntarget_loads - 1 &&freq >= tunables->target_loads[i+1]; i += 2)
		;

	ret = tunables->target_loads[i];
	spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
	return ret;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值