162306a36Sopenharmony_ci.. SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci.. include:: ../disclaimer-zh_CN.rst 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci:Original: Documentation/scheduler/schedutil.rst 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci:翻译: 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci 唐艺舟 Tang Yizhou <tangyeechou@gmail.com> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci========= 1162306a36Sopenharmony_ciSchedutil 1262306a36Sopenharmony_ci========= 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci.. note:: 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci 本文所有内容都假设频率和工作算力之间存在线性关系。我们知道这是有瑕疵的, 1762306a36Sopenharmony_ci 但这是最可行的近似处理。 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciPELT(实体负载跟踪,Per Entity Load Tracking) 2062306a36Sopenharmony_ci============================================== 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci通过PELT,我们跟踪了各种调度器实体的一些指标,从单个任务到任务组分片到CPU 2362306a36Sopenharmony_ci运行队列。我们使用指数加权移动平均数(Exponentially Weighted Moving Average, 2462306a36Sopenharmony_ciEWMA)作为其基础,每个周期(1024us)都会衰减,衰减速率满足y^32 = 0.5。 2562306a36Sopenharmony_ci也就是说,最近的32ms贡献负载的一半,而历史上的其它时间则贡献另一半。 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci具体而言: 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci ewma_sum(u) := u_0 + u_1*y + u_2*y^2 + ... 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci ewma(u) = ewma_sum(u) / ewma_sum(1) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci由于这本质上是一个无限几何级数的累加,结果是可组合的,即ewma(A) + ewma(B) = ewma(A+B)。 3462306a36Sopenharmony_ci这个属性是关键,因为它提供了在任务迁移时重新组合平均数的能力。 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci请注意,阻塞态的任务仍然对累加值(任务组分片和CPU运行队列)有贡献,这反映了 3762306a36Sopenharmony_ci它们在恢复运行后的预期贡献。 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci利用这一点,我们跟踪2个关键指标:“运行”和“可运行”。“运行”反映了一个调度实体 4062306a36Sopenharmony_ci在CPU上花费的时间,而“可运行”反映了一个调度实体在运行队列中花费的时间。当只有 4162306a36Sopenharmony_ci一个任务时,这两个指标是相同的,但一旦出现对CPU的争用,“运行”将减少以反映每个 4262306a36Sopenharmony_ci任务在CPU上花费的时间,而“可运行”将增加以反映争用的激烈程度。 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci更多细节见:kernel/sched/pelt.c 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci频率 / CPU不变性 4862306a36Sopenharmony_ci================ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci因为CPU频率在1GHz时利用率为50%和CPU频率在2GHz时利用率为50%是不一样的,同样 5162306a36Sopenharmony_ci在小核上运行时利用率为50%和在大核上运行时利用率为50%是不一样的,我们允许架构 5262306a36Sopenharmony_ci以两个比率来伸缩时间差,其中一个是动态电压频率升降(Dynamic Voltage and 5362306a36Sopenharmony_ciFrequency Scaling,DVFS)比率,另一个是微架构比率。 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci对于简单的DVFS架构(软件有完全控制能力),我们可以很容易地计算该比率为:: 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci f_cur 5862306a36Sopenharmony_ci r_dvfs := ----- 5962306a36Sopenharmony_ci f_max 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci对于由硬件控制DVFS的更多动态系统,我们使用硬件计数器(Intel APERF/MPERF, 6262306a36Sopenharmony_ciARMv8.4-AMU)来计算这一比率。具体到Intel,我们使用:: 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci APERF 6562306a36Sopenharmony_ci f_cur := ----- * P0 6662306a36Sopenharmony_ci MPERF 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci 4C-turbo; 如果可用并且使能了turbo 6962306a36Sopenharmony_ci f_max := { 1C-turbo; 如果使能了turbo 7062306a36Sopenharmony_ci P0; 其它情况 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci f_cur 7362306a36Sopenharmony_ci r_dvfs := min( 1, ----- ) 7462306a36Sopenharmony_ci f_max 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci我们选择4C turbo而不是1C turbo,以使其更持久性略微更强。 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cir_cpu被定义为当前CPU的最高性能水平与系统中任何其它CPU的最高性能水平的比率。 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci r_tot = r_dvfs * r_cpu 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci其结果是,上述“运行”和“可运行”的指标变成DVFS无关和CPU型号无关了。也就是说, 8362306a36Sopenharmony_ci我们可以在CPU之间转移和比较它们。 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci更多细节见: 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci - kernel/sched/pelt.h:update_rq_clock_pelt() 8862306a36Sopenharmony_ci - arch/x86/kernel/smpboot.c:"APERF/MPERF frequency ratio computation." 8962306a36Sopenharmony_ci - Documentation/translations/zh_CN/scheduler/sched-capacity.rst:"1. CPU Capacity + 2. Task utilization" 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciUTIL_EST / UTIL_EST_FASTUP 9362306a36Sopenharmony_ci========================== 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci由于周期性任务的平均数在睡眠时会衰减,而在运行时其预期利用率会和睡眠前相同, 9662306a36Sopenharmony_ci因此它们在再次运行后会面临(DVFS)的上涨。 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci为了缓解这个问题,(一个默认使能的编译选项)UTIL_EST驱动一个无限脉冲响应 9962306a36Sopenharmony_ci(Infinite Impulse Response,IIR)的EWMA,“运行”值在出队时是最高的。 10062306a36Sopenharmony_ci另一个默认使能的编译选项UTIL_EST_FASTUP修改了IIR滤波器,使其允许立即增加, 10162306a36Sopenharmony_ci仅在利用率下降时衰减。 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci进一步,运行队列的(可运行任务的)利用率之和由下式计算: 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci util_est := \Sum_t max( t_running, t_util_est_ewma ) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci更多细节见: kernel/sched/fair.c:util_est_dequeue() 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciUCLAMP 11162306a36Sopenharmony_ci====== 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci可以在每个CFS或RT任务上设置有效的u_min和u_max clamp值(译注:clamp可以理解 11462306a36Sopenharmony_ci为类似滤波器的能力,它定义了有效取值范围的最大值和最小值);运行队列为所有正在 11562306a36Sopenharmony_ci运行的任务保持这些clamp的最大聚合值。 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci更多细节见: include/uapi/linux/sched/types.h 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciSchedutil / DVFS 12162306a36Sopenharmony_ci================ 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci每当调度器的负载跟踪被更新时(任务唤醒、任务迁移、时间流逝),我们都会调用 12462306a36Sopenharmony_cischedutil来更新硬件DVFS状态。 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci其基础是CPU运行队列的“运行”指标,根据上面的内容,它是CPU的频率不变的利用率 12762306a36Sopenharmony_ci估计值。由此我们计算出一个期望的频率,如下:: 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci max( running, util_est ); 如果使能UTIL_EST 13062306a36Sopenharmony_ci u_cfs := { running; 其它情况 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci clamp( u_cfs + u_rt, u_min, u_max ); 如果使能UCLAMP_TASK 13362306a36Sopenharmony_ci u_clamp := { u_cfs + u_rt; 其它情况 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci u := u_clamp + u_irq + u_dl; [估计值。更多细节见源代码] 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci f_des := min( f_max, 1.25 u * f_max ) 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci关于IO-wait的说明:当发生更新是因为任务从IO完成中唤醒时,我们提升上面的“u”。 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci然后,这个频率被用来选择一个P-state或OPP,或者直接混入一个发给硬件的CPPC式 14262306a36Sopenharmony_ci请求。 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci关于截止期限调度器的说明: 截止期限任务(偶发任务模型)使我们能够计算出满足 14562306a36Sopenharmony_ci工作负荷所需的硬f_min值。 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci因为这些回调函数是直接来自调度器的,所以DVFS的硬件交互应该是“快速”和非阻塞的。 14862306a36Sopenharmony_ci在硬件交互缓慢和昂贵的时候,schedutil支持DVFS请求限速,不过会降低效率。 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci更多信息见: kernel/sched/cpufreq_schedutil.c 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci注意 15462306a36Sopenharmony_ci==== 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci - 在低负载场景下,DVFS是最相关的,“运行”的值将密切反映利用率。 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci - 在负载饱和的场景下,任务迁移会导致一些瞬时性的使用率下降。假设我们有一个 15962306a36Sopenharmony_ci CPU,有4个任务占用导致其饱和,接下来我们将一个任务迁移到另一个空闲CPU上, 16062306a36Sopenharmony_ci 旧的CPU的“运行”值将为0.75,而新的CPU将获得0.25。这是不可避免的,而且随着 16162306a36Sopenharmony_ci 时间流逝将自动修正。另注,由于没有空闲时间,我们还能保证f_max值吗? 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci - 上述大部分内容是关于避免DVFS下滑,以及独立的DVFS域发生负载迁移时不得不 16462306a36Sopenharmony_ci 重新学习/提升频率。 16562306a36Sopenharmony_ci 166