在BOLT中,識別和優化熱門的基本塊之所以關鍵,是因為BOLT的主要目標是優化程序以更好地利用硬件特性,特別是指令緩存(ICache)。以下是BOLT如何識別和優化熱門基本塊的流程:
-
收集性能數據:
- BOLT開始的時候并不知道哪些基本塊是“熱”的,因此它首先需要運行目標程序并收集性能數據。
- 使用諸如Linux的
perf
工具,BOLT記錄程序的執行profile,確定哪些基本塊和函數被頻繁地執行。 - 這種profile-guided的方法確保BOLT的優化是基于真實的程序行為,而不是某種靜態分析或猜測。
-
基本塊的重新布局:
- 一旦識別了熱門的基本塊,BOLT的下一步是確定如何重新布局這些塊以優化ICache性能。
- BOLT會將經常連續執行的基本塊放在一起,這樣當一個基本塊在ICache中時,其后續的基本塊很可能也在同一個ICache行或附近的行中。
- 這減少了ICache不命中的次數,從而提高了性能。
-
循環優化:
- 由于循環中的代碼會被反復執行,因此它們通常是性能關鍵區域。BOLT會特別關注這些區域,確保循環的基本塊在物理存儲中是連續的。
- 這不僅優化了ICache的命中率,還有助于減少分支預測錯誤和其他前端停頓[1]。
-
分支預測優化:
- BOLT不僅考慮ICache,它還嘗試減少分支預測錯誤。通過重新布局基本塊,BOLT可以確保熱門路徑中的分支更容易被正確預測。
-
跳轉優化:
- 重新布局基本塊可能會導致一些跳轉變得更長。BOLT會嘗試優化這些跳轉,例如通過使用跳轉表,或者在可能的情況下,使用更短的跳轉指令。
-
進一步的數據流和控制流優化:
- 除了布局優化,BOLT還會嘗試其他類型的優化,這些優化可能特別有利于熱門基本塊。例如,常量傳播、死代碼消除和其他傳統的編譯器優化技術。
通過上述流程,BOLT系統地識別和優化熱門基本塊,從而提高程序的整體性能。這種方法的優勢在于它基于真實的程序執行數據,因此優化更有可能在實際運行中產生明顯的性能提升。
[1] “前端停頓”是指在處理器的前端(取指、譯碼階段)所遇到的各種延遲或阻塞。處理器的前端是指令獲取、解碼和發射到執行管道的部分。這一階段的主要任務是以穩定和高效的速度從內存中取得指令,并為后續的執行階段提供準備好的指令。
前端停頓可能包括:
-
ICache不命中:如果所需的指令不在指令緩存(ICache)中,處理器需要從內存或更高級的緩存中取得指令,這會導致延遲。
-
分支預測錯誤:現代處理器使用分支預測器來猜測條件分支(例如if語句)會采取哪條路徑。如果預測錯誤,處理器需要撤銷錯誤路徑上的指令并加載正確路徑的指令,這會導致延遲。
-
指令獲取帶寬限制:由于各種原因,如資源沖突或復雜指令的解碼,處理器可能無法在每個周期內從內存中取得最大數量的指令。
-
指令隊列的阻塞:如果指令隊列(一個存儲待執行指令的緩沖區)滿了,新的指令無法被取出和解碼,直到之前的指令完成。
-
TLB不命中:TLB用于快速翻譯虛擬地址到物理地址。TLB不命中會導致處理器需要執行完整的頁面表查找,進一步增加延遲。
在優化循環時,降低前端停頓非常關鍵。因為循環中的指令會被多次執行,所以任何在前端遇到的延遲或阻塞都會被放大。BOLT通過確保循環中的基本塊在物理存儲上是連續的,可以減少多種前端停頓,從而提高程序的整體性能。