代碼版本:Linux4.9 android-msm-crosshatch-4.9-android12
代碼展示
static inline u64 scale_exec_time(u64 delta, struct rq *rq)
{
u32 freq;
// ⑴ 將 CPU cycles 轉(zhuǎn)換為 CPU 當(dāng)前頻率
freq = cpu_cycles_to_freq(rq->cc.cycles, rq->cc.time);
// ⑵ 歸一化 delta
delta = DIV64_U64_ROUNDUP(delta * freq, max_possible_freq);
delta *= rq->cluster->exec_scale_factor;
delta >>= 10;
return delta;
}
代碼邏輯:
scale_exec_time()
函數(shù)用于給任務(wù)的運(yùn)行時(shí)間 delta 進(jìn)行歸一化呐馆。
為什么歸一化续誉?
EAS 主要針對(duì)異構(gòu) CPU 架構(gòu),如 Arm big.LITTLE,因?yàn)檫@種架構(gòu)有不同性能和功耗的 CPU 核心蹦狂,不同 CPU 的最大算力厚满、最大頻率等都不同施逾。假定一個(gè)任務(wù)在當(dāng)前窗口中運(yùn)行了 5ms钉答,對(duì)不同頻率的兩個(gè) CPU 來(lái)說(shuō),5ms 帶來(lái)的負(fù)載是截然不同的灸叼。
WALT 算法引入了一種類似權(quán)重的方法神汹,根據(jù) CPU 的頻率(frequency)和 最大每周期指令數(shù)(efficiency)來(lái)對(duì)任務(wù)的運(yùn)行時(shí)間進(jìn)行歸一化庆捺。
(注:此處 efficiency 的定義并不確定,在內(nèi)核文檔中出現(xiàn)過(guò)這個(gè)定義屁魏。)
⑴ 將 CPU cycles 轉(zhuǎn)換為 CPU 當(dāng)前頻率
freq = cpu_cycles_to_freq(rq->cc.cycles, rq->cc.time);
static inline u32 cpu_cycles_to_freq(u64 cycles, u64 period)
{
return div64_u64(cycles, period);
}
在這里 freq = rq->cc.cycles / rq->cc.time滔以。其中,rq->cc.cycles 和 rq->cc.time 在函數(shù) update_task_rq_cpu_cycles()
中更新:
static void
update_task_rq_cpu_cycles(struct task_struct *p, struct rq *rq, int event,
u64 wallclock, u64 irqtime)
{
u64 cur_cycles;
int cpu = cpu_of(rq);
lockdep_assert_held(&rq->lock);
if (!use_cycle_counter) {
rq->cc.cycles = cpu_cur_freq(cpu);
rq->cc.time = 1;
return;
}
cur_cycles = read_cycle_counter(cpu, wallclock);
/*
* If current task is idle task and irqtime == 0 CPU was
* indeed idle and probably its cycle counter was not
* increasing. We still need estimatied CPU frequency
* for IO wait time accounting. Use the previously
* calculated frequency in such a case.
*/
if (!is_idle_task(rq->curr) || irqtime) {
if (unlikely(cur_cycles < p->cpu_cycles))
rq->cc.cycles = cur_cycles + (U64_MAX - p->cpu_cycles);
else
rq->cc.cycles = cur_cycles - p->cpu_cycles;
rq->cc.cycles = rq->cc.cycles * NSEC_PER_MSEC;
if (event == IRQ_UPDATE && is_idle_task(p))
/*
* Time between mark_start of idle task and IRQ handler
* entry time is CPU cycle counter stall period.
* Upon IRQ handler entry sched_account_irqstart()
* replenishes idle task's cpu cycle counter so
* rq->cc.cycles now represents increased cycles during
* IRQ handler rather than time between idle entry and
* IRQ exit. Thus use irqtime as time delta.
*/
rq->cc.time = irqtime;
else
rq->cc.time = wallclock - p->ravg.mark_start;
BUG_ON((s64)rq->cc.time < 0);
}
p->cpu_cycles = cur_cycles;
trace_sched_get_task_cpu_cycles(cpu, event, rq->cc.cycles, rq->cc.time, p);
}
⑵ 歸一化 delta
-
delta = DIV64_U64_ROUNDUP(delta * freq, max_possible_freq);
即 delta = delta * freq/max_possible_freq氓拼。freq 是當(dāng)前 CPU 的頻率你画,由 ⑴ 計(jì)算而得:freq = rq->cc.cycles / rq->cc.time。
max_possible_freq 就是 max(policy->cpuinfo.max_freq)桃漾。
policy 可以淺顯地認(rèn)為是簇號(hào)坏匪,如不同的 policy 指向小核簇、大核簇和超大核:- 對(duì)于擁有多個(gè) CPU 的簇來(lái)說(shuō)撬统,頻率的計(jì)算在 sugov_update_shared() 中進(jìn)行适滓,簇內(nèi)每個(gè) CPU 的頻率都是一致的秫逝,因此一個(gè)簇會(huì)擁有一個(gè)當(dāng)前頻率和一個(gè)最大頻率廓握,即 policy->cpuinfo.max_freq;
- 對(duì)于單個(gè) CPU 來(lái)說(shuō)晦炊,頻率的計(jì)算在 sugov_update_single() 中進(jìn)行几于,它也會(huì)有一個(gè)最大頻率 policy->cpuinfo.max_freq蕊苗。
在運(yùn)行該版本內(nèi)核的 pixel 3xl 中,8 個(gè) CPU 分為小核簇與大核簇沿彭,他們的最大頻率分別是 381 和 1024。
-
delta *= rq->cluster->exec_scale_factor;
cluster->exec_scale_factor = 1024 * cluster->efficiency/max_possible_efficiencycluster->efficiency 可能指 運(yùn)行任務(wù)的 CPU 的每周期指令數(shù) (IPC)尖滚。
max_possible_efficiency 可能指 系統(tǒng)中任何 CPU 提供的最大 IPC喉刘。
這個(gè)值在設(shè)備樹(shù)中給定,在運(yùn)行該版本內(nèi)核的 pixel 3xl 中漆弄,小核簇和大核簇的 max_possible_efficiency 分別是 1024 和 1740睦裳。 delta >>= 10;
即 delta = delta / 1024。
將三句代碼一起看撼唾,能得出一個(gè)等式: