開源分佈式存儲 Curve ChunkServer CPU 優化實踐

Curve ChunkServer的CPU瓶頸問題

Curve是網易數帆開源的新一代分佈式存儲系統,具備高性能、高可用、高可靠的特色,可做爲多種存儲場景的底層存儲,包括塊存儲、對象存儲、雲原生數據庫、EC等。html

對於分佈式塊存儲系統來講,IOPS是最重要的一個性能指標。從Curve目前的性能測試狀況看,讀IOPS瓶頸在Client端——對於6個存儲節點的集羣,單個Client節點讀IOPS接近30萬,兩個Client節點讀IOPS接近60萬。而Curve的寫IOPS還有必定提高空間——對於6個存儲節點的集羣,IOPS只能達到26萬~28萬,而ChunkServer節點CPU使用率接近100%,而底層SSD的使用率則不到90%。所以,隨機寫IOPS場景是Curve的一個優化重點。前端

在測試環境A 中部署Curve(具體配置見附錄1),在Client節點建立10個卷,進行4KB隨機寫測試。結果顯示,寫IOPS約爲13.5萬,而此時ChunkServer節點的CPU使用率接近100%,而全部SSD的使用率平均不到85%。git

這代表,ChunkServer端CPU成爲性能瓶頸。考慮到目前測試環境的SSD配置較低,若使用高性能NVME SSD,其IOPS可能比現有SSD高一個數量級,屆時CPU性能瓶頸將更爲嚴重。所以,優化CPU性能,釋放SSD的I/O能力,是Curve性能優化的一個重要方向。本文圍繞Curve的CPU性能優化進行了一些探索和實踐。github

CPU性能優化方法

在進行CPU性能優化以前,咱們必須進行性能測量,對CPU性能進行量化分析,從而有的放矢,針對性優化。web

先回顧下CPU的基本工做原理:現代CPU都是多核心架構,軟件須要以進程或線程的方式在CPU核心上運行。操做系統會將每一個CPU核心的運行時間劃分爲毫秒級的時間片,而後經過調度算法爲每一個時間片選擇一個就緒的線程執行。時間片結束或者運行的線程須要等待資源(I/O, 鎖,條件變量等),操做系統就會進行上下文切換,將對應的CPU核心分配給另外一個就緒的線程。算法

所以,咱們能夠從線程和CPU兩種視角去測量CPU性能。數據庫

  • 從線程視角,咱們能夠測量軟件線程的CPU時間開銷,定位CPU時間開銷較大的模塊或函數,進行重點優化,這也是一種經常使用的定位方法;另外,還能夠測量線程等待鎖的時間,判斷是否有不合理的鎖競爭影響軟件運行效率;而線程等待I/O、條件變量等資源的狀況,通常能夠從設計和代碼層面進行梳理分析,本文再也不贅述。
  • 從CPU視角,咱們能夠從外部測量CPU使用率和上下文切換頻率,判斷CPU時間是否被充分利用(這方面的知識你們都耳熟能詳,而Curve在這方面也沒有明顯問題,所以本文再也不贅述);還能夠從內部測量CPU微指令運行效率,分析CPU運算單元是否被充分利用,這須要採集CPU內置PMU(Performance Monitoring Unit,性能監控單元)的監控數據,並使用專門的分析方法,從而定位CPU運行的瓶頸。

下文將從這兩個角度,分別對Curve的CPU性能進行測量和優化。apache

Curve的線程級性能分析

線程級CPU性能分析能夠從三個方面進行:後端

  • 自上而下的CPU時間分析,即按照軟件調用關係,從主函數到各子函數,一層層分解和剖析CPU時間。這樣能夠大致區分各模塊、函數的CPU時間佔比,從而肯定CPU時間開銷最大或者超出預期的模塊或函數,爲下一步深刻分析提供參考。
  • 自下而上的CPU時間分析,即按照函數的被調用關係,從最底層子函數向上,一層層追溯各調用分支的CPU時間佔比。這種方式能夠定位出被多個父函數調用,其總運行時間異常大的函數。
  • 線程等待時間分析,上面兩方面都是關注的線程在CPU上運行的時間,屬於on-CPU分析;線程等待時間分析則關注線程由於等待資源而沒有在CPU上運行的時間,屬於off-CPU分析。線程可能由於I/O、條件變量等資源未就緒而等待,也可能由於資源競爭(好比鎖)而等待,前者通常屬於正常狀況,然後者須要重點關注。

