本文整理自 2024 年 4 月 QCon 全球軟件開發大會(北京站) 性能優化專題的同名主題分享。
當前數據中心的服務器中部署着各類 CPU(Intel/AMD/Ampere 等),這些平台的差異,使得運行在上面的程序無法保證始終運行在最佳狀態,成為了提升業務效能的一大阻礙。
CPU 性能調優,這不僅要求工程師對各個平台有着深入的理解,同時需要掌握各類性能分析工具和方法,並依據得到的觀測數據,綜合診斷出真正的瓶頸原因,並據此展開優化操作,最終提升業務表現。
這種傳統的 CPU 性能調優方式,對工程師的能力和經驗都有着不小的門檻,難以在更大規模範圍地普及,使得很多客户對錶現不理想的程序束手無策。
百度智能雲推出的一鍵調優套件 Btune,實現了整個 CPU 性能調優過程的一鍵自動化,使得用户能夠快速完成瓶頸定位,實現性能優化。
今天我的演講內容分三個部分:多元 CPU 性能調優的技術挑戰;Btune 一鍵調優產品設計方案;百度智能雲的調優實踐。
1 多元 CPU 性能調優的技術挑戰
隨着 AMD/ARM 的 CPU 在數據中心處理器興起,服務器領域 CPU 近幾年呈現多元化發展,Intel/AMD/ARM 並存的趨勢。
首先,Intel 仍然是數據中心 CPU 的主流,從 12 年開始的 E5 系列到目前最新的 GNR。
從 17 年開始,AMD 的 Naples 和 Rome 開始進入數據中心領域。到了 21 年,Milan 處理器以其多核優勢開始在數據中心大規模上量。
從 21 年開始,Ampere 的數據中心處理器,因為它對比同期其他架構處理器的性價比優勢,在數據中心開始佔有一席之地。
百度智能雲也是 CPU 多元化趨勢裏面的先行者,以及大規模實踐者。
從 2017 年至今先後引入 4 代 AMD 處理器。從 2018 年開始大規模試點 AMD 落地數據庫等存儲場景開始,之後的每一代 AMD 處理器都有相應的計算產品推出。
從 2022 年至今,先後引入 2 代 ARM 處理器。自 22 年開始,百度內部主流通用計算場景(包括搜索、推薦、商業、大數據等)都已經規模使用了 ARM 處理器。23 年,百度智能雲在國內首次發佈了 ARM 虛擬硬件產品 AVH。今年,我們在業內首次將 ARM 服務器大規模應用於智駕仿真場景,並基於 ARM 服務器對外推出了車雲同構的仿真方案。
那麼在 CPU 多元化的當下, 如何進行跨平台性能調優呢?接下來我們從不同層面來看一下。
首先看下 Core 層面。
我們知道,影響 CPU 性能的不只是 CPU 的頻率和核心數,還有指令和微架構。
我們以 Intel/AMD/Ampere 三家 CPU 對比可以看到:
SIMD 指令方面:Intel 從 21 年的 Ice Lake 開始就已經支持 AVX512 指令了,截止目前 AMD 一直是 AVX256,直到未來的 Zen4 架構才開始支持 AVX512,Ampere 目前的 Altra 包括最新一代 OneX,都是 128 位寬的 Neon 指令。在一些大位寬並行計算場景,Ampere 相對 Intel 和 AMD 要弱一些。
浮點指令方面:三家對 BF16/FP16 的支持是不一樣的,平台是否支持 BF16 或者 FP16,在模型進行 16 bit 量化的時候性能差異很大,在 AI 推理場景 BF16/FP16 的支持是個要特別注意的地方。
頻率與 HT 方面:Ampere 是固定頻率,不支持 HT,獨立的物理核,在雲原生場景有優勢。Intel 和 AMD 都可以超頻,但是有一些差異,Intel 是 all core boost,AMD 的不是 all core boost,這個在一些對頻率敏感的場景需要特別關注。
Socket 層面。
SubNUMA 方面,因為 Intel/AMD/Ampere 三者的架構不一樣,Intel 是 mesh 架構,AMD 是多 die 多 NUMA 架構,Ampere 是單 die 多 NUMA架構。AMD 和 Ampere 有subNUMA的劃分,而且可以看到 AMD 在 subNUMA 內和 subNUMA 間的延時差別要更明顯一些。這就導致在一些場景,AMD 做 subNUMA 親和策略對業務性能會有較大提升。
L3 Cache 方面,Intel/AMD/Ampere 三者的容量和延時都有很大差別,總體來看 Ampere 的 L3 無論容量還是延時性能要差一些。
這裏特別提一下 CPM。Ampere 有一個 CPM 的劃分,CPM 內的 Core 間通信比 CPM 之間的要小很多,這就導致在一些場景 CPM 親和策略對 Ampere 機器會有較大的性能提升。
在互聯層面。
Intel/AMD/Ampere 採用的是不同的互聯協議。
Ampere 採用的開源 CCIX 協議,這個協議的訪存延時和 DMA 讀帶寬,相比另外兩種協議要差很多。所以 Ampere 服務器在很多應用場景,是否支持跨路訪問對性能影響非常大,需要特別針對跨路訪問進行規避。
AMD 的 xGMI 協議,跨路 DMA 讀帶寬要更大,在一些大內存帶寬的場景(比如 EDA 仿真),表現會比較好。
內核層。
這裏列舉了 4 個例子。
第一個例子是 CPM 親和。這個問題可以通過內核 patch 進行調度層的 CPM 親和性優化。當然如果不放到內核層面解決,也可以在用户層面做特殊的綁核邏輯以達到親和的目的。
第二個例子是 page cache 親和。內核裏面默認是從容器所在 NUMA 來分配 page cache 內存區域。假如容器在 node0 運行,磁盤鏈接在 node1,就會導致磁盤的 I/O 要從 node1 跨路 DMA 到 node0 的內存區域,這樣在跨路延時大的機器上,I/O 性能就會下降很多。
第三個例子是頁表管理,在很多業務場景,64K 頁面性能要比 4K 頁面性能好。但同時大頁表也會導致一些業務內存容量佔用增加。兩者之間的選擇需要進行綜合考慮。
第四個例子是中斷綁定,內核是優先從中斷綁定 NUMA 來分配 skb\_buffer。假如中斷綁定到 node1,網卡鏈接在 node0,網絡數據就需要從 node0 跨路 DMA 到 node1 所在內存區域,這樣在跨路延時大的機器上,網絡性能就會下降。
Runtime 層面。
不同平台的底層加速指令是不同的。首先 ARM 和 x86 是不同的指令集,其次即使同樣是 x86 指令集的 Intel 和 AMD,他們在一些加速指令層面也有差異。
一個應用程序針對平台的指令加速優化,主要可以通過兩個環節進行:一個是編譯層面的優化;一個是 runtime 層面的優化。
我們把 runtime 劃分成了 3 個方面。
第一個是系統基礎運行時庫。不同版本的運行時庫,在有些場景性能差異會非常大,一般來説版本越高性能越好。
第二個是各平台廠商自己提供的專有加速庫。為了提升競爭力和壁壘,芯片廠商都會有一些針對自己芯片的加速庫,比如 Intel 有自己的 mkl 等庫,AMD 有自己的 aocl 等庫。這類庫有兩個問題:庫和平台廠商是綁定的,比如同樣的壓縮操作接口不一致;另外有些庫即使可以多平台運行,但是其性能差別會比較大,比如 mkl 庫同樣的操作在 Intel 平台性能發揮的比較好,到 AMD 平台雖然也能運行,但是因為 mkl 庫不會啓用 AMD 芯片的很多加速特性,這就導致其性能就會差很多。在跨平台之間進行遷移時候,會遇到相應的性能問題。
第三個是一些高級語言的運行時,如 JDK/Ruby/Python 等。這類運行時除了平台無關的策略優化外(比如 JDK 的 GC 策略、熱點運行時編譯策略等),針對平台還可以進行很多指令層面的加速優化。
應用層。
這裏舉了 4 個例子,都是同樣的程序往不同平台遷移過程中,遇到的性能問題。
第一個是 BRPC 的例子,我們將一個 BRPC 服務從 x86 遷移到 Ampere 後,發現其 CPU 利用率比 x86 高近一倍。這裏的原因就是由於 Ampere 跨路延時高,導致線程切換慢,這個通過調整 BRPC 的協程切換模式後問題得到了解決。
第二個例子是混部衝突。隨着節點算力越來越大,單節點可以部署的任務數越來越多,這個會帶來很多層面的性能挑戰。比如:sys\_use 增高、容器監控組件卡死、內核爭搶嚴重、lsof/ps 慢等性能問題。
第三個例子是 Spinlock 鎖。x86 平台和 ARM 平台在底層實現機制上有差別。x86 支持 HT,底層通過 pause 指令實現,只讓出 HT 計算單元而不釋放 CPU。ARM 物理核需要通過 yield 指令實現,有些程序默認使用的 wfe 指令會帶來喚醒延時。延時敏感作業需要注意這個差別。
第四個例子是編譯。在編譯適配環節,程序需要面向不同平台進行優化。比如編譯器層面,不同平台需要不同的編譯設置才能充分發揮平台性能。基礎庫層面,不同平台有不同的優化版本。彙編指令層面,有一些程序會直接調用匯編指令進行並行加速,這時候針對不同平台需要進行指令調用層面的優化。
整體來看,性能優化的挑戰是多維度的,從硬件層、系統層、runtime 層到應用層,每一層都需要充分優化。
面對多維度性能挑戰,如何充分發揮 CPU 性能,確保 SLA 是一個很有挑戰的事情。
2 Btune 一鍵調優產品設計方案
性能調優的典型步驟包括四個環節:指標檢測、瓶頸定位、性能優化、業務 SLA。這 4 個環節看起來簡單實現起來並不容易。
首先,通過指標檢測得到性能瓶頸就是一個很複雜的過程:往往需要藉助 10+ 檢測工具,圍繞 4+ 維度、100+ 指標,進行多個層面的分析,綜合後得瓶頸結論。
其次,即使知道了瓶頸在哪裏,並不意味着就能優化它:針對每一個瓶頸點,往往需要結合專家經驗並反覆測試,才可能找到可行的優化方法。
為了讓性能調優能夠被更多用户使用,所以百度智能雲將這 4 步調優過程進行了自動化,推出了 Btune ,實現了一鍵調優的效果。
接下來,我們首先圍繞以上幾個過程展開,進一步分享 CPU 性能調優在技術上的挑戰,再回到 Btune 本身來介紹他的產品設計思路。
第一步:指標檢測。
這裏面的主要問題是:從硬件/內核/runtime/應用數據維度很多,這些指標的相關分析工具眾多,使用方法和依賴環節複雜。
這個層面的問題我們看到市場上有一些性能分析展示的產品,已經可以一定程度地解決。所以這裏不展開了。
第二步:瓶頸分析
從工具指標到瓶頸結論仍然是一個複雜的過程。經典的瓶頸分析方法,我們都知道。
- USE:從資源使用率、飽和度、錯誤三個維度進行瓶頸分析。看哪裏的資源消耗多,是主要的資源瓶頸。
- TSA:通過時間片分析,看哪個環節時間消耗最長。
- TMA:從 CPU 資源角度,去分析程序資源 bound 在哪裏。
以上三種方法指向一個核心問題,一個程序在某個方面資源消耗多、時間消耗長、資源 bound,是合理的還是不合理的?其實,有時候是合理的,有時候是不合理的,這個跟程序行為有關。我們要區分它、判斷它,才能進一步去看能不能優化它。
這裏舉一個瓶頸定位的實際生產例子。
這個例子背景是這樣的,某個基於 spark 的大數據業務模塊,從 x86 平台遷移到 ARM 平台,發現有幾個常尾延時高的節點,需要定位性能瓶頸原因。
我們當時的定位過程是這樣的:
- 首先看資源指標:CPU/MEM/網絡資源使用比較低,I/O 資源無明顯區別;
- 然後我們發現網絡丟包重傳高,同時程序在 DFS 網絡數據讀取上消耗了過多的時間。為什麼會這樣呢?
- 進一步觀察 DFS 網絡數據讀寫操作,發現 DFS 網絡數據讀寫消耗了大量時間在 skb\_buffer 網絡數據拷貝;
- 進一步看內核 skb\_buffer 數據拷貝,發現長尾節點比非長尾節點存在更多跨路內存數據拷貝。為什麼會這樣呢?
- 進一步看程序內存分配情況和系統設置,發現長尾節點存在跨路數據拷貝的原因是進程內存沒有綁定 NUMA,正常節點是綁定 NUMA 的。
經過以上 5 個步驟,我們才得出結論:程序所在容器沒有合理綁定 NUMA,導致 skb\_buffer 內存跨路拷貝變慢,影響網絡性能。
從這個例子可以看到,要定位到瓶頸結論,往往需要多個層面的信息,綜合判斷,這是一個比較複雜的過程。
第三步:性能優化。
即使知道了瓶頸點,如何針對瓶頸進行優化,很多情況還是無從下手。
比方説我們分析實際業務的時候,會得出下圖這些結論,我們知道了程序的資源瓶頸在哪裏、時間消耗在哪裏、CPU 資源 bound 在哪裏。但怎麼優化他們,仍然是個問題。
這裏再舉一個性能優化的例子。
這個例子的背景是這樣的,某個廣告檢索的 ranker 模塊,運行在 x86 平台,希望進一步優化其性能。
我們先後找到 3 個可能的優化點,並進行了優化和測試。
第一個,優化熱點函數:
找到程序熱點最高的函數 general\_top 嘗試優化。通過函數 TMA 分析,發現其屬於分支預測 miss Bound。進一步分析函數代碼,發現有些分支可以優化,於是進行了函數代碼的優化。
在經過優化後,函數 30s 內運行耗時從 10s 減少到 7s,減少了 30%,函數優化效果非常明顯。但是在經過實際測試後,程序整體性能沒有大的變化,平響只降低 0.5ms。
第二個,優化內存分配:
我們發現這個程序消耗了很多時間在內存分配上。同時從火焰圖搜索可以看到內存分配棧 calloc 比 malloc 還高。
calloc 代表的是大塊內存,分配性能差,經常會有 page fault。malloc 代表的是小塊內存,多為紅黑樹和 hash 表被動分配。所以我們優化代碼替換掉 calloc。優化後發現效果不明顯,主要原因還是代碼修改點在整體耗時佔比不高。
第三個,優化內存分配:
我們看到程序整體的讀換頁佔比很高,於是嘗試通過使能大頁的方式,減少內存換頁的壓力。
測試發現,使能大頁後,讀換頁從 4.8% 降低到 1.8%。程序延時平均降低 3ms,97 分位 10-12ms,qps 增加 28.5%。程序整體性能優化效果明顯。
從這個例子可以看到,要針對某個瓶頸進行優化,往往需要結合專家經驗並反覆測試,才可能找到可行的優化方法。
這裏的主要問題有兩個。首先,很多瓶頸點不一定能找到有效的優化方式,需要反覆測試驗證。其次,有時候我們發現了一個瓶頸點,雖然針對這個瓶頸點我們能優化它,但是如果這個瓶頸不是主要矛盾,即使針對這個瓶頸進行了優化,但是程序整體的性能並不會有太大改善。
針對上面提到的瓶頸定位和性能優化難題,百度智能雲推出瞭如下的解決方案。
- 瓶頸定位:
我們覺得 USE/TSA/TMA 分析方法能夠從不同層面得到性能瓶頸,但還不夠直接。這些點有可能是主要矛盾,也可能不是主要矛盾。
百度智能雲的方案是通過 USE/TSA/TMA 三方面信息,得到程序資源分佈、耗時分佈、線程關係。然後基於這些信息,利用自研的瓶頸分析樹模塊,通過自頂向下的方式,從 CPU、內存、磁盤、網絡、並行度 5 個維度對負載瓶頸進行全面性能剖析,得到確定性的性能瓶頸點。
- 性能優化:
每個瓶頸點該如何優化,百度智能雲目前的方案是基於團隊在性能調優過程中的案例,彙總成瓶頸點和優化建議關聯知識庫,從而實現瓶頸定位和性能優化的閉環。
這裏展開介紹下瓶頸分析樹。
瓶頸分析樹把瓶頸定位的過程邏輯化為樹的深度優先遍歷過程。遍歷的過程就是將負載的瓶頸定位範圍逐步縮小的過程,直到葉子結點。下圖右邊是一個內存部分瓶頸分析樹的簡易示例圖。
以案例 1 為例,首先定位到程序為內存資源瓶頸,然後發現內存讀寫慢,進一步發現內存 TLBMiss 偏高,進一步發現機器匿名大頁未開啓。這樣一步步遍歷,得到瓶頸結論。
下圖的案例 2 和案例 3,也是相同的自頂向下的分析過程。
這裏展開介紹下性能優化。
瓶頸分析樹的每個葉子結點都對應了一個最終的瓶頸結論,而每個葉子結點都有相應的優化建議與之對應。每個瓶頸點所對應到的優化方式,來自於百度多年調優實踐案例,這些案例最終匯聚成為了專家知識庫。
下面是上文 3 個案例對應的瓶頸結論和可行優化建議。
基於上述方案,百度智能雲推出了一款性能調優套件 Btune,支持多元 CPU 加速,目前已經覆蓋百度智能雲的全部計算類產品。
在 Btune 中選擇機器實例和進程後,點擊一鍵分析,幾分鐘就可以生成性能分析報告,得到程序的瓶頸結論和優化建議。
在 Btune 提供的瓶頸結論和優化建議報告中包含兩部分:分析摘要和分析詳情。
- 分析摘要:清晰地展示了業務性能瓶頸點和相應的優化建議,可以滿足絕大部分場景的需求。
- 分析詳情:提供了更詳細的性能分析數據,從系統配置、系統性能、進程線程模型、函數指令熱點等多個維度呈現負載的運行特性,滿足用户更細粒度性能優化。
除了性能調優套件 Btune 之外,百度智能雲即將推出加速套件 BtuneAK。
- Btune:一鍵性能調優助手,可以快速定位瓶頸並得到優化建議。
- BtuneAK:打通性能優化的最後一環,一鍵使能,提升業務性能。
Btune 相關套件在百度智能雲 BBC 和 BCC 計算實例上都是免費使用的。
3 百度智能雲的調優實踐
第一個例子,檢索子系統需要進一步優化延時。通過 Btune 分析得到分析摘要。瓶頸結論為:內存子系統異常,關鍵瓶頸在 TLB,監測到系統沒有開啓大頁。優化建議為:系統層,系統開啓透明大頁。使能優化建議後,平均延遲優化 3.9%~4.6%,97 分位延遲優化 4.6%~4.7%。
第二個例子,某排序服務模塊基於 BRPC 構建,CPU 利用率高。通過 Btune 分析得到分析摘要。CPU 子系統異常,關鍵瓶頸在 usr 態,steal\_task 操作瓶頸,監測到 Bthread 模式為 steal\_task。優化建議為:應用層,優化 Bthread 模式,用 GlobalBalancer 替換 steal\_task。使能優化建議後,CPU 平均利用率降低 25.8%。
第三個例子,某數據存儲服務,大量的讀寫請求超過 28ms,突破 SLA 要求。通過 Btune 分析,得到分析摘要。瓶頸結論:進程和讀寫磁盤分屬不同 node 性能差,導致磁盤I/O 瓶頸。優化建議為:硬件層,磁盤親和(進程所在 node,使用本 node 磁盤)。使能優化建議後:平均請求延時降低 17%,99 分位請求延時降低 11.7%。
以上就是我今天和大家分享的全部內容了。
- - - - - - - - - - END - - - - - - - - - -
推薦閲讀
基於afx透明視頻的視覺增強前端方案
百度一站式數據自助分析平台(TDA)建設
淺析如何加速商業業務實時化
登錄系統演進、便捷登錄設計與實現
一文帶你完整了解Go語言IO基礎庫