Executar comparativos na integração contínua

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Você pode fazer comparações em integração contínua (CI) para acompanhar o desempenho ao longo do tempo e reconhecer regressões de desempenho (ou melhorias) antes mesmo do lançamento do app. Nesta página, você encontra informações básicas sobre comparações em CI.

Antes de começar a usar a comparação em CI, considere a diferença entre captura e avaliação dos resultados e testes regulares.

Resultados complexos

Embora as comparações sejam testes de instrumentação, os resultados não são apenas uma aprovação ou reprovação. As comparações fornecem medições de tempo para o dispositivo em que são executadas. Com os gráficos de resultados ao longo do tempo, você pode monitorar as mudanças e observar o ruído no sistema de medidas.

Usar dispositivos reais

As comparações devem ser executadas em dispositivos físicos Android. Embora seja possível, não é recomendado fazer isso em emuladores, já que eles não representam uma experiência realista do usuário e fornecem números vinculados aos recursos de SO e hardware do host. Use dispositivos reais ou um serviço que permita executar testes em dispositivos reais, como o Firebase Test Lab.

Executar as comparações

A execução das comparações como parte do pipeline de CI pode ser diferente da execução local no Android Studio. Normalmente, os testes de integração do Android seriam executados com uma tarefa connectedCheck do Gradle. Essa tarefa cria automaticamente os APKs de destino e de teste e executa os testes nos dispositivos conectados no momento. Com a execução em CI, esse fluxo geralmente precisa ser dividido em fases separadas.

Build

Para a biblioteca Microbenchmark, execute a tarefa assemble[VariantName]AndroidTest do Gradle, que cria o APK de teste que contém o código do aplicativo e o código testado.

Como alternativa, a biblioteca Macrobenchmark pede que você crie o APK de destino e o APK de teste separadamente. Portanto, execute as tarefas :app:assemble[VariantName] e :macrobenchmark:assemble[VariantName] do Gradle.

Instalar e executar

Geralmente, essas etapas são realizadas sem a necessidade de tarefas do Gradle, que podem ser abstraídas se você usa um serviço que permite executar os testes em dispositivos reais.

Para a instalação, use o comando adb install e especifique o APK de teste (ou o APK de destino).

Execute o comando de instrumento adb shell am para gerar todas as comparações.

adb shell am instrument -w com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Ao usar a biblioteca Macrobenchmark, use o androidx.test.runner.AndroidJUnitRunner normal como executor de instrumentação.

Você pode transmitir os mesmos argumentos de instrumentação usados na configuração do Gradle com o argumento -e. Para ver todas as opções de argumentos, consulte as páginas Microbenchmark ou Macrobenchmark.

Por exemplo, você pode definir o argumento dryRunMode para executar microcomparações como parte do processo de verificação de solicitação de envio. Com essa flag ativada, as microcomparações são geradas apenas em um único loop, permitindo verificar se estão sendo executadas corretamente, mas sem levar muito tempo no processo.

adb shell am instrument -w -e "androidx.benchmark.dryRunMode.enable" "true" com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Consulte Como executar testes com o adb para ver mais informações sobre a execução de testes de instrumentação na linha de comando.

Bloquear relógios

O plug-in do Gradle de Microbenchmark fornece o comando ./gradlew lockClocks para bloquear os relógios da CPU de um dispositivo com acesso root. Isso é útil para garantir que haja estabilidade em dispositivos com acesso root, como as versões "userdebug". É possível replicar isso com o script de shell lockClocks.sh, disponível na origem da biblioteca.

É possível executar o script diretamente de um host Linux ou Mac ou enviá-lo para o dispositivo com alguns comandos adb:

adb push path/lockClocks.sh /data/local/tmp/lockClocks.sh
adb shell /data/local/tmp/lockClocks.sh
adb shell rm /data/local/tmp/lockClocks.sh

Se você executar o script de shell diretamente em um host, ele enviará esses comandos para um dispositivo conectado.

Para saber por que é útil bloquear os relógios da CPU, veja como receber comparações consistentes.

