Misexpect

當開發人員使用 llvm.expect 內建函數時,例如透過使用 __builtin_expect(...),他們試圖向最佳化器傳達他們的程式碼在執行時的預期行為。然而,這些註解可能因為各種原因而不正確:程式碼庫的變更會靜默地使其失效、開發人員錯誤地註解它們(例如,使用 LIKELY 而不是 UNLIKELY),或者他們在撰寫註解時可能錯誤地假設了一些事情。無論原因為何,偵測這些情況是有用的,以便最佳化器可以對程式碼做出更有用的決策。MisExpect 診斷旨在透過比較 llvm.expect 內建函數的使用與剖析輸入提供的真實情況,來協助開發人員識別和解決這些情況。

LLVM 後端的 MisExpect 檢查遵循一個簡單的程序:如果剖析期間收集的分支權重與 llvm.expect 內建函數提供的權重之間存在不匹配,則它將向使用者發出診斷訊息。

執行驗證最自然的地點是在分支權重以分支權重元資料的形式指派給目標指令之前。

在 LLVM 後端中有 3 個關鍵位置,分支權重是根據剖析資訊或 llvm.expect 內建函數的使用而建立和指派的,我們的實作重點在於這些位置來執行驗證。

我們根據編譯器指派給 llvm.expect 內建函數的值來計算發出 MisExpect 相關診斷的閾值,這些值可以透過 -likely-branch-weight-unlikely-branch-weight LLVM 選項設定。在驗證期間,如果剖析權重與計算的閾值不匹配,那麼我們將發出備註或警告,詳細說明潛在的效能回歸。診斷還會報告剖析期間註解正確的百分比,以幫助開發人員推理如何進行。

診斷也以最佳化備註的形式提供,可以透過 LLVM 中的 opt-viewer.py 腳本進行序列化和處理。

-pass-remarks=misexpect

當剖析資料與 llvm.expect 內建函數的使用衝突時,啟用 misexpect 的最佳化備註。

-pgo-warn-misexpect

當剖析資料與 llvm.expect 內建函數的使用衝突時,啟用 misexpect 警告。

LLVM 支援 4 種剖析格式:前端、IR、CS-IR 和取樣。MisExpect 診斷與所有剖析格式相容。

剖析類型

描述

前端

在編譯期間由前端新增的剖析檢測,即 clang

IR

在 LLVM 後端期間新增的剖析檢測

CS-IR

基於上下文敏感 IR 的剖析

取樣

透過使用外部工具(例如 Linux 上的 perf)取樣收集的剖析