Macrobenchmark を作成する

Macrobenchmark ライブラリは、アプリの起動や複雑な UI 操作(RecyclerView のスクロールやアニメーションの実行など)のようなアプリ内の比較的大きな領域に対応するユースケースをテストするために使用します。比較的小さな領域のコードをテストする場合は、Microbenchmark ライブラリを参照してください。このページでは、Macrobenchmark ライブラリの設定方法について説明します。

ライブラリは、Android Studio コンソールと JSON ファイルの両方に詳細情報付きのベンチマーク結果を出力します。Android Studio に読み込んで分析できるトレース ファイルも提供します。

継続的インテグレーションでベンチマークを実行するで説明しているように、継続的インテグレーション(CI)環境で Macrobenchmark ライブラリを使用します。

Macrobenchmark を使用してベースライン プロファイルを生成できます。最初に Macrobenchmark ライブラリを設定すると、ベースライン プロファイルを作成できます。

プロジェクトの設定

Android Studio の最新バージョンの IDE は Macrobenchmark を統合する新機能を備えているため、Macrobenchmark はこのバージョンで使用することをおすすめします。

Macrobenchmark モジュールを設定する

Macrobenchmark では、アプリを測定するテストを実行するために、アプリコードとは別個の com.android.test モジュールが必要です。

Android Studio には、Macrobenchmark モジュールの設定を簡素化するテンプレートが用意されています。ベンチマーク モジュール テンプレートを使用すると、アプリ モジュールによってビルドされたアプリを測定するためのモジュール(サンプル起動ベンチマークなど)が、プロジェクト内に自動的に作成されます。

モジュール テンプレートを使用して新しいモジュールを作成する手順は次のとおりです。

  1. Android Studio の [Project] パネルでプロジェクトまたはモジュールを右クリックして、[New] > [Module] を選択します。

  2. [Templates] ペインで [Benchmark] を選択します。ターゲット アプリ(ベンチマーク対象のアプリ)に加えて、新しい Macrobenchmark モジュールのパッケージ名とモジュール名をカスタマイズできます。

  3. [Finish] をクリックします。

ベンチマーク モジュール テンプレート

図 1. ベンチマーク モジュール テンプレート。

アプリを設定する

アプリ(Macrobenchmark の「ターゲット」)のベンチマークを行うには、そのアプリを profileable にして、パフォーマンスを損なうことなく、詳細なトレース情報を読み取れるようにする必要があります。モジュール ウィザードを使用すると、アプリの AndroidManifest.xml ファイルに <profileable> タグが自動的に追加されます。

ターゲット アプリに ProfilerInstaller 1.3 以降が含まれていることを確認します。これは、Macrobenchmark ライブラリがプロファイルのキャプチャとリセット、およびシェーダー キャッシュの消去を有効にするために必要です。

できる限りリリース バージョン(製品版)に近くなるようにベンチマーク対象のアプリを構成します。 アプリはデバッグ不可として設定します。パフォーマンスを高めるために、圧縮をオンにすることをおすすめします。そのためには、通常、リリース バリアントのコピーを作成します。これは同じ処理を行いますが、デバッグキーを使用してローカルで署名されます。または、initWith を使用して Gradle に処理を指示することもできます。

Kotlin

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
    }

    create("benchmark") {
        initWith(getByName("release"))
        signingConfig = signingConfigs.getByName("debug")
    }
}

Groovy

buildTypes {
    val release = getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
        // In real app, this would use its own release keystore
        signingConfig = signingConfigs.getByName("debug")
    }

    create("benchmark") {
        initWith(release)
        signingConfig = signingConfigs.getByName("debug")
        proguardFiles("benchmark-rules.pro")
    }
}

図 2 に示すように、ベンチマークが実行されてアプリの正しいバリアントが作成およびテストされるようにするには、次の操作を行います。

  1. Gradle 同期を実行します。
  2. [Build Variants] パネルを開きます。
  3. アプリと Macrobenchmark モジュールの両方のベンチマーク バリアントを選択します。

