首頁>技術>

在之前的文章中,我們對軟體效能最佳化的方法做了非常全面、系統的介紹,但是對每種方法對應的具體操作寫的比較少。這裡,我們就來介紹一種指令最佳化的具體手段,就是 Facebook 開源工具,bolt。什麼是指令最佳化呢?

程式在執行時,不管是靜態連結,動態連結還是動態載入,所有的程式碼段,也就是二進位制指令,都要載入到記憶體。CPU 在執行到每個函式時,都要去程式碼記憶體塊相應位置取函式對應的二進位制指令。另一方面,CPU 從記憶體取資料時,要先經過快取(cache),再從快取讀資料。而每次存放到快取裡的資料大小都是一定的。CPU 從快取讀資料的速度比從記憶體取資料的速度快上一個量級。所以,我們希望,每次載入到快取裡的指令資料都是我們需要的,也就是儘量避免資料 cache miss。如何減少指令 cache miss 呢?

1 函式冷熱分割槽

簡單來說,就是把那些被頻繁呼叫的函式程式碼段放在一起,稱之為熱函式區。而那些不常用的函式程式碼段放在一起,稱之為冷函式區。

2 函式重排

程式在執行的過程中,按函式執行順序,可以畫出一個樹狀的軌跡圖。透過 gcc 自帶的打樁函式 __cyg_profile_func_enter,可以打印出函式的執行軌跡(帶函式名和函式地址)。然後寫個指令碼對 elf 格式的目標檔案裡的函式 .text 段按函式軌跡重排。這樣做有什麼好處呢?記憶體中相鄰位置的程式碼段會同時取到快取裡,而相鄰的程式碼在執行時也是相鄰的,這樣就避免的指令 cache miss。這裡要注意的是,so 檔案(共享庫)中函式地址是相對地址,而由 __cyg_profile_func_enter 獲取的函式地址是絕對地址,需要根據 /proc 目錄下 maps 檔案做個轉換。

3 分支預測

一個 if 語句,在處理器級別,它是一條分支指令。CPU 看到分支語句時,就去做條件判斷,如果 if 條件沒命中,繼續判斷 else if 中的條件,直到命中。我們最希望看到的結果是,CPU 在判斷第一個 if 條件時就命中,這可以大大加快程式執行速度。那如何做到這一點呢?透過分析過去大量的分支行為來進行預測, 就可以提高預測的效率。換句話說,我們可以嘗試識別一個模式並遵循這個模式,而在現實開發中,大多數應用程式都具有行為良好的分支(這裡的行為良好指的是分支的行為結果存在規律性)。因此,現代分支預測器通常將達到90%以上的命中率。

從上述指令 cache miss 的最佳化手段來看,所有的操作都是重新編排 gcc 編譯生成的二進位制目標檔案(透過 linux 下的 readelf 或者 objdump 命令可以開啟檢視其對應的彙編程式碼),一般情況下,我們可以都可以透過寫指令碼實現。而針對 icache miss 最佳化,Facebook 開源了一個非常好的工具,那就是 bolt。

Facebook 開源專案 - BOLT

這個專案在 github 上已經有 1.9k 的星了。它透過兩種方式實現目標檔案的重構,Facebook 是在 llvm 編譯器(什麼是 llvm 編譯器,請參考我之前的文章)的後端最佳化工具集基礎上開發的。

方法 1

使用 perf 工具採集 perf.data(函式執行期資料統計,包括函式呼叫棧,函式開銷等),執行 perf2bolt 生成 perf.fdata,再根據 perf.data 重新生成目標檔案。

方法 2

對原始目標檔案插樁,執行插樁後代碼,在程式執行期生成 perf.fdata。再由 llvm-bolt 生成最終重構後的目標檔案。

這個專案具體的使用方法也比較簡單,大家可以自行去 github 上研究研究。(END)

15
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 如何在VS Code中編寫、編譯、除錯Python程式碼