Macrobenchmark を使用してアプリのパフォーマンスを調査する

1. 始める前に

この Codelab では、Macrobenchmark ライブラリの使用方法を学びます。ユーザー エンゲージメントの重要な指標であるアプリの起動時間と、アプリでジャンクが発生する場所を示すヒントとなるフレーム時間を測定します。

必要なもの

演習内容

  • 既存のアプリにベンチマーク モジュールを追加する
  • アプリの起動時間とフレーム時間を測定する

学習内容

  • アプリケーションのパフォーマンスを確実に測定する

2. 設定方法

最初に、コマンドラインで次のコマンドを使用して、GitHub リポジトリのクローンを作成します。

$ git clone https://github.com/android/codelab-android-performance.git

または、次の 2 つの zip ファイルをダウンロードします。

Android Studio でプロジェクトを開く

  1. [Welcome to Android Studio] ウィンドウで、c01826594f360d94.png [Open an Existing Project] を選択します。
  2. [Download Location]/android-performance/benchmarking フォルダを選択します(ヒント: build.gradle が格納されている benchmarking ディレクトリを選択してください)。
  3. Android Studio にプロジェクトがインポートされたら、app モジュールを実行して、ベンチマーク対象のサンプルアプリをビルドできることを確認します。

3. Jetpack Macrobenchmark の概要

Jetpack Macrobenchmark ライブラリは、起動、UI の操作、アニメーションなどの大規模なエンドユーザー インタラクションのパフォーマンスを測定します。このライブラリでは、テスト対象のパフォーマンス環境を直接制御できます。アプリのコンパイル、起動、停止を制御して、アプリの起動時間、フレーム時間、トレースされるコード セクションを直接測定できます。

Jetpack Macrobenchmark では、次のことができます。

  • 確定的な起動パターンとスクロール速度でアプリを複数回測定する
  • 複数のテスト実行の結果を平均化して、パフォーマンスのばらつきをならす
  • パフォーマンスの安定性に大きく影響する要素であるアプリのコンパイル状態を制御する
  • Google Play ストアで行われるインストール時の最適化をローカルで再現して、実際のパフォーマンスを確認する

このライブラリを使用したインストルメンテーションでは、アプリケーション コードを直接呼び出す代わりに、タッチ、クリック、スワイプなどのユーザー操作でアプリ内を移動します。測定は、これらの操作中にデバイス上で行われます。アプリケーション コードの一部を直接測定する場合は、Jetpack Microbenchmark をご覧ください。

ベンチマークの作成はインストルメンテーション テストの作成と似ていますが、アプリの状態を検証する必要はありません。ベンチマークでは JUnit 構文(@RunWith@Rule@Test など)を使用しますが、テストは別個のプロセスで実行されるため、アプリを再起動またはプリコンパイルできます。これにより、ユーザーが操作するときと同様に、アプリの内部状態に干渉することなくアプリを実行できます。そのためには、UiAutomator を使用してターゲット アプリを操作します。

サンプルアプリ

この Codelab では、JetSnack サンプルアプリを使用します。これは、Jetpack Compose を使用するオンライン スナック注文アプリです。アプリのパフォーマンスを測定するために、アプリの設計構造を詳細に知る必要はありません。知る必要があるのは、アプリの動作と UI の構造です。これは、ベンチマークから UI 要素にアクセスするために必要です。アプリを実行し、さまざまなスナックを注文して、基本的な画面に慣れてください。

a1f684feb2456079.png

4. Macrobenchmark ライブラリを追加する

Macrobenchmark を使用するには、新しい Gradle モジュールをプロジェクトに追加する必要があります。モジュールをプロジェクトに追加する最も簡単な方法は、Android Studio モジュール ウィザードを使用することです。

新しいモジュールのダイアログを開きます([Project] パネルでプロジェクトまたはモジュールを右クリックして、[New] > [Module] を選択します)。

4ce8ef7fa59b41b8.png

[Templates] ペインで [Benchmark] を選択し、ベンチマーク モジュール タイプとして [Macrobenchmark] が選択されていることと、詳細が想定どおりであることを確認します。

