LWN:修復asymmetric CPU packing 中的小問題!

語言: CN / TW / HK

關注了就能看到更多這麼棒的文章哦~

Fixing a corner case in asymmetric CPU packing

January 7, 2022 This article was contributed by Marta Rybczyńska DeepL assisted translation https://lwn.net/Articles/880367/ 

Linux 支援系統中的 CPU 具有不同的處理能力的體系架構。例如,Arm big.LITTLE 系統就將高效能且更耗電的 CPU 與比較慢但是功耗效率更好的 CPU 結合了起來。Linux 多年來也一直支援同步多執行緒架構(SMT,Simultaneous multithredaing),其中每一個 CPU 都會執行多個獨立的執行執行緒 thread,這樣看起來就好像是有多個 CPU core 一樣。一些架構中混合了這兩種方法。最近 Ricardo Neri 提交的 patch set 所引發的討論就表明,在這些系統上 scheduler 可能在以很低效的方式來分配任務。

Simultaneous multithreading

SMT 功能多年來在 PowerPC 和 x86 等架構中一直存在。SMT 系統中的 CPU 可以從兩個(或多個)獨立的執行上下文中獲取並執行指令。每個邏輯執行緒(logical thread)看起來都像是一個獨立的 CPU,所以一個物理上真正的 CPU 執行著兩個執行緒,在 Linux 中就會被視為是兩個 CPU。按這種方式來使用這種型別的硬體的 SMT 處理器通常被稱為 "siblings (兄弟處理器)"。作業系統基本上無法控制 SMT 處理器在這些執行上下文(execution context)之間應該如何分配資源。

SMT 的做法可以更好地利用處理器的資源。因為當某一個執行路徑被卡住時(例如在等待記憶體返回資料),物理 CPU 就可以執行其他執行緒的指令了。然而,處理器中的執行緒數量雖然增加了一倍,但是通常並不會使其處理能力也增加一倍。兩個執行緒都在共同分享著相同的資源,因此當系統處於低負載狀態時,SMT 模式才是最有效的。因此,兩個 SMT 執行緒的計算能力肯定比兩個物理 CPU core 的計算能力要低。

計算能力既然是有下降的,那麼這個下降的數值就應該要在 scheduler 中有所反映,但具體應該降低多少,是完全取決於不同工作場景下的負載的,所以核心需要使用一些啟發式方法(heuristic)。Linux 對此進行建模的方法就是降低在同一個硬體上執行的第二個(及以下)CPU 執行緒的 CPU 優先順序(優先順序是用來調節 CPU 被選中從而執行下一個指定任務的可能性大小的)。

使用者可以在 /sys/devices/system/cpu/cpuX/topology/core_cpus_list(其中 X 是系統中的 CPU 編號)中看到這個系統的拓撲資訊中的同級 CPU。例如,在一個有 SMT 的 12 核系統中:

$cat /sys/devices/system/cpu/cpu0/topology/core_cpus_list
0,6

這個結果意味著使用者可見的 CPU core 0 和 6 是 SMT 方式的 sibling,所以它們使用的是同一個硬體 core。

ASYM_PACKING

Asymmetric packing(排程器中所謂的 SD_ASYM_PACKING 方式)是 2010 年為 PowerPC 架構新增的一個功能。排程器可以利用這個機制來將任務轉移到某些 CPU 上從而讓其他 CPU 空閒,來獲得更好的處理器效能。然後,繁忙的 CPU 就可以進入到較低的 SMT 模式(也就是執行較少的執行緒),從而讓整體系統效能更高。PowerPC 的 SMT 模式文件中包括了一些例子。

ASYM_PACKING 模式已經經歷過多次重構了,目前在 x86 和 PowerPC 上都可以支援。針對 x86 的支援也包括可以比其他 core 擁有更高的 CPU 頻率,例如使用 Turbo Boost Max Technology (ITMT)3.0 功能的情況下。2016 年的 Linux Plumbers Conference 上的 ppt 更詳細地解釋了這項工作。

Mixing SMT and ASYM_PACKING

Neri 在一個有著三個不同的 CPU 優先順序的系統上觀察到了一些不合理的排程決策。這個系統中包含高效能 CPU core(英特爾酷睿)與其 SMT sibling,以及較低效能的 CPU core(英特爾 Atom)。在這種情況下,合理的排程方法(至少是對於某些工作負載下)是應該首先使用高效能 core(但不用相應的 SMT 副執行緒),然後再使用較低效能的 core,最後再用 SMT secondary core。但是實際上排程器會先把 task 交給高效能 core 及其 SMT sibling 上,讓其他 core 空閒。結果就是這些 task 在爭奪處理器資源,而那些獨立的 CPU 仍然閒置。

為了理解這個問題,可以思考一下 Neri 的一個 patch 中提到的例子。想象一下,某個系統中有兩個物理 CPU,具有不同的優先順序,分別是 60 和 30。它們都有 SMT sibling core。核心程式碼中使用了 x86 支援程式碼中的一個方程式來計算分配 SMT 優先順序:

smt_prio = prio * smp_num_siblings / i;

其中 smt_prio 是 effective priority,prio 是 CPU 的 original priority,smp_num_siblings 是每個 CPU 的 sibling 的數量(在 Neri 的例子中是 2),i 是分配給當前所關注的物理 CPU 的 sibling 的數量,從 1 開始。根據該公式,第一個 CPU 的主執行緒的計算出來的優先順序是 120,而其 SMT 兄弟姐妹的計算出來的優先順序是 60。對於第二個 CPU,主執行緒的優先順序為 60,sibling 執行緒的優先順序為 30。在這種情況下,SMT sibling 和效能較低的主執行緒的優先順序就相等了。

Neri 想改變排程器的實現,希望在使用 SMT sibling CPU 之前,優先將任務分配給第二個(物理)CPU 的主執行緒。為此,他提議修改公式,使之除以 sibling 數量的平方:

smt_prio = prio * smp_num_siblings / (i*i);

在此情況下,第一個 CPU 的執行緒優先順序將是 120 和 30;然後是第二個 CPU 的執行緒的 60 和 15。因此,task 就會被優先安排在兩個主執行緒上。

當涉及到排程器的負載平衡決策時,具體來說在排程器決定將一個任務從一個 CPU 移到另一個 CPU 來進行系統負載平衡時,Neri 的 patch set 做出了另一個改動。在決定是否將一個任務從原 CPU 移動到新的 CPU 時,排程器會考慮目標 CPU 是否有 SMT sibling。如果沒有的話,就可以把一個至少有兩個繁忙執行緒的 SMT 原 CPU 的任務交給這個目標 CPU。如果原 CPU 中只有一個 sibling core 在忙,那麼僅當目標 CPU 的優先順序高於原 CPU 時,task 才會被遷移。

Summary

根據作者提供的基準測試結果,排程效能在某些情況下提高了幾個百分點,儘管在大多數情況下提升比較小。基準測試也展示了一些情況下的效能有下降,但是 Neri 沒有對這個結果做出解釋。

這個改動已經被合併到 5.16 核心中,所以使用新 kernel 的系統的所有者應該可以觀察到排程的變化,並希望能有更好的效能。這個改動修正了排程策略中的一個 corner case,很有可能這並不是唯一一個需要處理的 corner case。預期在未來應該能看到更多的關於 asymmetric CPU 的排程策略的調整。

全文完

LWN 文章遵循 CC BY-SA 4.0 許可協議。

歡迎分享、轉載及基於現有協議再創作~

長按下面二維碼關注,關注 LWN 深度文章以及開源社群的各種新近言論~