# 從 big.LITTLE 到 EAS ## big.LITTLE 濫觴 big.LITTLE 技術最早出現於 2011 年 10 月的 Arm Cortex-A7 中(ARMv7-A架構),Cortex-A7 作為「小核」,可搭配與其指令集相容的 Arm Cortex-A12/A15/A17 使用,後者也就是「大核」。 ![image](https://hackmd.io/_uploads/HkpxHNhX0.png) 後來推出以 ARMv8-A 為基礎的架構,如 Arm Cortex-A53 和 Arm Cortex-A57,也可以用來設計為 big.LITTLE 的組合。 大核和小核都屬於 Arm 架構,儘管二者在 micro-architecture 有別,例如 Cortex-A15 有 6 個 event counter,而 A7 只有 4 個,Cortex-A15 是亂序執行 (out-of-order execution) 處理器,而 Cortex-A7 則是循序執行 (in-order execution) 處理器,但因彼此指令集相容,於是它們可執行同一個作業系統。 ![image](https://hackmd.io/_uploads/SyuZHE3mR.png) 同質的大核構成 big cluster,同質的小核構成 LITTLE cluster,同一 cluster 的 CPU 共享 L2 cache。 big cluster 和 LITTLE cluster 共用中斷控制器 (如 GIC-400),且藉由 cache coherent interconnect 相互連接 (如 coreLink CCI-400)。 如果沒有硬體層面的 cache 一致性,資料在大核和小核之間的傳輸將必須經過共享記憶體,這會嚴重影響效率。 ![image](https://hackmd.io/_uploads/S1kGrEh7C.png) ## big.LITTLE 處理器核之間的遷移 (migration) big cluster 和 LITTLE cluster 中的 CPU 被同一個作業系統排程,作業系統中的任務在執行時,可根據負載的情況,在大小核之間動態地遷移,以提高系統的靈活程度。 ![image](https://hackmd.io/_uploads/SkKzH427C.png) 以作業系統排程來說,遷移的方案可分為兩種,其中最早出現也是相對最簡單的是 cluster migration。 - [ ] cluster migration ![image](https://hackmd.io/_uploads/rJUmS43mR.png) 在這方案中,作業系統在任何時刻都只能使用其中的一個 cluster,當負載變化時,任務將從一個 cluster 整體切換到另一個 cluster 上去,也就是說,它是以 cluster 為單位進行遷移。早期的 Samsung Exynos 5 Octa (5410) 和 NVIDIA 的 Tegra X1 採用此模式。 ![image](https://hackmd.io/_uploads/HJOVSNnmR.png) 這種方案的好處是:在任一時刻,作業系統要不全都在 big cores 上運行,要不全都在 LITTLE cores 上執行,雖然整個系統是 HMP,但從作業系統的角度來看,每個時刻的操作都是針對 SMP,因此對於那些預設支援 SMP 的系統,使用 big.LITTLE 架構,不用做太多程式碼變更。 - [ ] CPU migration 前述以 cluster 為遷移單位,會導致細緻度不夠,對於任務的負載介於 big cluster 和 LITTLE cluster 之間,不用對一個 cluster 中的所有 cores 進行遷移。而且,對一個 cluster 進行關閉和開啟的 latency 通常較大,因而所需的 "target_residency" 也較大,切換的頻率受到限制。 ![image](https://hackmd.io/_uploads/rkYrHE27R.png) 於是,更細緻的方案是 big cluster 和 LITTLE cluster 中的處理器核搭配使用,如下圖,低負載時用 4 個小核,中等負載時用 2 個大核和 2 個小核。 ![image](https://hackmd.io/_uploads/SJWUr42QC.png) 這就是以單個處理器核作為遷移單位的 CPU migration 方案,具體的做法是:一個大核和一個小核進行組隊,形成一組配對 (pair)。排程器可用每組配對,但在同一時刻,只允許配對中的一個處理器核運行,負載高時在大核上運行,低就在小核上運行。 從作業系統的角度來看,在任一時刻,每個配對看似只有一個處理器核,所以這樣的配對又被稱為 "virtual core"。 ![image](https://hackmd.io/_uploads/HkzFBV3XA.png) 在遷移過程中,"virtual core" 中的其中一個核被關閉 (outbound),另一個核被同步開啟 (inbound),任務的 context 從被關閉的處理器核轉移到同一配對中被開啟的處理器核上。 ![image](https://hackmd.io/_uploads/rJr9B4hXC.png) 在 Linux 中,CPU migration 的 context 轉移依靠的是 cpufreq 框架,切換到哪一個處理器核及什麼時候切換,都由底層的 cpufreq 的裝置驅動程式來決定。 從作業系統核心來說,它看到的是一個 cpufreq 展現給它的電壓/頻率的列表,從配對中一個處理器核到另一個處理器核的遷移,就好像只是調整 CPU 的電壓和頻率一樣,所以可說這個 CPU 配對的組成對核心是「通透」(transparent)。 ![image](https://hackmd.io/_uploads/HJsiSE2XC.png) 這套機制被稱為 IKS (In-kernel Switcher),被 NVIDIA 的 Tegra 3 採用。 > 延伸閱讀: [In Kernel Switcher: A solution to support ARM's new big.LITTLE technology](https://events.static.linuxfound.org/images/stories/slides/elc2013_poirier.pdf) ## GTS 無論是 cluster migration,抑或 CPU migration,在某一個時刻,都只有一半的 CPU cores 可執行 (假設系統中大核和小核的數目相等),這對 CPU 資源會造成浪費。所以,一種可以充分利用各個實體核的 GTS (Global Task Scheduling) 方案因應而生。 在 GTS 模型中,高優先權或者計算密集型的任務被分配到「大核」("big" core)上,其他的,如一些背景任務,則在「小核」("LITTLE" core) 上運行。所有的大核和小核被統一排程,可同時執行。 ![image](https://hackmd.io/_uploads/Byr6BN2mR.png) Samsung Exynos 5 Octa 系列自 5420 起,包括 5422 和 5430,及 Apple 公司的 A11,都採用 GTS。 ## 負載和遷移 ### 何時遷移 遷移的依據是負載的變化,那具體的標準為何?當正在小核上執行的任務的平均負載超過 "up migration threshold",就會被排程器遷移到 big 處理器核上繼續執行;而當 big 處理器核上的任務負載低於 "down migration threshold",就將被遷移到 LITTLE 處理器核上。負載處在這兩個 threshold 之間時,則沒有動作。 ![image](https://hackmd.io/_uploads/SkJCHEn7R.png) ### 任務喚醒 當任務從睡眠狀態被喚醒的時候,還沒有產生負載,那如何判斷它應該被放到大核還是小核上執行呢? 預設的做法是根據這個任務過去的負載情況,最簡單的依據就是該任務在上次進入睡眠前所處的處理器核。為此,需要由排程器去記錄一個任務既往的負載資訊。 如下圖所示,在這種場景中,第一次喚醒時將在大核上執行 ("Task state"代表狀態是運行還是睡眠,分別對應 "Core residency" 中的實線和虛線,"B" 代表大核,"L" 代表小核)。 ![image](https://hackmd.io/_uploads/SkEgUN3XR.png) 而第二次喚醒時則會排程到 LITTLE處理器核上執行。 ![image](https://hackmd.io/_uploads/ByzELVnQ0.png) Cortex-A 系列的 Arm 晶片常搭配 Linux 使用的,而 Linux 在針對多核應用的設計上主要針對 SMP,這會帶來一些問題。 其中之一就是:任務在大核和小核之間切換,但大核和小核的 cache line 的大小可能會不同 (如大核是 64B,小核是 32B),在某些情況下這可能引發非預期的問題。 ![image](https://hackmd.io/_uploads/Sy2VL4nm0.png) 一個典型的案例是,應用程式會被 SIGKILL 訊號所終結,但發生時的地址看似完全隨機的,爾後觀察到這些觸發異常的地址全都分佈於 0x40-0x7for0xc0-0xff 的範圍內,於是猜想是因為每次 cache flush 時,只處理每 128B 裡頭的 64B,之後若存取到另外的 64B 的區域,就會出錯。 最終,藉由列印 cache line 的內容,並查閱 big.LITTLE 的參考手冊,驗證確實是由於他們所呼叫的函式預設針對 cache line 大小一致的 SMP 的,但該晶片的大小核的 cache line 不同。 ## DynamIQ DynamIQ 的方案出現於 2017 年 5 月,目標是擴充 big.LITTLE,不同於後者,因為 DynamIQ 採用 ARMv8.2 獨有的特性,因此與之前的 Arm 架構無法完全相容,所以起初僅適用於較新的 Cortex-A75 和 Cortex-A55 處理器。 ### 混搭風 在 DynamIQ 中,「大核」和「小核」的概念依然存在,但構成一個 cluster 的 cores 可以屬於不同的 micro-architecture,因此其可擴展性比 big.LITTLE 更高。DynamIQ 允許至多 32 個 clusters,每個 cluster 支援最多 8 個 cores,具體的組態可以是 "0+8", "1+7", "2+2+4" 等等。 ![image](https://hackmd.io/_uploads/SJhL8E2Q0.png) ### DSU 和 L3 每個處理器核有自己獨立的 L2 cache,同一 cluster的所有處理器核共享DSU (DynamIQ Shared Unit) 單元中的 L3 cache。任務在大小核之間的遷移可以在同一 cluster 內完成,不需要跨越不同的 clusters,且遷移過程中數據的傳遞可借助 L3 cache,而非 CCI,從而減少匯流排上的競爭,因此更加高效。 ![image](https://hackmd.io/_uploads/SJKP8Nh7A.png) L3 cache 的大小從 0KB 到 4MB 不等,因為一個 cluster 中的 CPU 數目可能較多,為了減少維護 cache 一致性造成的 cache thrashing 問題,L3 可被劃分為至多 4 個 groups,且這種劃分可以在軟體執行期間動態進行。 ![image](https://hackmd.io/_uploads/B1Q_U4270.png) 此外,當 L3 的使用率不高時,還可以 group 為單位,通過 power-gating 技術關閉 L3 中的部分儲存空間,也節省其消耗的功率,這已被Energy Aware Scheduling 所支援。