Linux下做性能分析5:Amdahl模型
[前言]
前一個Blog我們使用了一個叫cs的程序作為例子,那個程序是我為了舉例子臨時寫的,這個代碼我共享在這裡:GitHub - nekin2012/btest。後面我要再舉例子的話,就都加到這個地方來。由於這些代碼沒有經過最基本的軟體質量保證工藝,所以質量相當低,讀者不要直接使用這些代碼。另外,這個代碼中的cs程序已經經過上次推演的調整,現在的性能已經可以達到調度最優了,CPU佔用率會全部100%,和上一個程序中的樣子已經不太一樣了。
cs這個程序的模型,是我們很多軟體的基礎模型。雖然經過很多模塊和隊列的分解,我們的程序會變得愈加難以辨認,但模型永遠都是為不同的隊列安排多組線程池的問題。這種模型的大部分優化工作是平衡線程的數量來保證CPU的利用率,然後通過限制每個隊列的長度,來控制時延和和通量之間的關係。
而這裡面需要特別小心的就是那個Provider-Consumer陷阱,也就是前面提到的,如果一個線程總用不完它的時間片,這個線程就會被自動提權為交互線程,這樣,只要發生調度它就會搶佔,這樣會大幅降低整個系統的性能。解決這個問題的方法通常是兩個:一個就是在那個cs中看到的,控制隊列的長度,沒有足夠的長度根本不要發起調度。第二個就是我們要有意識控制線程的設置,特別是不要一個模塊一個線程(這是最失敗的設計),如果某個線程的執行時間特別短,這個工作就應該和其他線程合併,而不是獨立線程。比如你發一個消息,僅僅是為了分配一個會話號,這個時間可能就是幾十個時鐘周期,你就不能圖方便為了排隊,使用一個模塊隊列+線程來完成這樣的工作。正確的做法是把這個模塊的介面直接做成函數,然後用鎖保護起來。
[Amdahl模型]
前面說的這種不使用短時線程會話的策略,在大部分時候是不會引起問題的,除非你線程配置不平衡,讓你的調度序列又出現交互線程。這些問題,都可以通過ftrace跟蹤出來。
但不少程序員沒有從這個角度考慮這個問題,他們就會試圖通過spinlock來降低這種調度的可能性。當所有線程共同分享這個公共的模塊的時候,我們就會形成Amdahl定律所描述的模型了。
Amdahl定律是並行計算最基礎的理論了,所有學計算機的人都學過,我這裡就不專門介紹了,讀者如果不知道自己上網查去。
現在大家都不怎麼把Amdahl當回事,因為現在大部分系統的核數遠遠沒有達到讓Amdahl觸頂的規模,下面這個是我用999:1的比例配置並行-串列比時(程序參考btest的amdahl的例子),在72核的x86平台上得到的效果:
這時增加核數基本上就會達到提升處理能力的目的。
但時延上仍是有影響的:
在串列的密度非常低的時候,我們還感覺一切受控,但如果我們把並行串列比提升到99:1,乃至90:10的時候,情況就變得非常糟糕了:
因為這不再是一個Amdahl模型了,Amdahl模型的依賴是在等待的時候,你的CPU還能幹其他並行的工作,而使用spinlock,你在等待的時候什麼都幹不了。這實際上是一個馬可夫鏈的排隊模型,
這裡有四條曲線,我們先看spin的兩條曲線,你會發現,當你把串列的配比增加的時候,系統在20個核左右就開始進入拐點,性能大幅跳水。而且串列的比例越大,跳水就越早。
根據一些研究報告,如果這是個標準的馬可夫鏈的排隊模型,曲線影響應該像後面兩條標記為MCS曲線那樣,僅僅是接近瓶頸。而這個跳水是因為,純粹的spinlock不但引起等待時間的增加,而且因為有更多的等待者,會導致Cache更新時間的延長,從而得到一個修正的馬可夫鏈模型,形成了跳水。如果用perf對這兩種情況進行跟蹤,你會發現,在系統發生跳水後,系統在的指令執行效率10個cycle執行不了一條指令:
很低的指令執行率,表明執行指令本身的執行效率低(基本在stalled-cycles上,這個指標的含義,我們後面專門寫一篇blog介紹),問題要不出現在cache/匯流排上,要不出現在處理器自身的調度器上。
我在一台64核的ARM64伺服器上做同樣的實驗,得到同樣的結果。有論文提出使用MCS鎖來避免這種情況,我在那個btest工程中快速用spinlock臨時封裝了一個MCS鎖,可以擬合出類似馬可夫鏈的模型(當然,這個實現沒有使用原子指令,速度肯定是比較慢的,只是為了擬合模型)。
使用MCS鎖後,上面的測試結果是這樣的:
同樣的行為在ARM64上測試結果也是一致的,理論上說,如果用ticket鎖,在ARM上可以獲得MCS鎖一樣的結果,我晚點加一個測試看看。
[Amdahl模型的啟示]
我們很多人更願意花時間去反覆嘗試各種設置參數,嘗試這樣提高系統的性能。我個人收到不少性能分析報告都是這樣的。我覺得這樣的分析報告相對來說價值是比較低的(當然也有其作用),因為即使一個參數進行調整帶來了好處,但這種好處和其他參數組合後可能就會消失。僅僅關注一個參數的效果,最多就是給我們建立模型提供參考,我們的分析還是要聚焦到模型上,發現系統真正的瓶頸是什麼。我前面的perf介紹下面,有人說perf的數據也就只是能「看看」,我認為抱有這種觀點,是因為他從來只是關注效果,而不關注模型,而在系統性能優化的時候,模型遠遠比效果重要。效果你確實可以拿去報功,但在軟體自身的架構進展上是沒有用的,要得到正確的構架調整方向,模型才是第一位的。
回到Amdahl這個模型,很多系統在進行架構調整的時候,決策下得非常早,一看見mutex發生了切換,就考慮用一個「不會切換」的spin_lock取代「有可能切換」的mutex鎖,而不願意花時間去分析「為什麼調度器」要做這個切換。這樣頭痛醫頭,腳痛醫腳的方法(對,我說的就是MySQL),會讓整個系統陷入更深層次的混亂之中,這樣整個架構就變得不可控了。
[小結]
在調度上,除了IO導致的等待外,消費者-使用者模型和Amdahl模型是兩個最常見的陷阱,好好基於btest類似的簡化模型對CPU和調度器的行為進行分析,會有助於我們正確理解更複雜系統的運行模型。
調度模型平衡了,我們就有可能進行下一步,針對CPU執行效率的分析了。
推薦閱讀:
※像dotTrace這樣的profiler怎麼實現的?
※遺傳演算法框架GAFT優化小記
※【求知探新】獨立遊戲《Abi》場景耗時分析
※基準測試包microbenchmark更新
※淺談基於數據分析的網路態勢感知
TAG:性能分析 |