Przechwytywanie danych analizy porównawczej

Dane to główny rodzaj informacji wyodrębnianych z testów porównawczych. Są one przekazywane do funkcji measureRepeated jako List, co pozwala określić wiele mierzonych danych jednocześnie. Do przeprowadzenia testu porównawczego wymagany jest co najmniej 1 rodzaj danych.

Poniższy fragment kodu rejestruje dane dotyczące czasu renderowania klatki i niestandardowej sekcji śledzenia w interfejsie układu Lazy w 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)
            }
        }
    }

W przykładzie poniżej EntryRowCustomTrace reprezentuje niestandardową sekcję śledzenia zdefiniowaną w warstwach elementu kompozycyjnego za pomocą standardowego kodu trace(sectionName) { ... } w języku Kotlin. Aby podać dane dla TraceSectionMetric, musisz opakować komponenty docelowego interfejsu w bazie kodu produkcyjnego aplikacji za pomocą standardowego kodu Jetpack runtime trace:

@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)
            )
        }
    }
}

Wyniki testu porównawczego są wyświetlane bezpośrednio na karcie terminala Benchmark w Android Studio, jak pokazano na rysunku 1. Jeśli zdefiniowano kilka wskaźników, wszystkie ich obliczone punkty danych są łączone w oknie podsumowania.

Wyniki TraceSectionMetric i FrameTimingMetric.
Rysunek 1. Połączone wyniki konsoli TraceSectionMetric i FrameTimingMetric dla nowoczesnego układu Compose.

StartupTimingMetric, FrameTimingMetric, TraceSectionMetric i PowerMetric zostały szczegółowo opisane poniżej. Pełną listę dostępnych danych porównawczych znajdziesz w podklasach Metric w dokumentacji API.

StartupTimingMetric

StartupTimingMetric rejestruje dane o czasie uruchamiania aplikacji z tymi wartościami:

  • timeToInitialDisplayMs: czas od momentu, gdy system otrzyma intencję uruchomienia, do momentu wyrenderowania pierwszej klatki ekranu docelowego.
  • timeToFullDisplayMs: czas od momentu, gdy system otrzyma intencję uruchomienia, do momentu, gdy aplikacja zgłosi pełne wyrenderowanie przy użyciu wewnętrznych mechanizmów raportowania platformy. Pomiar kończy się po zakończeniu renderowania pierwszej klatki po sygnale pełnego wyrenderowania lub zawierającej ten sygnał.

StartupTimingMetric wyświetla wartości minimalne, medianę i maksymalne z iteracji uruchamiania. Aby ocenić poprawę czasu uruchamiania, zawsze skupiaj się na wartościach mediany, ponieważ dają one najlepsze oszacowanie typowego czasu uruchamiania u użytkowników.

W architekturze opartej na Compose nie próbuj ręcznie wywoływać funkcji activity.reportFullyDrawn. Zamiast tego używaj bezpiecznych dla Compose asynchronicznych narzędzi ReportDrawn, ReportDrawnWhen lub ReportDrawnAfter w funkcjach kompozycyjnych ekranu, aby automatycznie sygnalizować Macrobenchmark, kiedy asynchroniczne dane sieciowe lub złożone stany interfejsu zostały wyrenderowane.

Więcej informacji o analizowaniu i optymalizowaniu wydajności inicjowania znajdziesz w artykule Czas uruchamiania aplikacji.

FrameTimingMetric

FrameTimingMetric rejestruje dokładne informacje o czasie z klatek wygenerowanych przez test porównawczy, np. przewijanie listy lub złożoną animację układu interfejsu, i zwraca te wartości diagnostyczne:

  • frameOverrunMs: czas, o jaki dana klatka przekroczyła termin. Liczby dodatnie oznaczają pominiętą klatkę, której towarzyszy widoczne zacinanie się lub przeskakiwanie. Liczby ujemne wskazują, o ile szybciej klatka została ukończona w stosunku do terminu sprzętowego podsystemu. Uwaga: te dane są dostępne tylko na urządzeniach z Androidem 12 (poziom interfejsu API 31) lub nowszym.
  • frameDurationCpuMs: czas, przez który klatka była aktywnie generowana przez procesor w głównym wątku UI aplikacji i w Compose RenderThread.

Te pomiary są zbierane w rozkładzie 50, 90, 95 i 99 centyla:

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

