Como criar uma comparação da Macrobenchmark

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

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.

Também é possível usar a biblioteca Macrobenchmark em um ambiente de integração contínua (CI), conforme descrito em Executar comparações na integração contínua.

Configuração do projeto

Recomendamos usar a Macrobenchmark com a versão mais recente do Android Studio (Bumblebee 2021.1.1 ou mais recente). Há novos recursos nessa versão 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 do app.

Bumblebee (ou mais recente)

No Android Studio Bumblebee 2021.1.1 há um modelo disponível para simplificar a configuração do módulo da 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 as etapas abaixo:

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

  2. Selecione Benchmark no painel Templates.

  3. É possível personalizar o aplicativo de destino (o app que vai ser comparado) e também o nome do pacote e do novo módulo da Macrobenchmark.

  4. Clique em Finish.

Modelo de módulo Benchmark

Arctic Fox

No Arctic Fox, você vai criar um módulo de biblioteca e o converter em um módulo de teste.

  1. Clique com o botão direito do mouse no seu projeto ou módulo no painel Project do Android Studio e clique em New > Module.
  2. Selecione Android Library no painel Templates.
  3. Digite macrobenchmark como o nome do módulo.
  4. Defina o Minimum SDK como API 23: Android M.
  5. Clique em Finish.

Configurar um novo módulo de biblioteca

Modificar o arquivo do Gradle

Personalize o build.gradle do módulo Macrobenchmark da seguinte maneira:

  1. Mude o plug-in de com.android.library para com.android.test.
    apply plugin 'com.android.test'
  2. Adicione outras propriedades obrigatórias do módulo de teste no bloco android {}:

    Groovy

    android {
        // ...
        // Note that your module name may have different name
        targetProjectPath = ":app"
        // Enable the benchmark to run separately from the app process
        experimentalProperties["android.experimental.self-instrumenting"] = true
    
        buildTypes {
            // declare a build type to match the target app's build type
            benchmark {
                debuggable = true
                signingConfig = debug.signingConfig
            }
        }
    }
      

    Kotlin

    android {
        // ...
        // Note that your module name may have different name
        targetProjectPath = ":app"
        // Enable the benchmark to run separately from the app process
        experimentalProperties["android.experimental.self-instrumenting"] = true
    
        buildTypes {
            // declare a build type to match the target app's build type
            create("benchmark") {
                isDebuggable = true
                signingConfig = signingConfigs.getByName("debug")
            }
        }
    }
      

  3. Mude todas as dependências com os nomes testImplementation ou androidTestImplementation para implementation.
  4. Adicione uma dependência à biblioteca Macrobenchmark:
    implementation 'androidx.benchmark:benchmark-macro-junit4:1.1.0-rc02'
  5. Permita apenas o buildType benchmark para este módulo. Após o bloco android{}, mas antes do bloco dependencies{}, adicione:

    Groovy

    androidComponents {
        beforeVariants(selector().all()) {
            // enable only the benchmark buildType, since we only want to measure close to release performance
            enabled = buildType == 'benchmark'
        }
    }
      

    Kotlin

    androidComponents {
        beforeVariants {
            // enable only the benchmark buildType, since we only want to measure close to release performance
            it.enable = it.buildType == "benchmark"
        }
    }
      
  6. Simplificar a estrutura de diretórios.

    Em um módulo com.android.test, há apenas um diretório de origem para todos os testes. Exclua os outros diretórios de origem, incluindo src/test e src/androidTest, já que eles não são usados.

Configurar o aplicativo

Para avaliar um app, conhecido como destino da Macrobenchmark, ele precisa ser profileable para ler informações detalhadas sobre o rastreamento. É possível ativar isso na tag <application> do arquivo AndroidManifest.xml do app:

<!-- enable profiling by macrobenchmark -->
<profileable
    android:shell="true"
    tools:targetApi="q" />

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 da versão, 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ê:

Groovy

buildTypes {
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }

    benchmark {
        initWith buildTypes.release
        signingConfig signingConfigs.debug

Kotlin

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

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

Sincronize com o Gradle, abra o painel Build Variants à esquerda e selecione a variante de comparação do app e do módulo da Macrobenchmark. Isso vai garantir que a comparação crie e teste a variante correta do app:

Selecionar variante de comparação

Configurar um aplicativo com vários módulos

Caso seu app tenha mais de um módulo do Gradle, verifique se os scripts de build sabem qual variante de build vai ser compilada. Sem isso, o buildType 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.

Para corrigir o problema, adicione a propriedade matchingFallbacks no buildType benchmark dos seus módulos :macrobenchmark e :app. Os outros módulos do Gradle podem ter a mesma configuração anterior.

Groovy

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
}

Kotlin

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

    matchingFallbacks += listOf('release')
}

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, como mostrado na imagem abaixo:

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

Para ver mais informações, consulte o gerenciamento de dependências com reconhecimento 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 como o aplicativo de destino precisa ser executado e comparado.

É necessário especificar pelo menos o packageName do aplicativo 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 = 5,
        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 ver todas as opções de personalização da 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 você executa qualquer outro @Test usando a ação de gutter ao lado da classe ou do método de teste, conforme mostrado na imagem abaixo.

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

Ou para executar um único teste:

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

Consulte a seção Comparações na CI para ver informações sobre como fazer 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 exibidas diretamente no Android Studio e também são geradas para uso da CI em um arquivo JSON. Cada iteração medida captura um rastreamento do sistema separado. É possível abrir esses rastreamentos de resultados clicando em um dos links no painel Test Results, conforme mostrado na imagem abaixo.

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:

Seleção de processos de rastreamento do Studio

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

Rastreamento do Studio

Acessar arquivos de rastreamento manualmente

Se você estiver usando uma versão mais antiga do Android Studio (anterior ao Arctic Fox 2020.3.1) ou quiser usar a ferramenta Perfetto (link em inglês) para analisar um arquivo de rastreamento, há outras etapas que 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 usar a linha de comando do Gradle, os arquivos de rastreamento vão ser copiados automaticamente do dispositivo para o host. Eles são gravados na máquina host em:

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

Quando o arquivo de rastreamento for criado no sistema host, você vai poder abri-lo no Android Studio no menu em File > Open. 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.

Erros também são gerados ao tentar medir o desempenho em um emulador ou em um dispositivo com bateria fraca, porque isso pode comprometer a disponibilidade de núcleos e a velocidade de processamento.

Personalizar as comparações

A função measureRepeated aceita vários parâmetros que têm influência sobre as métricas que a biblioteca coleta, como o aplicativo é iniciado e compilado ou quantas iterações a comparação vai executar.

Capturar as métricas

As métricas são o principal tipo de informação extraída das comparações. As opções disponíveis são StartupTimingMetric, FrameTimingMetric e TraceSectionMetric. Para ver mais informações sobre eles, consulte a página Capturar as métricas.

Melhorar os dados de rastreamento com eventos personalizados

Pode ser útil instrumentar o aplicativo com eventos de rastreamento personalizados, que são vistos 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 comparações da Macrobenchmark 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 comparações da Macrobenchmark são executadas com CompilationMode.DEFAULT, que instala um perfil de base (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 aplicativo de destino tiver um perfil de referência e usar a biblioteca ProfileInstaller.

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

Essa funcionalidade é criada 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 inicializar uma atividade, é possível transmitir um modo de inicialização predefinido (um de COLD, WARM ou HOT). 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 a documentação sobre inicialização do Android vitals.

Exemplos

Um projeto de exemplo está disponível como parte do repositório Android/performance-samples (link em inglês) no GitHub.

Enviar feedback

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