Capturar métricas de Macrobenchmark

As métricas são o principal tipo de informação extraída das comparações. Elas são transmitidas para a measureRepeated função como uma List, permitindo especificar várias métricas medidas de uma só vez. Pelo menos um tipo de métrica é necessário para que a comparação seja executada.

O snippet de código abaixo captura as métricas de marcação do tempo para a renderização do frame e as da seção de rastros personalizados para uma interface de layout preguiçoso do 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)
            }
        }
    }

No exemplo a seguir, EntryRowCustomTrace representa uma seção de rastreamento personalizada definida nas camadas de itens combináveis usando o wrapper de bloco trace(sectionName) { ... } padrão do Kotlin. Para fornecer dados para TraceSectionMetric, é necessário envolver os componentes de interface de destino no código de produção do aplicativo com o wrapper de bloco trace do ambiente de execução padrão do 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)
            )
        }
    }
}

Os resultados da comparação são gerados diretamente na guia do terminal Benchmark no Android Studio, conforme mostrado na Figura 1. Se várias métricas forem definidas, todos os pontos de dados calculados serão combinados na janela de resumo.

Resultados de TraceSectionMetric e FrameTimingMetric.
Figura 1. Resultados combinados do console de TraceSectionMetric e FrameTimingMetric para um layout moderno do Compose.

StartupTimingMetric, FrameTimingMetric, TraceSectionMetric e PowerMetric são abordados em detalhes abaixo. Para uma lista completa das métricas de comparação disponíveis, consulte as subclasses de Metric na documentação de referência da API.

StartupTimingMetric

StartupTimingMetric captura métricas de marcação de tempo de inicialização do app com estes valores:

  • timeToInitialDisplayMs: tempo total entre o momento em que o sistema recebe uma intent de inicialização e a renderização do primeiro frame da tela de destino.
  • timeToFullDisplayMs: tempo total entre o momento em que o sistema recebe uma intent de inicialização e o momento em que o app é totalmente renderizado usando os mecanismos internos de relatórios da plataforma. A medição será interrompida quando a renderização do primeiro frame após (ou que contém) o sinal totalmente renderizado for concluída.

A StartupTimingMetric gera os valores mínimos, medianos e máximos das iterações de inicialização. Para avaliar a melhoria da inicialização, concentre-se sempre nos valores medianos, porque eles oferecem a melhor estimativa dos tempos de inicialização típicos do usuário.

Em uma arquitetura com o Compose em primeiro lugar, não tente invocar activity.reportFullyDrawn manualmente. Em vez disso, use os utilitários assíncronos seguros do Compose ReportDrawn, ReportDrawnWhen ou ReportDrawnAfter nos combináveis da tela para sinalizar automaticamente para a Macrobenchmark quando os dados de rede assíncronos ou os estados complexos da interface forem renderizados.

Para mais informações sobre como analisar e otimizar a performance de inicialização, consulte Tempo de inicialização do app.

FrameTimingMetric

FrameTimingMetric captura informações precisas de marcação de tempo de frames produzidos por uma jornada de benchmark, como a rolagem de uma lista ou uma animação de layout da interface complexa, e gera os seguintes valores de diagnóstico:

  • frameOverrunMs: por quanto tempo um frame específico perdeu o prazo. Números positivos indicam uma queda de frame acompanhada de instabilidade ou travamento visíveis. Números negativos indicam a rapidez com que um frame foi concluído em relação ao prazo do hardware do subsistema. Observação: essa métrica está disponível apenas no Android 12 (nível 31 da API) e versões mais recentes.
  • frameDurationCpuMs: o tempo que o frame passou sendo produzido ativamente na CPU na linha de execução principal da interface do aplicativo e na RenderThread do Compose.

Essas medidas são coletadas em uma distribuição de percentis 50, 90, 95 e 99:

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

Ao otimizar as hierarquias de layout do Jetpack Compose, observe os frames com pior performance (os limites P95 e P99). Se frameOverrunMs aumentar para números inteiros positivos nos percentis altos, isso indica que as recomposições estão paralisando a linha de execução principal durante animações de rolagem pesadas.

Para insights mais detalhados sobre como identificar e resolver frames lentos, consulte Performance do Jetpack Compose.

TraceSectionMetric

TraceSectionMetric captura o número de vezes que uma seção de rastreamento específica ocorre e o tempo absoluto necessário para a execução. Para o rastreamento de tempo, ela gera os tempos mínimo, mediano e máximo em milissegundos. A seção de rastreamento de destino é definida pela chamada de função trace(sectionName) ou pelos limites de bloco de nível inferior entre Trace.beginSection(sectionName) e Trace.endSection() ou as variantes assíncronas deles.

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

Por padrão, a métrica só gera seções de rastreamento compiladas diretamente dos binários do pacote do aplicativo. Para incluir processos originados de fora do limite do pacote do app, defina a propriedade targetPackageOnly = false.

Ao trabalhar no rastreamento do ambiente de execução do Jetpack Compose, é possível exibir funções combináveis individuais nos gráficos de rastreamento do sistema sem gravar wrappers de rastreamento manual ativando o rastreamento de composição.

Embora adicionar a dependência androidx.compose.runtime:runtime-tracing ao aplicativo de destino seja suficiente para rastreamentos manuais do criador de perfil, a captura desses rastreamentos de maneira programática em uma execução de Macrobenchmark exige uma configuração adicional no módulo de comparação.

Para instruções de configuração completas, consulte Capturar rastros com a Macrobenchmark do Jetpack.

PowerMetric

PowerMetric captura a mudança no consumo de bateria durante a execução da Macrobenchmark. Cada categoria selecionada é dividida em componentes de hardware mensuráveis, enquanto as categorias não selecionadas são agrupadas em um bucket "não selecionado".

Requisito de hardware: essas métricas medem o consumo do sistema, não os cálculos por app. Consequentemente, a coleta de dados é limitada aos dispositivos físicos Google Pixel 6, Pixel 6 Pro e mais recentes.

A métrica gera duas medições por categoria:

  • power<category>Uw: quantidade de energia consumida durante o teste da categoria (medida em microwatts).
  • energy<category>Uws: quantidade total de energia transferida por unidade de tempo durante o teste da categoria (medida em microwatt-segundos).

As categorias incluem:

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

Com algumas categorias, como CPU, pode ser difícil separar o trabalho feito por outros processos do trabalho feito pelo seu app. Para minimizar a interferência, remova ou restrinja contas e apps desnecessários.

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

Analisar subsistemas principais

PowerMetric captura a mudança de energia durante o teste para as categorias de energia fornecidas. Cada categoria selecionada é dividida em subcomponentes mensuráveis, e as categorias não selecionadas são adicionadas à métrica "não selecionada".

As saídas do terminal são mapeadas para a configuração solicitada:

  • powerCategoryCpuUw: quantidade de energia consumida pela CPU durante o teste.
  • powerCategoryGpuUw: quantidade de energia consumida pela GPU durante o teste.
  • powerUnselectedUw: a energia agregada consumida por todas as categorias de hardware disponíveis que não foram solicitadas explicitamente no mapa de inicialização.

Para evitar picos de dados instáveis nos trilhos de hardware durante uma execução, bloqueie o brilho da tela em um valor fixo, mantenha uma temperatura estável do dispositivo e feche os processos em segundo plano concorrentes antes de iniciar o loop da Macrobenchmark.

Outros recursos

Conteúdo de visualizações