Como criar uma comparação da Macrobenchmark

Use a biblioteca Macrobenchmark para testar casos de uso maiores do app, incluindo a inicialização e manipulações complexas de interface, como rolar uma RecyclerView ou mostrar animações. Se você quiser testar áreas menores do código, consulte a biblioteca Microbenchmark. Esta página mostra como configurar a biblioteca Macrobenchmark.

A biblioteca gera resultados comparativos no console do Android Studio e em um arquivo JSON com mais detalhes. Ela também fornece arquivos de rastreamento que podem ser carregados e analisados no Android Studio.

Use a biblioteca Macrobenchmark em um ambiente de integração contínua (CI), conforme descrito em Comparação na integração contínua.

Você pode usar a Macrobenchmark para gerar perfis de referência. Primeiro, configure a biblioteca Macrobenchmark e, em seguida, crie um perfil de referência.

Configuração do projeto

Recomendamos que você use a Macrobenchmark com a versão mais recente do Android Studio para aproveitar os recursos do ambiente de desenvolvimento integrado à Macrobenchmark.

Configurar o módulo Macrobenchmark

A biblioteca Macrobenchmark exige um módulo com.android.test separado do código do app, responsável por executar os testes que medem o desempenho dele.

No Android Studio, há um modelo disponível para simplificar a configuração do módulo da biblioteca Macrobenchmark. O modelo de módulo de comparação cria automaticamente um módulo no projeto para medir o app criado por um módulo, incluindo um exemplo de inicialização de comparação.

Para usar o modelo e criar um novo módulo, siga estas etapas:

  1. Clique com o botão direito do mouse no seu projeto ou módulo no painel Project do Android Studio e selecione em New > Module.

  2. Selecione Benchmark no painel Templates. É possível personalizar o app de destino (o app que vai ser comparado) e também o nome do pacote e do novo módulo da Macrobenchmark.

  3. Clique em Finish.

Modelo de módulo de comparação

Figura 1. Modelo de módulo de comparação

Configurar o app

Para comparar um app, conhecido como destino da Macrobenchmark, ele precisa ser profileable, o que permite ler informações detalhadas sobre o trace sem afetar o desempenho. O assistente de módulo adiciona a tag <profileable> automaticamente ao arquivo AndroidManifest.xml do app.

Confira se o app de destino inclui o ProfilerInstaller 1.3 ou mais recente, que a biblioteca Macrobenchmark precisa para ativar a captura de perfil, redefinir e limpar o cache do sombreador.

Configure o app comparado para que fique o mais próximo possível da versão de lançamento ou de produção. Defina-o como não depurável e, de preferência, com a minificação ativada, que melhora o desempenho. Normalmente, isso é feito criando uma cópia da variante de lançamento, que tem o mesmo desempenho, mas é assinada localmente com chaves de depuração. Como alternativa, use initWith para instruir o Gradle a fazer isso por você:

Kotlin

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
    }

    create("benchmark") {
        initWith(getByName("release"))
        signingConfig = signingConfigs.getByName("debug")
    }
}

Groovy

buildTypes {
    val release = getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
        // In real app, this would use its own release keystore
        signingConfig = signingConfigs.getByName("debug")
    }

    create("benchmark") {
        initWith(release)
        signingConfig = signingConfigs.getByName("debug")
        proguardFiles("benchmark-rules.pro")
    }
}

Para garantir que a comparação crie e teste a variante correta do app, conforme mostrado na Figura 2, faça o seguinte:

  1. Faça uma sincronização com o Gradle.
  2. Abra o painel Build Variants.
  3. Selecione a variante de comparação do app e do módulo da Macrobenchmark.

Selecionar a variante de comparação

Figura 2. Selecione a variante da comparação.

(Opcional) Configurar apps com vários módulos

Caso seu app tenha mais de um módulo do Gradle, confira se os scripts de build sabem qual variante vai ser compilada. Adicione a propriedade matchingFallbacks ao tipo de build benchmark dos módulos :macrobenchmark e :app. Os outros módulos do Gradle podem ter a mesma configuração anterior.

Kotlin

create("benchmark") {
    initWith(getByName("release"))
    signingConfig = signingConfigs.getByName("debug")

    matchingFallbacks += listOf("release")
}

Groovy

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
}

Sem isso, o tipo de build benchmark recém-adicionado faz com que o build falhe e mostra esta mensagem de erro:

> Could not resolve project :shared.
     Required by:
         project :app
      > No matching variant of project :shared was found.
      ...

Ao selecionar as variantes de build no seu projeto, escolha benchmark para os módulos :app e :macrobenchmark, e release para qualquer outro módulo no app, conforme mostrado na Figura 3:

Variantes de comparação para projetos com vários módulos, com tipos de build de lançamento e de comparação selecionados

Figura 3. Variantes de comparação para projetos com vários módulos, com tipos de build de lançamento e de comparação selecionados.

Para mais informações, consulte Usar gerenciamento de dependências com reconhecimento de variantes.

