Makro-Benchmark-Messwerte erfassen

Messwerte sind die wichtigste Art von Informationen, die aus Ihren Benchmarks extrahiert werden. Sie werden als List an die measureRepeated Funktion übergeben, mit der Sie mehrere gemessene Messwerte gleichzeitig angeben können. Für die Ausführung des Benchmarks ist mindestens ein Messwerttyp erforderlich.

Das folgende Code-Snippet erfasst Messwerte für die Frame-Zeit und benutzerdefinierte Trace-Abschnitte für eine Lazy-Layout-Oberfläche von Jetpack Compose:

@OptIn(ExperimentalMetricApi::class)
    @Test
    fun scrollComposeList() {
        benchmarkRule.measureRepeated(
            // [START_EXCLUDE]
            packageName = TARGET_PACKAGE,
            metrics = listOf(
                FrameTimingMetric(),
                // Measure power usage. This is supported on Pixel 6 and later.
                PowerMetric(PowerMetric.Type.Power(
                    mapOf(
                        PowerCategory.CPU to PowerCategoryDisplayLevel.TOTAL,
                        PowerCategory.DISPLAY to PowerCategoryDisplayLevel.TOTAL,
                        PowerCategory.GPU to PowerCategoryDisplayLevel.TOTAL,
                        PowerCategory.NETWORK to PowerCategoryDisplayLevel.TOTAL,
                    )
                )),
                // Measure custom trace sections by name EntryRow (which is added to the EntryRow composable).
                // Mode.Sum measures combined duration and also how many times it occurred in the trace.
                // This way, you can estimate whether a composable recomposes more than it should.
                TraceSectionMetric("EntryRowCustomTrace", TraceSectionMetric.Mode.Sum),
                // This trace section takes into account the SQL wildcard character %,
                // which can find trace sections without the full name.
                // This way, you can measure composables produced by the composition tracing
                // and measure how long they took and how many times they recomposed.
                // WARNING: This metric only shows results when running with composition tracing, otherwise it won't be visible in the outputs.
                TraceSectionMetric("%EntryRow%", TraceSectionMetric.Mode.Sum),
            ),
            // Try switching to different compilation modes to see the effect
            // it has on frame timing metrics.
            compilationMode = CompilationMode.None(),
            startupMode = StartupMode.WARM, // restarts activity each iteration
            iterations = DEFAULT_ITERATIONS,
            // [END_EXCLUDE]
            setupBlock = {
                uiAutomator {
                    // Before starting to measure, navigate to the UI to be measured.
                    startIntent(Intent("$packageName.COMPOSE_ACTIVITY"))
                }
            }
        ) {
            uiAutomator {
                onElement { isScrollable }.fling(Direction.DOWN)
            }
        }
    }

Im folgenden Beispiel stellt EntryRowCustomTrace einen benutzerdefinierten Trace-Abschnitt dar, der in den zusammensetzbaren Elementebenen mit dem Standard-Kotlin-Block-Wrapper trace(sectionName) { ... } definiert wurde. Wenn Sie Daten für TraceSectionMetric bereitstellen möchten, müssen Sie die Ziel-UI-Komponenten im Produktionscode Ihrer Anwendung mit dem Standard-Jetpack-Laufzeit-Block-Wrapper trace umschließen:

