Mikro-Benchmark schreiben

Informationen zur Verwendung der MicroBenchmark-Bibliothek durch Hinzufügen von Änderungen zu Ihrem Anwendungscode finden Sie im Abschnitt Kurzanleitung. Informationen zum vollständigen Einrichten mit komplexeren Änderungen an Ihrer Codebasis finden Sie im Abschnitt Vollständige Projekteinrichtung.

Kurzanleitung

In diesem Abschnitt wird gezeigt, wie Sie Benchmarking ausprobieren und einmalige Messungen ausführen können, ohne Code in Module verschieben zu müssen. Um genaue Leistungsergebnisse zu erhalten, muss mit diesen Schritten die Fehlerbehebung in der Anwendung deaktiviert werden. Bewahren Sie dies in einer lokalen Arbeitskopie auf, ohne die Änderungen in das Versionsverwaltungssystem zu übernehmen.

So führen Sie ein einmaliges Benchmarking durch:

  1. Fügen Sie die Bibliothek der Datei build.gradle oder build.gradle.kts des Moduls hinzu:

    Kotlin

    dependencies {
        implementation("androidx.benchmark:benchmark-junit4:1.2.3")
    }
    

    Groovig

    dependencies {
        implementation 'androidx.benchmark:benchmark-junit4:1.2.3'
    }
    

    Verwenden Sie eine implementation-Abhängigkeit anstelle einer androidTestImplementation-Abhängigkeit. Wenn Sie androidTestImplementation verwenden, können die Benchmarks nicht ausgeführt werden, da das Manifest der Bibliothek nicht mit dem App-Manifest zusammengeführt wird.

  2. Aktualisieren Sie den Build-Typ debug so, dass er nicht debuggt werden kann:

    Kotlin

    android {
        ...
        buildTypes {
            debug {
                isDebuggable = false
            }
        }
    }
    

    Groovig

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
    
  3. Ändern Sie testInstrumentationRunner zu AndroidBenchmarkRunner:

    Kotlin

    android {
        ...
        defaultConfig {
            testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
    

    Groovig

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
    
  4. Fügen Sie eine Instanz von BenchmarkRule in eine Testdatei im Verzeichnis androidTest ein, um die Benchmark hinzuzufügen. Weitere Informationen zum Schreiben von Benchmarks finden Sie unter MicroBenchmark-Klasse erstellen.

    Das folgende Code-Snippet zeigt, wie Sie einem instrumentierten Test eine Benchmark hinzufügen:

    Kotlin

    @RunWith(AndroidJUnit4::class)
    class SampleBenchmark {
        @get:Rule
        val benchmarkRule = BenchmarkRule()
    
        @Test
        fun benchmarkSomeWork() {
            benchmarkRule.measureRepeated {
                doSomeWork()
            }
        }
    }
    

    Java

    @RunWith(AndroidJUnit4.class)
    class SampleBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
        @Test
        public void benchmarkSomeWork() {
                BenchmarkRuleKt.measureRepeated(
                    (Function1<BenchmarkRule.Scope, Unit>) scope -> doSomeWork()
                );
           }
        }
    }
    

Weitere Informationen zum Schreiben einer Benchmark erhalten Sie unter MicroBenchmark-Klasse erstellen.

Vollständige Projekteinrichtung

Um ein regelmäßiges anstatt einmaliges Benchmarking einzurichten, isolieren Sie Benchmarks in ein eigenes Modul. So wird sichergestellt, dass die Konfiguration, z. B. das Festlegen von debuggable auf false, von regulären Tests getrennt ist.

Microbenchmark führt Ihren Code direkt aus. Platzieren Sie den zu vergleichenden Code daher in einem separaten Gradle-Modul und legen die Abhängigkeit von diesem Modul fest, wie in Abbildung 1 gezeigt.

App-Struktur
Abbildung 1: App-Struktur mit den Gradle-Modulen :app, :microbenchmark und :benchmarkable, mit denen Microbenchmarks Code im :benchmarkable-Modul Benchmarks erstellen kann.