Podczas optymalizowania hierarchii układów Jetpack Compose przyjrzyj się najmniej wydajnym klatkom (granice P95 i P99). Jeśli wartość frameOverrunMs gwałtownie wzrasta do dodatnich liczb całkowitych w wysokich percentylach, oznacza to, że rekompozycje blokują wątek główny podczas intensywnych animacji przewijania.

Więcej informacji o identyfikowaniu i rozwiązywaniu problemów z powolnymi klatkami znajdziesz w artykule o wydajności Jetpack Compose.

TraceSectionMetric

TraceSectionMetric rejestruje liczbę wystąpień określonej sekcji logu czasu i bezwzględny czas jej wykonania. W przypadku śledzenia czasu wyświetla minimalny, średni i maksymalny czas w milisekundach. Docelowa sekcja logu czasu jest definiowana przez wywołanie funkcji trace(sectionName) lub granice bloków niższego poziomu między Trace.beginSection(sectionName) a Trace.endSection() lub ich warianty asynchroniczne.

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

Domyślnie dane wyjściowe zawierają tylko sekcje śladu skompilowane bezpośrednio z plików binarnych pakietu aplikacji. Aby uwzględnić procesy pochodzące spoza granicy pakietu aplikacji, ustaw właściwość targetPackageOnly = false.

Podczas pracy nad śledzeniem środowiska wykonawczego Jetpack Compose możesz wyświetlać poszczególne funkcje typu „composable” na wykresach śledzenia systemu bez konieczności ręcznego pisania kodu śledzenia. Wystarczy, że włączysz śledzenie kompozycji.

Chociaż dodanie zależności androidx.compose.runtime:runtime-tracing do aplikacji docelowej wystarczy do ręcznego śledzenia profilerów, przechwytywanie tych śladów automatycznie w ramach testu Macrobenchmark wymaga dodatkowej konfiguracji w module testu porównawczego.

Pełne instrukcje konfiguracji znajdziesz w artykule Przechwytywanie logu czasu za pomocą Jetpack Macrobenchmark.

PowerMetric

PowerMetric rejestruje zmiany mocy lub energii podczas działania testu porównawczego makro. Każda wybrana kategoria jest podzielona na mierzalne komponenty sprzętowe, a niewybrane kategorie są zgrupowane w zasobniku „niewybrane”.

Wymagania sprzętowe: te dane mierzą zużycie w całym systemie, a nie w poszczególnych aplikacjach. W związku z tym zbieranie danych jest ograniczone do fizycznych urządzeń Google Pixel 6, Pixel 6 Pro i nowszych.

Wskaźnik zwraca 2 pomiary na kategorię:

  • power<category>Uw: ilość energii zużytej podczas testu w tej kategorii (mierzona w mikrowatach).
  • energy<category>Uws: łączna ilość energii przesłanej na jednostkę czasu podczas testu w tej kategorii (mierzona w mikrowatosekundach).

Kategorie obejmują:

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

W przypadku niektórych kategorii, takich jak CPU, może być trudno oddzielić pracę wykonywaną przez inne procesy od pracy wykonywanej przez Twoją aplikację. Aby zminimalizować zakłócenia, usuń lub ogranicz niepotrzebne aplikacje i konta.

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

Analizowanie podstawowych podsystemów

PowerMetric rejestruje zmiany w poborze mocy lub energii w czasie trwania testu w przypadku podanych kategorii zasilania. Każda wybrana kategoria jest dzielona na mierzalne podskładniki, a niewybrane kategorie są dodawane do danych „niewybrane”.

Dane wyjściowe terminala są mapowane na żądaną konfigurację:

  • powerCategoryCpuUw: ilość energii zużytej przez procesor podczas testu.
  • powerCategoryGpuUw: ilość energii zużytej przez GPU podczas testu.
  • powerUnselectedUw: łączna moc zużywana przez wszystkie dostępne kategorie sprzętu, które nie zostały wyraźnie określone w mapie inicjalizacji.

Aby zapobiec nieregularnym skokom danych na szynach sprzętowych podczas działania, przed rozpoczęciem pętli Macrobenchmark ustaw jasność ekranu blokady na stałą wartość, utrzymuj stabilną temperaturę urządzenia i zamknij konkurujące procesy w tle.

Dodatkowe materiały

Wyświetlanie treści