プロファイルガイド付き最適化によるClangとLLVMのビルド方法¶
はじめに¶
PGO(プロファイルガイド付き最適化)を使用すると、コンパイラはコードが実際に実行される方法に合わせてより適切にコードを最適化できます。ユーザーによると、これをClangとLLVMに適用することで、全体のコンパイル時間を20%削減できることが報告されています。
このガイドでは、PGOを使用してClangをビルドする方法について説明しますが、LLDなどの他のサブプロジェクトにも適用できます。
PGOを使用して他のソフトウェアをビルドする場合は、PGOのエンドユーザー向けドキュメントを参照してください。
事前に設定されたCMakeキャッシュの使用¶
https://llvm.dokyumento.jp/docs/AdvancedBuilds.html#multi-stage-pgoを参照してください。
スクリプトの使用¶
utils/collect_and_build_with_pgo.py
にスクリプトがあります。このスクリプトはいくつかのLinuxフレーバーでテストされており、LLVM、Clang、およびcompiler-rtのチェックアウトが必要です。名前とは異なり、Clangのクリーンビルドを4回実行するため、完了するまでに時間がかかる場合があります。実行方法と利用可能なさまざまなオプションの詳細については、スクリプトの--help
を参照してください。特定のユースケース(特定の大規模なソフトウェアのコンパイルなど)でPGOを最大限に活用したい場合は、以下の「ベンチマーク」の選択に関するセクションをよくお読みください。
このスクリプトは、少数のLinuxディストリビューションでのみテストされていることに注意してください。他のプラットフォームのサポートを追加するためのパッチは、常に高く評価されています。 :)
このスクリプトは、--dry-run
オプションもサポートしており、コマンドを実行する代わりに重要なコマンドを出力します。
「ベンチマーク」の選択¶
PGOは、収集されたプロファイルがユーザーがコンパイラを使用する予定の方法を表している場合に最適に機能します。特に、x86_64コードをビルドするllcのプロファイルが非常に正確であっても、ARMをターゲットにする予定の場合には、それほど役立ちません。
デフォルトでは、上記のスクリプトは堅牢なカバレッジを取得するために2つのことを行います。
ClangとLLVMのすべてのlitテストを実行し、
インストルメント化されたClangを使用して、Clang、LLVM、および利用可能な他のすべてのLLVMサブプロジェクトをビルドします。
これらを組み合わせることで、以下を実現できます。
C++のビルドに関する堅牢なカバレッジ、
Cのビルドに関する良好なカバレッジ、
最適化の実行に関する優れたカバレッジ、
ホストアーキテクチャのバックエンドに関する優れたカバレッジ、
その他のアーキテクチャに関するいくつかのカバレッジ(他のアーキテクチャがバックエンドとしてサポートされている場合)。
全体として、これにより、ClangとLLVMのさまざまな用途をカバーできます。非常に具体的なニーズがある場合(たとえば、コンパイラが4つの異なるプラットフォーム用に大規模なブラウザをコンパイルするためなど)、別の方法を行う必要があるかもしれません。これはスクリプト自体で設定できます。
PGOを使用したClangのビルド¶
スクリプトまたはcmakeキャッシュを使用しない場合は、PGOを使用してClang/LLVMをビルドする方法について簡単に説明します。
まず、LLVM、Clang、およびcompiler-rtをローカルにチェックアウトする必要があります。
次に、概要として、次の手順を実行する必要があります。
標準のリリース版Clangと関連するlibclang_rt.profileライブラリをビルドします。
上記でビルドしたClangを使用してClangをビルドしますが、インストルメンテーションを使用します。
インストルメント化されたClangを使用してプロファイルを生成します。これは2つの手順で構成されます。
ユーザーがこれらのツールを使用する方法を表すタスクで、インストルメント化されたClang/LLVM/lldなどを実行します。
ツールを使用して、上記で生成された「raw」プロファイルを単一の最終的なPGOプロファイルに変換します。
ベンチマークから収集したプロファイルを使用して、最終的なリリース版Clang(必要なその他のバイナリも含む)をビルドします。
より詳細な手順
通常どおりにClangビルドを設定します。これは別のClangをビルドするために使用されるため、リリース構成を使用することを強くお勧めします。Clangとサポートライブラリが必要なため、
all
ターゲットをビルドする必要があります(例:ninja all
またはmake -j4 all
)。上記のように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
ターゲット(およびベンチマークに必要なサポートツール)をビルドするだけです。
上記のように、これにはプロファイルデータの収集と、それを有用な形式に変換する2つの手順があります。
手順2で生成されたClangを使用してベンチマークをビルドします。推奨される標準のベンチマークは、インストルメント化されたClangのビルドディレクトリで
check-clang
とcheck-llvm
を実行し、インストルメント化されたClangを使用してClang/LLVMを完全にビルドすることです。そのため、次のCMake引数を使用して、別のビルドディレクトリを作成します。-DCMAKE_C_COMPILER=/path/to/stage2/clang
- 手順2でビルドしたClangを使用します。-DCMAKE_CXX_COMPILER=/path/to/stage2/clang++
- 上記と同じです。
ユーザーがデバッグ情報を使用する場合、
-DCMAKE_BUILD_TYPE=Release
の代わりに-DCMAKE_BUILD_TYPE=RelWithDebInfo
を使用することを検討してください。これにより、clangのデバッグ情報のより優れたカバレッジが得られますが、完了するまでに時間がかかり、ビルドディレクトリがはるかに大きくなります。インストルメント化されたClangで
all
ターゲットをビルドすることをお勧めします。カバレッジが多いほど良いからです。
path/to/stage2/profiles/
にいくつかの*.profraw
ファイルがあるはずです。llvm-profdata
を使用してこれらをマージする必要があります(1つしかない場合でも!プロファイルのマージはprofrawを実際のプロファイルデータに変換します)。これは/path/to/stage1/llvm-profdata merge -output=/path/to/output/profdata.prof path/to/stage2/profiles/*.profraw
を使用して実行できます。
これで、最終的な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の一部としてカバレッジのためにこれをビルドするのは潜在的に良いことですが、他のビルドではこれをビルドすることからメリットはありません。手順2以降にCMakeオプション-DLLVM_NATIVE_TOOL_DIR=/path/to/stage1/bin
を渡して、これらの不要な再ビルドを回避できます。