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ć jednocześnie kilka mierzonych danych. Aby można było przeprowadzić test porównawczy, wymagany jest co najmniej 1 rodzaj danych.

Poniższy fragment kodu rejestruje dane dotyczące czasu renderowania klatek i niestandardowe dane sekcji śledzenia w przypadku interfejsu leniwego układu 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 poniższym przykładzie EntryRowCustomTrace reprezentuje niestandardową sekcję śledzenia zdefiniowaną w warstwach elementów kompozycyjnych za pomocą standardowego opakowania bloku Kotlin trace(sectionName) { ... }. Aby udostępnić dane dla TraceSectionMetric, musisz opakować docelowe komponenty interfejsu w produkcyjnej bazie kodu aplikacji standardowym opakowaniem bloku trace środowiska wykonawczego Jetpack:

@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 testów porównawczych są wyświetlane bezpośrednio na karcie terminala Benchmark w Android Studio, jak pokazano na ilustracji 1. Jeśli zdefiniowano kilka danych, wszystkie obliczone punkty danych są łączone w oknie podsumowania.

Wyniki TraceSectionMetric i FrameTimingMetric.
Ilustracja 1. Połączone wyniki konsoli TraceSectionMetric i FrameTimingMetric w przypadku nowoczesnego układu Compose.

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

StartupTimingMetric

StartupTimingMetric rejestruje dane dotyczące czasu uruchamiania aplikacji z tymi wartościami:

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

StartupTimingMetric wyświetla wartości minimalne, mediany i maksymalne z iteracji uruchamiania. Aby ocenić poprawę czasu uruchamiania, zawsze skupiaj się na medianach, ponieważ najlepiej odzwierciedlają one typowe czasy uruchamiania przez użytkowników.

W architekturze opartej na Compose nie próbuj ręcznie wywoływać activity.reportFullyDrawn. Zamiast tego użyj bezpiecznych dla Compose narzędzi asynchronicznych ReportDrawn, ReportDrawnWhen lub ReportDrawnAfter w kompozycyjnych elementach ekranu, aby automatycznie sygnalizować Macrobenchmark, kiedy dane sieci asynchronicznej lub złożone stany interfejsu zostaną wyrenderowane.

Więcej informacji o analizowaniu i optymalizowaniu wydajności inicjowania, zobacz Czas uruchamiania aplikacji.

FrameTimingMetric

FrameTimingMetric rejestruje dokładne informacje o czasie renderowania klatek wygenerowanych podczas testu porównawczego, np. przewijania listy lub animacji złożonego układu interfejsu, i wyświetla te wartości diagnostyczne:

  • frameOverrunMs: czas, o jaki dana klatka przekroczyła limit czasu. Liczby dodatnie wskazują na pominiętą klatkę, której towarzyszy widoczne zacinanie się lub przerywanie. Liczby ujemne wskazują, o ile szybciej klatka została wyrenderowana w porównaniu z limitem czasu sprzętu podsystemu. Uwaga: ta wartość jest dostępna tylko w Androidzie 12 (poziom API 31) i nowszych wersjach.
  • frameDurationCpuMs: czas, przez jaki klatka była aktywnie generowana na procesorze zarówno w głównym wątku UI aplikacji, jak i w RenderThread Compose.

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 optymalizacji hierarchii układu Jetpack Compose zwróć uwagę na klatki o najgorszej wydajności (granice P95 i P99). Jeśli frameOverrunMs osiąga wysokie wartości dodatnie w wysokich centylach, oznacza to, że ponowne komponowanie blokuje główny wątek podczas intensywnych animacji przewijania.

Więcej informacji o identyfikowaniu i rozwiązywaniu problemów z wolnymi klatkami znajdziesz w artykule Jetpack Compose Performance.

TraceSectionMetric

TraceSectionMetric rejestruje liczbę wystąpień określonej sekcji śledzenia i bezwzględny czas jej wykonania. W przypadku śledzenia czasu wyświetla minimalny, medianowy i maksymalny czas w milisekundach. Docelowa sekcja śledzenia jest definiowana przez wywołanie funkcji trace(sectionName) lub granice bloku 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 ta wartość wyświetla tylko sekcje śledzenia skompilowane bezpośrednio z plików binarnych pakietu aplikacji. Aby uwzględnić procesy pochodzące spoza granic pakietu aplikacji, ustaw właściwość targetPackageOnly = false.

Podczas pracy nad śledzeniem środowiska wykonawczego Jetpack Compose możesz wyświetlać poszczególne funkcje kompozycyjne na wykresach śledzenia systemu bez pisania ręcznych opakowań śledzenia, włączając śledzenie kompozycji.

Dodanie zależności androidx.compose.runtime:runtime-tracing do aplikacji docelowej wystarczy w przypadku ręcznego śledzenia profilu, ale programowe rejestrowanie tych śladów podczas testu Macrobenchmark wymaga dodatkowej konfiguracji w module testu.

Pełne instrukcje konfiguracji znajdziesz w artykule Rejestrowanie śladu za pomocą Jetpack Macrobenchmark.

PowerMetric

PowerMetric rejestruje zmianę mocy lub energii w czasie trwania testu Macrobenchmark. Każda wybrana kategoria jest dzielona na mierzalne komponenty sprzętowe, a niewybrane kategorie są grupowane w zasobniku „niewybrane”.

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

Ta wartość wyświetla 2 pomiary w każdej kategorii:

  • power<category>Uw: ilość energii zużytej podczas testu w tej kategorii (mierzona w mikrowatach).
  • energy<category>Uws: łączna ilość energii przekazanej 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, np. 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 zmianę mocy lub energii w czasie trwania testu w przypadku podanych kategorii mocy. Każda wybrana kategoria jest dzielona na mierzalne podkomponenty, a niewybrane kategorie są dodawane do wartości „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 procesor graficzny podczas testu.
  • powerUnselectedUw: łączna moc zużyta przez wszystkie dostępne kategorie sprzętu, które nie zostały wyraźnie zażądane w mapie inicjowania.

Aby zapobiec nieprawidłowym skokom danych na szynach sprzętowych podczas testu, zablokuj jasność ekranu na stałej wartości, utrzymuj stabilną temperaturę urządzenia i zamknij konkurencyjne procesy działające w tle przed rozpoczęciem pętli Macrobenchmark.

Dodatkowe materiały

Wyświetlanie treści