CPU性能分析工具原理

轉載請保留以下聲明

  作者: 趙宗晟

  出處: https://www.cnblogs.com/zhao-zongsheng/p/13067733.html

很多軟件都要做性能分析和性能優化。很多語言都會有他的性能分析工具,例如如果優化C++的性能,我們可以用Visual Studio自帶的性能探測器,或者使用Intel VTune Profiler。了解性能分析工具的原理有助於了解工具給出的數據與結果,也能幫助我們在遇到異常結果時排查哪裡出了問題。這篇博客簡單總結一下常見的性能分析工具原理。

性能分析器原理分類

性能分析工具大部分都可以分為下面幾類

  1. 基於採樣(Sampling)
  2. 基於插裝(Instrumentation)
  3. 基於事件(Event-based)

1. 基於採樣

基於採樣的分析器會每隔一個固定時間間隔暫停所有線程的執行,然後分析有哪些線程正在執行,那些線程處於就緒狀態。對於這類線程,我們記錄每個線程的調用堆棧,以及其他需要的信息。我們稱這個行為為一次採樣,記錄下來的每個堆棧為一個樣本。然後在結束性能分析的時候我們統計記錄下載的堆棧,看每個堆棧被記錄了多少次,或者每個函數被記錄了多少次。統計學告訴我們,那些執行時間比較長的函數、堆棧,被記錄的次數會更多。如果堆棧A被記錄了200次,堆棧B被記錄了100次,那麼堆棧B的執行時間是堆棧A的2倍。我們可以計算某個堆棧樣本的數量佔總樣本數的比例,這個比例就是堆棧執行時間的佔比。用Visual Studio的性能探測器我們看到的百分比和数字就是值樣本的佔比(也就是時間佔比)和樣本次數。

很多性能分析工具都是基於採樣的方式。運行性能分析器是會影響被測程序的性能的,而基於採樣的有點是對性能影響比較小,不需要重新編譯程序,非常方便。

2.基於插裝

插裝是指通過修改程序,往裡面加入性能分析相關代碼,來收集、監控相關性能指標。例如我們可以在一個函數的開頭寫下計數代碼,我們就可以知道在運行中這個函數被執行了多少次。一般來說基於插裝的性能分析更為準確,但是對性能影響比較大。因為需要修改代碼,所以也不是很方便。另外,基於插裝的分析也可能會引起海森堡bug(heisenbug)。海森堡bug是指當你再運行程序的時候能遇到這個bug,但是試圖定位這個bug時卻遇不到這個bug。這個bug往往是因為在定位bug時修改了軟件的運行環境/流程,導致軟件執行和生產時不一樣,於是就遇不到這個bug了。這個命名的來源很有意思,海森堡是量子力學的著名科學家,他提出了不確定性原理,以及觀察者理論。這個理論認為,觀察會影響例子的狀態,導致觀察粒子和不觀察粒子會導致不同的結果,這個和海森堡bug的情形非常相似。關於觀察者理論,有興趣的人可以再了解一下。

回到正題,基於插裝也可以再進行劃分:

  • 人手修改源碼:這個是我們非常常用的性能分析方法。我們做性能分析有時候就會直接修改源碼,計算某一段代碼的執行時長,或者計算函數調用次數,分析哪段代碼耗時。
  • 工具自動修改源碼
  • 工具自動修改彙編/中間碼
  • 運行時注入
  • ……

3.基於事件

在軟件執行過程中,可能會拋出某些事件。我們通過統計事件出現的次數,事件出現的時機,可以得到軟件的某些性能指標。事件又可以分為軟件事件和硬件事件。軟件事件比如Java可以在class-load的時候拋出事件。硬件事件則是使用專門的性能分析硬件。現在很多CPU裏面都有用於分析性能的性能監控單元(PMU),PMU本身是一個寄存器,在某個事件發生時寄存器裏面的值會+1。例如我們可以設置為當運行中發生memory cache miss的時候PMU寄存器+1,這樣我們就知道一段時間內發生了多少次memory cache miss。性能分析器使用PMU時,會給PMU設置需要統計的事件類型,以及Sample After Value (SAV)。SAV是指寄存器達到什麼值之後出發硬件中斷。例如,我們可以給PMU設置SAV為2000,統計事件為memory cache miss。那麼當cache miss發生2000次時,發生硬件中斷。這個時候性能分析器就可以收集CPU的IP,調用堆棧,進程ID等等信息,分析器結束時進行統計分析。

