天天看點

Linux排程器何時需觸發搶占?—— 從hackbench談起

作者:何惟禹 吳一昊

一、背景:性能之戰

“不服跑個分”雖然已經淪為手機行業的調侃用語,但在作業系統領域仍然是最重要的評價方式之一。本文的故事也源于一次 Alinux3 與 CentOS8 的一次跑分的較量。當然比分較量并不是目的,更重要的是發現存在的回歸缺陷并進行修複,最終讓 Alinux3 全方位持平或超過 CentOS8。

在本次較量中,我們使用 hackbench 作為跑分軟體,我們在測試過程中發現 Alinux3 的回歸缺陷出現在核心排程器上,我們通過解剖 hackbench 工作模型、深入分析核心排程器行為将回歸缺陷修複,最終讓 Alinux3 重新持平 CentOS8。

本文将從幾個方面展開,并重點介紹黑體字部分:

  • 相關知識簡介
  • hackbench 工作模式簡介
  • hackbench 性能受損之源
  • 雙參數優化
  • 思考與拓展

二、相關知識簡介

2.1 CFS排程器

Linux 中大部分(可以粗略認為是實時任務之外的所有)線程/程序,都由一個叫 CFS(完全公平排程器)的排程器進行排程,它是 Linux 最核心的元件之一。(在Linux中,線程和程序隻有細微差别,下文統一用程序表述)

CFS的核心是紅黑樹,用于管理系統中程序的運作時間,作為選擇下一個将要運作的程序的依據。此外,它還支援優先級、組排程(基于我們熟知的cgroup實作)、限流等功能,滿足各種進階需求。

CFS 的詳細介紹

[1]。

2.2 hackbench

hackbench 是一個針對 Linux 核心排程器的壓力測試工具,它的主要工作是建立指定數量的排程實體對(線程/程序),并讓它們通過 sockets/pipe 進行資料傳輸,最後統計整個運作過程的時間開銷。

2.3 CFS 排程器參數

本文重點關注以下兩個參數,這兩個參數也是影響 hackbench 跑分性能的重要因素。系統管理者可以使用 sysctl 指令來進行設定。

  • 最小粒度時間:kernel.sched_min_granularity_ns    

        通過修改 kernel.sched_min_granularity_ns,可以影響 CFS 排程周期(sched period)的時間長短。例如:設定kernel.sched_min_granularity_ns = m,當系統中存在大量可運作程序時,m 越大,CFS 排程周期就越長。

        如圖 1 所示,每個程序都能夠在 CPU 上運作且時間各有長短,sched_min_granularity_ns 保證了每個程序的最小運作時間(優先級相同的情況下),sched_min_granularity_ns 越大每個程序單次可運作的時間就越長。

Linux排程器何時需觸發搶占?—— 從hackbench談起

圖 1:sched_min_granularity_ns 示意圖

  • 喚醒搶占粒度:kernel.sched_wakeup_granularity_ns

        kernel.sched_wakeup_granularity_ns 保證了重新喚醒的程序不會頻繁搶占正在運作的程序,kernel.sched_wakeup_granularity_ns 越大,喚醒程序進行搶占的頻率就越小。

        如圖 2 所示,有 process-{1,2,3} 三個程序被喚醒,因為 process-3 的運作時間大于 curr(正在 CPU 上運作的程序)無法搶占運作,而 process-2 運作時間小于 curr 但其內插補點小于 sched_wakeup_granularity_ns 也無法搶占運作,隻有 process-1 能夠搶占 curr 運作,是以 sched_wakeup_granularity_ns 越小,程序被喚醒後的響應時間就越快(等待運作時間越短)。

Linux排程器何時需觸發搶占?—— 從hackbench談起

圖 2:sched_wakeup_granularity_ns 示意圖

三、hackbench 工作模式簡介

hackbench 工作模式分為 process mode 和 thread mode 主要差別就是以建立 process 還是 thread 為基礎來進行測試,下面以 thread 來進行介紹。

  1. hackbench 會建立若幹線程(偶數),均分為兩類線程:sender 和 receiver
  2. 并将其劃分為 n 個 group,每個 group 包含 m 對 sender 和 receiver。
  3. 每個 sender 的任務就是給其所在 group 的所有 receiver 輪流發送 loop 次大小為 datasize 的資料包
  4. receiver 則隻負責接收資料包即可。
  5. 同一個 group 中的sender 和 receiver 有兩種方式進行通信:pipe 和 local socket(一次測試中隻能都是 pipe 或者 socket),不同 group 之間的線程沒有互動關系。

通過上面 hackbench 模型分析,可以得知同一個 group 中的 thread/process 主要是 I/O 密集型,不同 group 之間的 thread/process 主要是 CPU 密集型。

Linux排程器何時需觸發搶占?—— 從hackbench談起

圖 3: hackbench 工作模式

主動上下文切換:

  • 對于 receiver,當 buffer 中沒有資料時,receiver 會被阻塞并主動讓出 CPU 進入睡眠。
  • 對于 sender,如果 buffer 中沒有足夠空間寫入資料時, sender 也會被阻塞且主動讓出 CPU。

