Android 13 以前の Android ビルドシステムは、ブループリント ビルドルールを持つネイティブ Android モジュールで、Clang の プロファイルに基づく最適化(PGO)を使用できます。このページでは、Clang の PGO について説明し、PGO に使用するプロファイルを継続的に生成して更新する方法と、PGO とビルドシステムを統合する方法を(ユースケースとともに)示します。
注意: このドキュメントでは、Android プラットフォームでの PGO の使用について説明します。Android アプリから PGO を使用する方法については、こちらのページをご覧ください。
Clang の PGO について
Clang は、2 種類のプロファイルを使用して、プロファイルに基づく最適化を実施できます。
- インストルメンテーション ベースのプロファイルは、インストゥルメント化されたターゲット プログラムから生成されます。こうしたプロファイルは詳細であり、実行時のオーバーヘッドが高くなります。
- サンプリング ベースのプロファイルは、一般的に、ハードウェア カウンタのサンプリングにより生成されます。実行時のオーバーヘッドは低く、バイナリの変更またはインストルメンテーションなしで収集できます。インストルメンテーション ベースのプロファイルほど詳細ではありません。
すべてのプロファイルは、アプリの一般的な動作を喚起する典型的なワークロードから生成する必要があります。Clang は AST ベース(-fprofile-instr-generate)と LLVM IR ベース(-fprofile-generate))の両方をサポートしますが、Android はインストルメンテーション ベースの PGO に対する LLVM IR ベースのみをサポートします。
プロファイル収集用にビルドするには、次のフラグが必要です。
-fprofile-generate: IR ベースのインストルメンテーション。このオプションでは、バックエンドは重み付き最小スパンツリーのアプローチを使用してインストルメンテーション ポイントの数を減らし、配置を重みの小さい辺に最適化します(リンクのステップにもこのオプションを使用します)。Clang ドライバは、自動的にプロファイリング ランタイム(libclang_rt.profile-arch-android.a)をリンカーに渡します。このライブラリには、プログラムの終了時にプロファイルをディスクに書き込むルーチンが含まれています。-gline-tables-only: 最小限のデバッグ情報を生成するためのサンプリング ベースのプロファイル収集。
インストルメンテーション ベースのプロファイルには -fprofile-use=pathname を使用し、サンプリング ベースのプロファイルには -fprofile-sample-use=pathname を使用して、PGO にプロファイルを使用できます。
注: コードの変更に伴って Clang がプロファイル データを使用できなくなると、-Wprofile-instr-out-of-date の警告が生成されます。
PGO を使用する
PGO を使用する手順は次のとおりです。
- コンパイラとリンカーに
-fprofile-generateを渡し、インストルメンテーションを使用してライブラリ / 実行ファイルをビルドします。 - インストゥルメント化されたバイナリで典型的なワークロードを実行することにより、プロファイルを収集します。
llvm-profdataユーティリティを使用してプロファイルを後処理します(詳細については、LLVM プロファイル ファイルの処理をご覧ください)。- コンパイラとリンカーに
-fprofile-use=<>.profdataを渡し、プロファイルを使用して PGO を適用します。
Android で PGO を使用する場合は、プロファイルをオフラインで収集し、コードとともにチェックインしてビルドを再現可能にする必要があります。プロファイルはコードが進化しても使用できますが、定期的に(または、プロファイルが最新でないことを Clang が警告するたびに)再生成する必要があります。
プロファイルを収集する
Clang は、ライブラリのインストルメント化されたビルドでベンチマークを実行するか、またはベンチマークの実行時にハードウェア カウンタをサンプリングすることによって収集したプロファイルを使用できます。現時点では、Android はサンプリング ベースのプロファイル収集をサポートしていないため、インストルメント化されたビルドを使用してプロファイルを収集する必要があります。
- ベンチマークと、そのベンチマークによって包括的に使用されるライブラリのセットを決定します。
pgoプロパティをベンチマークとライブラリに追加します(詳細については後述します)。- 次のコマンドを使用して、これらのライブラリのインストゥルメント化されたコピーで Android ビルドを生成します。
make ANDROID_PGO_INSTRUMENT=benchmark
benchmark は、ビルド中にインストルメント化されたライブラリのコレクションを識別するプレースホルダです。実際の典型的な入力は(場合によってはベンチマーク対象のライブラリにリンクしている別の実行ファイルも)PGO に固有のものではなく、このドキュメントの範囲外です。
- デバイス上のインストルメント化されたビルドをフラッシュまたは同期します。
- ベンチマークを実行してプロファイルを収集します。
llvm-profdataツール(後述します)を使用してプロファイルを後処理し、ソースツリーにチェックインできるようにします。
ビルド時にプロファイルを使用する
プロファイルを Android ツリーの toolchain/pgo-profiles にチェックインします。名前は、ライブラリの pgo プロパティの profile_file サブプロパティで指定されているものと一致する必要があります。ビルドシステムは、ライブラリのビルド時にプロファイル ファイルを自動的に Clang に渡します。ANDROID_PGO_DISABLE_PROFILE_USE 環境変数を true に設定すると、PGO を一時的に無効にしてパフォーマンスのメリットを測定できます。
プロダクト固有のプロファイル ディレクトリを追加で指定するには、BoardConfig.mk の PGO_ADDITIONAL_PROFILE_DIRECTORIES make 変数に追加します。追加のパスを指定すると、それらのパスのプロファイルにより、toolchain/pgo-profiles のプロファイルがオーバーライドされます。
make に dist ターゲットを使用してリリース イメージを生成する場合、ビルドシステムは、見つからなかったプロファイル ファイルの名前を $DIST_DIR/pgo_profile_file_missing.txt に書き込みます。このファイルをチェックすると、誤って削除されたプロファイル ファイル(自動的に PGO を無効にします)を確認できます。
Android.bp ファイルで PGO を有効にする
ネイティブ モジュールの Android.bp ファイルで PGO を有効にするには、単に pgo プロパティを指定します。このプロパティには、次のサブプロパティがあります。
| プロパティ | 説明 |
|---|---|
instrumentation
|
インストルメンテーションを使用する PGO では、true に設定します。デフォルトは false です。 |
sampling
|
サンプリングを使用する PGO では、true に設定します。デフォルトは false です。 |
benchmarks
|
文字列のリスト。リスト内のいずれかのベンチマークが ANDROID_PGO_INSTRUMENT ビルド オプションで指定されている場合、このモジュールはプロファイリング用にビルドされます。 |
profile_file
|
PGO で使用するプロファイル ファイル(toolchain/pgo-profile に対応)。enable_profile_use プロパティが false に設定されている場合または ANDROID_PGO_NO_PROFILE_USE ビルド変数が true に設定されている場合を除き、ビルドは、このファイルを $DIST_DIR/pgo_profile_file_missing.txt に追加することで、このファイルが存在しないことを警告します。 |
enable_profile_use
|
ビルド時にプロファイルを使用しない場合は、false に設定します。このプロパティをブートストラップ中に使用すると、プロファイル収集を有効にするか、または PGO を一時的に無効にできます。デフォルトは true です。 |
cflags
|
インストゥルメント化されたビルドで使用する追加のフラグのリスト。 |
PGO を使用するモジュールの例:
cc_library { name: "libexample", srcs: [ "src1.cpp", "src2.cpp", ], static: [ "libstatic1", "libstatic2", ], shared: [ "libshared1", ] pgo: { instrumentation: true, benchmarks: [ "benchmark1", "benchmark2", ], profile_file: "example.profdata", } }
ベンチマーク benchmark1 および benchmark2 が、libstatic1、libstatic2、または libshared1 ライブラリの典型的な動作を喚起する場合、これらのライブラリの pgo プロパティにもベンチマークを含めることができます。Android.bp の defaults モジュールでは、複数のモジュールで同じビルドルールが繰り返されないようにするために、一群のライブラリに対して共通の pgo を指定できます。
異なるプロファイル ファイルを選択するか、アーキテクチャの PGO を選択的に無効にするには、アーキテクチャごとに profile_file、enable_profile_use、cflags プロパティを指定します。例(アーキテクチャ ターゲットを太字で示します):
cc_library { name: "libexample", srcs: [ "src1.cpp", "src2.cpp", ], static: [ "libstatic1", "libstatic2", ], shared: [ "libshared1", ], pgo: { instrumentation: true, benchmarks: [ "benchmark1", "benchmark2", ], } target: { android_arm: { pgo: { profile_file: "example_arm.profdata", } }, android_arm64: { pgo: { profile_file: "example_arm64.profdata", } } } }
インストルメンテーション ベースのプロファイリング中にプロファイリング ランタイム ライブラリへの参照を解決するには、ビルドフラグ -fprofile-generate をリンカーに渡します。PGO でインストゥルメント化された静的ライブラリ、すべての共有ライブラリ、静的ライブラリに直接依存するバイナリも、PGO 用にインストゥルメント化されている必要があります。ただし、このような共有ライブラリまたは実行ファイルで PGO プロファイルを使用する必要はなく、enable_profile_use プロパティを false に設定できます。この制限の範囲外で、PGO を任意の静的ライブラリ、共有ライブラリ、または実行ファイルに適用できます。
LLVM プロファイル ファイルを処理する
インストルメント化されたライブラリまたは実行ファイルを実行すると、default_unique_id_0.profraw というプロファイル ファイルが /data/local/tmp に生成されます(ここで、unique_id はこのライブラリに固有の数値ハッシュです)。このファイルがすでに存在する場合、プロファイリング ランタイムは、プロファイルの作成中に新しいプロファイルを古いプロファイルと統合します。なお、アプリ デベロッパーは /data/local/tmp にアクセスできません。代わりに /storage/emulated/0/Android/data/packagename/files などを使用する必要があります。プロファイル ファイルの場所を変更するには、実行時に LLVM_PROFILE_FILE 環境変数を設定します。
次に、llvm-profdata ユーティリティで .profraw ファイルを変換(場合によっては複数の .profraw ファイルを結合)して、.profdata ファイルを作成します。
llvm-profdata merge -output=profile.profdata <.profraw and/or .profdata files>
そうすると、profile.profdata をソースツリーにチェックインしてビルド時に使用できます。
ベンチマーク中に複数のインストゥルメント化されたバイナリ / ライブラリが読み込まれると、各ライブラリは、個別の一意の ID を持つ個別の .profraw ファイルを生成します。通常は、これらのファイルをすべて単一の .profdata ファイルに結合して、PGO ビルドに使用できます。ライブラリが別のベンチマークで使用される場合、そのライブラリは、両方のベンチマークからのプロファイルを使用して最適化する必要があります。この場合、llvm-profdata の show オプションが役立ちます。
llvm-profdata merge -output=default_unique_id.profdata default_unique_id_0.profraw llvm-profdata show -all-functions default_unique_id.profdata
unique_id を個別のライブラリにマッピングするには、unique_id ごとの show 出力で、ライブラリに固有の関数名を検索します。
ケーススタディ: ART 向けの PGO
このケーススタディでは、ART を参考例として提示しますが、ART 向けにプロファイリングされたライブラリの実際のセット、またはその相互依存関係を正確に記述するものではありません。
ART の dex2oat 事前コンパイラが依存する libart-compiler.so は、libart.so に依存します。ART ランタイムは、主に libart.so に実装されています。コンパイラとランタイムのベンチマークは異なります。
| ベンチマーク | プロファイリングされたライブラリ |
|---|---|
dex2oat
|
dex2oat(実行ファイル)、libart-compiler.so、libart.so |
art_runtime
|
libart.so
|
- 次の
pgoプロパティをdex2oat、libart-compiler.soに追加します。pgo: { instrumentation: true, benchmarks: ["dex2oat",], profile_file: "dex2oat.profdata", } - 次の
pgoプロパティをlibart.soに追加します。pgo: { instrumentation: true, benchmarks: ["art_runtime", "dex2oat",], profile_file: "libart.profdata", } - 次のコマンドを使用して、
dex2oatベンチマークとart_runtimeベンチマーク用にインストゥルメント化ビルドを作成します。make ANDROID_PGO_INSTRUMENT=dex2oat make ANDROID_PGO_INSTRUMENT=art_runtime dex2oatとart_runtimeを喚起するベンチマークを実行して、以下を取得します。dex2oatからの 3 つの.profrawファイル(dex2oat_exe.profdata、dex2oat_libart-compiler.profdata、dexeoat_libart.profdata)。これらは、LLVM プロファイル ファイルの処理で説明している方法によって特定されます。- 単一の
art_runtime_libart.profdata。
- 次のコマンドを使用して、
dex2oat実行ファイルとlibart-compiler.soの共通の profdata ファイルを生成します。llvm-profdata merge -output=dex2oat.profdata \ dex2oat_exe.profdata dex2oat_libart-compiler.profdata - 2 つのベンチマークからのプロファイルを結合して、
libart.soのプロファイルを取得します。llvm-profdata merge -output=libart.profdata \ dex2oat_libart.profdata art_runtime_libart.profdataベンチマークによってテストケースの件数とテストケースの実行時間が異なるため、2 つのプロファイルからの
libart.soの未加工のカウント数が異なる可能性があります。この場合、重み付け結合を使用できます。llvm-profdata merge -output=libart.profdata \ -weighted-input=2,dex2oat_libart.profdata \ -weighted-input=1,art_runtime_libart.profdata上記のコマンドでは、
dex2oatからのプロファイルに 2 倍の重みを割り当てています。実際の重み付けは、ドメインの知識または実験に基づいて決定してください。 - プロファイル ファイル
dex2oat.profdataおよびlibart.profdataをtoolchain/pgo-profilesにチェックインして、ビルド時に使用できるようにします。
または、次のコマンドを使用して、すべてのライブラリがインストゥルメント化された単一のインストゥルメント化ビルドを作成します。
make ANDROID_PGO_INSTRUMENT=dex2oat,art_runtime
(or)
make ANDROID_PGO_INSTRUMENT=ALL2 番目のコマンドは、プロファイリング用にすべての PGO 対応モジュールをビルドします。