ベースライン プロファイルを手動で作成して測定する

手作業を減らし、一般的な拡張性を高めるために、Jetpack Macrobenchmark ライブラリを使用してプロファイル ルールを自動生成することを強くおすすめします。ただし、アプリでプロファイル ルールを手動で作成して測定することは可能です。

プロファイル ルールを手動で定義する

src/main ディレクトリに baseline-prof.txt というファイルを作成することで、アプリまたはライブラリ モジュールでプロファイル ルールを手動で定義できます。これは、AndroidManifest.xml ファイルが入っているフォルダと同じです。

このファイルでは、1 行に 1 つのルールを指定します。各ルールは、最適化が必要なアプリやライブラリのメソッドまたはクラスをマッチングするためのパターンを表します。

ルールの構文は adb shell profman --dump-classes-and-methods を使用する際の、人が読める ART プロファイル形式(HRF)のスーパーセットです。この構文は記述子と署名の構文とよく似ていますが、ルール作成プロセスを簡素化するためにワイルドカードも使用できます。

Jetpack Compose ライブラリに含まれるベースライン プロファイル ルールの例を次に示します。

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;

プロファイル ルールの変更は、このCompiler Explorer のサンプル プロジェクトで試すことができます。Compiler Explorer は、人が読める ART プロファイル形式(HRF)のみをサポートしているため、ワイルドカードはサポートされていません。

ルールの構文

ルールは、メソッドまたはクラスをターゲットとする 2 つの形式のいずれかを使用します。

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

クラスルールでは、次のパターンを使用します。

[CLASS_DESCRIPTOR]

詳細な説明は次の表をご覧ください。

構文 説明
FLAGS HSP のうち 1 つ以上の文字を表し、起動タイプに関して HotStartupPost Startup のフラグをこのメソッドに付ける必要があるかどうかを示します。

H フラグの付いたメソッドは、「ホット」メソッドです。つまり、アプリの存続期間中に何度も呼び出されます。

S フラグの付いたメソッドは、起動時に呼び出されるメソッドです。

P フラグの付いたメソッドは、起動後に呼び出されるメソッドです。

このファイルに存在するクラスは、起動時に使用されるクラスであり、クラス読み込みのコストを回避するためにヒープで事前割り当てする必要があることを示しています。ART コンパイラは上述のメソッドの AOT コンパイルや、生成された AOT ファイルでのレイアウト最適化など、さまざまな最適化戦略を採用しています。
CLASS_DESCRIPTOR ターゲットとなるメソッドのクラスの記述子。たとえば、androidx.compose.runtime.SlotTable の記述子は Landroidx/compose/runtime/SlotTable; です。Dalvik Executable(DEX)形式に従って L が先頭に付加されています。
METHOD_SIGNATURE メソッドのシグネチャ(メソッドの名前、パラメータの型、戻り値の型を含む)。例として、LayoutNode

// LayoutNode.kt

fun isPlaced():Boolean {
// ...
}

のシグネチャは isPlaced()Z です。

1 つのルールに複数のメソッドまたはクラスを含める場合は、これらのパターンにワイルドカードを使用できます。Android Studio でルールの構文に沿って記述する際のガイドについては、Android Baseline Profiles プラグインをご覧ください。

ワイルドカード ルールの例を次に示します。

