Comparação em integração contínua

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 como a captura e avaliação dos resultados é diferente dos testes regulares.

Resultados fuzzy

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

Faça as comparações 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 são executados com uma tarefa connectedCheck do Gradle. Essa tarefa cria automaticamente os APKs de destino e teste e executa os testes nos dispositivos conectados ao servidor de CI. Com a execução em CI, esse fluxo geralmente precisa ser dividido em etapas separadas.

Criar

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

Normalmente, essas etapas são realizadas sem a necessidade de executar tarefas do Gradle. É possível que eles sejam abstraídos pelo uso de um serviço que permita executar 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 conferir todas as opções de argumentos, consulte Argumentos de instrumentação de Microbenchmark ou Adicionar argumentos de instrumentação para a 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, verificando 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

Para saber mais sobre como executar testes de instrumentação na linha de comando, consulte Executar testes com o adb.

Bloquear relógios

O plug-in do Gradle de Microbenchmark fornece o comando ./gradlew lockClocks para bloquear os clocks 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 vai enviar esses comandos a um dispositivo conectado.

Saiba mais sobre como receber comparações consistentes para entender por que é útil bloquear os clocks da CPU.

Coletar os resultados

As bibliotecas de comparação geram medições em JSON, bem como 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 serão 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 copiar os arquivos 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 mostra 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 serão armazenados no dispositivo usando o argumento de instrumentação additionalTestOutputDir. O app precisa ter permissões de gravação nessa 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 saber mais informações, consulte Desativar temporariamente o armazenamento com escopo.

Recuperar os arquivos

Para recuperar 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, confira 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