一些C/C++優化工具
性能數據收集
perf
Linux下收集性能數據通常用perf。關於perf的使用可以參考Brendan Gregg的網站。
其中最常用的命令是perf record
和perf report
,而常用的指標有cycles、branch miss、cache miss等。
然後用FlameGraph工具可以生成火焰圖,直觀顯示性能消耗在何處。
perf的原理
收集cycles、branch miss、cache miss這些數據,主要應用到了CPU當中的Performance Monitoring Unit(簡稱PMU)。PMU其實就是CPU當中的一個計數器,當發生特定事件的時候加一,並可以向內核報告寄存器狀態,尤其是Program Counter寄存器的狀態,這樣就可以知道branch miss、cache miss發生在何處。
因爲會帶來性能損耗,所以使用PMU的時候一般採用採樣模式。例如,每發生一萬次cache miss纔會報告一次。
此外,Intel的CPU還有一項功能,叫Least Recent Branch(簡稱LRB),可以記錄控制流,也被perf用於發現程序中的熱點。
關於PMU和LRB的原理,參見:
- Performance Monitoring Unit
- PMU counters and profiling basics. | Easyperf - Denis Bakhvalov
- An introduction to last branch records
手工優化
關於怎麼優化程序,有一些放之四海而皆準的老生常談,比如如何優化系統調用、如何用鎖等等、使用-O3
編譯選項等等。不過,也有一些更精妙的東西。
其一是likely
/unlikely
宏,而這些宏依賴了GCC中的__builtin_expect
函數。如下:
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
這個宏可以告訴編譯器在分支處更容易跳轉到哪裏。編譯器根據這些提示,可以做出相應優化,防止發生太長的跳轉,進而提高分支預測的成功率,並減少程序加載時出現page fault的可能性。
從C++20開始,likely和unlikely以attribute的形式進入了C++標準。使用方法如下:
double pow(double x, long long n) {
if (n > 0) [[likely]]
return x * pow(x, n - 1);
else [[unlikely]]
return 1;
}
此外,也有一些builtin功能可以提示編譯器對緩存做更精確的控制,例如__builtin_prefetch
。
參見:
- Other Builtins (Using the GNU Compiler Collection (GCC))
- C++ attribute: likely, unlikely (since C++20)
自動優化
上面的手工優化,實際操作起來往往很繁瑣,所以只能在少量熱點處使用。如果要對程序進行大量優化,免不了要使用自動化工具。
LTO
LTO全稱是Link-Time Optimization,即"鏈接期優化"。LTO可以對程序進行全局優化,而不僅僅着眼於局部,故可以執行一些更激進的優化策略。但是缺點是優化方法比較複雜,且內存消耗大;針對這些缺點又有了Thin LTO。LTO使用起來也很簡單,利用clang編譯,然後在編譯和鏈接時加上-flto
和-flto=thin
即可。
參見:
PGO
PGO,全名Profile-Guided Optimazation,即通過編譯的時候,構建一個運行模型,通過Profile數據來指導編譯器優化。目前GCC已經提供了該功能。但是缺點是這個過程通常非常耗時,而且不好收集數據集,構建模型也很複雜。針對此,谷歌做了一些PGO自動化相關的工作,開源了AutoFDO,全名Automatic Feedback-Directed Optimization,通過採集生產機器上的數據來做反饋優化。不過,谷歌的AutoFDO雖然開源了,但是Github上似乎並沒有詳細的使用說明,只能夠找到一些零散的資料。
參見:
- Profile-guided optimization
- GCC: Options That Control Optimization
- google/autofdo
- AutoFDO: Automatic Feedback-Directed Optimization for Warehouse-Scale Applications
- LLVM Docs: Using Sampling Profilers
- GCC Wiki: AutoFDO Tutorial
- How to use profile guided optimizations in g++?
BOLT
BOLT由Facebook開發(現在改名meta了),也是一種反饋優化,全名是Binary Optimization and Layout Tool。根據Facebook的數據,BOLT給他們的Hack語言(一種php類似物)虛擬機HHVM帶來了8%的性能提升。
參見:
正如其名,BOLT在優化時不需要源代碼,可以直接操作二進制。由重排代碼、優化控制流,而提高指令緩存的效率。如果程序依賴了一些沒有源代碼的二進制庫,那麼BOLT在這種場景下就有很大優勢。