理解應用程序性能的第一步是學會對它進行測量。
與絕大多數功能問題相比,性能問題通常很難跟蹤和復現。
任何關注過性能評估的人可能都知道公允地進行性能測量并從中得到準確結論是多么困難。
因為在測量中存在誤差,性能分析通常需要統計方法進行處理。
開展公允地性能實驗是獲得精確及有意義結果的基本步驟。設計性能測試和配置測試環境都是性能評估工作的重要組成部分。
2.1 現代系統中的噪聲
動態頻率調節是一個硬件特性,但是測量結果差異還有可能來自軟件功能。
不幸的是,測量偏差還不只來自環境配置。Unix環境變量的大小(即存儲環境變量所需要的字節數)和鏈接順序(提供給鏈接器的目標文件順序)能夠對性能產生不可預知的影響。
在運行時有效且重復地將代碼、堆棧和堆對象隨機放置,可以消除由內存排布引起的測量偏差。
獲得一致的測量結果需要所有基準測試都在同樣的條件下進行。
消除系統的不確定性有助于進行定義明確、穩定的性能測試,需要控制基準測試中大部分變量,包括輸入、環境配置等。
當估計實際程序的性能優化效果時,不建議去除系統的不確定的行為。工程師應當嘗試復制被優化的目標系統的配置,在被測系統中引入人為調整會導致用戶在實際使用中的結果不一致。此外,任何性能分析工作-包括采樣,都應當在與實際部署最接近的系統下進行。
2.2 生產環境中的性能測量
隨著虛擬化和容器等技術的日漸流行,公有云供應商也嘗試最大化服務器資源的利用率。但是這種環境為性能測量帶來了新的困難,因為與相鄰進程共享資源會對性能測量產生不可預知的影響。
大型服務提供商通過部署遙測系統來監控用戶設備的性能已經成為一種趨勢。
測量開銷是生產環境監控的一個重要問題。由于任何監控都會影響正在運行的服務的性能,因此應該使用盡可能輕量的性能剖析方法。
通常可以接受整體不超過1%的性能損失的檢測開銷,減少監控開銷的辦法包括限制被監控的機器數量和使用更小的監控時間間隔。
2.3 自動檢測性能退化問題
軟件供應商提高產品的部署頻率逐漸成為一種趨勢,但是軟件的性能缺陷會以驚人的速度蔓延到生產環境中。
軟件性能退化是指軟件從一個版本演進到下一個版本時被錯誤地引入缺陷。解決辦法如下:
? ? ? ? 1. 安排人員看圖來比較結果,不過這種辦法很快就被拋棄了,因為人很容易因為注意力不集中而錯過性能退化缺陷。
? ? ? ? 2. 設定1個簡單的閾值閥門。缺點在于選擇合適的閾值門限是非常困難的事情,并且不能保證誤報率低。閾值設定過低會導致誤報一些由隨機噪聲引起的而不是代碼變化引起的性能退化數據,閾值設定過高會導致過濾不出真正存在的性能退化問題。
無論使用何種算法來檢測性能退化問題,典型的CI系統都應當能夠自動進行以下動作:
? ? ? ? 1. 配置待測試系統。
? ? ? ? 2. 運行程序;
? ? ? ? 3. 報告運行結果;
? ? ? ? 4. 判斷性能是否發生變化;
? ? ? ? 5. 將結果可視化。
CI系統應當能夠同時支持自動基準測試和手動基準測試,產生可復現的結果,并對發現的性能退化問題生成工單。迅速檢測性能退化問題也非常重要。
2.4 手動性能測試
本地性能評估的基本建議:
? ? ? ? 1. 多次測量基線性能;
? ? ? ? 2. 多次測量修改后的程序的性能;
? ? ? ? 3. 對2者進行比較;
使用統計分布圖的一個優勢是可以發現基準測試中的不良行為。如果數據分布是雙峰的,基準測試會表現出兩類不同的行為,引起雙峰分布的常見原因是代碼有快、有慢兩條執行路徑,例如訪存緩存、獲取鎖等。解決這些問題的方法是隔離不同的功能模塊并分別進行基準測試。
性能數據分布的可視化展示可以幫助我們發現某些異常,但我們不應當同它來進行加速比的計算。假設檢驗非常適合用來確定性能加速(減速)的表現是否具有隨機性。
一旦通過假設檢驗方法確定2組數據存在統計上顯著的差異,就可以使用算術平均或幾何平均的方法來計算加速比。注意對于小樣本采樣,均值和幾何均值會受到異常值影響。除非數據分布具有小方差,否則不應當只考慮使用均值。
如果測量值的方差與均值大小在同一個數量級,那么均值就不是具有代表性的指標。
為了準確地計算加速比,最重要的工作之一就是收集大量的樣本數據。這聽上去很容易,但有時并不可行,太多基準測試疊加下來的測試時間太長了。
需要收集多少樣本數據才滿足統計分布需要呢?這取決于對比測試的精確到要求。分布數據中樣本的方差越小,需要的樣本數越少。實施自適應策略,收集樣本直到標準差達到特定的范圍。
另一個需要特別小心的是異常值的存在。對某些類型的基準測試而言,異常值可能是重要的指標。
2.5 軟件計時器和硬件計時器
系統級高分辨率計時器:通過統計自某任意時間起開始流逝的滴答數而實現。系統級計時器分辨率是ns級別,并且在所有CPU上都是一致的,它適合用來測量持續時間超過1us的事件。
時間戳計時器TSC:通過硬件寄存器實現的硬件計時器,它適合用來測量持續時間從ns到1 min之間的事件,可以用編譯器的內置函數__rdtsc查詢。
如果需要測量的時間很短暫,則TSC可以提供更好的準確度。相反,如果需要測量的時間長達數小時,則TSC測量毫無意義。除非真的需要時鐘周期的精度,否則大部分情況下選擇系統計時器通常就足夠了。
2.6 微基準測試
微基準測試程序是在優化某些特定功能時跟蹤優化進展的手段。對于C++而言,使用Google benchmark庫,C#則是BenchmarkDotNet庫,Julia則是BenchmarkTools庫,Java則是Java Microbenchmark Harness。
定義一個基準測試優劣的依據是,它能否在真實條件下測試將來要使用的功能的性能。
如果基準測試使用的合成輸入與實際使用的輸入不同,那么基準測試可能會誤導你。所以,在根據單元測試的結果總結結論時要小心。
2.7 本章小結
1. 由于測試的不穩定性,調試性能通常比調試功能更為困難。
2.確定預期目標,需要為如何衡量該目標設定有意義的定義和指標。根據關心的內容,它可能是吞吐量、延遲、每秒操作數(屋頂線模型)等。
3.再生產部署中衡量性能時,為了處理環境噪聲的問題,需要使用統計方法分析結果。
4. 越來越多的大型分布式軟件供應商選擇直接在生產系統上剖析和監控性能,這要求只能使用輕量級的剖析技術。
5. 采用自動化性能跟蹤系統有助于防止性能退化問題滲透到生產軟件系統中,此類CI系統應能夠運行自動化性能測試,可視化結果并標記潛在缺陷,這也是向受眾展示性能結果的穩妥辦法。
6. 性能數據分布之間的統計關系可以通過假設檢驗方法來識別和發現。
7. 系統級高分辨率計時器適合測量持續時間超過1us的事件,若需要高精度測量短事件,則可以使用時間戳計時器。
8. 微基準測試適合迅速證明一些事情,但是你應該始終在實際條件下用真實的程序驗證你的想法。