Coletar os resultados

As bibliotecas de comparação geram medições em JSON, além de rastros de caracterização de perfil para um diretório no dispositivo Android após cada comparação. A biblioteca Macrobenchmark gera vários arquivos de rastreamento do perfetto, um por iteração medida de cada loop MacrobenchmarkRule.measureRepeated. No entanto, a Microbenchmark cria apenas um arquivo de rastreamento para todas as iterações de cada BenchmarkRule.measureRepeated. A caracterização de perfil com arquivos de rastreamento também é enviada para o mesmo diretório.

Salvar e localizar os arquivos

Se você executar as comparações com o Gradle, esses arquivos vão ser copiados automaticamente para o diretório de saídas do computador host em build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/. Se a comparação estiver em execução direta com o comando adb, você vai precisar copiá-los manualmente. Por padrão, os relatórios são armazenados no dispositivo, no diretório de mídia do armazenamento externo do app testado. Por conveniência, a biblioteca imprime o caminho do arquivo no Logcat. A pasta de saída pode ser diferente, dependendo da versão do Android em que as comparações estão sendo executadas.

Benchmark: writing results to /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Também é possível configurar o local em que os relatórios de comparação vão ser armazenados no dispositivo usando o argumento de instrumentação additionalTestOutputDir. O aplicativo precisa ter permissões de gravação nesta pasta.

adb shell am instrument -w -e additionalTestOutputDir /sdcard/Download/ com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

No Android 10 (nível 29 da API) e versões mais recentes, por padrão, os testes do app são executados em um sandbox de armazenamento, evitando que ele acesse arquivos fora do diretório específico. Para salvar em um diretório global (como /sdcard/Download), transmita o seguinte argumento de instrumentação:

-e no-isolated-storage true

Também é necessário permitir explicitamente as opções de armazenamento legadas no manifesto da comparação:

<application android:requestLegacyExternalStorage="true" ... >

Para ver mais informações, consulte Desativar temporariamente o armazenamento com escopo.

Recuperar os arquivos

Para encontrar os arquivos gerados no dispositivo, use o comando adb pull, que extrai o arquivo especificado no diretório atual do host.

adb pull /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Para recuperar todo o benchmarkData de uma pasta especificada, veja o seguinte snippet:

# The following command pulls all files ending in -benchmarkData.json from the directory
# hierarchy starting at the root /storage/emulated/0/Android.
adb shell find /sdcard/Download -name "*-benchmarkData.json" | tr -d '\r' | xargs -n1 adb pull

Os arquivos de rastreamento (.trace ou .perfetto-trace) são armazenados na mesma pasta que o benchmarkData.json. Assim, você pode encontrá-los da mesma maneira.

Exemplo de dados de comparação

As bibliotecas de comparação geram arquivos JSON com informações sobre o dispositivo em que o processo estava sendo executado e sobre as comparações executadas. O snippet a seguir representa o arquivo JSON gerado.

{
    "context": {
        "build": {
            "brand": "google",
            "device": "blueline",
            "fingerprint": "google/blueline/blueline:12/SP1A.210812.015/7679548:user/release-keys",
            "model": "Pixel 3",
            "version": {
                "sdk": 31
            }
        },
        "cpuCoreCount": 8,
        "cpuLocked": false,
        "cpuMaxFreqHz": 2803200000,
        "memTotalBytes": 3753299968,
        "sustainedPerformanceModeEnabled": false
    },
    "benchmarks": [
        {
            "name": "startup",
            "params": {},
            "className": "com.example.macrobenchmark.startup.SampleStartupBenchmark",
            "totalRunTimeNs": 4975598256,
            "metrics": {
                "timeToInitialDisplayMs": {
                    "minimum": 347.881076,
                    "maximum": 347.881076,
                    "median": 347.881076,
                    "runs": [
                        347.881076
                    ]
                }
            },
            "sampledMetrics": {},
            "warmupIterations": 0,
            "repeatIterations": 3,
            "thermalThrottleSleepSeconds": 0
        }
    ]
}

Outros recursos