Um ein neues Gradle-Modul hinzuzufügen, können Sie den Modulassistenten in Android Studio verwenden. Der Assistent erstellt ein Modul, das für das Benchmarking vorkonfiguriert ist. Dabei wird ein Benchmarkverzeichnis hinzugefügt und debuggable auf false gesetzt.

  1. Klicken Sie in Android Studio im Steuerfeld Projekt mit der rechten Maustaste auf Ihr Projekt oder Modul und dann auf Neu > Modul.

  2. Wählen Sie im Bereich Vorlagen die Option Benchmark aus.

  3. Wählen Sie als Benchmark-Modultyp Mikro-Benchmark aus.

  4. Geben Sie als Modulnamen „microbenchmark“ ein.

  5. Klicken Sie auf Fertig.

Neues Bibliotheksmodul konfigurieren
Abbildung 2. Füge in Android Studio Bumblebee ein neues Gradle-Modul hinzu.

Nachdem das Modul erstellt wurde, ändern Sie die Datei build.gradle oder build.gradle.kts und fügen Sie dem Modul androidTestImplementation hinzu, das den Code für das Benchmarking enthält:

Kotlin

dependencies {
    // The module name might be different.
    androidTestImplementation(project(":benchmarkable"))
}

Groovig

dependencies {
    // The module name might be different.
    androidTestImplementation project(':benchmarkable')
}

MicroBenchmark-Klasse erstellen

Benchmarks sind Standard-Instrumentierungstests. Verwenden Sie zum Erstellen einer Benchmark die Klasse BenchmarkRule, die von der Bibliothek bereitgestellt wird. Verwenden Sie zum Benchmarking von Aktivitäten ActivityScenario oder ActivityScenarioRule. Für das Benchmarking von UI-Code verwenden Sie @UiThreadTest.

Der folgende Code zeigt eine Beispiel-Benchmark:

Kotlin

@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun benchmarkSomeWork() {
        benchmarkRule.measureRepeated {
            doSomeWork()
        }
    }
}
    

Java

@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
    @Rule
    public BenchmarkRule benchmarkRule = new BenchmarkRule();

    @Test
    public void benchmarkSomeWork() {
        final BenchmarkState state = benchmarkRule.getState();
        while (state.keepRunning()) {
            doSomeWork();
        }
    }
}
    

Zeitsteuerung für die Einrichtung deaktivieren

Mit dem runWithTimingDisabled{}-Block können Sie das Timing für Codeabschnitte deaktivieren, die Sie nicht messen möchten. Diese Abschnitte stellen normalerweise Code dar, den Sie bei jeder Iteration der Benchmark ausführen müssen.

Kotlin

// using random with the same seed, so that it generates the same data every run
private val random = Random(0)

// create the array once and just copy it in benchmarks
private val unsorted = IntArray(10_000) { random.nextInt() }

@Test
fun benchmark_quickSort() {
    // ...
    benchmarkRule.measureRepeated {
        // copy the array with timing disabled to measure only the algorithm itself
        listToSort = runWithTimingDisabled { unsorted.copyOf() }

        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort)
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(listToSort.isSorted)
}
    

Java

private final int[] unsorted = new int[10000];

public SampleBenchmark() {
    // Use random with the same seed, so that it generates the same data every
    // run.
    Random random = new Random(0);

    // Create the array once and copy it in benchmarks.
    Arrays.setAll(unsorted, (index) -> random.nextInt());
}

@Test
public void benchmark_quickSort() {
    final BenchmarkState state = benchmarkRule.getState();

    int[] listToSort = new int[0];

    while (state.keepRunning()) {
        
        // Copy the array with timing disabled to measure only the algorithm
        // itself.
        state.pauseTiming();
        listToSort = Arrays.copyOf(unsorted, 10000);
        state.resumeTiming();
        
        // Sort the array in place and measure how long it takes.
        SortingAlgorithms.quickSort(listToSort);
    }

    // Assert only once, not to add overhead to the benchmarks.
    assertTrue(SortingAlgorithmsKt.isSorted(listToSort));
}
    

