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 深度文章以及開源社區的各種新近言論~