Mikro-Benchmark schreiben

Informationen zur Verwendung der Microbenchmark-Bibliothek durch Hinzufügen von Änderungen am Anwendungscode finden Sie im Abschnitt Kurzanleitung. Informationen zur vollständigen Einrichtung mit komplexeren Änderungen an Ihrer Codebasis finden Sie im Abschnitt Vollständige Projekteinrichtung.

Kurzanleitung

In diesem Abschnitt wird beschrieben, wie Sie Benchmarking ausprobieren und einmalige Messungen ausführen können, ohne Code in Module verschieben zu müssen. Für genaue Leistungsergebnisse wird bei diesen Schritten das Debugging in Ihrer App deaktiviert. Behalten Sie dies also in einer lokalen Arbeitskopie bei, ohne die Änderungen an Ihr Quellcodeverwaltungssystem zu übertragen.

So führen Sie ein einmaliges Benchmarking durch:

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

    Kotlin

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

    Groovy

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

    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, damit er nicht debuggfähig ist:

    Kotlin

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

    Groovy

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

    Kotlin

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

    Groovy

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
  4. Fügen Sie eine Instanz von BenchmarkRule in einer Testdatei im Verzeichnis androidTest hinzu, um Ihren 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 einen 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()
                );
           }
        }
    }

Wenn Sie wissen möchten, wie Sie einen Benchmark schreiben, fahren Sie mit Microbenchmark-Klasse erstellen fort.

Vollständige Projekteinrichtung

Wenn Sie regelmäßiges Benchmarking anstelle von einmaligem Benchmarking einrichten möchten, isolieren Sie Benchmarks in einem eigenen Modul. So wird sichergestellt, dass die Konfiguration, z. B. das Festlegen von debuggable auf false, von regulären Tests getrennt ist.

Da Microbenchmark Ihren Code direkt ausführt, platzieren Sie den Code, den Sie benchmarken möchten, in einem separaten Gradle-Modul und legen Sie eine Abhängigkeit von diesem Modul fest, wie in Abbildung 1 dargestellt.

App-Struktur
Abbildung 1: App-Struktur mit den Gradle-Modulen :app, :microbenchmark und :benchmarkable, mit denen Microbenchmarks Code im Modul :benchmarkable benchmarken können.

Wenn Sie ein neues Gradle-Modul hinzufügen möchten, können Sie den Modulassistenten in Android Studio verwenden. Der Assistent erstellt ein Modul, das für Benchmarking vorkonfiguriert ist. Es wird ein Benchmark-Verzeichnis hinzugefügt und debuggable wird auf false gesetzt.

  1. Klicken Sie in Android Studio im Bereich 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 Microbenchmark als Benchmark-Modultyp aus.

  4. Geben Sie „microbenchmark“ als Modulnamen ein.

  5. Klicken Sie auf Fertig.

Neues Bibliotheksmodul konfigurieren
Abbildung 2. Fügen Sie 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 androidTestImplementation dem Modul hinzu, das den Code für den Benchmark enthält:

Kotlin

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

Groovy

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

Microbenchmark-Klasse erstellen

Benchmarks sind standardmäßige Instrumentierungstests. Verwenden Sie zum Erstellen eines Benchmarks die von der Bibliothek bereitgestellte Klasse BenchmarkRule. Verwenden Sie für Benchmark-Aktivitäten ActivityScenario oder ActivityScenarioRule. Verwenden Sie zum Benchmarking von UI-Code @UiThreadTest.

Der folgende Code zeigt einen 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();
        }
    }
}
    

Zeitplanung für die Einrichtung deaktivieren

Sie können die Zeitmessung für Codeabschnitte, die Sie nicht messen möchten, mit dem Block runWithTimingDisabled{} deaktivieren. Diese Abschnitte stellen normalerweise Code dar, den Sie bei jeder Iteration des Benchmarks 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 im measureRepeated-Block und in runWithTimingDisabled zu minimieren. Der Block measureRepeated wird mehrmals ausgeführt und kann sich auf die Gesamtzeit auswirken, die für die Ausführung des Benchmarks erforderlich ist. Wenn Sie einige Ergebnisse eines Benchmarks prüfen müssen, können Sie das letzte Ergebnis bestätigen, anstatt dies bei jeder Iteration des Benchmarks zu tun.

Benchmark ausführen

Führen Sie den Benchmark in Android Studio wie jeden @Test mit der Gutter-Aktion neben der Testklasse oder -methode aus, wie in Abbildung 3 dargestellt.

Microbenchmark ausführen
Abbildung 3. Führen Sie den Microbenchmark-Test mit der Gutter-Aktion neben einer Testklasse aus.

Alternativ können Sie in der Befehlszeile den Befehl connectedCheck ausführen, um alle Tests aus dem angegebenen Gradle-Modul auszuführen:

./gradlew benchmark:connectedCheck

Oder einen einzelnen Test:

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

Benchmark-Ergebnisse

Nach einem erfolgreichen Microbenchmark-Lauf werden die Messwerte direkt in Android Studio angezeigt. Außerdem ist ein vollständiger Benchmark-Bericht mit zusätzlichen Messwerten und Geräteinformationen im JSON-Format verfügbar.

Microbenchmark-Ergebnisse
Abbildung 4. Microbenchmark-Ergebnisse.

JSON-Berichte und alle Profiling-Traces werden ebenfalls automatisch vom Gerät auf den Host kopiert. Diese werden auf dem Hostcomputer am folgenden Speicherort geschrieben:

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

Standardmäßig wird der JSON-Bericht auf dem Gerät im externen freigegebenen Medienordner des Test-APK gespeichert, der sich normalerweise in /storage/emulated/0/Android/media/**app_id**/**app_id**-benchmarkData.json befindet.

Konfigurationsfehler

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

  • „Debuggable“ ist auf false festgelegt.
  • Es wird ein physisches Gerät verwendet. Emulatoren werden nicht unterstützt.
  • Die Taktraten sind gesperrt, wenn das Gerät gerootet ist.
  • Der Akkustand des Geräts muss mindestens 25 % betragen.

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

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

Sie können dies wie im folgenden Beispiel gezeigt über Ihr Gradle-Skript festlegen:

Kotlin

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

Groovy

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

Wenn Fehler unterdrückt werden, wird der Benchmark in einem falsch konfigurierten Zustand ausgeführt. Die Ausgabe des Benchmarks wird absichtlich umbenannt, indem den Testnamen der Fehler vorangestellt wird. Wenn Sie beispielsweise einen Benchmark im Debug-Modus mit der Unterdrückung im vorherigen Snippet ausführen, wird den Testnamen DEBUGGABLE_ vorangestellt.