天天看點

關于多核 CPU 自旋鎖 (spinlock) 的優化

CPU的總線為銅薄膜,雖然摩爾定律使機關面積半導體的密度不斷增加,但是對于連接配接導線的電阻卻沒有明顯的下降,導線的RC延遲幾乎決定現有CPU性能,是以我們會看到傳輸資料在CPU的角度來看是個極為沉重的負擔。我們看到intel 為了引入更多的CPU核心,從Skylake開始晶片總線由上一代的 ring-bus 轉變為 2D-mesh, 雖然2D-mesh為資料提供了更多的遷移路徑減少了資料堵塞,但也同樣為資料一緻性帶來更多問題,例如過去ring-bus 結構下對于存在于某個CPU私用緩存的資料争搶請求隻有兩個方向(左和右), 但是在2D-mesh環境下會來自于4個方向(上,下,左,右),同時大家不久會看到更多CPU socket的伺服器将會出現,為了優化現有的和将來會出現的自旋鎖問題,我們開展了自旋鎖的優化工作,在代碼中具體包含了以下優化方法:

  1. MCS spinlock

    MCS 優化原理( 最新的Linux Kernel也采用了MCS)能夠減少鎖在不同CPU核心之間的遷移

具體參照:

http://www.cise.ufl.edu/tr/DOC/REP-1992-71.pdf
  1. Critical Section Integration (CSI)

    本質上自旋鎖産生的效果就是一個CPU core 按順序逐一執行關鍵區域的代碼,是以在我們的優化代碼中将關鍵區域的代碼以函數的形式表現出來,當線程搶鎖的時候,如果發現有沖突,那麼就将自己的函數挂在鎖擁有者的隊列上,然後使用MCS進入spinning 狀态,而鎖擁有者在執行完自己的關鍵區域之後,會檢測是否還有其他鎖的請求,如果有那麼依次執行并且通知申請者,然後傳回。可以看到通過這個方法所有的共享資料更新都是在CPU私用緩存内完成,能夠大幅度減少共享資料的遷移,由于減少了遷移時間,那麼加快了關鍵區域運作時間最終也減少了沖突可能性。

https://users.ece.cmu.edu/~omutlu/pub/acs_asplos09.pdf
  1. NUMA Aware Spinlock (NAS)

    目前伺服器多CPU socket (2/4/8) 給我們提供了更好的性能/功耗比值,但由于CPU片外的資料傳輸非常昂貴(慢),也引入了更加複雜的一緻性需求。 是以我們引入了分布式一緻性機制來減少鎖的緩存行在不同CPU socket 之間的遷移,最終加速性能。

具體可以參照:

https://www.usenix.org/system/files/conference/atc17/atc17-kashyap.pdf
  1. Mutex Schedule per core

    當線程數大于CPU核數的時候, 在自旋鎖的場景下由于作業系統的CFS排程,那些沒有搶到鎖的線程

或者剛剛開始搶鎖的線程會不斷争搶鎖的擁有者和已經處于spinning 狀态線程的CPU資源。為了解決

這個問題我們為每一個CPU core 引入mutex, 這樣在任何一個階段隻有一個線程可以進入spinning 狀

态或者成為鎖的擁有者。由于mutex 鎖總是存在CPU core 的私用緩存,另外第一個擷取mutex鎖的線

程資料不會引入任何的系統調用,最終解決了OS帶來的問題,資料表明該方法大大提升了整體性能。

  1. Optimization when NUMA is ON or OFF

    雖然NUMA ON可以減少程式通路記憶體的延遲,但是如果有些程式需要更大的記憶體帶寬,NUMA OFF也是很好的選擇。是以在本次優化中我們的代碼同時考慮了 NUMA ON 和 NUMA OFF

以下是具體提升資料(與GLIBC pthread_spinlock 對比):

關于多核 CPU 自旋鎖 (spinlock) 的優化

使用同樣的測試代碼,由上圖2個CPU socket 變為4個CPU socket(即使是NUMA off), 我們看到了又再次動态的提升了一倍的比例(由96核心的15倍變為192核心的30倍),說明我們的優化持續有效減少了資料和鎖的遷移,最終提升性能。

關于多核 CPU 自旋鎖 (spinlock) 的優化

最新代碼:

http://gitlab.alipay-inc.com/eff/numa-spinlock

在進行軟體優化的同時,我們看到仍然存在不必要的資料遷移,而這些問題必須有對應CPU硬體指令,為了徹底解決自旋鎖問題,我們已經提出了CPU優化方案, 通過該方案可以達到自旋鎖的理論值。

雖然性能得到提升,但是我們希望着重說明,如果能夠避免使用這個技術,即避免産生由于同步導緻的資料傳輸,才是最佳方案。舉例說明:如果一個任務可以并行化并且我們有64個CPU核心, 但他們之間隻有1%的串行化代碼(如即使性能達到理論值的Spinlock操作), 那麼根據 阿姆達爾法則,我們的吞吐量提升度是: T/(0.99T/64 + 0.01T) = 1/(0.99/64 + 0.01) = 39.26, 也就是說64個核心隻給了我們40個核心的吞吐量,是以spinlock會嚴重影響吞吐量。越來越多的經驗提醒我們,我們學習技術的目的也許是為了了解事物的規律,進而避免使用這些技術,例如如果使用好的設計進而避免産生同步,不使用spinlock的吞吐量一定是最大的,對麼?

繼續閱讀