ベンチマーク バリアントを選択する

図 2. ベンチマーク バリアントを選択します。

(省略可)マルチモジュール アプリを設定する

アプリに複数の Gradle モジュールがある場合は、コンパイルするビルド バリアントをビルド スクリプトが認識できるようにします。:macrobenchmark および :app モジュールの benchmark ビルドタイプに matchingFallbacks プロパティを追加します。残りの Gradle モジュールについては、以前と同じ構成で問題ありません。

Kotlin

create("benchmark") {
    initWith(getByName("release"))
    signingConfig = signingConfigs.getByName("debug")

    matchingFallbacks += listOf("release")
}

Groovy

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
}

そうしないと、新しく追加された benchmark ビルドタイプが原因でビルドが失敗し、次のエラー メッセージが表示されます。

> Could not resolve project :shared.
     Required by:
         project :app
      > No matching variant of project :shared was found.
      ...

プロジェクト内のビルド バリアントを選択する際は、:app:macrobenchmark の各モジュールには benchmark を、アプリ内の他のモジュールには release を選択します。図 3 をご覧ください。

release と benchmark のビルドタイプが選択されたマルチモジュール プロジェクトのベンチマーク バリアント

図 3. release と benchmark のビルドタイプが選択されたマルチモジュール プロジェクトのベンチマーク バリアント。

詳細については、バリアント認識型の依存関係管理を使用するをご覧ください。

(省略可)プロダクト フレーバーを設定する

アプリに複数のプロダクト フレーバーを設定している場合は、:macrobenchmark モジュールを構成して、ビルドとベンチマークを行うアプリのプロダクト フレーバーをモジュールが認識できるようにします。

この例では、次のスニペットに示すように、:app モジュールで 2 つのプロダクト フレーバー(demoproduction)を使用しています。

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
        // ...
    }
    create("production") {
        dimension = "environment"
        // ...
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
        // ...
    }

    production {
        dimension 'environment'
        // ...
    }
}

この構成を行わないと、複数の Gradle モジュールがある場合と似たビルドエラーが発生する可能性があります。

Could not determine the dependencies of task ':macrobenchmark:connectedBenchmarkAndroidTest'.
> Could not determine the dependencies of null.
   > Could not resolve all task dependencies for configuration ':macrobenchmark:benchmarkTestedApks'.
      > Could not resolve project :app.
        Required by:
            project :macrobenchmark
         > The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'benchmark', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.3.0'. However we cannot choose between the following variants of project :app:
             - demoBenchmarkRuntimeElements
             - productionBenchmarkRuntimeElements
           All of them match the consumer attributes:
           ...

以下の 2 つのセクションでは、複数のプロダクト フレーバーでベンチマークを構成する方法について説明します。

missingDimensionStrategy を使用する

:macrobenchmark モジュールの defaultConfigmissingDimensionStrategy を指定すると、ビルドシステムに対してフレーバー ディメンションにフォールバックするよう指示できます。モジュールにディメンションが見つからない場合は、使用するディメンションを指定します。次の例では、デフォルトのディメンションとして production フレーバーを使用しています。

Kotlin

defaultConfig {
    missingDimensionStrategy("environment", "production")
}

Groovy

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

このように、:macrobenchmark モジュールは指定されたプロダクト フレーバーのビルドとベンチマークのみを行うことができます。これは、ベンチマーク対象の適切な構成を持つプロダクト フレーバーが 1 つのみであることがわかっている場合に便利です。

:macrobenchmark モジュールでプロダクト フレーバーを定義する

他のプロダクト フレーバーをビルドしてベンチマークを行う場合は、:macrobenchmark モジュールでそれらを定義します。指定方法は :app モジュールの場合と似ていますが、productFlavorsdimension に割り当てるだけです。その他の設定は必要ありません。

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
    }
    create("production") {
        dimension = "environment"
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
    }

    production {
        dimension 'environment'
    }
}