24884734761bcfed.png

  • Target application – ベンチマーク対象のアプリ
  • Module name – ベンチマーク用の Gradle モジュールの名前
  • Package name – ベンチマークのパッケージ名
  • Minimum SDK – Android 6(API レベル 23)以上が必要

[完了] をクリックします。

モジュール ウィザードによる変更

モジュール ウィザードにより、以下のようないくつかの変更がプロジェクトに加えられます。

macrobenchmark という名前の Gradle モジュール(またはウィザードで選択した名前)が追加されます。このモジュールは com.android.test プラグインを使用します。このプラグインはそれをアプリに含めないように Gradle に指示するので、テストコード(つまりベンチマーク)のみが含められます。

また、選択されたターゲット アプリ モジュールにも変更が加えられます。具体的には、次のスニペットに示すように、新しい benchmark ビルドタイプが :app モジュールの build.gradle.kts に追加されます。

create("benchmark") {
   initWith(buildTypes.getByName("release"))
   matchingFallbacks += listOf("release")
   isDebuggable = false
}

この buildType は、release buildType を可能な限りエミュレートしたものです。release buildType との違いは、signingConfigdebug に設定されていることです。これは、本番環境のキーストアがなくてもローカルでアプリをビルドできるようにするために必要です。

ただし、debuggable フラグが無効になっているため、ウィザードは AndroidManifest.xml<profileable> タグを追加します。これにより、ベンチマークはアプリのリリース パフォーマンスをプロファイリングできます。

<application>

  <profileable
     android:shell="true"
     tools:targetApi="29" />

</application>

<profileable> の機能について詳しくは、ドキュメントをご覧ください。

最後に、ウィザードは、起動時間のベンチマークを行うためのスキャフォールドを作成します(これは次のステップで使用します)。

以上で、ベンチマークの作成を開始する準備が整いました。

5. アプリの起動時間を測定する

アプリの起動時間(ユーザーがアプリの使用を開始するまでにかかる時間)は、ユーザー エンゲージメントに影響する重要な指標です。モジュール ウィザードにより、アプリの起動時間を測定できる ExampleStartupBenchmark テストクラスが作成されます。これは次のようなクラスです。

@RunWith(AndroidJUnit4::class)
class ExampleStartupBenchmark {
   @get:Rule
   val benchmarkRule = MacrobenchmarkRule()

   @Test
   fun startup() = benchmarkRule.measureRepeated(
       packageName = "com.example.macrobenchmark_codelab",
       metrics = listOf(StartupTimingMetric()),
       iterations = 5,
       startupMode = StartupMode.COLD,
   ){
        pressHome()
        startActivityAndWait()
   }
}

すべてのパラメータの意味を理解する

ベンチマークを作成する際の出発点は、MacrobenchmarkRulemeasureRepeated 関数です。この関数はベンチマークに関するすべてを処理しますが、以下のパラメータを指定する必要があります。

  • packageName - ベンチマークはテスト対象のアプリとは別のプロセスで実行されるため、測定対象のアプリを指定する必要があります。
  • metrics - ベンチマーク中に測定する情報のタイプ。この例では、アプリの起動時間です。他のタイプの指標については、ドキュメントをご覧ください。
  • iterations - ベンチマークを反復する回数。反復する回数が多いほど結果は安定しますが、それと引き換えに実行時間が長くなります。理想的な回数は、この指標がアプリでどの程度重要視されるかに応じて異なります。
  • startupMode - このパラメータでは、ベンチマークの開始時にアプリを起動する方法を定義できます。指定可能な値は COLDWARMHOT です。この例では、COLD を使用します。これは、アプリが行う必要がある作業の量が最大であることを意味します。
  • measureBlock(最後のラムダ パラメータ)– この関数で、ベンチマーク中に測定するアクション(アクティビティの開始、UI 要素のクリック、スクロール、スワイプなど)を定義します。Macrobenchmark は、定義された metrics をこのブロックで収集します。

ベンチマーク アクションの記述方法

Macrobenchmark はアプリを再インストールして再起動します。インタラクションはアプリの状態から独立したものとして記述してください。Macrobenchmark には、アプリを操作するための便利な関数とパラメータが用意されています。