下面在測試環境B(見附錄2)進行4KB隨機寫測試,而後分別從這三個方面對curve進行CPU性能分析實踐。緩存

軟件配置以下:

代碼版本:d29c4991 (略新於v1.1-beta)
chunkserver數量:每節點20個chunkserver
copyset數量:4000個(測試前已確保leader均衡,range <= 2)
用戶卷數量:10個
用戶卷大小:30GB
4k隨機寫IOPS:234k

自上而下的CPU時間分析

使用vtune採集ChunkServer進程的threading數據:

vtune -c threading -target-pid=pidof curve-chunkserver | awk '{print $1}' -duration 50

結果顯示:

  • braft日誌落盤部分的CPU時間佔比最大,超過23%,最值得關注;
  • brpc模塊的CPU時間佔比17.8%;
  • bthread的work_thread函數CPU時間佔比11.3%,偏高;
  • ChunkServer併發控制層(大部分爲寫數據開銷)的CPU時間佔比11.2%;
  • bvar的sampling_thread函數的CPU時間佔比4%,超出預期。

小結:

  1. braft日誌落盤與併發控制層落盤的CPU開銷達到整個進程的近三分之一,考慮後續經過異步改造等方式進行優化;
  2. bthread個別函數的CPU時間偏大,須要關注;
  3. bvar個別函數的CPU佔用比較異常,須要定位。

自下而上的CPU時間分析

對上述測試數據,使用vtune進行自下而上的CPU分析,其測量結果以下所示:

能夠獲得如下信息:

  • ChunkServer自身代碼耗時137秒,除此以外,其調用的外部lib庫共耗時約99秒。
  • bthread模塊有6個函數的CPU時間開銷名列前茅,累計有44秒,佔比很大,須要特別關注。
  • 另外,bvar模塊有6個函數的CPU時間上榜,累計有22秒,對於一個維護監控指標的工具,這個開銷是過大了,須要定位分析。

對於bthread CPU開銷較大的問題,Curve團隊從其它方面進行了測試優化,寫IOPS最多可提高約20%,具體狀況就不在這裏展開討論了。

對於bvar模塊CPU開銷過大的問題,咱們進行了測試分析。在代碼中去除braft、brpc模塊中幾個耗時較長的bvar操做語句後,IOPS從236k提高到246k,提高了4%。

可見bvar確實對性能產生了影響,但咱們對bvar監控指標的預期是性能降低低於5%,這樣的影響能夠接受。所以,咱們暫不對bvar部分進行優化改動,但會對bvar指標的使用進行規範,包括增長監控開關,去除多餘的監控指標,及控制新指標的增長等。

線程等鎖時間分析

因vtune屢次測試未採集到線程wait time相關數據,咱們使用brpc內置的contention profiler組件,分析花在等待鎖上的時間及發生等待的函數[1]

在測試環境A中(見附錄1)進行隨機寫測試,同時打開brpc服務的web頁面,點擊contention選項卡,觸發contention profiler。(爲確保測試結果穩定,總測試時間被修改成30s)

測量結果以下所示:

在30秒的採集時間內,ChunkServer等待鎖的總時間爲0.442秒,比例很小。

其中,curve::common::TaskQueue::Push等待鎖的時間佔比**86.8%,**這也符合併發控制層的邏輯。

所以,ChunkServer進程在等待鎖方面很正常,不須要關注。

CPU微指令運行效率的測量方法TMAM

本節主要介紹CPU微指令運行效率的測量方法。首先,簡單介紹下CPU的微指令和流水線的概念。操做系統提交給CPU的指令,會被CPU分解爲若干條微指令(uOp),並在內部以流水線的方式調度執行。通常來講,CPU 流水線在概念上分爲兩部分,即前端(Front-end)和後端(Back-end)。Front-end 負責獲取程序代碼指令,並將其解碼爲一個或多個稱爲微操做(uOps)的底層硬件指令,併發送到後端。Back-end 負責監控 uOp 的數據什麼時候可用,並在可用的執行單元中執行 uOp。 uOp 執行的完成稱爲退役(Retirement),uOp 的執行結果提交併反饋到架構狀態(CPU 寄存器或寫回內存)。