プロジェクトを定義して同期したら、図 4 のように、[Build Variants] ペインから該当するビルド バリアントを選択します。

productionBenchmark と release が選択された、プロダクト フレーバーを含むプロジェクトのベンチマーク バリアント

図 4. productionBenchmark と release が選択された、プロダクト フレーバーがあるプロジェクトのベンチマーク バリアント

詳しくは、バリアント マッチングに関連するビルドエラーを解決するをご覧ください。

Macrobenchmark クラスを作成する

ベンチマーク テストは、Macrobenchmark ライブラリの MacrobenchmarkRule JUnit4 Rule API によって提供されます。この API には、ターゲット アプリを実行してベンチマークを行う方法についてさまざまな条件を指定できる measureRepeated メソッドが含まれています。

少なくとも、ターゲット アプリの packageName、測定する metrics、ベンチマークの実行対象である iterations の数を指定する必要があります。

Kotlin

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

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = TARGET_PACKAGE,
        metrics = listOf(StartupTimingMetric()),
        iterations = DEFAULT_ITERATIONS,
        setupBlock = {
            // Press home button before each run to ensure the starting activity isn't visible.
            pressHome()
        }
    ) {
        // starts default launch activity
        startActivityAndWait()
    }
}

Java

@LargeTest
@RunWith(AndroidJUnit4.class)
public class SampleStartupBenchmark {
    @Rule
    public MacrobenchmarkRule benchmarkRule = new MacrobenchmarkRule();

    @Test
    public void startup() {
        benchmarkRule.measureRepeated(
            /* packageName */ TARGET_PACKAGE,
            /* metrics */ Arrays.asList(new StartupTimingMetric()),
            /* iterations */ 5,
            /* measureBlock */ scope -> {
                // starts default launch activity
                scope.startActivityAndWait();
                return Unit.INSTANCE;
            }
        );
    }
}

ベンチマークをカスタマイズに関するすべてのオプションについては、ベンチマークをカスタマイズするセクションをご覧ください。

ベンチマークを実行する

Android Studio 内からテストを実行して、デバイス上のアプリのパフォーマンスを測定します。図 5 に示すように、テストクラスまたはテストメソッドの横にあるガター アクションを使用して、他の @Test を実行する場合と同じようにベンチマークを実行できます。

テストクラスの横にあるガター アクションを使用して Macrobenchmark を実行する

図 5. テストクラスの横にあるガター アクションを使用して Macrobenchmark を実行する

または、コマンドラインから connectedCheck コマンドを実行して、Gradle モジュール内のすべてのベンチマークを実行することもできます。

./gradlew :macrobenchmark:connectedCheck

以下を実行すると、1 件のテストを実行できます。

./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startup

継続的インテグレーションでベンチマークを実行およびモニタリングする方法については、継続的インテグレーションでのベンチマークをご覧ください。

ベンチマークの結果

ベンチマークの実行が成功すると、指標は Android Studio 内に直接表示されます。また、JSON ファイルの CI 使用状況に出力されます。測定される反復処理は、それぞれが個別のシステム トレースをキャプチャします。トレース結果を開くには、図 6 のように、[Test Results] ペインにあるリンクをクリックします。

Macrobenchmark の起動結果

図 6. Macrobenchmark の起動結果.。

トレースが読み込まれると、Android Studio によって、分析するプロセスの選択を求められます。図 7 のように、選択フィールドにはターゲット アプリのプロセスが事前入力されます。

Studio トレース プロセスの選択

図 7. Studio トレース プロセスの選択。

トレース ファイルが読み込まれると、Studio は CPU Profiler ツールで結果を表示します。

Studio トレース

図 8. Studio トレース。

JSON レポートとプロファイリング トレースも、デバイスからホストに自動的にコピーされます。これらはホストマシンの次の場所に書き込まれます。

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/

