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 umożliwia jednoczesne określanie wielu mierzonych rodzajów danych. Aby można było uruchomić test porównawczy, wymagany jest co najmniej 1 typ wskaźnika.

Ten fragment kodu rejestruje czas 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 przykładzie poniżej symbol EntryRowCustomTrace oznacza niestandardową sekcję śledzenia zdefiniowaną w warstwach elementów kompozycyjnych za pomocą standardowego bloku opakowującego w języku Kotlin trace(sectionName) { ... }. Aby dostarczać dane dla TraceSectionMetric, musisz umieścić docelowe komponenty interfejsu w bazie kodu produkcyjnego aplikacji w standardowym opakowaniu bloku środowiska wykonawczego Jetpack: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 wiele rodzajów danych, wszystkie obliczone punkty danych są łączone w oknie podsumowania.

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

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

StartupTimingMetric

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

  • timeToInitialDisplayMs: czas od momentu otrzymania przez system intencji uruchomienia do momentu wyrenderowania pierwszej klatki ekranu docelowego.
  • timeToFullDisplayMs: czas od momentu, w którym system otrzyma intencję uruchomienia, do momentu, w którym aplikacja zgłosi pełne wyrenderowanie za pomocą wewnętrznych mechanizmów raportowania platformy. Pomiar kończy się po wyrenderowaniu pierwszej klatki po sygnale w pełni narysowanym lub zawierającej ten sygnał.

StartupTimingMetric zwraca wartości minimalną, medianę i maksymalną z iteracji początkowych. Aby ocenić poprawę czasu uruchamiania, zawsze skupiaj się na wartościach mediany, ponieważ zapewniają one najlepsze oszacowanie typowych czasów uruchamiania u użytkowników.

W architekturze opartej na Compose nie próbuj ręcznie wywoływać activity.reportFullyDrawn. Zamiast tego używaj bezpiecznych dla Compose narzędzi asynchronicznych ReportDrawn, ReportDrawnWhen lub ReportDrawnAfter w kompozycjach ekranu, aby automatycznie sygnalizować bibliotece Macrobenchmark, kiedy zakończy się renderowanie asynchronicznych danych sieciowych lub złożonych stanów interfejsu.

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. podczas przewijania listy lub złożonego układu interfejsu użytkownika, i wyświetla te wartości diagnostyczne:

  • frameOverrunMs: o ile dana ramka przekracza termin. Liczby dodatnie oznaczają pominiętą klatkę, której towarzyszy widoczne szarpanie lub zacinanie się obrazu. Liczby ujemne wskazują, o ile szybciej klatka została wyrenderowana w porównaniu z terminem sprzętowym podsystemu. Uwaga: ten rodzaj danych jest dostępny tylko na urządzeniach z Androidem 12 (poziom API 31) lub nowszym.
  • frameDurationCpuMs: czas, przez jaki ramka 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ładów Jetpack Compose zwróć uwagę na klatki o najgorszej wydajności (granice P95 i P99). Jeśli w przypadku wysokich wartości procentowych frameOverrunMs gwałtownie wzrasta do dodatnich liczb całkowitych, oznacza to, że ponowne kompozycje blokują wątek główny podczas animacji przewijania.

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

TraceSectionMetric

TraceSectionMetric rejestruje liczbę wystąpień określonej sekcji śledzenia i bezwzględny czas jej wykonania. W przypadku śledzenia czasu podaje minimalny, średni 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 dane wyjściowe tego wskaźnika zawierają 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 typu „composable” na wykresach śledzenia systemu bez pisania ręcznych otoczek śledzenia, włączając śledzenie kompozycji.

Dodanie zależności androidx.compose.runtime:runtime-tracing do aplikacji docelowej wystarczy w przypadku ręcznych śladów profilera, ale programowe rejestrowanie tych śladów w ramach uruchomienia 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 trakcie działania testu porównawczego. Każda wybrana kategoria jest dzielona na mierzalne komponenty sprzętowe, a niewybrane kategorie są grupowane w „niewybranej” puli.

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

Ten rodzaj danych wyjściowych podaje 2 wartości w przypadku każdej kategorii:

  • power<category>Uw: ilość energii zużytej podczas testu w tej kategorii (mierzona w mikrowatach).
  • energy<category>Uws: łączna ilość energii przeniesionej 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 odróżnić pracę wykonaną przez inne procesy od pracy wykonanej 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 trakcie testu w przypadku podanych kategorii mocy. Każda wybrana kategoria jest dzielona na mierzalne podkategorie, a niewybrane kategorie są dodawane do danych „niewybrane”.

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

  • powerCategoryCpuUw: ilość energii zużytej przez procesor w trakcie 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 wskazane na mapie inicjowania.

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

Dodatkowe materiały

Wyświetla treści