(Opcional) Configurar variações de produtos

Se você tem diversas variações de produto definidas no app, configure o módulo :macrobenchmark para que ele saiba qual variação vai ser criada e comparada.

Os exemplos nesta página estão usando as duas variações de produto no módulo :app: demo e production, conforme mostrado no snippet a seguir:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
        // ...
    }
    create("production") {
        dimension = "environment"
        // ...
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
        // ...
    }

    production {
        dimension 'environment'
        // ...
    }
}

Sem essa configuração, você pode receber um erro de build semelhante ao que acontece com vários módulos Gradle:

Could not determine the dependencies of task ':macrobenchmark:connectedBenchmarkAndroidTest'.
> Could not determine the dependencies of null.
   > Could not resolve all task dependencies for configuration ':macrobenchmark:benchmarkTestedApks'.
      > Could not resolve project :app.
        Required by:
            project :macrobenchmark
         > The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'benchmark', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.3.0'. However we cannot choose between the following variants of project :app:
             - demoBenchmarkRuntimeElements
             - productionBenchmarkRuntimeElements
           All of them match the consumer attributes:
           ...

As duas seções a seguir são maneiras de configurar a comparação com diversas variações de produto.

Usar missingDimensionStrategy

Especificar missingDimensionStrategy no defaultConfig do módulo :macrobenchmark instrui o sistema de build a usar a dimensão de variações como substituta. Especifique quais dimensões usar se você não as encontrar no módulo. No exemplo a seguir, a variação production é usada como a dimensão padrão:

Kotlin

defaultConfig {
    missingDimensionStrategy("environment", "production")
}

Groovy

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

Dessa forma, o módulo :macrobenchmark só pode criar e comparar a variação de produto especificada, o que é útil quando você sabe que apenas uma delas tem a configuração adequada para comparação.

Definir variações de produtos no módulo :macrobenchmark

Para criar e comparar outras variações de produto, defina-as no módulo :macrobenchmark. Especifique-as de maneira semelhante ao módulo :app, mas atribua apenas a productFlavors a uma dimension. Nenhuma outra configuração é necessária:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
    }
    create("production") {
        dimension = "environment"
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
    }

    production {
        dimension 'environment'
    }
}

Depois de definir e sincronizar o projeto, escolha a variante de build relevante no painel Build Variants, conforme mostrado na Figura 4:

Variantes de comparação do projeto com variações de produtos mostrando as opções
&quot;productBenchmark&quot; e &quot;release&quot;
selecionadas

Figura 4. Variantes de comparação do projeto com variações de produto mostrando as opções "productionBenchmark" e "release" selecionadas.

Para saber mais, consulte Resolver erros de build relacionados à correspondência de variantes.

Criar uma classe de Macrobenchmark

O teste de comparação é fornecido pela API de regra MacrobenchmarkRule do JUnit4 na biblioteca Macrobenchmark. Ele contém um método measureRepeated que permite especificar várias condições de execução e comparar o app de destino.

É necessário especificar pelo menos o packageName do app de destino, quais metrics você quer medir e quantas iterations a comparação precisa executar.

Kotlin

@LargeTest
@RunWith(AndroidJUnit4::class)
class SampleStartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = TARGET_PACKAGE,
        metrics = listOf(StartupTimingMetric()),
        iterations = DEFAULT_ITERATIONS,
        setupBlock = {
            // Press home button before each run to ensure the starting activity isn't visible.
            pressHome()
        }
    ) {
        // starts default launch activity
        startActivityAndWait()
    }
}

Java

@LargeTest
@RunWith(AndroidJUnit4.class)
public class SampleStartupBenchmark {
    @Rule
    public MacrobenchmarkRule benchmarkRule = new MacrobenchmarkRule();

    @Test
    public void startup() {
        benchmarkRule.measureRepeated(
            /* packageName */ TARGET_PACKAGE,
            /* metrics */ Arrays.asList(new StartupTimingMetric()),
            /* iterations */ 5,
            /* measureBlock */ scope -> {
                // starts default launch activity
                scope.startActivityAndWait();
                return Unit.INSTANCE;
            }
        );
    }
}

Para conferir todas as opções de personalização da sua comparação, consulte a seção Personalizar as comparações.

Executar a comparação

Faça o teste no Android Studio para medir o desempenho do app no dispositivo. É possível executar as comparações da mesma forma que qualquer outro @Test usando a ação de gutter ao lado da classe ou do método de teste, como mostrado na Figura 5.

Executar a macrobenchmark com ação de gutter ao lado da classe de
teste

Figura 5. Executar a Macrobenchmark com ação de gutter ao lado da classe de teste

Também é possível fazer todas as comparações em um módulo do Gradle na linha de comando usando o comando connectedCheck:

./gradlew :macrobenchmark:connectedCheck

Você pode fazer um único teste executando o seguinte:

./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startup

Consulte Comparação em integração contínua para informações sobre como executar e monitorar comparações na integração contínua.

