如何使用 Profile-Guided Optimizations 建置 Clang 和 LLVM

簡介

PGO(Profile-Guided Optimization,剖析導引最佳化)允許您的編譯器根據程式碼實際執行的方式更好地最佳化程式碼。使用者回報說,將其應用於 Clang 和 LLVM 可以將整體編譯時間縮短 20%。

本指南將逐步說明如何使用 PGO 建置 Clang,但它也適用於其他子專案,例如 LLD。

如果您想使用 PGO 建置其他軟體,請參閱 PGO 的終端使用者文件

使用預先配置的 CMake 快取

請參閱 https://llvm.dev.org.tw/docs/AdvancedBuilds.html#multi-stage-pgo

使用腳本

我們在 utils/collect_and_build_with_pgo.py 提供了一個腳本。這個腳本在幾個 Linux 版本上進行了測試,並且需要簽出 LLVM、Clang 和 compiler-rt。儘管名稱如此,但它會執行四次 Clang 的乾淨建置,因此可能需要一段時間才能完成執行。有關如何執行腳本以及可用的不同選項的更多資訊,請參閱腳本的 --help。如果您想為特定用例(例如,編譯一個特定的龐大軟體)充分利用 PGO,請閱讀下方關於「基準測試」選擇的部分。

請注意,此腳本僅在少數 Linux 發行版上進行過測試。一如既往,我們非常歡迎您提供新增對其他平台支援的補丁。:)

此腳本也支援 --dry-run 選項,這會導致它列印重要命令而不是執行它們。

選擇「基準測試」

當收集到的剖析資料代表使用者計畫如何使用編譯器時,PGO 的效果最佳。值得注意的是,如果您要以 ARM 為目標,那麼高度準確的 llc 建置 x86_64 程式碼的剖析資料並不是非常有用。

根據預設,上述腳本會執行兩件事以獲得可靠的覆蓋率。它

  • 會執行所有 Clang 和 LLVM 的 lit 測試,並且

  • 會使用已檢測的 Clang 來建置 Clang、LLVM 以及所有其他可用的 LLVM 子專案。

這些加起來應該可以讓您

  • 可靠地涵蓋 C++ 建置,

  • 良好地涵蓋 C 建置,

  • 極佳地涵蓋執行最佳化,

  • 極佳地涵蓋主機架構的後端,並且

  • 涵蓋其他架構(如果支援其他架構的後端)。

總之,這應該涵蓋 Clang 和 LLVM 的各種使用方式。如果您有非常特定的需求(例如,您的編譯器旨在為四種不同的平台編譯一個大型瀏覽器,或類似情況),您可能需要執行其他操作。這可以在腳本本身中進行配置。

使用 PGO 建置 Clang

如果您不想使用腳本或 cmake 快取,以下簡要介紹如何使用 PGO 建置 Clang/LLVM。

首先,您應該至少在本機檢出 LLVM、Clang 和 compiler-rt。

接下來,從高層次來看,您需要執行以下操作

  1. 建置一個標準的 Release Clang 和相關的 libclang_rt.profile 函式庫

  2. 使用您在上方建置的 Clang 建置 Clang,但要使用插裝

  3. 使用插裝的 Clang 產生效能剖析資料,這包含兩個步驟

  • 在代表使用者將如何使用上述工具的任務上執行插裝的 Clang/LLVM/lld/etc。

  • 使用工具將上方產生的「原始」效能剖析資料轉換為單一、最終的 PGO 效能剖析資料。

  1. 使用從基準測試收集的效能剖析資料建置最終的 Release Clang(以及您需要的任何其他二進位檔案)

更詳細的步驟

  1. 以您通常的方式設定 Clang 建置。強烈建議您為此使用 Release 組態,因為它將用於建置另一個 Clang。因為您需要 Clang 和支援函式庫,所以您需要建置 all 目標(例如 ninja allmake -j4 all)。

  2. 如上所述設定 Clang 建置,但新增以下 CMake 參數

    • -DLLVM_BUILD_INSTRUMENTED=IR – 這會導致我們使用插裝建置所有內容。

    • -DLLVM_BUILD_RUNTIME=No – 一些專案在使用效能剖析建置時會產生不良互動,並且不需要建置。此旗標會將其關閉。

    • -DCMAKE_C_COMPILER=/path/to/stage1/clang - 使用我們在步驟 1 中建置的 Clang。

    • -DCMAKE_CXX_COMPILER=/path/to/stage1/clang++ - 與上相同。

在此建置目錄中,您只需要建置 clang 目標(以及您的基準測試所需的任何支援工具)。

  1. 如上所述,這包含兩個步驟:收集效能剖析資料,然後將其調整為可用的形式

    1. 使用在步驟 2 中產生的 Clang 建置您的基準測試。建議的「標準」基準測試是在您插裝的 Clang 的建置目錄中執行 check-clangcheck-llvm,並使用您插裝的 Clang 進行 Clang/LLVM 的完整建置。因此,建立另一個建置目錄,並使用以下 CMake 參數

      • -DCMAKE_C_COMPILER=/path/to/stage2/clang - 使用我們在步驟 2 中建置的 Clang。

      • -DCMAKE_CXX_COMPILER=/path/to/stage2/clang++ - 與上相同。

      如果您的使用者是偵錯資訊的愛好者,您可能會考慮使用 -DCMAKE_BUILD_TYPE=RelWithDebInfo 而不是 -DCMAKE_BUILD_TYPE=Release。這將提供 clang 偵錯資訊部分的更好覆蓋率,但需要更長的時間才能完成,並且會導致更大的建置目錄。

      建議使用您插裝的 Clang 建置 all 目標,因為更高的覆蓋率通常更好。

  1. 您現在應該在 path/to/stage2/profiles/ 中有一些 *.profraw 檔案。您需要使用 llvm-profdata 合併這些檔案(即使您只有一個檔案!效能剖析資料合併也會將 profraw 轉換為實際的效能剖析資料)。這可以使用 /path/to/stage1/llvm-profdata merge -output=/path/to/output/profdata.prof path/to/stage2/profiles/*.profraw 完成。

  1. 現在,建置您最終的 PGO 優化 Clang。為此,您需要將以下額外的參數傳遞給 CMake。

    • -DLLVM_PROFDATA_FILE=/path/to/output/profdata.prof - 使用上一步驟中的 PGO 設定檔。

    • -DCMAKE_C_COMPILER=/path/to/stage1/clang - 使用我們在步驟 1 中建置的 Clang。

    • -DCMAKE_CXX_COMPILER=/path/to/stage1/clang++ - 與上相同。

    從這裡開始,您可以建置您需要的任何目標。

    備註

    您可能會在建置輸出中看到有關設定檔不符的警告。這些警告通常是無害的。若要隱藏它們,您可以將 -DCMAKE_C_FLAGS='-Wno-backend-plugin' -DCMAKE_CXX_FLAGS='-Wno-backend-plugin' 加入您的 CMake 呼叫中。

恭喜!您現在擁有一個使用設定檔引導最佳化建置的 Clang,如果您願意,您可以刪除除最終建置目錄以外的所有目錄。

如果這對您來說很有效,而且您打算經常這樣做,那麼可以進行一個小小的優化:LLVM 和 Clang 有一個名為 tblgen 的工具,它會在建置過程中建置和執行。雖然在步驟 3 中將其建置以進行覆蓋率分析可能很好,但您的其他建置應該不會從建置它中受益。您可以將 CMake 選項 -DLLVM_NATIVE_TOOL_DIR=/path/to/stage1/bin 傳遞給步驟 2 及之後的步驟,以避免這些不必要的重建。