最も重要な関数は startActivityAndWait() です。この関数は、デフォルトのアクティビティを開始し、アクティビティが最初のフレームをレンダリングするまで待ってから、ベンチマークの手順を続行します。別のアクティビティを開始する場合や開始インテントを微調整する場合は、オプションの intent または block パラメータを使用します。

もう一つの便利な関数は pressHome() です。この関数を使用すると、各反復処理でアプリを強制終了しない場合(StartupMode.HOT を使用している場合など)に、ベンチマークを基本状態にリセットできます。

それ以外のインタラクションについては、device パラメータを使用して、UI 要素を見つけたり、スクロールしたり、特定のコンテンツを待機したりできます。

以上で、起動ベンチマークの定義が完了しました。次のステップでは、それを実行します。

6. ベンチマークを実行する

ベンチマーク テストを実行する前に、Android Studio で適切なビルド バリアントが選択されていることを確認してください。

  1. [Build Variants] パネルを選択します。
  2. [Active Build Variant] を [benchmark] に変更します。
  3. Android Studio が同期を完了するまで待ちます。

4f47527b618433a.gif

上記の操作を行わなかった場合は、実行時にベンチマークが失敗し、debuggable アプリのベンチマークを行う必要はないというエラーが表示されます。

java.lang.AssertionError: ERRORS (not suppressed): DEBUGGABLE
WARNINGS (suppressed):

ERROR: Debuggable Benchmark
Benchmark is running with debuggable=true, which drastically reduces
runtime performance in order to support debugging features. Run
benchmarks with debuggable=false. Debuggable affects execution speed
in ways that mean benchmark improvements might not carry over to a
real user's experience (or even regress release performance).

インストルメンテーション引数 androidx.benchmark.suppressErrors = "DEBUGGABLE" を使用すると、一時的にこのエラーを抑制できます。Android Emulator でベンチマークを実行する場合と同じ手順を使用できます。

これで、インストルメンテーション テストと同じ方法でベンチマークを実行できるようになりました。テスト関数またはクラス全体を実行するには、関数またはクラスの横のガターアイコンをクリックします。

29c3b85f79816f17.png

物理デバイスが選択されていることを確認してください。Android Emulator でベンチマークを実行すると、実行時に失敗し、誤った結果が生成されるという警告が表示されます。技術的にはベンチマークをエミュレータで実行することは可能ですが、基本的にホストマシンのパフォーマンスを測定することになります。負荷が高ければベンチマークのパフォーマンスは低下し、負荷が低ければその逆になります。

335e1009223de4b3.png

ベンチマークを実行すると、アプリが再ビルドされ、アプリによってベンチマークが実行されます。ベンチマークは、定義された iterations に基づいて、アプリの開始、停止、さらには再インストールを複数回行います。

7. (省略可)Android Emulator でベンチマークを実行する

実機がない状態でベンチマークを実行する場合は、インストルメンテーション引数 androidx.benchmark.suppressErrors = "EMULATOR" を使用してランタイム エラーを抑制できます。

エラーを抑制するには、実行構成を編集します。

  1. 実行メニューから [Edit Configurations...] を選択します。5ed4cbeb68ec4c97.png
  2. 表示されたウィンドウで、[Instrumentation arguments] の横のオプション アイコン d628c071dd2bf454.png を選択します。89be2d51bf94c098.png
  3. ➕ をクリックして詳細を入力することにより、インストルメンテーション追加パラメータを追加します。a06c7f6359d6b92c.png
  4. [OK] をクリックして、選択を確定します。[Instrumentation arguments] の行に引数が表示されます。f0a8a7f54d47e5dc.png
  5. [OK] をクリックして、実行構成を確定します。

また、この構成をコードベースに永続的に保持する必要がある場合は、build.gradle:macrobenchmark モジュールで設定できます。

defaultConfig {
    // ...
    testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = 'EMULATOR'
}

8. 起動の結果を分析する

ベンチマークの実行が完了すると、次のスクリーンショットのように、直接 Android Studio 内に結果が表示されます。

a9bddcc8dced7879.png