このドキュメントでは、問題を診断して、ベースライン プロファイルが正しく機能し、最大限のメリットをもたらすようにするためのおすすめの方法とトラブルシューティングの手順を紹介します。
ビルドの問題
Now in Android サンプルアプリにあるベースライン プロファイルのサンプルをコピーした場合、ベースライン プロファイルのタスク中に、エミュレータではテストを実行できないことを示すテスト失敗が発生することがあります。
./gradlew assembleDemoRelease
Starting a Gradle Daemon (subsequent builds will be faster)
Calculating task graph as no configuration cache is available for tasks: assembleDemoRelease
Type-safe project accessors is an incubating feature.
> Task :benchmarks:pixel6Api33DemoNonMinifiedReleaseAndroidTest
Starting 14 tests on pixel6Api33
com.google.samples.apps.nowinandroid.foryou.ScrollForYouFeedBenchmark > scrollFeedCompilationNone[pixel6Api33] FAILED
java.lang.AssertionError: ERRORS (not suppressed): EMULATOR
WARNINGS (suppressed):
...
この失敗の原因は、Now in Android はベースライン プロファイルの生成に Gradle で管理されているデバイスを使用することにあります。一般的に、エミュレータではパフォーマンスのベンチマークを実行しないため、この失敗は想定内です。ただし、ベースライン プロファイルの生成時にパフォーマンスの指標を収集していないため、エミュレータで便宜上、ベースライン プロファイルの収集を実行することは可能です。エミュレータでベースライン プロファイルを使用するには、コマンドラインからビルドとインストールを行い、以下の引数を設定してベースライン プロファイルのルールを有効にします。
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
また、Android Studio で [Run] > [Edit Configurations] を選択して、エミュレータでベースライン プロファイルを有効にするためのカスタムの実行構成を作成することもできます。
プロファイルのインストールと適用を確認する
検査している APK または Android App Bundle(AAB)のビルド バリアントにベースライン プロファイルが含まれていることを確認するには、次の操作を行います。
- Android Studio で、[Build] > [Analyze APK] を選択します。
- AAB または APK を開きます。
baseline.profファイルが存在することを確認します。- AAB を検査している場合、プロファイルは
/BUNDLE-METADATA/com.android.tools.build.profiles/baseline.profにあります。 APK を検査している場合、プロファイルは
/assets/dexopt/baseline.profにあります。このファイルが存在することは、ビルド構成が正しいことを示す最初の兆候です。このファイルがない場合、インストール時に Android ランタイムが事前コンパイルの指示を受け取らないことを意味します。
図 2.Android Studio の APK Analyzer を使用してベースライン プロファイルがあるかどうかを確認する。
- AAB を検査している場合、プロファイルは
ベースライン プロファイルは、アプリを実行しているデバイスでコンパイルする必要があります。Android Studio または Gradle ラッパー コマンドライン ツールを使用してデバッグ不可ビルドをインストールすると、オンデバイスのコンパイルが自動的に行われます。Google Play ストアからアプリをインストールする場合、ベースライン プロファイルはインストール時ではなく、デバイスのバックグラウンド更新時にコンパイルされます。他のツールを使用してアプリがインストールされた場合は、Jetpack の ProfileInstaller ライブラリにより、次回のバックグラウンドの DEX 最適化プロセスの際にコンパイルのキューにプロファイルが追加されます。
この場合、ベースライン プロファイルが確実に使用されるようにするには、ベースライン プロファイルの強制コンパイルが必要になることがあります。以下の例に示すように、プロファイルのインストールとコンパイルのステータスは ProfileVerifier を使ってクエリできます。
Kotlin
private const val TAG = "MainActivity" class MainActivity : ComponentActivity() { ... override fun onResume() { super.onResume() lifecycleScope.launch { logCompilationStatus() } } private suspend fun logCompilationStatus() { withContext(Dispatchers.IO) { val status = ProfileVerifier.getCompilationStatusAsync().await() when (status.profileInstallResultCode) { RESULT_CODE_NO_PROFILE -> Log.d(TAG, "ProfileInstaller: Baseline Profile not found") RESULT_CODE_COMPILED_WITH_PROFILE -> Log.d(TAG, "ProfileInstaller: Compiled with profile") RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> Log.d(TAG, "ProfileInstaller: App was installed through Play store") RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST -> Log.d(TAG, "ProfileInstaller: PackageName not found") RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ -> Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read") RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE -> Log.d(TAG, "ProfileInstaller: Can't write cache file") RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") else -> Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued") } } }
Java
public class MainActivity extends ComponentActivity { private static final String TAG = "MainActivity"; @Override protected void onResume() { super.onResume(); logCompilationStatus(); } private void logCompilationStatus() { ListeningExecutorService service = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor()); ListenableFuture<ProfileVerifier.CompilationStatus> future = ProfileVerifier.getCompilationStatusAsync(); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(CompilationStatus result) { int resultCode = result.getProfileInstallResultCode(); if (resultCode == RESULT_CODE_NO_PROFILE) { Log.d(TAG, "ProfileInstaller: Baseline Profile not found"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE) { Log.d(TAG, "ProfileInstaller: Compiled with profile"); } else if (resultCode == RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING) { Log.d(TAG, "ProfileInstaller: App was installed through Play store"); } else if (resultCode == RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST) { Log.d(TAG, "ProfileInstaller: PackageName not found"); } else if (resultCode == RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ) { Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read"); } else if (resultCode == RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE) { Log.d(TAG, "ProfileInstaller: Can't write cache file"); } else if (resultCode == RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else { Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued"); } } @Override public void onFailure(Throwable t) { Log.d(TAG, "ProfileInstaller: Error getting installation status: " + t.getMessage()); } }, service); } }
以下の結果コードに基づいて、問題の原因を特定します。
RESULT_CODE_COMPILED_WITH_PROFILE- プロファイルはインストールされ、コンパイルされて、アプリの実行時に常に使用されます。これが、目指している結果です。
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED- 実行中の APK にプロファイルがありません。このエラーが表示された場合は、ベースライン プロファイルを含むビルド バリアントを使用していることと、APK にプロファイルが含まれていることを確認します。
RESULT_CODE_NO_PROFILE- アプリストアまたはパッケージ管理システムからアプリをインストールする際にこのアプリ用のプロファイルがインストールされませんでした。このエラーコードは主に、
ProfileInstallerInitializerが無効になっているためにプロファイル インストーラが実行されなかった場合に表示されます。なお、このエラーが報告されても、アプリの APK には埋め込みのプロファイルがあります。埋め込みのプロファイルがない場合のエラーコードはRESULT_CODE_ERROR_NO_PROFILE_EMBEDDEDです。 RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION- APK または AAB にプロファイルがあり、コンパイルのキューに登録されています。
ProfileInstallerによってインストールされたプロファイルは、次回のバックグラウンド DEX 最適化がシステムで実行されたときにコンパイルのキューに追加されます。コンパイルが完了するまでプロファイルは有効になりません。コンパイルが完了するまでは、ベースライン プロファイルのベンチマークを試行しないでください。ベースライン プロファイルの強制コンパイルが必要になることがあります。コンパイルはインストール中に行われるため、Android 9(API 28)以降を搭載したデバイスで Google Play ストアまたはパッケージ管理システムからアプリをインストールした場合、このエラーは発生しません。 RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING- 一致しないプロファイルがインストールされ、そのプロファイルでアプリがコンパイルされています。これは、Google Play ストアまたはパッケージ管理システムからのインストールの結果です。一致しないプロファイルでも、プロファイルとアプリ間で共有されているメソッドのみはコンパイルされるため、この結果は
RESULT_CODE_COMPILED_WITH_PROFILEとは異なります。このプロファイルは想定よりも実質的に小さく、コンパイルされるメソッドはベースライン プロファイルに含まれていたメソッドより少なくなります。 RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILEProfileVerifierが検証結果のキャッシュ ファイルを作成できません。アプリフォルダの権限に問題があるか、デバイスに十分なディスク空き容量がないかのいずれかの場合に発生する可能性があります。RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION- ProfileVerifier の
is running on an unsupported API version of Android. ProfileVerifierは Android 9(API レベル 28)以降でのみサポートされます。 RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST- アプリ パッケージで
PackageManagerをクエリするとPackageManager.NameNotFoundExceptionがスローされます。このエラーが発生することはまずありません。アプリをアンインストールしてから、すべて再インストールしてみてください。 RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ- 以前の検証結果のキャッシュ ファイルがありますが、読み取れません。このエラーが発生することはまずありません。アプリをアンインストールしてから、すべて再インストールしてみてください。
本番環境で ProfileVerifier を使用する
本番環境では、ProfileVerifier を Firebase 向け Google アナリティクスなどの分析レポート ライブラリと組み合わせて使用することで、プロファイルのステータスを示す分析イベントを生成できます。たとえば、ベースライン プロファイルを含まない新しいアプリ バージョンがリリースされた場合は、すぐに通知されます。
ベースライン プロファイルのコンパイルを強制的に行う
ベースライン プロファイルのコンパイル ステータスが RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION の場合は、adb を使用して即時コンパイルを強制的に実行できます。
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
ProfileVerifier を使用せずにベースライン プロファイルのコンパイル ステータスを確認する
ProfileVerifier を使用していない場合は、adb を使用してコンパイル ステータスを確認できます。ただし、ProfileVerifier ほど詳細な分析情報は得られません。
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
adb を使用すると、次のような結果が出力されます。
[com.google.samples.apps.nowinandroid.demo]
path: /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/base.apk
arm64: [status=speed-profile] [reason=bg-dexopt] [primary-abi]
[location is /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/oat/arm64/base.odex]
status 値はプロファイルのコンパイル ステータスを示します。以下のいずれかの値になります。
| コンパイル ステータス | 意味 |
|---|---|
speed‑profile |
コンパイル済みのプロファイルがあり、使用されている。 |
verify |
コンパイル済みのプロファイルがない。 |
verify ステータスは、APK または AAB にプロファイルが含まれていないという意味ではありません。この場合は、プロファイルは次回のバックグラウンド DEX 最適化タスクでコンパイルのキューに追加されます。
reason 値は、プロファイルがコンパイルされた状況を示します。以下のいずれかの値になります。
| 理由 | 意味 |
|---|---|
install‑dm
|
ベースライン プロファイルがアプリのインストール時に手動または Google Play によってコンパイルされた。 |
bg‑dexopt
|
デバイスがアイドル状態のときにプロファイルがコンパイルされた。これは、ベースライン プロファイルの場合もあれば、アプリの使用中に収集されたプロファイルの場合もあります。 |
cmdline
|
コンパイルは adb を使用してトリガーされた。これは、ベースライン プロファイルの場合もあれば、アプリの使用中に収集されたプロファイルの場合もあります。 |
起動プロファイルのアプリケーションから DEX と r8.json への適用を確認
起動プロファイル ルールは、ビルド時に R8 によって使用され、DEX ファイル内のクラスのレイアウトを最適化します。このビルド時の最適化は、ベースライン プロファイル(baseline.prof)の使用方法とは異なります。ベースライン プロファイルは、ART がデバイス上でコンパイルを実行するために APK または AAB 内にパッケージ化されます。スタートアップ プロファイル ルールはビルドプロセス自体で適用されるため、APK または AAB 内に検査する startup.prof ファイルは存在しません。起動プロファイルの効果は、DEX ファイルのレイアウトに反映されます。
r8.json で DEX の配置を検査する(AGP 8.8 以降で推奨)
Android Gradle プラグイン(AGP)8.8 以降を使用するプロジェクトでは、生成された r8.json ファイルを調べることで、スタートアップ プロファイルが適用されたかどうかを確認できます。このファイルは AAB 内にパッケージ化されます。
- AAB アーカイブを開き、
r8.jsonファイルを探します。 - 生成された DEX ファイルを一覧表示する
dexFiles配列をファイル内で検索します。 Key-Value ペア
"startup": trueを含むdexFilesオブジェクトを探します。これは、起動プロファイル ルールが適用され、その特定の DEX ファイルのレイアウトが最適化されたことを明示的に示しています。"dexFiles": [ { "checksum": "...", "startup": true // This flag confirms profile application to this DEX file }, // ... other DEX files ]
すべての AGP バージョンの DEX 配置を検査
AGP バージョンが 8.8 より低い場合は、DEX ファイルを検査することが、起動プロファイルが正しく適用されていることを確認する主な方法となります。AGP 8.8 以降を使用しており、DEX レイアウトを手動で確認したい場合にも、この方法を使用できます。たとえば、期待どおりのパフォーマンスの改善が見られない場合などです。DEX アレンジメントを検査するには、次の操作を行います。
- Android Studio で [Build] > [Analyze APK] を使用して AAB または APK を開きます。
- 最初の DEX ファイルに移動します。たとえば、
classes.dexです。 - この DEX ファイルの内容を検査します。スタートアップ プロファイル ファイル(
startup-prof.txt)で定義された重要なクラスとメソッドが、このプライマリ DEX ファイルに存在することを確認できるはずです。アプリケーションが成功すると、これらの起動に不可欠なコンポーネントの読み込みが優先されます。
パフォーマンスの問題
このセクションでは、ベースライン プロファイルを正しく定義してベンチマークを実行し、そのメリットを最大限に引き出すためのおすすめの方法について説明します。
起動時の指標のベンチマークを正しく実行する
起動時の指標が明確に定義されていれば、ベースライン プロファイルの効果は高まります。主な 2 つの指標は、初期表示までの時間(TTID)と完全表示までの時間(TTFD)です。
TTID はアプリが最初のフレームを描画する時点を指します。何かを表示するとは、アプリが実行されていることをユーザーに示すことであるため、可能な限り時間がかからないようにすることが重要です。アプリが応答していることを示す不確定形式の進行状況インジケーターを表示する方法もあります。
TTFD はアプリを実際に操作できるようになった時点を指します。ユーザーがフラストレーションを感じないよう、できる限り時間がかからないようにすることが重要です。TTFD を正しく通知すれば、TTFD までの間に実行されるコードがアプリの起動に関連するものであることをシステムに伝えることになり、結果として、そのコードがプロファイルに入る可能性が高くなります。
TTID と TTFD のどちらも可能な限り短くして、アプリが応答していると感じられるようにしてください。
システムは TTID を検出して Logcat に表示し、起動ベンチマークの一部として報告することはできますが、TTFD を判断することはできません。完全に描画された操作可能な状態に達したら、アプリが報告する必要があります。このためには、reportFullyDrawn() を呼び出すか、Jetpack Compose を使用している場合は ReportDrawn を呼び出します。アプリが完全に描画されたと見なされるまでにすべて完了する必要がある複数のバックグラウンド タスクがある場合は、FullyDrawnReporter を使用できます。これについては、起動時間の精度を改善するをご覧ください。
ライブラリ プロファイルとカスタム プロファイル
プロファイルの影響をベンチマークする際に、アプリのプロファイルのメリットと、Jetpack ライブラリなどのライブラリが提供するプロファイルのメリットを分離することは困難な場合があります。APK をビルドすると、Android Gradle プラグインは、ライブラリの依存関係にあるプロファイルとカスタム プロファイルを追加します。これは全体的なパフォーマンスの最適化に役立ち、リリースビルドに推奨されます。ただし、カスタム プロファイルによるパフォーマンスの向上を測定することは困難になります。
カスタム プロファイルによって提供される追加の最適化を手動で確認する簡単な方法は、カスタム プロファイルを削除してベンチマークを実行することです。その後、置き換えてベンチマークを再度実行します。この 2 つを比較すると、ライブラリ プロファイルのみで提供される最適化と、ライブラリ プロファイルとカスタム プロファイルの両方で提供される最適化を確認できます。
プロファイルを自動的に比較する方法としては、ライブラリ プロファイルのみを含み、カスタム プロファイルを含まない新しいビルド バリアントを作成する方法があります。このバリエーションのベンチマークを、ライブラリ プロファイルとカスタム プロファイルの両方を含むリリース バリエーションと比較します。次の例は、ライブラリ プロファイルのみを含むバリアントを設定する方法を示しています。通常はアプリ モジュールであるプロファイル コンシューマ モジュールに、releaseWithoutCustomProfile という名前の新しいバリアントを追加します。
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile(project(":baselineprofile")) } baselineProfile { variants { create("release") { from(project(":baselineprofile")) } } }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile ':baselineprofile"' } baselineProfile { variants { release { from(project(":baselineprofile")) } } }
上記のコード例では、すべてのバリアントから baselineProfile 依存関係を削除し、release バリアントにのみ選択的に適用しています。プロファイル プロデューサー モジュールへの依存関係が削除されたときに、ライブラリ プロファイルが引き続き追加されるのは、直感に反するように思えるかもしれません。ただし、このモジュールはカスタム プロファイルの生成のみを行います。Android Gradle プラグインはすべてのバリアントで実行され、ライブラリ プロファイルを含める役割を担っています。
また、新しいバリエーションをプロファイル ジェネレータ モジュールに追加する必要があります。この例では、プロデューサー モジュールの名前は :baselineprofile です。
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") {} ... } ... }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile {} ... } ... }
Android Studio からベンチマークを実行する場合は、releaseWithoutCustomProfile バリアントを選択してライブラリ プロファイルのみでパフォーマンスを測定するか、release バリアントを選択してライブラリ プロファイルとカスタム プロファイルでパフォーマンスを測定します。
I/O バウンドのアプリの起動を避ける
アプリが起動時に実行する I/O 呼び出しやネットワーク呼び出しが多いと、アプリの起動時間と起動ベンチマークの精度の両方に悪影響が及ぶ可能性があります。これらの重量級の呼び出しは所要時間が不確定で、時間の経過とともに変わる可能性があります。またベンチマークが同じでも反復処理ごとに変わる可能性もあります。ネットワーク呼び出しは、デバイスの外部要因やデバイス自体の要因の影響を受ける可能性があるため、どちらかと言えば I/O 呼び出しの方がネットワーク呼び出しよりましです。起動時のネットワーク呼び出しは回避し、どちらか一方の使用が避けられない場合は、I/O を使用してください。
起動ベンチマークを実行する場合のみ使用するとしても、アプリ アーキテクチャで、ネットワーク呼び出しや I/O 呼び出しのないアプリの起動をサポートすることをおすすめします。これにより、ベンチマークの反復処理間のばらつきを最小限に抑えることができます。
アプリで Hilt を使用している場合は、Microbenchmark と Hilt でベンチマークを行うときに、I/O バウンドのフェイク実装を提供できます。
重要なユーザー ジャーニーをすべてカバーする
重要なユーザー ジャーニーはすべて、ベースライン プロファイルの生成で正確にカバーすることが大切です。カバーされていないユーザー ジャーニーは、ベースライン プロファイルで改善されません。一般的な起動のユーザー ジャーニーとパフォーマンス重視のアプリ内ユーザー ジャーニー(リストのスクロールなど)を含むベースライン プロファイルが、最も効果的です。
A/B テストのコンパイル時プロファイルの変更
起動プロファイルとベースライン プロファイルはコンパイル時の最適化であるため、Google Play ストアを使用してさまざまな APK を直接 A/B テストすることは、一般的に本番環境リリースではサポートされていません。本番環境に近い環境で影響を評価するには、次のアプローチを検討してください。
オフサイクルのリリース: プロファイルの変更のみを含むオフサイクルのリリースを、ユーザーベースのごく一部にアップロードします。これにより、パフォーマンスの違いに関する実際の指標を収集できます。
ローカル ベンチマーク: プロファイルを適用した場合と適用しない場合で、アプリのローカル ベンチマークを行います。ただし、ローカル ベンチマークは、本番環境のデバイスに存在する ART の Cloud Profiles の影響を含まないため、プロファイルのベストケース シナリオを示すことに注意してください。