咱們能夠用流水線槽(pipeline slot)表明處理一個 uOp 所需的硬件資源。在最近的英特爾微體系結構上,流水線的 Front-end 每一個 CPU 週期能夠分配4個 uOps ,而 Back-end 能夠在每一個週期中退役4個 uOps。在每一個 CPU 週期中,pipeline slot 能夠是空的或者被 uOp 填充。 若是在一個 CPU 週期內某個 pipeline slot 是空的,稱之爲一次停頓(stall)。若是 CPU 常常停頓,系統性能確定是受到影響的,以下圖所示(綠色圓圈表示該流水線槽有微指令運行,灰色圓圈表示沒有):

因而可知,咱們能夠統計流水線槽的運行狀態,以此衡量CPU微指令運行效率。

現代 CPU 大多具備性能監控單元(Performance Monitoring Unit, PMU),用於統計系統中發生的特定硬件事件,例如緩存未命中(Cache Miss)或者分支預測錯誤(Branch Misprediction)等。同時,多個事件能夠結合計算出一些高級指標,例如每指令週期數(CPI),緩存命中率等。一個特定的微體系架構能夠經過 PMU 提供數百個事件。對於發現和解決特定的性能問題,咱們很難從這數百個事件中挑選出那些真正有用的事件。 這須要咱們深刻了解微體系架構的設計和 PMU 規範,才能從原始事件數據中獲取有用的信息。