Versuchen Sie, den Arbeitsaufwand innerhalb des measureRepeated-Blocks und in runWithTimingDisabled zu minimieren. Der Block measureRepeated wird mehrmals ausgeführt und kann sich auf die Gesamtzeit auswirken, die zum Ausführen der Benchmark benötigt wird. Wenn Sie einige Ergebnisse einer Benchmark verifizieren müssen, können Sie das letzte Ergebnis bestätigen, anstatt es bei jeder Iteration der Benchmark zu tun.

Benchmark ausführen

Führen Sie die Benchmark in Android Studio wie bei jedem beliebigen @Test aus. Verwenden Sie dazu die Gutter-Aktion neben Ihrer Testklasse oder -methode (siehe Abbildung 3).

Micro-Benchmark ausführen
Abbildung 3: Führen Sie einen MicroBenchmark-Test durch, indem Sie die Gutter-Aktion neben einer Testklasse verwenden.

Alternativ können Sie connectedCheck über die Befehlszeile ausführen, um alle Tests des angegebenen Gradle-Moduls auszuführen:

./gradlew benchmark:connectedCheck

Oder einen einzelnen Test:

./gradlew benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.benchmark.SampleBenchmark#benchmarkSomeWork

Benchmarkergebnisse

Nach einer erfolgreichen MicroBenchmark-Ausführung werden die Messwerte direkt in Android Studio angezeigt. Außerdem steht ein vollständiger Benchmark-Bericht mit zusätzlichen Messwerten und Geräteinformationen im JSON-Format zur Verfügung.

Mikro-Benchmark-Ergebnisse
Abbildung 4: Mikro-Benchmark-Ergebnisse.

JSON-Berichte und alle Traces zur Profilerstellung werden ebenfalls automatisch von Gerät zu Host kopiert. Sie werden auf den Hostcomputer an folgendem Speicherort geschrieben:

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

Der JSON-Bericht wird standardmäßig im externen freigegebenen Medienordner des Test-APKs auf dem Gerät auf das Laufwerk geschrieben. Dieser befindet sich normalerweise im /storage/emulated/0/Android/media/**app_id**/**app_id**-benchmarkData.json.

Konfigurationsfehler

Die Bibliothek erkennt die folgenden Bedingungen, um sicherzustellen, dass Ihr Projekt und Ihre Umgebung für eine versionsgenaue Leistung eingerichtet sind:

  • „Debuggable“ ist auf false festgelegt.
  • Ein physisches Gerät wird verwendet – Emulatoren werden nicht unterstützt.
  • Die Uhr ist gesperrt, wenn das Gerät gerootet ist.
  • Der Akkuladestand des Geräts beträgt mindestens 25%.

Wenn eine der vorherigen Prüfungen fehlschlägt, meldet die Benchmark einen Fehler, um ungenaue Messungen zu vermeiden.

Wenn Sie bestimmte Fehlertypen als Warnungen unterdrücken und verhindern möchten, dass die Benchmark angehalten wird, übergeben Sie den Fehlertyp in einer durch Kommas getrennten Liste an das Instrumentierungsargument androidx.benchmark.suppressErrors.

Diese Einstellungen kannst du in deinem Gradle-Skript festlegen, wie im folgenden Beispiel gezeigt:

Kotlin

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Groovig

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Sie können Fehler auch über die Befehlszeile unterdrücken:

$ ./gradlew :benchmark:connectedCheck -P andoidtestInstrumentationRunnerArguments.androidx.benchmark.supperssErrors=DEBUGGABLE,LOW-BATTERY

Durch das Unterdrücken von Fehlern wird die Benchmark in einem falsch konfigurierten Status ausgeführt und die Ausgabe der Benchmark wird absichtlich umbenannt, indem Testnamen mit dem Fehler vorangestellt werden. Wenn Sie beispielsweise eine debugfähige Benchmark mit der Unterdrückung im vorherigen Snippet ausführen, wird den Testnamen DEBUGGABLE_ vorangestellt.