Benchmarks im Bereich Continuous Integration

Sie können Benchmarks in Continuous Integration (CI) ausführen, um die Leistung im Zeitverlauf zu verfolgen und Leistungsabfälle oder -verbesserungen zu erkennen, bevor Ihre Anwendung überhaupt veröffentlicht wird. Diese Seite enthält grundlegende Informationen zum Benchmarking in CI.

Bevor Sie mit dem Benchmarking in CI beginnen, sollten Sie sich überlegen, wie sich die Erfassung und Bewertung von Ergebnissen von regulären Tests unterscheidet.

Unscharfe Ergebnisse

Auch wenn es sich bei Benchmarks um instrumentierte Tests handelt, sind die Ergebnisse nicht nur bestanden oder nicht bestanden. Benchmarks liefern zeitliche Messungen für das jeweilige Gerät, auf dem sie ausgeführt werden. Wenn Sie die Ergebnisse im Zeitverlauf grafisch darstellen, können Sie Veränderungen im Messsystem beobachten und Rauschen beobachten.

Echte Geräte verwenden

Benchmarks auf physischen Android-Geräten durchführen Sie können zwar auf Emulatoren ausgeführt werden, es wird jedoch dringend davon abgeraten, da dies keine realistische Nutzererfahrung darstellt und stattdessen Zahlen liefert, die mit dem Hostbetriebssystem und den Hardwarefunktionen verbunden sind. Ziehen Sie die Verwendung von echten Geräten oder einem Dienst in Betracht, mit dem Sie Tests auf echten Geräten wie Firebase Test Lab ausführen können.

Benchmarks durchführen

Die Ausführung der Benchmarks als Teil der CI-Pipeline kann sich von der lokalen Ausführung über Android Studio unterscheiden. Lokal führen Sie die Android-Integrationstests in der Regel mit einer Gradle-Aufgabe connectedCheck aus. Diese Aufgabe erstellt automatisch Ihr APK und Test-APK und führt die Tests auf den Geräten aus, die mit dem CI-Server verbunden sind. Bei der Ausführung in CI muss dieser Ablauf normalerweise in separate Phasen aufgeteilt werden.

Eine Community

Führen Sie für die MicroBenchmark-Bibliothek die Gradle-Aufgabe assemble[VariantName]AndroidTest aus. Dadurch wird Ihr Test-APK erstellt, das sowohl Ihren Anwendungscode als auch Ihren getesteten Code enthält.

Alternativ müssen Sie für die MacroBenchmark-Bibliothek Ihr Ziel-APK und Ihr Test-APK separat erstellen. Führen Sie daher die Gradle-Tasks :app:assemble[VariantName] und :macrobenchmark:assemble[VariantName] aus.

Installieren und ausführen

Diese Schritte werden in der Regel ohne Ausführung von Gradle-Aufgaben ausgeführt. Hinweis: Sie können abstrahiert sein, je nachdem, ob Sie einen Dienst verwenden, mit dem Sie Tests auf echten Geräten ausführen können.

Verwenden Sie für die Installation den Befehl adb install und geben Sie das Test-APK oder das Ziel-APK an.

Führen Sie den Befehl adb shell am aus, um alle Benchmarks auszuführen:

adb shell am instrument -w com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Verwenden Sie bei Verwendung der MacroBenchmark-Bibliothek den regulären androidx.test.runner.AndroidJUnitRunner als Instrumentierungs-Runner.

Sie können mit dem Argument -e dieselben Instrumentierungsargumente wie in der Gradle-Konfiguration übergeben. Informationen zu allen Optionen für Instrumentierungsargumente finden Sie unter MicroBenchmark-Instrumentierungsargumente oder Instrumentierungsargumente hinzufügen für MacroBenchmark.

Sie können beispielsweise das Argument dryRunMode so festlegen, dass im Rahmen der Überprüfung von Pull-Anfragen Mikro-Benchmarks ausgeführt werden. Wenn dieses Flag aktiviert ist, werden Mikro-Benchmarks nur in einer einzigen Schleife ausgeführt. So wird überprüft, ob sie korrekt ausgeführt werden. Die Ausführung dauert aber nicht zu lange.

adb shell am instrument -w -e "androidx.benchmark.dryRunMode.enable" "true" com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Weitere Informationen zum Ausführen von Instrumentierungstests über die Befehlszeile finden Sie unter Tests mit ADB ausführen.

Sperruhren

Das Gradle-Plug-in von Microbenchmark bietet den Befehl ./gradlew lockClocks, um die CPU-Taktung eines gerooteten Geräts zu sperren. Das ist nützlich, um für Stabilität zu sorgen, wenn Sie Zugriff auf gerootete Geräte wie „userdebug“-Builds haben. Sie können dies mit dem Shell-Skript lockClocks.sh replizieren, das in der Quelle der Bibliothek verfügbar ist.

Sie können das Skript entweder direkt auf einem Linux- oder Mac-Host ausführen oder mit einigen ADB-Befehlen auf das Gerät übertragen:

adb push path/lockClocks.sh /data/local/tmp/lockClocks.sh
adb shell /data/local/tmp/lockClocks.sh
adb shell rm /data/local/tmp/lockClocks.sh

Wenn Sie das Shell-Skript direkt auf einem Host ausführen, werden die Befehle an ein verbundenes Gerät weitergeleitet.

Weitere Informationen dazu, warum es hilfreich ist, die CPU-Taktung zu sperren, finden Sie unter Konsistente Benchmarks abrufen.

Die Ergebnisse erfassen

Die Benchmarking-Bibliotheken geben Messwerte in JSON aus, zusammen mit Profilerstellungs-Traces in einem Verzeichnis auf dem Android-Gerät nach jeder Benchmarkausführung. Die MacroBenchmark-Bibliothek gibt mehrere Perfetto-Trace-Dateien aus: eine pro gemessener Iteration jeder MacrobenchmarkRule.measureRepeated-Schleife. Die Mikro-Benchmark erstellt jedoch nur eine Trace-Datei für alle Iterationen einer BenchmarkRule.measureRepeated. Trace-Dateien für die Profilerstellung werden ebenfalls in dasselbe Verzeichnis ausgegeben.

Dateien speichern und suchen

Wenn du die Benchmarks mit Gradle ausführst, werden diese Dateien automatisch in das Ausgabeverzeichnis deines Hostcomputers unter build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/ kopiert.

Wenn Sie die Dateien direkt mit dem Befehl adb ausführen, müssen Sie die Dateien manuell abrufen. Die Berichte werden standardmäßig auf dem Gerät im Medienverzeichnis des externen Speichers der getesteten Anwendung gespeichert. Der Einfachheit halber gibt die Bibliothek den Pfad der Datei in Logcat aus. Der Ausgabeordner kann je nach Android-Version, auf der die Benchmarks ausgeführt werden, variieren.

Benchmark: writing results to /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Sie können auch mit dem Instrumentierungsargument additionalTestOutputDir den Speicherort konfigurieren, an dem Benchmarkberichte auf dem Gerät gespeichert werden. Ihre Anwendung muss in diesen Ordner schreiben können.

adb shell am instrument -w -e additionalTestOutputDir /sdcard/Download/ com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Unter Android 10 (API-Level 29) und höher werden die Tests deiner App standardmäßig in einer Speicher-Sandbox ausgeführt. Dadurch wird verhindert, dass deine App auf Dateien außerhalb des app-spezifischen Verzeichnisses zugreift. Übergeben Sie das folgende Instrumentierungsargument, um die Datei in einem globalen Verzeichnis wie /sdcard/Download speichern zu können:

-e no-isolated-storage true

Außerdem müssen Sie im Manifest Ihrer Benchmark explizit Legacy-Speicheroptionen zulassen:

<application android:requestLegacyExternalStorage="true" ... >

Weitere Informationen finden Sie unter Begrenzten Speicher vorübergehend deaktivieren.

Dateien abrufen

Um die generierten Dateien vom Gerät abzurufen, verwenden Sie den Befehl adb pull, der die angegebene Datei in das aktuelle Verzeichnis auf Ihrem Host lädt:

adb pull /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Prüfen Sie das folgende Snippet, um alle benchmarkData aus einem bestimmten Ordner abzurufen:

# The following command pulls all files ending in -benchmarkData.json from the directory
# hierarchy starting at the root /storage/emulated/0/Android.
adb shell find /sdcard/Download -name "*-benchmarkData.json" | tr -d '\r' | xargs -n1 adb pull

Die Trace-Dateien (.trace oder .perfetto-trace) werden im selben Ordner wie benchmarkData.json gespeichert, sodass Sie sie auf die gleiche Weise erfassen können.

Beispiel für Benchmarkdaten

Die Benchmark-Bibliotheken generieren JSON-Dateien mit Informationen zu dem Gerät, auf dem die Benchmarks ausgeführt wurden, und zu den tatsächlich ausgeführten Benchmarks. Das folgende Snippet stellt die generierte JSON-Datei dar:

{
    "context": {
        "build": {
            "brand": "google",
            "device": "blueline",
            "fingerprint": "google/blueline/blueline:12/SP1A.210812.015/7679548:user/release-keys",
            "model": "Pixel 3",
            "version": {
                "sdk": 31
            }
        },
        "cpuCoreCount": 8,
        "cpuLocked": false,
        "cpuMaxFreqHz": 2803200000,
        "memTotalBytes": 3753299968,
        "sustainedPerformanceModeEnabled": false
    },
    "benchmarks": [
        {
            "name": "startup",
            "params": {},
            "className": "com.example.macrobenchmark.startup.SampleStartupBenchmark",
            "totalRunTimeNs": 4975598256,
            "metrics": {
                "timeToInitialDisplayMs": {
                    "minimum": 347.881076,
                    "maximum": 347.881076,
                    "median": 347.881076,
                    "runs": [
                        347.881076
                    ]
                }
            },
            "sampledMetrics": {},
            "warmupIterations": 0,
            "repeatIterations": 3,
            "thermalThrottleSleepSeconds": 0
        }
    ]
}

Weitere Informationen