手動でトレース ファイルにアクセスする

Perfetto ツールを使用してトレース ファイルを分析する場合は、追加の手順が必要です。Perfetto では、トレース中にデバイスで実行されているすべてのプロセスを検査できます。これに対して、Android Studio の CPU Profiler では、検査は単一のプロセスに限定されます。

Android Studio から、または Gradle コマンドラインからテストを呼び出した場合、トレース ファイルはデバイスからホストに自動的にコピーされます。これらはホストマシンの次の場所に書き込まれます。

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace

ホストシステムでトレース ファイルが準備できたら、Android Studio の [File] > [Open] メニューからトレース ファイルを開くことができます。前のセクションで示したプロファイラ ツールのビューが表示されます。

構成エラー

アプリが誤ってデバッグ可能またはプロファイル不可に構成されている場合、Macrobenchmark は不正確または不完全な測定をレポートするのではなく、エラーをスローします。androidx.benchmark.suppressErrors 引数を使用すると、このようなエラーの出力を抑制できます。

エミュレータまたは電池容量の少ないデバイスで測定しようとした場合も、コアの可用性とクロック速度が損なわれる可能性があるため、Macrobenchmark はエラーを返します。

ベンチマークをカスタマイズする

measureRepeated 関数は、ライブラリが収集する指標、アプリの起動とコンパイルの方法、ベンチマークの実行対象となる反復処理の数に影響を及ぼすさまざまなパラメータを受け取ります。

指標をキャプチャする

指標は、ベンチマークから抽出される主要な情報タイプです。以下の指標を選択できます。

指標について詳しくは、Macrobenchmark の指標をキャプチャするをご覧ください。

カスタム イベントを使用してトレースデータを改善する

場合によっては、カスタム トレース イベントを使用したアプリのインストルメント化が有用です。カスタム トレース イベントは他のトレース レポートに表示され、アプリ固有の問題を特定するために役立ちます。カスタム トレース イベントの作成方法については、カスタム イベントを定義するをご覧ください。

CompilationMode

Macrobenchmark では、CompilationMode を指定して、アプリのどのくらいの容量を DEX バイトコード(APK 内のバイトコード形式)からマシンコード(プリコンパイル後の C++ のようなコード)にプリコンパイルするかを定義できます。

デフォルトでは、Macrobenchmark は CompilationMode.DEFAULT で実行されます。この場合、Android 7(API レベル 24)以降ではベースライン プロファイル(利用可能な場合)がインストールされます。Android 6(API レベル 23)以前では、このコンパイル モードの場合、デフォルトのシステム動作として APK はフルコンパイルされます。

ターゲット アプリにベースライン プロファイルと ProfileInstaller ライブラリの両方が含まれている場合は、ベースライン プロファイルをインストールできます。

Android 7 以降では、CompilationMode をカスタマイズすることにより、デバイス上のプリコンパイルの量を調整して、さまざまなレベルの事前(AOT)コンパイルまたは JIT キャッシュ処理を模倣できます。CompilationMode.FullCompilationMode.PartialCompilationMode.NoneCompilationMode.Ignore をご覧ください。

この機能は、ART コンパイル コマンドをベースにして構築されています。ベンチマーク間の干渉が起こらないようにするため、各ベンチマークは開始前にプロファイル データを消去します。

StartupMode

アクティビティの起動を実行するには、事前定義された起動モード(COLDWARMHOT のいずれか)を渡します。このパラメータにより、アクティビティの起動方法と、テスト開始時のプロセス状態が変更されます。

起動のタイプについて詳しくは、アプリの起動時間をご覧ください。

サンプル

サンプル プロジェクトは、GitHub のリポジトリの Macrobenchmark のサンプルにあります。

フィードバックを送信

Jetpack Macrobenchmark に関する問題の報告または機能リクエストの送信については、公開されている Issue Trackerをご利用ください。