Resultados da comparação

Após a execução da comparação, as métricas são mostradas diretamente no Android Studio e são geradas para uso da CI em um arquivo JSON. Cada iteração medida captura um rastreamento do sistema separado. Você pode abrir esses resultados de rastreamento clicando nos links no painel Test Results, conforme mostrado na Figura 6:

Resultados de inicialização da
Macrobenchmark

Figura 6. Resultados de inicialização da Macrobenchmark

Quando o rastreamento é carregado, o Android Studio pede que você selecione o processo a ser analisado. A seleção é pré-preenchida com o processo do app de destino, conforme mostrado na Figura 7:

Seleção de processos de rastreamento do
Studio

Figura 7. Seleção de processos de rastreamento do Studio

Após o arquivo de rastreamento ser carregado, o Studio mostra os resultados na ferramenta CPU Profiler:

Rastreamento do
Studio

Figura 8. Rastreamento do Studio.

Os relatórios JSON e todos os rastros de criação de perfil também são copiados automaticamente do dispositivo para o host. Eles são gravados na máquina host no seguinte local:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/

Acessar arquivos de rastreamento manualmente

Para usar a ferramenta Perfetto para analisar um arquivo de rastreamento, outras etapas precisam ser seguidas. O Perfetto possibilita inspecionar todos os processos que ocorrem em todo o dispositivo durante o rastreamento, enquanto o CPU Profiler do Android Studio limita a inspeção a um único processo.

Se você invocar os testes no Android Studio ou na linha de comando do Gradle, os arquivos de rastreamento serão copiados automaticamente do dispositivo para o host. Eles são gravados na máquina host no seguinte local:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace

Quando você tiver o arquivo de rastreamento no sistema host, poderá abri-lo no Android Studio com File > Open no menu. A tela da ferramenta do criador de perfil mostrada na seção anterior vai aparecer.

Erros de configuração

Se o app estiver configurado incorretamente (por exemplo, se for depurável ou não estiver relacionado à criação de perfil), a Macrobenchmark vai retornar um erro, em vez de relatar uma medição incorreta ou incompleta. É possível suprimir esses erros usando o argumento androidx.benchmark.suppressErrors.

A Macrobenchmark também retorna erros ao tentar medir um emulador ou em um dispositivo com bateria fraca, o que pode comprometer a disponibilidade de núcleos e a velocidade do clock.

Personalizar as comparações

A função measureRepeated aceita vários parâmetros que influenciam quais métricas a biblioteca coleta, como o app é iniciado e compilado ou quantas iterações a comparação executa.

Capturar as métricas

As métricas são o principal tipo de informação extraída das comparações. As seguintes métricas estão disponíveis:

Para mais informações sobre métricas, consulte Capturar métricas da Macrobenchmark.

Melhorar os dados de rastreamento com eventos personalizados

Pode ser útil instrumentar o app com eventos de rastreamento personalizados, que são mostrados com o restante do relatório de rastreamento e podem ajudar a apontar problemas específicos do app. Para saber mais sobre como criar eventos de rastreamento personalizados, consulte o guia Definir eventos personalizados.

CompilationMode

As Macrobenchmarks podem especificar uma classe CompilationMode, que define quanto do app precisa ser pré-compilado do bytecode DEX (o formato de bytecode dentro de um APK) para código de máquina (semelhante à linguagem C++ pré-compilada).

Por padrão, as Macrobenchmarks são executadas com o CompilationMode.DEFAULT, que instala um perfil de referência, se disponível, no Android 7 (nível 24 da API) e versões mais recentes. Se você estiver usando o Android 6 (nível 23 da API) ou uma versão anterior, o modo de compilação compila o APK completamente como o comportamento padrão do sistema.

Você pode instalar um perfil de referência se o app de destino tiver um perfil de referência e usar a biblioteca ProfileInstaller.

No Android 7 e versões mais recentes, é possível personalizar o CompilationMode para afetar a quantidade de pré-compilação no dispositivo e imitar diferentes níveis de compilação antecipada (AOT, na sigla em inglês) ou armazenamento em cache JIT. Consulte CompilationMode.Full, CompilationMode.Partial, CompilationMode.None e CompilationMode.Ignore.

Esse recurso é criado com base em comandos de compilação do ART. Cada comparação limpa os dados de perfil antes de começar para garantir a não interferência entre elas.

StartupMode

Para iniciar uma atividade, é possível transmitir um modo de inicialização predefinido: COLD, WARM ouHOT. Esse parâmetro muda a forma como a atividade é iniciada e o estado do processo no início do teste.

Para saber mais sobre os tipos de inicialização, consulte Tempo de inicialização do app.

Exemplos

Um projeto de exemplo está disponível no Exemplo de Macrobenchmark do repositório no GitHub.

Enviar feedback

Para comunicar problemas ou enviar solicitações de recursos para a MacroBenchmark Jetpack, consulte o Issue Tracker público.