@Composable
private fun EntryRow(entry: Entry, modifier: Modifier = Modifier) = trace("EntryRowCustomTrace") {
    Card(modifier = modifier) {
        Row(verticalAlignment = Alignment.CenterVertically) {
            Text(
                text = entry.contents,
                modifier = Modifier
                    .padding(16.dp)
                    .wrapContentSize()
            )

            Spacer(modifier = Modifier.weight(1f))

            Checkbox(
                checked = false,
                onCheckedChange = {},
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

Benchmark-Ergebnisse werden direkt auf dem Terminaltab Benchmark in Android Studio ausgegeben, wie in Abbildung 1 dargestellt. Wenn mehrere Messwerte definiert sind, werden alle berechneten Datenpunkte im Zusammenfassungsfenster kombiniert.

Ergebnisse von TraceSectionMetric und FrameTimingMetric.
Abbildung 1 Kombinierte Konsolenergebnisse von TraceSectionMetric und FrameTimingMetric für ein modernes Compose-Layout.

StartupTimingMetric, FrameTimingMetric, TraceSectionMetric und PowerMetric werden unten ausführlich behandelt. Eine vollständige Liste der verfügbaren Benchmark-Messwerte finden Sie in der API Referenz unter den Unterklassen von Metric.

StartupTimingMetric

StartupTimingMetric erfasst Messwerte für die App-Startzeit mit den folgenden Werten:

  • timeToInitialDisplayMs: Die Zeitspanne zwischen dem Empfang einer Startabsicht durch das System und dem Rendern des ersten Frames des Zielbildschirms.
  • timeToFullDisplayMs: Die Zeitspanne zwischen dem Empfang einer Startabsicht durch das System und dem Zeitpunkt, zu dem die App mithilfe der internen Plattform-Berichtsmechanismen meldet, dass sie vollständig gezeichnet wurde. Die Messung endet mit dem Rendern des ersten Frames nach dem Signal „vollständig gezeichnet“ oder mit dem Rendern des ersten Frames, der dieses Signal enthält.

StartupTimingMetric gibt die Minimal-, Median- und Maximalwerte der Startiterationen aus. Wenn Sie die Startzeit verbessern möchten, sollten Sie sich immer auf die Medianwerte konzentrieren, da sie die beste Schätzung der typischen Startzeiten für Nutzer liefern.

In einer Compose-First-Architektur sollten Sie nicht versuchen, activity.reportFullyDrawn manuell aufzurufen. Verwenden Sie stattdessen die Compose-sicheren asynchronen Dienstprogramme ReportDrawn, ReportDrawnWhen oder ReportDrawnAfter in Ihren zusammensetzbaren Bildschirmelementen, um Macrobenchmark automatisch zu signalisieren, wenn Ihre asynchronen Netzwerkdaten oder komplexen UI-Zustände gerendert wurden.

Weitere Informationen zum Analysieren und Optimieren der Initialisierungsleistung finden Sie unter App-Startzeit.

FrameTimingMetric

FrameTimingMetric erfasst genaue Zeitinformationen von Frames, die durch einen Benchmark-Testlauf erzeugt werden, z. B. beim Scrollen einer Liste oder einer komplexen UI-Layout-Animation, und gibt die folgenden Diagnosewerte aus:

  • frameOverrunMs: Die Zeitspanne, um die ein bestimmter Frame seine Frist verpasst. Positive Zahlen weisen auf einen verworfenen Frame hin, der mit sichtbaren Rucklern oder Stottern einhergeht. Negative Zahlen geben an, um wie viel schneller ein Frame im Vergleich zur Hardware-Frist des Subsystems fertiggestellt wurde. Hinweis: Dieser Messwert ist nur unter Android 12 (API-Level 31) und höher verfügbar.
  • frameDurationCpuMs: Die Zeitspanne, in der der Frame aktiv auf der CPU erzeugt wurde, sowohl im Haupt-UI-Thread der Anwendung als auch im Compose-RenderThread.

Diese Messungen werden in einer Verteilung des 50., 90., 95. und 99. Perzentils erfasst:

frameDurationCpuMs P50 3.5, P90 6.0, P95 6.4, P99 11.0
frameOverrunMs P50 -11.6, P90 -7.2, P95 -7.1, P99 -1.2

Wenn Sie Jetpack Compose-Layout-Hierarchien optimieren, sollten Sie sich die Frames mit der schlechtesten Leistung ansehen (die Grenzen des 95. und 99. Perzentils). Wenn frameOverrunMs bei den hohen Perzentilen in positive Ganzzahlen ansteigt, bedeutet das, dass Neuzusammensetzungen den Hauptthread bei intensiven Scroll-Animationen blockieren.

Weitere Informationen zum Identifizieren und Beheben langsamer Frames finden Sie unter Jetpack Compose-Leistung.

TraceSectionMetric

TraceSectionMetric erfasst, wie oft ein bestimmter Trace-Abschnitt vorkommt und wie viel Zeit für die Ausführung benötigt wird. Für die Zeitmessung werden die Minimal-, Median- und Maximalzeiten in Millisekunden ausgegeben. Der Ziel Trace-Abschnitt wird entweder durch den Funktionsaufruf trace(sectionName) oder durch die Blockgrenzen auf niedrigerer Ebene zwischen Trace.beginSection(sectionName) und Trace.endSection() oder deren asynchronen Varianten definiert.

EntryRowCustomTraceCount min 20.0, median 28.0, max 50.0
EntryRowCustomTraceSumMs min 34.9, median 44.4, max 66.6

Standardmäßig werden mit dem Messwert nur Trace-Abschnitte ausgegeben, die direkt aus den Binärdateien Ihres eigenen Anwendungspakets kompiliert wurden. Wenn Sie Prozesse einbeziehen möchten, die außerhalb der Paketgrenze Ihrer App ausgeführt werden, legen Sie die Eigenschaft targetPackageOnly = false fest.

Wenn Sie Jetpack Compose-Laufzeit-Tracing verwenden, können Sie einzelne zusammensetzbare Funktionen in Ihren System-Trace-Diagrammen anzeigen lassen, ohne manuelle Trace Wrapper zu schreiben. Aktivieren Sie dazu das Zusammensetzungs-Tracing.

Das Hinzufügen der Abhängigkeit androidx.compose.runtime:runtime-tracing zu Ihrer Zielanwendung reicht für manuelle Profiler-Traces aus. Wenn Sie diese Traces programmatisch während eines Macrobenchmark-Testlaufs erfassen möchten, ist eine zusätzliche Konfiguration in Ihrem Benchmark-Modul erforderlich.

Eine vollständige Einrichtungsanleitung finden Sie unter Trace mit Jetpack Macrobenchmark erfassen.

PowerMetric

PowerMetric erfasst die Änderung der Leistung oder Energie während des Macrobenchmark-Testlaufs. Jede ausgewählte Kategorie wird in ihre messbaren Hardwarekomponenten unterteilt, während nicht ausgewählte Kategorien in einem Bucket „nicht ausgewählt“ zusammengefasst werden.

Hardwareanforderung: Bei diesen Messwerten wird der systemweite Verbrauch gemessen und nicht die Berechnungen pro App. Daher ist die Datenerhebung auf physische Google Pixel 6-, Pixel 6 Pro- und neuere physische Geräte beschränkt.

Der Messwert gibt zwei Messungen pro Kategorie aus:

  • power<category>Uw: Die in dieser Kategorie während Ihres Tests verbrauchte Leistung (gemessen in Mikrowatt).
  • energy<category>Uws: Die insgesamt übertragene Energiemenge pro Zeiteinheit während des Tests in dieser Kategorie (gemessen in Mikrowattsekunden).

Zu den Kategorien gehören:

  • CPU
  • DISPLAY
  • GPU
  • GPS
  • MEMORY
  • MACHINE_LEARNING
  • NETWORK
  • UNCATEGORIZED

Bei einigen Kategorien wie CPU ist es möglicherweise schwierig, die Arbeit anderer Prozesse von der Arbeit Ihrer eigenen App zu trennen. Um die Störungen zu minimieren, entfernen oder beschränken Sie unnötige Apps und Konten.

powerCategoryCpuUw min 300.2, median 346.1, max 519.6
powerCategoryDisplayUw min 319.8, median 325.8, max 329.7
powerCategoryGpuUw min 18.8, median 23.3, max 36.9
powerCategoryNetworkUw min 97.3, median 123.3, max 681.3
powerTotalUw min 1234.8, median 1316.6, max 2112.4
powerUnselectedUw       min  483.3,  median  512.6,  max  561.7

Wichtige Subsysteme analysieren

PowerMetric erfasst die Änderung der Leistung oder Energie während des Tests für die angegebenen Leistungskategorien. Jede ausgewählte Kategorie wird in ihre messbaren Unterkomponenten unterteilt und nicht ausgewählte Kategorien werden dem Messwert „nicht ausgewählt“ hinzugefügt.

Die Terminalausgabe entspricht der angeforderten Konfiguration:

  • powerCategoryCpuUw: Die von der CPU während des Tests verbrauchte Leistung.
  • powerCategoryGpuUw: Die von der GPU während des Tests verbrauchte Leistung.
  • powerUnselectedUw: Die aggregierte Leistung, die von allen verfügbaren Hardwarekategorien verbraucht wird, die in Ihrer Initialisierungskonfiguration nicht explizit angefordert wurden.

Um während eines Testlaufs unregelmäßige Datenspitzen auf den Hardware-Rails zu vermeiden, sperren Sie die Bildschirmhelligkeit auf einen festen Wert, halten Sie eine stabile Gerätetemperatur ein und schließen Sie konkurrierende Hintergrundprozesse, bevor Sie die Macrobenchmark-Schleife starten.

Zusätzliche Ressourcen

Inhalte ansehen