Schedutil

注意

所有這些都假設頻率和工作能力之間存線上性關係,我們知道這是有缺陷的,但這是最好的可行近似值。

PELT (按實體負載跟蹤)

透過 PELT,我們跟蹤各種排程器實體的多個指標,從單個任務到任務組切片再到 CPU 執行佇列。 作為此的基礎,我們使用指數加權移動平均 (EWMA),每個週期 (1024us) 衰減,使得 y^32 = 0.5。 也就是說,最近的 32 毫秒貢獻一半,而其餘歷史貢獻另一半。

具體來說

ewma_sum(u) := u_0 + u_1*y + u_2*y^2 + ...

ewma(u) = ewma_sum(u) / ewma_sum(1)

由於這本質上是一個無限幾何序列的級數,因此結果是可組合的,即 ewma(A) + ewma(B) = ewma(A+B)。 此屬性是關鍵,因為它能夠在任務移動時重新組合平均值。

請注意,被阻塞的任務仍然對聚合(任務組切片和 CPU 執行佇列)做出貢獻,這反映了它們在恢復執行時的預期貢獻。

使用此方法,我們跟蹤 2 個關鍵指標:“running” 和 “runnable”。 “Running” 反映實體在 CPU 上花費的時間,而 “runnable” 反映實體在執行佇列中花費的時間。 當只有一個任務時,這兩個指標是相同的,但是一旦發生 CPU 爭用,“running” 將減少以反映每個任務在 CPU 上花費的時間百分比,而 “runnable” 將增加以反映爭用量。

有關更多詳細資訊,請參見:kernel/sched/pelt.c

頻率/CPU 不變性

由於在 1GHz 下消耗 CPU 50% 與在 2GHz 下消耗 CPU 50% 不同,並且在 LITTLE CPU 上執行 50% 與在 big CPU 上執行 50% 不同,因此我們允許架構使用兩個比率來縮放時間增量,一個動態電壓和頻率縮放 (DVFS) 比率和一個微架構比率。

對於簡單的 DVFS 架構(軟體完全控制),我們可以輕鬆地將比率計算為

          f_cur
r_dvfs := -----
          f_max

對於硬體控制 DVFS 的更動態的系統,我們使用硬體計數器(Intel APERF/MPERF、ARMv8.4-AMU)來提供該比率。 具體來說,對於 Intel,我們使用

         APERF
f_cur := ----- * P0
         MPERF

           4C-turbo;  if available and turbo enabled
f_max := { 1C-turbo;  if turbo enabled
           P0;        otherwise

                  f_cur
r_dvfs := min( 1, ----- )
                  f_max

我們選擇 4C turbo 而不是 1C turbo,以使其更可持續。

r_cpu 確定為當前 CPU 的最高效能級別與系統中任何其他 CPU 的最高效能級別的比率。

r_tot = r_dvfs * r_cpu

結果是上述 “running” 和 “runnable” 指標變為 DVFS 和 CPU 型別的不變數。 換句話說,我們可以在 CPU 之間傳輸和比較它們。

有關更多詳細資訊,請參見

  • kernel/sched/pelt.h:update_rq_clock_pelt()

  • arch/x86/kernel/smpboot.c:”APERF/MPERF frequency ratio computation.”

  • 容量感知排程:”1. CPU 容量 + 2. 任務利用率”

UTIL_EST

由於定期任務在睡眠時其平均值會衰減,即使在執行時其預期利用率將相同,但在再次執行後,它們也會遭受(DVFS)斜坡上升。

為了緩解此問題(預設啟用選項),UTIL_EST 使用無限脈衝響應 (IIR) EWMA,並在取消排隊時使用 “running” 值(此時最高)。 UTIL_EST 過濾以立即增加並且僅在減小時衰減。

還維護了一個更廣泛的執行佇列總和(可執行任務):

util_est := Sum_t max( t_running, t_util_est_ewma )

有關更多詳細資訊,請參見:kernel/sched/fair.c:util_est_dequeue()

UCLAMP

可以為每個 CFS 或 RT 任務設定有效的 u_min 和 u_max 鉗位; 執行佇列保持所有正在執行的任務的這些鉗位的最大聚合。

有關更多詳細資訊,請參見:include/uapi/linux/sched/types.h

Schedutil / DVFS

每次更新排程器負載跟蹤時(任務喚醒、任務遷移、時間程序),我們都會呼叫 schedutil 以更新硬體 DVFS 狀態。

基礎是 CPU 執行佇列的 “running” 指標,根據上述內容,它是 CPU 的頻率不變利用率估計。 由此,我們計算所需的頻率,例如

           max( running, util_est );  if UTIL_EST
u_cfs := { running;                   otherwise

             clamp( u_cfs + u_rt , u_min, u_max );    if UCLAMP_TASK
u_clamp := { u_cfs + u_rt;                            otherwise

u := u_clamp + u_irq + u_dl;          [approx. see source for more detail]

f_des := min( f_max, 1.25 u * f_max )

XXX IO-wait:當更新是由於來自 IO 完成的任務喚醒時,我們會提升上面的 ‘u’。

然後,此頻率用於選擇 P-state/OPP 或直接將其修改為 CPPC 樣式的硬體請求。

XXX:截止日期任務(Sporadic Task Model)允許我們計算滿足工作負載所需的硬性 f_min。

由於這些回撥直接來自排程程式,因此 DVFS 硬體互動應該是 “快速” 且非阻塞的。 當硬體互動緩慢且昂貴時,Schedutil 支援速率限制 DVFS 請求,這會降低效率。

有關更多資訊,請參見:kernel/sched/cpufreq_schedutil.c

註釋

  • 在低負載場景中,DVFS 最相關,"running" 數字將密切反映利用率。

  • 在飽和場景中,任務移動將導致一些瞬時下降,假設我們有一個 CPU 被 4 個任務飽和,那麼當我們將任務遷移到空閒 CPU 時,舊 CPU 的 “running” 值為 0.75,而新 CPU 將獲得 0.25。 這是不可避免的,並且時間程序將糾正這一點。 XXX 我們是否仍然保證由於沒有空閒時間的 f_max?

  • 以上大部分是關於避免 DVFS 下降,以及獨立的 DVFS 域在負載變化時必須重新學習/加速。