1. Hinweis
In diesem Codelab erfahren Sie, wie Sie Baseline-Profile generieren, um die Leistung Ihrer Anwendung zu optimieren, und wie Sie die Leistungsvorteile der Verwendung von Baseline-Profilen überprüfen.
Voraussetzungen
- Android Studio Hedgehog (2023.1.1) oder höher
- Android Gradle Plugin 8.0 oder höher
- Grundlegende Informationen zu Jetpack Macrobenchmark
- Ein Android-Gerät mit Android 7 (API-Level 24) oder höher
Aufgaben
- Richten Sie das Projekt so ein, dass Generatoren für Baseline-Profile verwendet werden.
- Erstellen Sie Referenzprofile, um die Start- und Scrollleistung von Apps zu optimieren.
- Überprüfen Sie die Leistungssteigerungen mit der Jetpack MacroBenchmark-Bibliothek.
Lerninhalte
- Baseline-Profile und wie sie die Leistung der App verbessern können
- So erstellen Sie Baseline-Profile.
- Leistungssteigerungen von Basisprofilen.
2. Einrichtung
Klonen Sie zuerst das GitHub-Repository über die Befehlszeile mit dem folgenden Befehl:
$ git clone https://github.com/android/codelab-android-performance.git
Alternativ können Sie zwei ZIP-Dateien herunterladen:
Projekt in Android Studio öffnen
- Wählen Sie im Fenster „Willkommen bei Android Studio“ die Option Vorhandenes Projekt öffnen aus.
- Wählen Sie den Ordner
[Download Location]/codelab-android-performance/baseline-profiles
aus. Wählen Sie das Verzeichnisbaseline-profiles
aus. - Achten Sie beim Importieren des Projekts in Android Studio darauf, dass Sie das Modul
app
ausführen können, um die Beispielanwendung zu erstellen, mit der Sie später arbeiten.
Beispiel-App
In diesem Codelab arbeiten Sie mit der Beispielanwendung „JetSnack“. Es ist eine virtuelle App zum Bestellen von Snacks, die Jetpack Compose verwendet.
Um die Leistung der Anwendung zu messen, müssen Sie die Struktur der Benutzeroberfläche und das Verhalten der App kennen, damit Sie über die Benchmarks auf die UI-Elemente zugreifen können. Führen Sie die App aus und machen Sie sich mit den grundlegenden Bildschirmen vertraut, indem Sie Snacks bestellen. Sie müssen nicht die Details zur Architektur der App kennen.
3. Was sind Basisprofile
Baseline-Profile verbessern die Codeausführungsgeschwindigkeit nach dem ersten Start um etwa 30 %, da die Interpretation und die Just-in-Time-Kompilierung (JIT) für die enthaltenen Codepfade vermieden werden. Wenn Sie ein Baseline-Profil in einer App oder Bibliothek bereitstellen, kann die Android Runtime (ART) die enthaltenen Codepfade durch eine AOT-Kompilierung (Ahead of Time) optimieren. So werden Leistungsverbesserungen für jeden neuen Nutzer und bei jedem App-Update erzielt. Mit dieser profilgesteuerten Optimierung (PGO) können Apps den Start optimieren, Interaktionsverzögerungen reduzieren und die Gesamtlaufzeitleistung für Endnutzer ab dem ersten Start verbessern.
Mit einem Baseline-Profil laufen alle Nutzerinteraktionen – z. B. das Starten von Apps, das Navigieren zwischen Bildschirmen oder das Scrollen durch Inhalte – ab der ersten Ausführung reibungsloser. Wenn Sie die Geschwindigkeit und Reaktionsschnelligkeit einer App erhöhen, steigt die Anzahl der täglich aktiven Nutzer und die durchschnittliche Wiederkehrrate.
Mit Baseline-Profilen können Sie die App nicht nur beim Start optimieren, sondern auch häufige Nutzerinteraktionen berücksichtigen, um die App-Laufzeit bereits beim ersten Start zu verbessern. Die AOT-Kompilierung mit Anleitung ist unabhängig von Nutzergeräten und kann einmal pro Release auf einem Entwicklungscomputer statt auf einem Mobilgerät durchgeführt werden. Wenn Sie Releases mit einem Baseline-Profil bereitstellen, sind App-Optimierungen viel schneller verfügbar als bei der Verwendung von Cloud-Profilen allein.
Wenn kein Baseline-Profil verwendet wird, wird der gesamte App-Code nach der Interpretation im Arbeitsspeicher oder in eine odex-Datei im Hintergrund kompiliert, wenn das Gerät inaktiv ist. Wenn Nutzer eine App nach der Erstinstallation oder -aktualisierung ausführen, bevor die neuen Pfade optimiert wurden, kann es zu Leistungseinbußen kommen.
4. Modul „Baseline Profile Generator“ einrichten
Sie können Baseline-Profile mit einer Instrumentierungstestklasse generieren, für die Ihrem Projekt ein neues Gradle-Modul hinzugefügt werden muss. Am einfachsten fügen Sie es Ihrem Projekt mit dem Android Studio-Modulassistenten hinzu, der in Android Studio Hedgehog oder höher enthalten ist.
Öffnen Sie das Fenster für den neuen Modul-Assistenten, indem Sie im Bereich Projekt mit der rechten Maustaste auf Ihr Projekt oder Modul klicken und Neu > Modul auswählen.
Wählen Sie im geöffneten Fenster im Bereich „Vorlagen“ die Option Baseline Profile Generator aus.
Neben den üblichen Parametern wie Modulname, Paketname, Sprache oder Build-Konfigurationssprache gibt es zwei Eingaben, die für ein neues Modul nicht üblich sind: Ziel-App und Gradle-verwaltetes Gerät verwenden.
Die Zielanwendung ist das App-Modul, für das Baseline-Profile generiert werden. Wenn Sie in Ihrem Projekt mehrere App-Module haben, wählen Sie aus, für welches Sie die Generatoren ausführen möchten.
Wenn Sie das Kästchen Gradle-verwaltetes Gerät verwenden aktivieren, wird das Modul so konfiguriert, dass die Generatoren für Baseline-Profile auf automatisch verwalteten Android-Emulatoren ausgeführt werden. Weitere Informationen zu von Gradle verwalteten Geräten finden Sie unter Tests mit Gradle-verwalteten Geräten skalieren. Wenn du das Häkchen entfernst, verwenden die Generatoren ein beliebiges verbundenes Gerät.
Wenn Sie alle Details zum neuen Modul festgelegt haben, klicken Sie auf Fertigstellen, um mit der Erstellung des Moduls fortzufahren.
Änderungen, die über den Modulassistenten vorgenommen wurden
Der Modul-Assistent nimmt mehrere Änderungen an Ihrem Projekt vor.
Dadurch wird ein Gradle-Modul mit dem Namen baselineprofile
oder der Name, den Sie im Assistenten auswählen, hinzugefügt.
Dieses Modul verwendet das com.android.test
-Plug-in, das Gradle anweist, es nicht in Ihre Anwendung aufzunehmen. Es kann daher nur Testcode oder Benchmarks enthalten. Außerdem wird das androidx.baselineprofile
-Plug-in angewendet, mit dem sich das Erstellen von Baseline-Profilen automatisieren lässt.
Der Assistent nimmt außerdem Änderungen am ausgewählten Zielanwendungsmodul vor. Insbesondere wird das androidx.baselineprofile
-Plug-in angewendet, die androidx.profileinstaller
-Abhängigkeit hinzugefügt und die baselineProfile
-Abhängigkeit dem neu erstellten Modul build.gradle(.kts)
hinzugefügt:
plugins {
id("androidx.baselineprofile")
}
dependencies {
// ...
implementation("androidx.profileinstaller:profileinstaller:1.3.0")
"baselineProfile"(project(mapOf("path" to ":baselineprofile")))
}
Wenn Sie die androidx.profileinstaller
-Abhängigkeit hinzufügen, haben Sie folgende Möglichkeiten:
- Leistungssteigerungen der generierten Baseline-Profile lokal prüfen
- Baseline-Profile unter Android 7 (API-Level 24) und Android 8 (API-Level 26) verwenden, die keine Cloud-Profile unterstützen.
- Verwenden Sie Baseline-Profile auf Geräten ohne Google Play-Dienste.
Über die baselineProfile(project(":baselineprofile"))
-Abhängigkeit wird Gradle mitgeteilt, aus welchem Modul die generierten Baseline-Profile übernommen werden müssen.
Nachdem Sie das Projekt eingerichtet haben, schreiben Sie eine Klasse für den Generator von Baseline-Profilen.
5. Baseline-Profilgenerator schreiben
Normalerweise generieren Sie Basisprofile für die typischen Nutzerpfade Ihrer App.
Der Modulassistent erstellt eine einfache BaselineProfileGenerator
-Testklasse, die das Baseline-Profil für den Start Ihrer App generieren kann. Sie sieht so aus:
@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {
@get:Rule
val rule = BaselineProfileRule()
@Test
fun generate() {
rule.collect("com.example.baselineprofiles_codelab") {
// This block defines the app's critical user journey. This is where you
// optimize for app startup. You can also navigate and scroll
// through your most important UI.
// Start default activity for your app.
pressHome()
startActivityAndWait()
// TODO Write more interactions to optimize advanced journeys of your app.
// For example:
// 1. Wait until the content is asynchronously loaded.
// 2. Scroll the feed content.
// 3. Navigate to detail screen.
// Check UiAutomator documentation for more information about how to interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
}
}
Diese Klasse verwendet eine BaselineProfileRule
-Testregel und enthält eine Testmethode zum Generieren des Profils. Der Einstiegspunkt für die Generierung des Profils ist die Funktion collect()
. Es sind nur zwei Parameter erforderlich:
packageName
: das Paket Ihrer App.profileBlock
: den letzten Lambda-Parameter.
In der profileBlock
-Lambda-Funktion geben Sie die Interaktionen an, die die typischen User Journeys Ihrer App abdecken. Die Bibliothek führt die profileBlock
mehrmals aus, erfasst die aufgerufenen Klassen und Funktionen und generiert das Baseline-Profil auf dem Gerät mit dem zu optimierenden Code.
Standardmäßig enthält die erstellte Generatorklasse Interaktionen, um den Standard-Activity
zu starten, und wartet, bis der erste Frame Ihrer App mit der startActivityAndWait()
-Methode gerendert wird.
Generator mit benutzerdefinierten Kaufprozessen erweitern
Wie Sie sehen, enthält die generierte Klasse auch einige TODO
, um weitere Interaktionen zu schreiben und erweiterte Aufrufabfolgen Ihrer App zu optimieren. Dies wird empfohlen, damit Sie die Leistung auch über den App-Start hinaus optimieren können.
In unserer Beispiel-App können Sie diese Aufrufabfolgen so identifizieren:
- Starten Sie die Anwendung. Dies wird bereits teilweise durch die generierte Klasse abgedeckt.
- Warten Sie, bis die Inhalte asynchron geladen wurden.
- Scrolle durch die Liste der Snacks.
- Zu den Snackdetails.
Ändern Sie den Generator so, dass er die beschriebenen Funktionen enthält, die die typischen Kaufprozesse im folgenden Snippet abdecken:
// ...
rule.collect("com.example.baselineprofiles_codelab") {
// This block defines the app's critical user journey. This is where you
// optimize for app startup. You can also navigate and scroll
// through your most important UI.
// Start default activity for your app.
pressHome()
startActivityAndWait()
// TODO Write more interactions to optimize advanced journeys of your app.
// For example:
// 1. Wait until the content is asynchronously loaded.
waitForAsyncContent()
// 2. Scroll the feed content.
scrollSnackListJourney()
// 3. Navigate to detail screen.
goToSnackDetailJourney()
// Check UiAutomator documentation for more information about how to interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
// ...
Erstellen Sie nun Interaktionen für jeden der genannten Kaufprozesse. Du kannst sie als Erweiterungsfunktion von MacrobenchmarkScope
schreiben, damit du Zugriff auf die bereitgestellten Parameter und Funktionen hast. So können Sie die Interaktionen mit den Benchmarks wiederverwenden, um die Leistungssteigerungen zu überprüfen.
Auf asynchrone Inhalte warten
Viele Apps laden beim Start der App asynchron. Dieser wird auch als vollständig angezeigter Status bezeichnet. Dieser Status teilt dem System mit, wann die Inhalte geladen und gerendert wurden und der Nutzer mit ihnen interagieren kann. Warten Sie mit diesen Interaktionen auf den Status im Generator (waitForAsyncContent
):
- Suchen Sie die Liste der Feed-Snacks.
- Warten Sie, bis einige Elemente in der Liste auf dem Bildschirm sichtbar sind.
fun MacrobenchmarkScope.waitForAsyncContent() {
device.wait(Until.hasObject(By.res("snack_list")), 5_000)
val contentList = device.findObject(By.res("snack_list"))
// Wait until a snack collection item within the list is rendered.
contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}
Weg durch Scrollen durch Liste
Für die Scrollansicht der Snackliste (scrollSnackListJourney
) können Sie die folgenden Interaktionen ausführen:
- Suchen Sie das UI-Element für die Snackliste.
- Legen Sie die Gestenränder so fest, dass die Systemnavigation nicht ausgelöst wird.
- Scrollen Sie durch die Liste und warten Sie, bis die Benutzeroberfläche stabil ist.
fun MacrobenchmarkScope.scrollSnackListJourney() {
val snackList = device.findObject(By.res("snack_list"))
// Set gesture margin to avoid triggering gesture navigation.
snackList.setGestureMargin(device.displayWidth / 5)
snackList.fling(Direction.DOWN)
device.waitForIdle()
}
Zur detaillierten Anleitung
Im letzten Journey (goToSnackDetailJourney
) werden diese Interaktionen implementiert:
- Hier findest du die Liste mit Snacks und allen Snackartikeln, die du verwenden kannst.
- Wählen Sie ein Element aus der Liste aus.
- Klicken Sie auf das Element und warten Sie, bis der Detailbildschirm geladen ist. Sie können die Tatsache nutzen, dass die Snackliste nicht mehr auf dem Bildschirm angezeigt wird.
fun MacrobenchmarkScope.goToSnackDetailJourney() {
val snackList = device.findObject(By.res("snack_list"))
val snacks = snackList.findObjects(By.res("snack_item"))
// Select snack from the list based on running iteration.
val index = (iteration ?: 0) % snacks.size
snacks[index].click()
// Wait until the screen is gone = the detail is shown.
device.wait(Until.gone(By.res("snack_list")), 5_000)
}
Nachdem Sie alle Interaktionen definiert haben, die für die Ausführung des Generators für Baseline-Profile erforderlich sind, müssen Sie das Gerät definieren, auf dem er ausgeführt wird.
6. Gerät vorbereiten, auf dem der Generator ausgeführt werden soll
Zum Generieren von Baseline-Profilen empfehlen wir entweder einen Emulator wie ein von Gradle verwaltetes Gerät oder ein Gerät mit Android 13 (API 33) oder höher.
Um den Prozess reproduzierbar zu machen und das Erstellen von Baseline-Profilen zu automatisieren, können Sie verwaltete Gradle-Geräte verwenden. Mit Gradle-verwalteten Geräten können Sie Tests auf einem Android-Emulator ausführen, ohne ihn manuell starten und beenden zu müssen. Weitere Informationen zu von Gradle verwalteten Geräten finden Sie unter Tests mit Gradle-verwalteten Geräten skalieren.
Wenn Sie ein verwaltetes Gradle-Gerät definieren möchten, fügen Sie dessen Definition der Datei build.gradle.kts
des :baselineprofile
-Moduls hinzu, wie im folgenden Snippet gezeigt:
android {
// ...
testOptions.managedDevices.devices {
create<ManagedVirtualDevice>("pixel6Api31") {
device = "Pixel 6"
apiLevel = 31
systemImageSource = "aosp"
}
}
}
In diesem Fall verwenden wir Android 11 (API-Level 31) und das aosp
-System-Image unterstützt den Root-Zugriff.
Konfigurieren Sie als Nächstes das Gradle-Plug-in für das Baseline-Profil, um das definierte Gradle Managed Device zu verwenden. Fügen Sie dazu den Namen des Geräts in die Property managedDevices
ein und deaktivieren Sie useConnectedDevices
, wie im folgenden Snippet gezeigt:
android {
// ...
}
baselineProfile {
managedDevices += "pixel6Api31"
useConnectedDevices = false
}
dependencies {
// ...
}
Generieren Sie als Nächstes das Basisprofil.
7. Baseline-Profil erstellen
Sobald das Gerät bereit ist, können Sie das Baseline-Profil erstellen. Das Gradle-Plug-in für Baseline-Profile erstellt Gradle-Aufgaben, um den gesamten Prozess zum Ausführen der Generator-Testklasse und zum Anwenden der generierten Baseline-Profile auf Ihre App zu automatisieren.
Mit dem neuen Modul-Assistenten wurde eine Ausführungskonfiguration erstellt, mit der die Gradle-Aufgabe schnell mit allen erforderlichen Parametern ausgeführt werden kann, ohne zwischen Terminal und Android Studio wechseln zu müssen.
Klicken Sie zum Ausführen auf die Generate Baseline Profile
-Ausführungskonfiguration und dann auf die Schaltfläche „Ausführen“ .
Die Aufgabe startet das zuvor definierte Emulator-Image. Führen Sie die Interaktionen aus der BaselineProfileGenerator
-Testklasse mehrmals aus, beenden Sie den Emulator und geben Sie die Ausgabe an Android Studio weiter.
Sobald der Generator erfolgreich abgeschlossen wurde, fügt das Gradle-Plug-in die generierte baseline-prof.txt
automatisch in die Zielanwendung (:app
-Modul) im Ordner src/release/generated/baselineProfile/
ein.
(Optional) Generator über die Befehlszeile ausführen
Alternativ kann der Generator auch über die Befehlszeile ausgeführt werden. Sie können die Aufgabe verwenden, die vom verwalteten Gradle-Gerät :app:generateBaselineProfile
erstellt wurde. Mit diesem Befehl werden alle Tests im Projekt ausgeführt, die durch die baselineProfile(project(:baselineProfile))
-Abhängigkeit definiert sind. Da das Modul auch Benchmarks zur späteren Überprüfung der Leistungssteigerungen enthält, schlagen diese Tests fehl und es wird eine Warnung angezeigt, dass Benchmarks nicht auf einem Emulator ausgeführt werden sollten.
android .testInstrumentationRunnerArguments .androidx.benchmark.enabledRules=BaselineProfile
Dazu können Sie alle Generatoren für Baseline-Profile mit dem folgenden Argument für den Instrumentierungs-Runner filtern. Alle Benchmarks werden dann übersprungen:
Der vollständige Befehl sieht so aus:
./gradlew :app:generateBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
App mit Baseline-Profilen bereitstellen
Nachdem das Baseline-Profil generiert und in den Quellcode Ihrer App kopiert wurde, erstellen Sie wie gewohnt die Produktionsversion Ihrer App. Sie müssen nichts weiter tun, um die Baseline-Profile an Ihre Nutzer zu verteilen. Sie werden während des Builds vom Android Gradle-Plug-in ausgewählt und in Ihr AAB oder APK aufgenommen. Laden Sie als Nächstes das Build in Google Play hoch.
Wenn Nutzer die App installieren oder von der vorherigen Version aktualisieren, wird auch das Baseline-Profil installiert. Dies führt zu einer besseren Leistung beim ersten Ausführen der App.
Im nächsten Schritt wird gezeigt, wie Sie mit Baseline-Profilen überprüfen können, wie stark sich die Leistung der Anwendung verbessert.
8. (Optional) Generierung von Baseline-Profilen anpassen
Das Gradle-Plug-in für Baseline-Profile bietet Optionen zum Anpassen, wie die Profile an Ihre spezifischen Anforderungen angepasst werden. Sie können das Verhalten mit dem Konfigurationsblock baselineProfile { }
in Build-Scripts ändern.
Der Konfigurationsblock im :baselineprofile
-Modul wirkt sich darauf aus, wie die Generatoren ausgeführt werden. Sie können managedDevices
hinzufügen und festlegen, ob useConnectedDevices
oder Gradle-verwaltete Geräte verwendet werden sollen.
Im Konfigurationsblock des :app
-Zielmoduls wird festgelegt, wo die Profile gespeichert oder wie sie generiert werden. Sie können die folgenden Parameter ändern:
automaticGenerationDuringBuild
: Wenn diese Option aktiviert ist, können Sie das Referenzprofil beim Erstellen des Produktions-Release-Builds generieren. Dies ist hilfreich, wenn Sie vor der Veröffentlichung Ihrer App mit CI arbeiten.saveInSrc
: Gibt an, ob die generierten Baseline-Profile im Ordnersrc/
gespeichert werden. Alternativ können Sie über den Build-Ordner:baselineprofile
auf die Datei zugreifen.baselineProfileOutputDir
: definiert, wo die generierten Referenzprofile gespeichert werden sollen.mergeIntoMain
: Standardmäßig werden Baseline-Profile pro Buildvariante (Produktvariante und Buildtyp) generiert. Wenn Sie alle Profile insrc/main
zusammenführen möchten, aktivieren Sie dieses Flag.filter
: Sie können filtern, welche Klassen oder Methoden in die generierten Referenzprofile ein- oder daraus ausgeschlossen werden sollen. Das kann für Bibliotheksentwickler hilfreich sein, die nur den Code aus der Bibliothek einbinden möchten.
9. Verbesserungen der Startleistung prüfen
Nachdem Sie das Baseline-Profil generiert und Ihrer App hinzugefügt haben, prüfen Sie, ob es die gewünschte Wirkung auf die Leistung Ihrer App hat.
Mit dem neuen Modulassistenten wird eine Benchmark-Klasse namens StartupBenchmarks
erstellt. Sie enthält eine Benchmark, um die App-Startzeit zu messen und mit der Verwendung von Baseline-Profilen für die App zu vergleichen.
Die Klasse sieht so aus:
@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {
@get:Rule
val rule = MacrobenchmarkRule()
@Test
fun startupCompilationNone() =
benchmark(CompilationMode.None())
@Test
fun startupCompilationBaselineProfiles() =
benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
private fun benchmark(compilationMode: CompilationMode) {
rule.measureRepeated(
packageName = "com.example.baselineprofiles_codelab",
metrics = listOf(StartupTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.COLD,
iterations = 10,
setupBlock = {
pressHome()
},
measureBlock = {
startActivityAndWait()
// TODO Add interactions to wait for when your app is fully drawn.
// The app is fully drawn when Activity.reportFullyDrawn is called.
// For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
// from the AndroidX Activity library.
// Check the UiAutomator documentation for more information on how to
// interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
)
}
}
Dabei wird MacrobenchmarkRule
verwendet, mit dem Benchmarks für Ihre App ausgeführt und Leistungsmesswerte erfasst werden können. Der Einstiegspunkt zum Erstellen eines Benchmarks ist die measureRepeated
-Funktion aus der Regel.
Es sind mehrere Parameter erforderlich:
packageName:
, welche Anwendung gemessen werden soll.metrics
: Welche Art von Informationen Sie während des Benchmarks erfassen möchten.iterations
: wie oft der Benchmark wiederholt wird.startupMode
: wie Ihre Anwendung am Anfang des Benchmarks gestartet werden soll.setupBlock
: Gibt an, welche Interaktionen mit Ihrer App vor der Messung erfolgen müssen.measureBlock
: Interaktionen mit Ihrer App, die Sie während des Benchmarks messen möchten.
Die Testklasse enthält außerdem zwei Tests: startupCompilationeNone()
und startupCompilationBaselineProfiles()
, die die Funktion benchmark()
mit unterschiedlichen compilationMode
aufrufen.
CompilationMode
Der Parameter CompilationMode
definiert, wie die Anwendung vorab in Maschinencode kompiliert wird. Folgende Optionen stehen zur Verfügung:
DEFAULT
: Die Anwendung wird teilweise mit Baseline-Profilen vorkompiliert, sofern verfügbar. Dieser Wert wird verwendet, wenn keincompilationMode
-Parameter angewendet wird.None()
: Der App-Kompilierungsstatus wird zurückgesetzt und die App wird nicht vorab kompiliert. Die Just-in-time-Kompilierung (JIT) ist während der Ausführung der App weiterhin aktiviert.Partial()
: kompiliert die Anwendung mit Baseline-Profilen und/oder Aufwärmläufen vor.Full()
: kompiliert den gesamten Anwendungscode vorab. Das ist die einzige Option unter Android 6 (API 23) und niedriger.
Wenn du die Leistung deiner App optimieren möchtest, kannst du den DEFAULT
-Kompilierungsmodus auswählen, da die Leistung ähnlich ist wie beim Installieren der App über Google Play. Wenn Sie die Leistungsvorteile von Baseline-Profilen vergleichen möchten, können Sie die Ergebnisse der Kompilierungsmodi None
und Partial
vergleichen.
Benchmark ändern, um auf Inhalte zu warten
Die Benchmarks werden ähnlich wie die Generatoren für Baseline-Profile erstellt, indem Interaktionen mit Ihrer App geschrieben werden. Standardmäßig warten die erstellten Benchmarks nur darauf, dass der erste Frame gerendert wird, ähnlich wie bei der BaselineProfileGenerator
. Wir empfehlen, sie so zu verbessern, dass sie auf die asynchronen Inhalte warten.
Dazu können Sie die Erweiterungsfunktionen wiederverwenden, die Sie für den Generator geschrieben haben. Da dieser Benchmark die Startzeit erfasst (StartupTimingMetric()
), empfehlen wir, hier nur das Warten auf die asynchronen Inhalte zu berücksichtigen und dann einen separaten Benchmark für die anderen im Generator definierten User Journeys zu erstellen.
// ...
measureBlock = {
startActivityAndWait()
// The app is fully drawn when Activity.reportFullyDrawn is called.
// For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
// from the AndroidX Activity library.
waitForAsyncContent() // <------- Added to wait for async content.
// Check the UiAutomator documentation for more information on how to
// interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
Benchmarks ausführen
Sie können die Benchmarks auf die gleiche Weise ausführen wie instrumentierte Tests. Sie können die Testfunktion oder den gesamten Kurs mit dem entsprechenden Symbol ausführen.
Achten Sie darauf, dass Sie ein physisches Gerät ausgewählt haben, da das Ausführen von Benchmarks im Android-Emulator zur Laufzeit fehlschlägt und eine Warnung angezeigt wird, dass der Benchmark falsche Ergebnisse liefern kann. Sie können es zwar technisch in einem Emulator ausführen, messen aber die Leistung Ihres Hostcomputers. Bei hoher Auslastung sind die Benchmarks langsamer und umgekehrt.
Nachdem Sie den Benchmark ausgeführt haben, wird Ihre App neu erstellt und die Benchmarks werden ausgeführt. Durch die Benchmarks wird deine App basierend auf den von dir definierten iterations
mehrmals gestartet, beendet und sogar neu installiert.
Nach Abschluss der Benchmarks sehen Sie die Zeitangaben in der Android Studio-Ausgabe, wie im folgenden Screenshot dargestellt:
Wie Sie auf dem Screenshot sehen, ist die App-Startzeit für jede CompilationMode
unterschiedlich. Die Medianwerte sind in der folgenden Tabelle aufgeführt:
timeToInitialDisplay [ms] | timeToFullDisplay [ms] | |
Keine | 202,2 | 818,8 |
BaselineProfiles | 193,7 | 637,9 |
Verbesserung | 4 % | 28% |
Der Unterschied zwischen den Kompilierungsmodi für timeToFullDisplay
beträgt 180 ms. Das entspricht einer Verbesserung um ca. 28 %, wenn nur ein Referenzprofil vorhanden ist. Die CompilationNone
hat eine schlechtere Leistung, da das Gerät beim Starten der App die meisten JIT-Kompilierungen ausführen muss. Die CompilationBaselineProfiles
hat eine bessere Leistung, da bei der teilweisen Kompilierung mit Baseline-Profilen AOT der Code kompiliert wird, den der Nutzer am wahrscheinlichsten verwendet, und der nicht kritische Code nicht vorab kompiliert wird, sodass er nicht sofort geladen werden muss.
10. (Optional) Verbesserung der Scrollleistung prüfen
Ähnlich wie im vorherigen Schritt können Sie die Scrollleistung messen und prüfen. Erstellen Sie zuerst eine ScrollBenchmarks
-Testklasse mit der Benchmark-Regel und zwei Testmethoden, die unterschiedliche Kompilierungsmodi verwenden:
@LargeTest
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {
@get:Rule
val rule = MacrobenchmarkRule()
@Test
fun scrollCompilationNone() = scroll(CompilationMode.None())
@Test
fun scrollCompilationBaselineProfiles() = scroll(CompilationMode.Partial())
private fun scroll(compilationMode: CompilationMode) {
// TODO implement
}
}
Verwenden Sie in der Methode scroll
die Funktion measureRepeated
mit den erforderlichen Parametern. Verwenden Sie für den Parameter metrics
den Parameter FrameTimingMetric
, mit dem gemessen wird, wie lange es dauert, UI-Frames zu generieren:
private fun scroll(compilationMode: CompilationMode) {
rule.measureRepeated(
packageName = "com.example.baselineprofiles_codelab",
metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.WARM,
iterations = 10,
setupBlock = {
// TODO implement
},
measureBlock = {
// TODO implement
}
)
}
Diesmal müssen Sie die Interaktionen stärker zwischen setupBlock
und measureBlock
aufteilen, um nur die Framedauern während des ersten Layouts und des Scrollens der Inhalte zu messen. Fügen Sie daher die Funktionen, die den Standardbildschirm starten, in setupBlock
und die bereits erstellten Erweiterungsfunktionen waitForAsyncContent()
und scrollSnackListJourney()
in measureBlock
ein:
private fun scroll(compilationMode: CompilationMode) {
rule.measureRepeated(
packageName = "com.example.baselineprofiles_codelab",
metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.WARM,
iterations = 10,
setupBlock = {
pressHome()
startActivityAndWait()
},
measureBlock = {
waitForAsyncContent()
scrollSnackListJourney()
}
)
}
Sobald der Benchmark fertig ist, können Sie ihn wie zuvor ausführen, um Ergebnisse wie im folgenden Screenshot zu erhalten:
FrameTimingMetric
gibt die Dauer der Frames in Millisekunden (frameDurationCpuMs
) für das 50., 90., 95. und 99. Perzentil aus. Unter Android 12 (API-Level 31) und höher wird auch zurückgegeben, wie lange Ihre Frames über dem Grenzwert liegen (frameOverrunMs
). Der Wert kann negativ sein, was bedeutet, dass mehr Zeit für die Erstellung des Frames zur Verfügung stand.
Den Ergebnissen können Sie entnehmen, dass die Frame-Dauer von CompilationBaselineProfiles
im Durchschnitt um 2 ms kürzer ist, was für Nutzer möglicherweise nicht erkennbar ist. Für die anderen Perzentile sind die Ergebnisse jedoch offensichtlicher. Beim P99 beträgt die Differenz 43, 5 ms, was mehr als drei übersprungene Frames auf einem Gerät mit 90 fps entspricht. Bei Pixel 6 beträgt die maximale Zeit zum Rendern eines Frames beispielsweise 1.000 ms ÷ 90 fps = ca. 11 ms.
11. Glückwunsch
Glückwunsch! Sie haben dieses Codelab erfolgreich abgeschlossen und die Leistung Ihrer App mit Baseline-Profilen verbessert.
Weitere Informationen
Weitere Informationen finden Sie unter den folgenden Links:
- App-Leistung mit MacroBenchmark prüfen: Codelab, in dem das Benchmarking ausführlicher behandelt wird.
- Leistungsbeispiele: Repository, das MacroBenchmark- und andere Leistungsbeispiele enthält.
- Beispiel-App „Now In Android“: Eine echte Anwendung, die Benchmarking und Baseline-Profile verwendet, um die Leistung zu verbessern.