是以,系統中"主動上下文切換"是很多的,但同時也存在“被動上下文切換”。後者會受到接下來我們将要介紹的參數影響。

四、hackbench性能受損之源

在 Alinux3. VS CentOS8. 的 hackbench-socket 測試中,Alinux3 的性能均遜于 CentOS8,通過排查鎖定了 CFS 中的 sched_min_granularity_ns 和 sched_wakeup_granularity_ns 兩個參數,具體如下:

sched_min_granularity_ns sched_wakeup_granularity_ns 性能
Alinux3 2.25ms 3ms
CentOS8 10ms 15ms

接下來我們調整這兩個排程參數來進行進一步的深入分析。

五、雙參數優化

為了簡介表達下面會以 m 表示 kernel.sched_min_granularity_ns,w 表示 kernel.sched_wakeup_granularity_ns

為了探索雙參數對于排程器的影響,我們選擇每次固定一個參數,研究另一個參數變化對于性能的影響,并使用系統知識來解釋這種現象背後的原理。

5.1 固定 sched_wakeup_granularity_ns

Linux排程器何時需觸發搶占?—— 從hackbench談起

圖 4: 固定 w,調整m

在上圖中我們固定了參數 w 并根據參數 m 變化趨勢其劃分為三個部分:區域A(1ms~4ms),區域B(4ms~17ms),區域C(17ms~30ms)。在區域A中四條曲線均呈現一個極速下降的趨勢,而在區域B中四條曲線都處于一種震蕩狀态,波動較大,最後在區域C中四條曲線都趨于穩定。

在第二節相關知識中可以知道 m 影響着程序的運作時間,同時也意味着它影響着程序的“被動上下文切換”。

  • 對于區域A而言,搶占過于頻繁,而大部分搶占都是無意義的,因為對端無資料可寫/無緩沖區可用,導緻大量備援的“主動上下文切換“。此時較大的 w 能讓 sender/receiver 有更多的時間來寫入資料/消耗資料來減少對端程序無意義的“主動上下文切換“。
  • 對于區域B而言,随着 m 的增加漸漸滿足 sender/receiver 執行任務的時間需求能夠在緩沖區寫入/讀出足夠的資料,是以需要較小的 w 來增加喚醒程序的搶占幾率,讓對端程序能夠更快的響應處理資料,減少下一輪排程時的“主動上下文切換”。
  • 對于區域C而言,m已經足夠大,已經幾乎不會有“被動上下文切換”發生,程序會在執行完任務之後進行“主動上下文切換”等待對端程序進行處理,此時 m 對性能的影響就很小了。

5.2 固定 sched_min_granularity_ns

Linux排程器何時需觸發搶占?—— 從hackbench談起

圖 5: 固定 m,調整w

在上圖中我們固定了參數 m,同樣劃分了三個區域:

  • 在區域A中,同樣存在圖 4 中的現象,較大 m 受 w 的影響較小,而較小的 m 随着 w 的增大性能會越來越好。
  • 在區域B中,中等大小的 m(8ms/12ms)程序還是存在較多“被動上下文切換”,并且其中的程序已經處理了相當一部分資料期望對端程序能夠盡快的響應處理,是以較大 w 會嚴重影響中等大小 m 的性能。
  • 在區域C中圖5和圖4表現一緻都是趨于穩定,因為 w 過大時幾乎不會發生喚醒搶占,是以這時單純 w 值的變化對性能的影響并不大,但是過大的 w 對于中等大小的 m 則會造成性能問題(原因同上條)。 

5.3 性能趨勢總覽

下面是一個實驗資料的熱力總覽圖,來直覺展示 m 和 w 之間的制約關系,以供需要的同學參考分析。三個區域和圖 4、圖 5中的劃分方法略有不同。

Linux排程器何時需觸發搶占?—— 從hackbench談起

圖 6:總覽圖

5.4 最優雙參數(參考)

  1. 從上面兩節的分析可知對于 hackbench 這樣帶有“主動上下文切換”的場景可以選擇較大的 m(例如:15~20ms),
  2. 在pipe/socket 雙向通信的場景中,對端的響應時間會對影響程序的下一次處理,為了讓對端程序能夠及時響應可以選擇一個中等大小的 w(例如:6~8ms)來擷取較高的性能。

六、思考與擴充

  1. 在桌面場景中,應用更偏向于互動型,應用的服務品質也更多的展現在應用對于使用者操作的響應時間,是以可以選擇較小的 sched_wakeup_granularity_ns 來提高應用的互動性。
  2. 在伺服器場景中,應用更偏向于計算處理,應用需要更多的運作時間來進行密集計算,是以可以選擇較大的 sched_min_granularity_ns,但是為了防止單個程序獨占 CPU 過久同時也為了能夠及時處理用戶端請求響應,應該選擇一個中等大小的 sched_wakeup_granularity_ns。
  3. 在 Linux 原生核心中 m 和 w 的預設參數被設定為适配桌面場景[2],是以預設參數并不适合 Alinux。

參考資料

[1] 

http://www.wowotech.net/process_management/451.html

[2] 

https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt

繼續閱讀