Intel公司提出了自頂向下的微體系架構分析方法(Top-Down Microarchitecture Analysis Method, TMAM),能夠在亂序執行的內核中識別性能瓶頸。TMAM方法經過CPU內置的PMU數據,測量這些流水線槽的運行狀態,從而肯定cpu微指令運行效率的瓶頸,顯示運行應用程序時 CPU 流水線的使用狀況。(CPU微指令與TMAM相關背景內容主要摘自[2][3]

TMAM方法將CPU性能問題劃分爲4大類——FrontEnd Bound, BackEnd Bound,Retiring, Bad Speculation。每大類又可劃分爲1-3個層級的若干子類,咱們只需利用相關工具按圖索驥,便可一層層區分、細化cpu性能問題,直至最終肯定影響最大的問題所在。最終目標是,將Retiring比例提高至最大。

這種自頂向下的分析框架的優勢是一種結構化的方法,有選擇地探索可能的性能瓶頸區域。 帶有權重的層次化節點,使得咱們可以將分析的重點放在確實重要的問題上,同時無視那些不重要的問題。例如,若是應用程序性能受到指令提取問題的嚴重影響, TMAM 將它分類爲 Front-end Bound 這個大類。 用戶或者工具能夠向下探索並僅聚焦在 Front-end Bound 這個分類上,直到找到致使應用程序性能瓶頸的直接緣由或一類緣由。TMAM方法的具體問題分類及對應的優化方法可查詢intel各cpu的優化指南(如Xeon E5 v3系列cpu的優化文檔爲[4]

Intel VTune Profiler是一款全面的性能測量分析軟件[5],它能夠從系統、線程、CPU微架構等多個角度測量和分析性能數據,其中CPU微架構部分就應用了TMAM方法。

TMAM在Curve的實踐

在測試環境中,對curve進行4KB隨機寫,軟件配置以下:

代碼版本:d29c4991 (略新於v1.1-beta)
chunkserver數量:3個節點,每節點20個chunkserver
copyset數量:500個(測試前已確保leader均衡,range <= 2)
用戶卷數量:10個
用戶卷大小:30GB
4k隨機寫IOPS:129k

同時使用Intel VTune Profiler採集CPU微架構相關性能數據,結果以下圖所示:

從上圖中vtune的cpu微架構性能數據看,主要的cpu瓶頸在Frond-End Latency, 佔比50.3%。參考intel的優化指南,能夠實施的優化方法主要是編譯優化,包括:

  1. PGO (profile-guided optimization) 編譯優化
  2. -O3編譯優化性能
  3. -Os/-O1優化代碼尺寸。
  4. LTO連接時優化。
  5. march/mtune, 針對cpu指令集優化。

其中,PGO優化是指基於性能反饋的編譯優化,它須要進行兩輪編譯[6]。第一輪編譯完成後,會生成帶profiler功能的可執行文件,將它部署到測試環境,按最經常使用的負載進行運行後,它會自動採集性能數據;而後將性能數據複製到編譯環境中進行第二輪編譯,便可獲得優化後的可執行文件。

對curve分別應用這些優化方法,實測方法3相比默認的-O2優化,性能明顯降低;而方法4的優化會致使編譯失敗,未能解決。所以,curve沒法應用這兩種優化。

實測單獨進行-O3優化或PGO優化時,性能提高在-5%和5%之間,無提高。

最終結合-O3和PGO優化,及cpu指令集優化時,性能提高了7.7%(IOPS從129k提升到了139k)。

具體的編譯優化選項爲:

第一遍編譯時gcc優化選項:
-O3 -march=core2 -mtune=haswell -fprofile-generate=/tmp/pgo
連接優化選項爲-fprofile-generate=/tmp/pgo
第二遍編譯時gcc優化選項:
-O3 -march=core2 -mtune=haswell -fprofile-use=/tmp/pgo -fprofile-correction
連接優化選項爲-fprofile-use=/tmp/pgo

從vtune的微架構性能數據看,編譯優化後cpu的pipeline slots的retiring比例從19.1%提高到了22.3%。

而Front-End Bound從58.7%降至47.3%,這證實編譯優化確實緩解了Front-End Bound,提高了CPU執行效率。

總結

本文從線程的CPU運行時間與鎖等待時間,以及CPU微指令運行效率等多個角度,對Curve ChunkServer的CPU瓶頸問題進行了測量分析和優化實踐,結果以下:

  • 從線程的CPU運行時間方面看,braft日誌落盤與併發控制層落盤的CPU開銷達到整個進程的近三分之一,考慮後續經過異步改造等方式進行優化;bthread組件CPU佔比太高的問題,經過其它方法進行了優化,效果顯著;bvar對性能影響不到5%,能夠接受,後續將對bvar使用進行梳理和限制。
  • 從線程的等鎖時間看,ChunkServer主要是併發控制層有少許等鎖狀況,屬於正常狀態。
  • 在CPU微指令運行效率方面,本文經過自頂向下的TMAM方法進行了分析,肯定ChunkServer主要問題是Front-End Bound,並經過編譯優化方法進行了優化,使IOPS提高了7.7%。

另外,Curve近期還有其它優化工做在進行,歡迎關注下一個版本v1.2,其性能會進一步顯著提高。

附錄1 測試環境A配置

client節點(1臺)

雙路Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz

256GB內存

雙10Gb網卡bond

mds和chunkserver節點(3臺)

雙路Intel(R) Xeon(R) CPU E5-2670 v3 @ 2.30GHz

256GB內存

雙10Gb網卡bond

20個 SSD用於chunkserver服務(SATA3接口,型號分別爲INTEL DC S3610和三星PM863a,標稱寫IOPS分別爲27000和24000)

copyset總數量爲500個

附錄2 測試環境B配置

client節點(1臺)

雙路Intel(R) Xeon(R) CPU E5-2660 v4 @ 2.00GHz

256GB內存

雙10Gb網卡bond

chunkserver節點(6臺,其中3臺部署了mds)

雙路Intel(R) Xeon(R) CPU E5-2670 v3 @ 2.30GHz

384GB內存

雙10Gb網卡bond

20個 SSD用於chunkserver服務(SATA3接口,型號是INTEL DC S3610標稱寫IOPS爲27000)

copyset總數量爲500個

做者簡介

秦亦,網易數帆資深服務端開發工程師,計算機系統結構博士,專一於分佈式存儲領域;曾參與華爲FusionStorage分支項目,獨立攻關天璣數據PhegData X數據存儲雲平臺的後端存儲引擎,現爲Curve團隊核心開發。

參考

[1] https://github.com/apache/incubator-brpc/blob/master/docs/cn/contention_profiler.md

[2] https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf

[3] https://kernel.taobao.org/2019/03/Top-down-Microarchitecture-Analysis-Method/

[4] https://software.intel.com/sites/default/files/managed/48/7d/Using_Intel_VTune_Amplifier_XE_on_Xeon_E5v3_Family_1.0.pdf

[5] https://software.intel.com/content/www/cn/zh/develop/tools/vtune-profiler.html?elq_cid=6919108_ts1603694716651

[6] https://en.wikipedia.org/wiki/Profile-guided_optimization

相關文章
相關標籤/搜索