Mikro-Benchmark schreiben

Informationen zur Verwendung der MicroBenchmark-Bibliothek durch Hinzufügen von Änderungen an Ihrem Anwendungscode finden Sie im Abschnitt Kurzanleitung. Wie Sie eine vollständige Einrichtung mit komplexeren Änderungen an Ihrer Codebasis vornehmen, erfahren Sie im Abschnitt Vollständige Projekteinrichtung.

Kurzanleitung

In diesem Abschnitt wird beschrieben, wie Sie Benchmarking ausprobieren und einmalige Messungen ausführen, ohne Code in Module verschieben zu müssen. Um genaue Leistungsergebnisse zu erhalten, muss bei diesen Schritten die Fehlerbehebung in Ihrer Anwendung deaktiviert werden. Bewahren Sie dies daher in einer lokalen funktionierenden Kopie auf, ohne die Änderungen in Ihr 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 Ihres Moduls hinzu:

    Kotlin

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

    Cool

    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 Bibliotheksmanifest nicht mit dem App-Manifest zusammengeführt wird.

  2. Aktualisieren Sie den Build-Typ debug, damit er nicht debuggen kann:

    Kotlin

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

    Cool

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

    Kotlin

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

    Cool

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

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

Vollständige Projekteinrichtung

Wenn Sie ein regelmäßiges Benchmarking statt einmaliges Benchmarking einrichten möchten, isolieren Sie die Benchmarks in einem eigenen Modul. Dadurch wird sichergestellt, dass ihre 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 zu vergleichenden Code in einem separaten Gradle-Modul und legen Sie die Abhängigkeit von diesem Modul fest, wie in Abbildung 1 gezeigt.

App-Struktur
Abbildung 1. Anwendungsstruktur mit :app-, :microbenchmark- und :benchmarkable-Gradle-Modulen, mit denen MicroBenchmarks den Code im :benchmarkable-Modul Benchmarking ausführen können.

Mit dem Modulassistenten in Android Studio können Sie ein neues Gradle-Modul hinzufügen. Der Assistent erstellt ein Modul, das für das Benchmarking vorkonfiguriert ist, wobei ein Benchmark-Verzeichnis hinzugefügt und debuggable auf false gesetzt ist.

  1. Klicken Sie in Android Studio im Bereich Project (Projekt) mit der rechten Maustaste auf Ihr Projekt oder Modul und klicken Sie dann auf New > Module (Neu > Modul).

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

  3. Wählen Sie als Typ des Benchmark-Moduls MicroBenchmark aus.

  4. Geben Sie „microBenchmark“ als Modulnamen ein.

  5. Klicken Sie auf Fertig.

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

Ändern Sie nach dem Erstellen des Moduls die Datei build.gradle oder build.gradle.kts und fügen Sie dem Modul, das den Code für das Benchmark enthält, androidTestImplementation hinzu:

Kotlin

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

Cool

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

MicroBenchmark-Klasse erstellen

Benchmarks sind Standard-Instrumentierungstests. Verwenden Sie zum Erstellen einer Benchmark die von der Bibliothek bereitgestellte Klasse BenchmarkRule. Verwenden Sie ActivityScenario oder ActivityScenarioRule, um Aktivitäten zu vergleichen. Verwenden Sie @UiThreadTest, um den UI-Code zu vergleichen.

Der folgende Code zeigt ein Beispiel für eine 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();
        }
    }
}
    

Zeit 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 enthalten in der Regel Code, den Sie in 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 von 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 prüfen müssen, können Sie das letzte Ergebnis bestätigen, anstatt dies bei jeder Iteration der Benchmark zu tun.

Benchmark ausführen

Führen Sie in Android Studio Ihre Benchmark wie bei jeder @Test aus. Verwenden Sie dazu die Gutter-Aktion neben der Testklasse oder -methode, wie in Abbildung 3 gezeigt.

MicroBenchmark ausführen
Abbildung 3: Führen Sie einen MicroBenchmark-Test mit der gutter-Aktion neben einer Testklasse aus.

Alternativ können Sie über die Befehlszeile connectedCheck ausführen, um alle Tests über das angegebene Gradle-Modul 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 und ein vollständiger Benchmark-Bericht mit zusätzlichen Messwerten und Geräteinformationen ist im JSON-Format verfügbar.

MicroBenchmark-Ergebnisse
Abbildung 4: MicroBenchmark-Ergebnisse.

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

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

Standardmäßig wird der JSON-Bericht auf das Laufwerk auf dem Gerät im externen freigegebenen Medienordner des Test-APKs geschrieben, der sich normalerweise im Verzeichnis /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 Release-genaue Leistung eingerichtet sind:

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

Wenn eine der vorherigen Prüfungen fehlschlägt, meldet die Benchmark einen Fehler, um ungenauen Messungen vorzubeugen.

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.

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

Kotlin

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

Cool

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 Sie Fehler unterdrücken, wird die Benchmark in einem falsch konfigurierten Zustand ausgeführt. 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.