1 概述
性能優化是軟件開發過程中必不可少,但又很困難的工作。這里是我長期對C/C++開發的性能優化的經驗總結。
2 原則
性能優化必須遵循必要的原則進行。
2.1 明確目標
優化前必須有個明確的目標。目標可以有近期的,中期的和遠期的。
並且目標必須是可達到,可量化的具體的值。
2.2 性能測試先行
在任何優化前必須進行性能測試,得到的測試結果必須保存下來。這些數據有如下用處:
-
與之前的測試結果進行比較。如果沒有任何數據,僅僅靠自我推斷絕對是不可靠的。
-
對外公布。這是讓同事/領導/客戶相信性能的最直接的信息。
-
學習。多次優化得到的測試數據是學習和選擇高效的優化方式的最好的參照物。
-
發現熱點。如果沒有這些測試數據,我們無法明確的指導最大最長的耗時發生在哪里。這是優化的前提。
2.3 記錄
性能優化的任何方法和嘗試,以及得到的測試數據都應該記錄下來。
2.4 數據量
性能測試在很大程度上實際就是壓力負載測試。對於這類的性能不需要盡可能的加大數據壓力,測試對應的性能。
另一個必須要進行多次反復的相同測試,並執行相關的數理統計計算。有些產品和流程只有運行幾百萬次才能真正說明性能。這個是非常重要的。
2.5 重構
性能優化不是改變功能。
所以這些都應該基於重構的原則進行,這就意味這任何性能優化不能對上層客戶代碼造成影響。如果這是無法避免的,必須明確說明。
2.6 2/8原則
發現了熱點后,我們必須將從最大的耗時着手。2/8原則有兩層含義:
-
最為耗時最影響性能的熱點僅占所有代碼或者流程的非常小的比例;
-
僅對很小的一部分的代碼執行優化,性能即可得到極大提升,甚至達到預訂目標。
所以我們不能盲目的優化,更不能以自己的推斷或者所為的“理論上是這樣的”想法執行優化,必須實事求是。
2.7 區分Debug/Release
很多時候我們執行優化時是在debug模式下執行的,但是最終我們形成release模式下的性能數據。如果其中加入了為了記錄性能的debug代碼,那么在release模式下必須關閉。
2.8 自動化
很多場景下,多是開發人員的性能測試指導性能優化。如果我們將整個流程自動化,那么可以極大的提升優化效率,更快發布。另外自動化最大的好處是可以將該過程嵌入測試人員測試過程,或者自動化測試/集成/交付(CI/CD)。
但是這是非常困難的,所以需要視情況而定。
2.9 真實可信
我們的性能優化以及得到的結果必須是真實可信的,不能有半點作假和推測。
3 性能測試
性能測試是我們優化的第一步。良好的測試方法是好的開始,而不良甚至錯誤的測試方法會得出錯誤的結果。
4 優化方法
優化的方法有很多,但是這不意味着所有的方法在每個場景下都是可用的。
可以分為宏觀和微觀兩個層次:宏觀主要是基礎設施以及工程設計的優化,這個層面是不會對實現做很大變動的;而微觀則是對具體的編碼調整,內部調整可能會非常大。
4.1 宏觀層面
4.1.1 升級基礎設施
-
硬件升級:這是最直接,有時甚至是最高效的優化方法。
-
操作系統升級:新的操作系統版本擁有更好的性能表現,特別是在內核操作以及內存操作方面。
-
編程語言:使用其他更好的編程語言,或者更加符合性能表現的編程語言也是常用的性能優化方法。
-
編譯器:很多語言擁有不同的編譯器,不同的編譯器得到可執行程序性能有時差別很到。而同一個編譯器的不同版本也有不同的性能表現。
4.1.2 架構設計
4.1.3 結構和流程設計
4.2 微觀層面
4.2.1 變量
-
臨時變量
-
成員變量
-
靜態變量
-
函數參數和返回值
4.2.2 內存管理
-
動態內存分配
-
內存池
-
內存拷貝
-
緩存
4.2.3 多線程和並發
-
阻塞和同步
-
無鎖隊列
-
內存映射
-
共享內存
4.2.4 面向對象
-
使用模板和范型替換繼承
-
錯誤碼替換異常
4.2.5 數據結構和算法
-
字符串
-
浮點
-
遞歸
-
搜索
-
排序
4.2.6 IO和系統調用
-
網絡通信
-
文件讀寫
-
用戶態和內核態的切換
4.2.7 編譯
-
編譯期計算
-
編譯選項
-
內聯-inline
4.2.8 語言/庫版本
-
新的語言版本性能更好;
-
新的語言版本提供了更好性能工具和特性的選擇;
-
新的內存布局;
-
新的內存管理方式。
5 耐心和信心
在很多時候優化可能得出完全相反的結果,即性能反而更糟糕。這是非常正常的,優化路線可能很曲折漫長。有些熱點需要長時間嘗試不同的方法才能有效優化。
所以耐心和信心是優化過程中必備的良好心理素質。