基於硬件事件的優點是,對被測程序的影響非常小,比基於採樣還小,可以使用更短的時間間隔收集信息,而且可以收集CPU的非常重要的性能指標,例如cache miss,分支預測錯誤,等等。

但是基於硬件事件的分析器也有它的一些問題,導致數據上的誤差:

  • SAV一般會設置成很大的數值:
    像是Intel VTune Profiler一般會把SAV設置成10,000到2,000,000,發生中斷時我們能知道最後一次觸發該事件是哪段代碼引起的,但是在這之前的9,999到1,999,999次事件我們是不知道的。他會認為所有10,000到2,000,000次事件都是由同一處代碼引起的。如果發生了很多次中斷,收集了很多次信息,而某一行代碼出現了很多次,那麼根據統計學,我們能知道這行代碼觸發了多少次事件。但是如果某一行代碼只出現了一兩次,那麼我們很難知道這行代碼到底出發了多少次時間。
  • CPU一個核只有幾個PMU,但是可以統計的事件有幾十種:
    一個PMU可以統計一個事件,但是我們分析一個軟件可能需要統計幾十種事件。一般的處理方法是多路復用(Multiplexing)。比如說前10ms記錄事件A,后10ms記錄事件B,再后10ms由記錄事件A,……,這樣輪流記錄事件A和事件B,那麼一個PMU就可以同時統計兩個事件。多路復用可以解決少量PMU統計大量事件的問題,但是也會導致每種事件記錄的樣本數減少,倒是最後統計計算的結果誤差更大。
  • Intel® Hyper-Threading Technology導致記錄不準:
    Hyper-Threading技術可以讓一個CPU物理核變成兩個邏輯核,但是這兩個邏輯核會共享很多硬件,包括PMU。這會出現什麼問題呢?例如我們有兩個線程再兩個CPU核同時運行。我們覺得實在兩個核上執行,但是實際上是在同一個物理核上。所以PMU會同時統計兩個程序觸發的事件,我們很難區分到底是哪個邏輯核出發了多少事件,所以PMU記錄的數據就會不準確。另外,性能分析器計算性能指標時會使用一些硬件參數,Hyper-Threading技術會讓這些參數不準確。例如一般個CPU核能再一個clock執行4個uop,所以CPI(Cycle Per Instruction,每個clock執行的指令數)是0.25。但是如果使用了Hyper-Threading技術,實際的CPI會是0.5
  • Event Skid(事件打滑)會導致記錄的IP不準確:
    PMU記錄有些事件會出現一定延時,所以在執行分析器的中斷處理代碼時,可能被測程序已經又執行了好多指令,IP已經向後滑動了很遠了。一般如果我們只是統計函數的話不會太大問題,但是我們想統計指令時就會有很大問題了。比如我們可能會看到某個add指令導致了大量的分支預測錯誤,顯然這個是不可能的。往往這種時候我們可以看看前面一點的指令。
  • Interrupt Masking(中斷屏蔽),導致統計出來的空閑狀態比正常的高
    不同的中斷有不同的優先級,為了高優先級的中斷處理程序不被打斷,我們可以選擇屏蔽一部分中斷事件。但是PMU的事件也是一个中斷,如果系統中有大量的中斷屏蔽,那麼會有PMU的中斷被屏蔽掉一部分,導致統計出來的數據不準確。表現出來的效果就是,統計出來的處於空閑狀態的時間比實際的要高。

總結

這幾個就是比較常見的性能分析方法。我們知道了性能分析的原理,可以更好地理解性能分析器給出的結果,也可以在出現明顯異常的結果時,分析判斷可能的原因,針對性的解決。

參考

https://en.wikipedia.org/wiki/Profiling_(computer_programming)
https://software.intel.com/content/www/us/en/develop/articles/understanding-how-general-exploration-works-in-intel-vtune-amplifier-xe.html
https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/analyze-performance/user-mode-sampling-and-tracing-collection.html
https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/analyze-performance/hardware-event-based-sampling-collection.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!