HSPLandroidx/compose/ui/layout/**->**(**)**

ベースライン プロファイル ルールでサポートされている型

ベースライン プロファイル ルールでサポートされている型は次のとおりです。これらの型の詳細については、Dalvik Executable(DEX)形式をご覧ください。

文字 説明
B byte 符号付きバイト
C char UTF-16 でエンコードされた Unicode 文字コードポイント
D double 倍精度浮動小数点値
F float 単精度浮動小数点値
I int 整数
J long 長整数
S short 符号付き短整数
V void Void
Z boolean True または False
L(クラス名) reference クラス名のインスタンス

また、ライブラリでは、AAR アーティファクトにパッケージ化されるルールを定義できます。このアーティファクトを含むように APK をビルドすると、ルールはマージされ(マニフェストのマージと同様の方法)、APK 固有のコンパクトなバイナリ ART プロファイルにコンパイルされます。

ART はデバイスで APK が使用される際にこのプロファイルを利用して、Android 9(API レベル 28)、または ProfileInstaller を使用する場合は Android 7(API レベル 24)でのインストール時に、アプリの特定のサブセットを AOT コンパイルします。

ベースライン プロファイルを手動で収集する

Macrobenchmark ライブラリを設定せずにベースライン プロファイルを手動で生成し、クリティカル ユーザー ジャーニーの UI の自動化を作成できます。Macrobenchmark の使用をおすすめしますが、常に使用できるとは限りません。 たとえば、Gradle 以外のビルドシステムを使用している場合、ベースライン プロファイル Gradle プラグインは使用できません。その場合は、ベースライン プロファイル ルールを手動で収集できます。API 34 以上を搭載しているデバイスまたはエミュレータを使用すれば、これははるかに簡単に設定できます。それより低い API レベルでも可能ですが、root アクセス権が必要で、AOSP イメージを実行するエミュレータを使用する必要があります。次のようにすることで、ルールを直接収集できます。

  1. アプリのリリース バージョンをテストデバイスにインストールします。正確なプロファイルを得るには、アプリのビルドタイプを R8 用に最適化し、デバッグ不可にしておく必要があります。
  2. プロファイルがまだコンパイルされていないことを確認します。

    API 34 以上

    adb shell cmd package compile -f -m verify $PACKAGE_NAME
    adb shell pm art clear-app-profiles $PACKAGE_NAME

    API 33 以下

    adb root
    adb shell cmd package compile --reset $PACKAGE_NAME

    APK に Jetpack のプロファイル インストーラ ライブラリとの依存関係がある場合、ライブラリは APK の初回起動時にプロファイルをブートストラップします。これにより、プロファイル生成プロセスが妨げられる可能性があるため、次のコマンドを使用して無効にします。

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
  3. アプリを実行し、プロファイルを収集するクリティカル ユーザー ジャーニーを手動でナビゲートします。
  4. プロファイルをダンプするように ART に指示します。APK に Jetpack プロファイル インストーラ ライブラリとの依存関係がある場合は、それを利用してプロファイルをダンプします。

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    adb shell am force-stop $PACKAGE_NAME
    プロファイル インストーラを使用しない場合は、次のコマンドを使用してエミュレータにプロファイルを手動でダンプします。

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    adb shell am force-stop $PACKAGE_NAME
  5. プロファイルの生成が完了するまで 5 秒以上待ちます。
  6. 生成されたバイナリ プロファイルをテキストに変換します。

    API 34 以上

    adb shell pm dump-profiles --dump-classes-and-methods $PACKAGE_NAME

    API 33 以下

    参照プロファイルと現在のプロファイルのどちらが作成されたかを確認します。 参照プロファイルは次の場所にあります。

    /data/misc/profiles/ref/$$PACKAGE_NAME/primary.prof

    現在のプロファイルは次の場所にあります。

    /data/misc/profiles/cur/0/$PACKAGE_NAME/primary.prof

    APK の場所を確認します。

    adb root
    adb shell pm path $PACKAGE_NAME

    次のように変換します。

    adb root
    adb shell profman --dump-classes-and-methods --profile-file=$PROFILE_PATH --apk=$APK_PATH > /data/misc/profman/$PACKAGE_NAME-primary.prof.txt

  7. adb を使用して、ダンプされたプロファイルをデバイスから取得します。

    adb pull /data/misc/profman/$PACKAGE_NAME-primary.prof.txt PATH_TO_APP_MODULE/src/main/

これにより、生成されたプロファイル ルールが取得され、アプリ モジュールにインストールされます。 次にアプリをビルドすると、ベースライン プロファイルが組み込まれます。これを確認するには、インストールに関する問題の手順に沿って操作してください。

アプリの改善を手動で測定する

ベンチマークを行ってアプリの改善を測定することを強くおすすめします。ただし、改善を手動で測定する場合は、まず参考として最適化されていないアプリの起動を測定します。

PACKAGE_NAME=com.example.app
# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Reset compiled state
adb shell cmd package compile --reset $PACKAGE_NAME
# Measure App startup
# This corresponds to `Time to initial display` metric.
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

次に、ベースライン プロファイルをサイドローディングします。

# Unzip the Release APK first.
unzip release.apk
# Create a ZIP archive.
# The name should match the name of the APK.
# Copy `baseline.prof{m}` and rename it `primary.prof{m}`.
cp assets/dexopt/baseline.prof primary.prof
cp assets/dexopt/baseline.profm primary.profm
# Create an archive.
zip -r release.dm primary.prof primary.profm
# Confirm that release.dm only contains the two profile files:
unzip -l release.dm
# Archive:  release.dm
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#      3885  1980-12-31 17:01   primary.prof
#      1024  1980-12-31 17:01   primary.profm
# ---------                     -------
#                               2 files
# Install APK + Profile together.
adb install-multiple release.apk release.dm

インストール時にパッケージが最適化されたことを確認するために、次のコマンドを実行します。

# Check dexopt state.
adb shell dumpsys package dexopt | grep -A 1 $PACKAGE_NAME

出力でパッケージがコンパイルされたことが示されます。

[com.example.app]
  path: /data/app/~~YvNxUxuP2e5xA6EGtM5i9A==/com.example.app-zQ0tkJN8tDrEZXTlrDUSBg==/base.apk
  arm64: [status=speed-profile] [reason=install-dm]

これで、以前と同様に、ただしコンパイル状態はリセットせずに、アプリ起動のパフォーマンスを測定できるようになりました。パッケージのコンパイル状態はリセットしないでください。

# Force stop app
adb shell am force-stop $PACKAGE_NAME
# Measure app startup
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

ベースライン プロファイルと profgen

このセクションでは、profgen ツールがベースライン プロファイルのコンパクトなバイナリ バージョンを作成するときに行う処理について説明します。

profgen-cli は、プロファイルのコンパイル、イントロスペクション、ART プロファイルのトランスパイルに役立つため、ターゲット SDK バージョンに関係なく Android デバイスにインストールできます。

profgen-cli は、ベースライン プロファイルの HRF をコンパイル後の形式にコンパイルする CLI です。CLI は Android SDK の一部として cmdline-tools リポジトリにも搭載されています。

studio-main ブランチでは以下の機能を使用できます。

 ../cmdline-tools/latest/bin
apkanalyzer
avdmanager
lint
profgen
retrace
screenshot2
sdkmanager

profgen-cli を使用してコンパクトなバイナリ プロファイルを作成する

profgen-cli で使用できるコマンドは、binvalidatedumpProfile です。使用可能なコマンドを表示するには、profgen --help を使用します。

  profgen --help
Usage: profgen options_list
Subcommands:
    bin - Generate Binary Profile
    validate - Validate Profile
    dumpProfile - Dump a binary profile to a HRF

Options:
    --help, -h -> Usage info

コンパクトなバイナリ プロファイルを生成するには、bin コマンドを使用します。以下に、呼び出しの例を示します。

profgen bin ./baseline-prof.txt \
  --apk ./release.apk \
  --map ./obfuscation-map.txt \
  --profile-format v0_1_0_p \
  --output ./baseline.prof \

使用可能なオプションを表示するには、profgen bin options_list を使用します。

Usage: profgen bin options_list
Arguments:
    profile -> File path to Human Readable profile { String }
Options:
    --apk, -a -> File path to apk (always required) { String }
    --output, -o -> File path to generated binary profile (always required)
    --map, -m -> File path to name obfuscation map { String }
    --output-meta, -om -> File path to generated metadata output { String }
    --profile-format, -pf [V0_1_0_P] -> The ART profile format version
      { Value should be one of [
         v0_1_5_s, v0_1_0_p, v0_0_9_omr1, v0_0_5_o, v0_0_1_n
        ]
      }
    --help, -h -> Usage info

最初の引数は、baseline-prof.txt HRF のパスを表します。

profgen-cli には、APK のリリースビルドのパスと、R8 または Proguard を使用して APK を難読化するために使用される難読化マップも必要です。このようにして、コンパイル済みプロファイルの作成時に、profgen は HRF 内のソースシンボルを、対応する難読化された名前に変換できます。

ART プロファイルの形式には上位互換性も下位互換性もないため、プロファイルの形式を指定することにより、profgen がプロファイル メタデータ(profm)をパッケージ化して、ある ART プロファイルの形式を必要に応じて別の形式にコード変換する際に使用できるようにします。

プロファイルの形式とプラットフォームのバージョン

プロファイルの形式を選択する際は、以下のオプションを使用できます。

プロファイルの形式 プラットフォームのバージョン API レベル
v0_1_5_s Android S+ 31+
v0_1_0_p Android P、Q、R 28-30
v0_0_9_omr1 Android O MR1 27
v0_0_5_o Android O 26
v0_0_1_n Android N 24-25

出力ファイル baseline.profbaseline.profm を、APK の assets フォルダまたは dexopt フォルダにコピーします。

難読化マップ

難読化マップの指定は、HRF がソースシンボルを使用する場合にのみ必要です。難読化されたリリースビルドから HRF が生成され、マッピングが不要な場合は、このオプションを無視して、出力を assets フォルダまたは dexopt フォルダにコピーできます。

ベースライン プロファイルの従来のインストール

これまで、ベースライン プロファイルは以下の 2 つの方法のいずれかでデバイスに配布されています。

install-multiple と DexMetadata を使用する

API 28 以降を搭載したデバイスでは、Play クライアントは、インストールされている APK バージョンの APK と DexMetadata(DM)ペイロードをダウンロードします。DM には、デバイスのパッケージ管理システムに渡されるプロファイル情報が含まれています。

APK と DM は、次のような単一のインストール セッションの一部としてインストールされます。

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

API レベル 29 以降では、Jetpack ProfileInstaller ライブラリが代替メカニズムを使用して、APK がデバイスにインストールされた後に、assets または dexopt にパッケージ化されたプロファイルをインストールします。ProfileInstaller は、ProfileInstallReceiver から呼び出されるか、アプリから直接呼び出されます。

ProfileInstaller ライブラリは、対象デバイスの SDK バージョンに基づいてプロファイルをコード変換し、そのプロファイルをデバイスの cur ディレクトリ(デバイス上の ART プロファイル用のパッケージ固有のステージング ディレクトリ)にコピーします。

デバイスがアイドル状態になると、デバイス上の bg-dexopt というプロセスによってプロファイルが取得されます。

ベースライン プロファイルをサイドローディングする

このセクションでは、APK がある場合にベースライン プロファイルをインストールする方法について説明します。

androidx.profileinstaller でブロードキャストする

API 24 以降を搭載しているデバイスでは、プロファイルをインストールするコマンドをブロードキャストできます。

# Broadcast the install profile command - moves binary profile from assets
#     to a location where ART uses it for the next compile.
#     When successful, the following command prints "1":
adb shell am broadcast \
    -a androidx.profileinstaller.action.INSTALL_PROFILE \
    <pkg>/androidx.profileinstaller.ProfileInstallReceiver

# Kill the process
am force-stop <pkg>

# Compile the package based on profile
adb shell cmd package compile -f -m speed-profile <pkg>

ProfileInstaller は、ベースライン プロファイルを使用するほとんどの APK(Play の 45 万件のアプリのうち約 77,000 件)には含まれていませんが、Compose を使用するほぼすべての APK に存在します。これは、ライブラリが ProfileInstaller への依存関係を宣言せずにプロファイルを提供できるためです。プロファイルを使用して各ライブラリに依存関係を追加する場合、Jetpack から適用されます。

profgen または DexMetaData で install-multiple を使用する

API 28 以降を搭載したデバイスでは、アプリに ProfileInstaller ライブラリがなくても、ベースライン プロファイルをサイドローディングできます。

そのためには、Profgen-cli を使用します。

profgen extractProfile \
        --apk app-release.apk \
        --output-dex-metadata app-release.dm \
        --profile-format V0_1_5_S # Select based on device and the preceding table.

# Install APK and the profile together
adb install-multiple appname-release.apk appname-release.dm

APK の分割をサポートするには、上記のプロファイル抽出手順を APK ごとに 1 回ずつ実行します。インストール時に、各 APK と関連 .dm ファイルを渡して、APK と .dm の名前が一致していることを確認します。

adb install-multiple appname-base.apk appname-base.dm \
appname-split1.apk appname-split1.dm

検証

プロファイルが正しくインストールされているかどうかは、アプリの改善を手動で測定するの手順で確認できます。

バイナリ プロファイルの内容をダンプする

ベースライン プロファイルのコンパクト バイナリ バージョンの内容をイントロスペクトするには、Profgen-cli の dumpProfile オプションを使用します。

Usage: profgen dumpProfile options_list
Options:
    --profile, -p -> File path to the binary profile (always required)
    --apk, -a -> File path to apk (always required) { String }
    --map, -m -> File path to name obfuscation map { String }
    --strict, -s [true] -> Strict mode
    --output, -o -> File path for the HRF (always required) { String }
    --help, -h -> Usage info

dumpProfile で APK が必要となるのは、コンパクト バイナリ表現には DEX オフセットのみが保存され、そのためクラス名とメソッド名を再構成する必要があるからです。

厳格モードはデフォルトで有効になっています。これにより、APK 内の DEX ファイルに対してプロファイルの互換性チェックが実行されます。別のツールで生成されたプロファイルをデバッグしようとすると、互換性エラーが発生し、調査用にダンプできない可能性があります。そのような場合は、--strict false を使用して厳格モードを無効にできます。ただし、ほとんどの場合、厳格モードは有効にしておく必要があります。

難読化マップは省略可能です。指定すると、難読化されたシンボルが人間が読み取れるバージョンに再マッピングされ、簡単に使用できるようになります。