A biblioteca Jetpack Benchmark permite que você compare rapidamente seu código baseado em Kotlin ou Java no Android Studio. A biblioteca lida com o aquecimento, mede o desempenho do código e as contagens de alocação e gera resultados comparativos no Console do Android Studio e em um arquivo JSON com mais detalhes.
Os casos de uso incluem rolagem de RecyclerView
, execução de consultas de banco de dados e
a medição de qualquer parte lenta do código que você quer acelerar.
É possível usar a biblioteca em um ambiente de integração contínua (CI, na sigla em inglês), conforme descrito em Executar comparações na integração contínua.
Se você ainda não adotou o AndroidX em um projeto que quer comparar, consulte Migrar um projeto existente usando o Android Studio.
Início rápido
Esta seção apresenta etapas rápidas para testar a comparação sem exigir que você transfira o código para os módulos. Como as etapas envolvem desativar a depuração de resultados de desempenho precisos, você não fará mudanças no sistema de controle de origem, mas elas ainda podem ser úteis quando quiser executar medições únicas.
Para realizar uma comparação única rapidamente, faça o seguinte:
Adicione a biblioteca ao arquivo
build.gradle
do módulo:project_root/module_dir/build.gradle
Groovy
dependencies { androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.1.0-alpha13' }
Kotlin
dependencies { androidTestImplementation("androidx.benchmark:benchmark-junit4:1.1.0-alpha13") }
Para desativar a depuração no manifesto de teste, atualize o elemento
<application>
para forçar a desativação temporária da depuração da seguinte maneira:project_root/module_dir/src/androidTest/AndroidManifest.xml
<!-- Important: disable debuggable for accurate performance results --> <application android:debuggable="false" tools:ignore="HardcodedDebugMode" tools:replace="android:debuggable"/>
Para adicionar sua comparação, inclua uma instância de
BenchmarkRule
em um arquivo de teste no diretórioandroidTest
. Para mais informações sobre como programar comparações, consulte Como programar uma comparação.O snippet de código a seguir mostra como adicionar uma comparação a um teste do JUnit:
Kotlin
@RunWith(AndroidJUnit4::class) class MyBenchmark { @get:Rule val benchmarkRule = BenchmarkRule() @Test fun benchmarkSomeWork() = benchmarkRule.measureRepeated { doSomeWork() } }
Java
@RunWith(AndroidJUnit4.class) class MyBenchmark { @Rule public BenchmarkRule benchmarkRule = new BenchmarkRule(); @Test public void benchmarkSomeWork() { final BenchmarkState state = benchmarkRule.getState(); while (state.keepRunning()) { doSomeWork(); } } }
Quando fazer a comparação
É recomendável criar o perfil do código antes de programar uma comparação. Isso ajuda você a encontrar operações caras que valem a pena otimizar. Ela também pode esclarecer por que as operações estão lentas, mostrando o que está acontecendo enquanto elas são executadas. Exemplos podem incluir: execução em uma linha de execução de baixa prioridade, suspensão devido ao acesso ao disco ou chamada inesperada em uma função cara, como decodificação de bitmap.
A adição de pontos de rastreamento personalizados por meio da API TraceCompat, ou do
wrapper -ktx
, permite visualizá-los na
CPU do Android Studio Profiler ou Systrace:
Kotlin
fun proccessBitmap(bitmap: Bitmap): Bitmap { trace("processBitmap") { // perform work on bitmap... } }
Java
public Bitmap processBitmap(Bitmap bitmaptest) { TraceCompat.beginSection("processBitmap"); try { // perform work on bitmap... } finally { TraceCompat.endSection(); } }
Em que usar a comparação
As comparações são úteis principalmente para trabalhos da CPU que são executados no app muitas vezes. Bons
exemplos são a rolagem de RecyclerView
, conversão/processamento de dados e trechos
de código que são usados repetidamente.
É mais difícil medir outros tipos de código com uma comparação. Como as comparações são executadas em loop, qualquer código que não seja executado com frequência ou que apresente desempenhos diferentes quando chamado várias vezes pode não funcionar para a comparação.
Armazenamento em cache
Tente evitar medir apenas o cache. Por exemplo, uma comparação para o layout de uma visualização personalizada pode medir apenas o desempenho do cache de layout. Para evitar isso, é possível transmitir diferentes parâmetros de layout em cada loop. Isso pode ser difícil em outros casos, como ao medir o desempenho do sistema de arquivos, porque o SO armazena o sistema de arquivos em cache durante o loop.
Código pouco executado
É pouco provável que a compilação JIT pelo Android Runtime (ART) ocorra para um código que é executado apenas uma vez durante a inicialização do aplicativo. Por esse motivo, fazer a comparação desse código com o microbenchmark, em que ele é executado em um loop restrito, não é uma forma realista de medir o desempenho.
Para comparar esse tipo de código, recomendamos o Jetpack Macrobenchmark, que permite medir interações de usuário de nível mais alto, como a inicialização do app e o desempenho de rolagem.
Configuração completa do projeto
Para configurar a comparação como habitual em vez de única,
isole as comparações no módulo delas. Isso garante que
a configuração, como definir debuggable
como false
, é diferente
dos testes regulares.
Antes de adicionar seu módulo de comparação, coloque o código e os recursos que você quer comparar em um módulo de biblioteca, se eles ainda não estiverem em um.
Nossas amostras apresentam exemplos de como configurar um projeto dessa forma.
Configurar as propriedades do Android Studio
Se você estiver usando o Android Studio 3.5, será necessário definir as propriedades do Android Studio manualmente para ativar a compatibilidade com o assistente de módulo de comparação. Isso não é necessário para o Android Studio 3.6 ou versões mais recentes.
Para ativar o modelo do Android Studio para uma comparação, faça o seguinte:
No Android Studio 3.5, clique em Help > Edit Custom Properties.
Adicione a seguinte linha ao arquivo que será aberto:
npw.benchmark.template.module=true
Salve e feche o arquivo.
Reinicie o Android Studio.
Criar um novo módulo
O modelo do módulo de comparação define automaticamente as configurações da comparação.
Para usar o modelo do módulo para criar um novo módulo, siga as seguintes etapas:
Clique com o botão direito do mouse no seu projeto ou módulo e selecione New > Module.
Selecione Benchmark Module e clique em Next.
Figura 1. Módulo de comparação.
Insira um nome para o módulo, escolha o idioma e clique em Finish.
Um módulo pré-configurado para comparação será criado, com um diretório de comparação adicionado e
debuggable
definido comofalse
.
Escrever uma comparação
As comparações são testes de instrumentação padrão. Para criar uma comparação, use a
classe BenchmarkRule
oferecida pela biblioteca. Para comparar atividades, use
ActivityTestRule
ou ActivityScenarioRule
. Para comparar o código da IU,
use @UiThreadTest
.
O código a seguir apresenta uma amostra de comparação:
Kotlin
@RunWith(AndroidJUnit4::class) class ViewBenchmark { @get:Rule val benchmarkRule = BenchmarkRule() @Test fun simpleViewInflate() { val context = ApplicationProvider.getApplicationContext<Context>() val inflater = LayoutInflater.from(context) val root = FrameLayout(context) benchmarkRule.measureRepeated { inflater.inflate(R.layout.test_simple_view, root, false) } } }
Java
@RunWith(AndroidJUnit4::class) public class ViewBenchmark { @Rule public BenchmarkRule benchmarkRule = new BenchmarkRule(); @Test public void simpleViewInflate() { Context context = ApplicationProvider.getApplicationContext<Context>(); final BenchmarkState state = benchmarkRule.getState(); LayoutInflater inflater = LayoutInflater.from(context); FrameLayout root = new FrameLayout(context); while (state.keepRunning()) { inflater.inflate(R.layout.test_simple_view, root, false); } } }
Você pode desativar a medição de tempo em seções de código que você não queira avaliar, conforme mostrado na amostra de código a seguir:
Kotlin
@Test fun bitmapProcessing() = benchmarkRule.measureRepeated { val input: Bitmap = runWithTimingDisabled { constructTestBitmap() } processBitmap(input) }
Java
@Test public void bitmapProcessing() { final BenchmarkState state = benchmarkRule.getState(); while (state.keepRunning()) { state.pauseTiming(); Bitmap input = constructTestBitmap(); state.resumeTiming(); processBitmap(input); } }
Para saber mais sobre como executar uma comparação, consulte Executar a comparação.
Executar a comparação
No Android Studio, execute a comparação da mesma forma que faria com qualquer @Test
. No Android
Studio 3.4 e versões mais recentes, é possível ver a saída enviada para o console.
Para executar a comparação, vá até benchmark/src/androidTest
no módulo e
pressione Control
+Shift
+F10
(Command
+Shift
+R
no Mac). Os resultados da
comparação aparecem no console, conforme mostrado na Figura 2:
Figura 2. Saída de comparação no Android Studio
Na linha de comando, execute o connectedCheck
comum:
./gradlew benchmark:connectedCheck
Coletar dados
Um relatório de comparação completo com mais métricas e informações sobre o dispositivo está disponível em formato JSON.
Por padrão, o plug-in para Gradle androidx.benchmark
ativa a saída JSON. Para
ativar a saída JSON manualmente em ambientes de criação que não sejam o Gradle, transmita um
argumento de instrumentação, androidx.benchmark.output.enable
, definido como true
.
Veja a seguir um exemplo usando o comando adb shell am instrument
:
adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
Por padrão, o relatório JSON é gravado no disco do dispositivo na pasta de downloads compartilhados externos do APK de teste, que normalmente fica localizada em:
/storage/emulated/0/Download/app_id-benchmarkData.json
É possível configurar o local em que os relatórios de comparação serão salvos no dispositivo
usando o argumento de instrumentação additionalTestOutputDir
.
adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" -e "additionalTestOutputDir" "/path_to_a_directory_on_device_test_has_write_permissions_to/" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
Os relatórios JSON são copiados 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/app_id-benchmarkData.json
Plug-in do Android para Gradle 3.6 e versões mais recentes
Ao executar comparações na linha de comando com o Gradle, os projetos que usam
o Plug-in do Android para Gradle 3.6 e versões mais recentes podem adicionar a seguinte sinalização ao arquivo
gradle.properties
do projeto:
android.enableAdditionalTestOutput=true
Isso permitirá que um recurso experimental do Plug-in do Android para Gradle extraia relatórios de comparação de dispositivos com API 16 e versões mais recentes.
Plug-in do Android para Gradle 3.5 e versões anteriores
Para usuários de versões mais antigas do Plug-in do Android para Gradle, é
o plug-in androidx.benchmark
para Gradle que copia os relatórios de comparação JSON do dispositivo para o host.
Ao usar o AGP 3.5 ou versões anteriores, voltado à API de nível 29 ou mais recente, é necessário
adicionar uma sinalização ao manifesto do Android no diretório androidTest
das
comparações para ativar o comportamento de armazenamento externo legado.
<manifest ... > <!-- This attribute is "false" by default on apps targeting Android 10. --> <application android:requestLegacyExternalStorage="true" ... > ... </application> </manifest>
Para ver mais informações, consulte Desativar temporariamente o armazenamento com escopo.
Estabilidade do relógio
Os relógios em dispositivos móveis mudam dinamicamente do estado alto (para maior desempenho) para o estado baixo (para economizar energia ou quando o dispositivo esquenta). Esses relógios variados podem fazer com que os números de comparação variem muito. Por esse motivo, a biblioteca oferece maneiras de lidar com esse problema.
Bloquear os relógios (acesso root necessário)
Bloquear os relógios é a melhor maneira de alcançar um desempenho estável. Isso garante que os relógios
nunca cheguem a um estado alto o suficiente para aquecer o dispositivo ou um estado baixo caso uma comparação não esteja utilizando
a CPU totalmente. Embora essa seja a melhor forma de garantir um desempenho estável, ela
não é compatível com a maior parte dos dispositivos, porque exige o acesso root ao adb
.
Para bloquear seus relógios, adicione o plug-in de ajuda fornecido ao caminho de classe
de nível mais alto do projeto no arquivo build.gradle
principal:
Groovy
buildscript { ... dependencies { ... classpath "androidx.benchmark:benchmark-gradle-plugin:1.1.0-alpha13" } }
Kotlin
buildscript { ... dependencies { ... classpath("androidx.benchmark:benchmark-gradle-plugin:1.1.0-alpha13") } }
Aplique o plug-in no build.gradle
do seu módulo que está sendo comparado:
Groovy
plugins { id 'com.android.app' id 'androidx.benchmark' } ...
Kotlin
plugins { id("com.android.app") id("androidx.benchmark") ... }
Isso adiciona as tarefas de comparação do Gradle ao seu projeto, incluindo
./gradlew lockClocks
e ./gradlew unlockClocks
. Use essas tarefas para bloquear e
desbloquear a CPU do dispositivo usando o adb.
Se você tiver vários dispositivos visíveis para o adb, use a variável de ambiente
ANDROID_SERIAL
para especificar em qual dispositivo a tarefa Gradle deve operar:
ANDROID_SERIAL=device-id-from-adb-devices ./gradlew lockClocks
Modo de desempenho sustentado
Window.setSustainedPerformanceMode()
é um recurso compatível com alguns
dispositivos que permite que um app opte por uma frequência máxima de CPU mais baixa. Quando executada
em dispositivos compatíveis, a biblioteca Benchmark usa uma combinação dessa API e
inicia a própria atividade para impedir a limitação térmica e estabilizar
os resultados.
Essa função é ativada por padrão pelo testInstrumentationRunner
definido
pelo plug-in para Gradle. Caso queira usar um executor personalizado, você pode criar uma subclasse
AndroidBenchmarkRunner
e usá-lo como seu testInstrumentationRunner
.
O executor lança uma atividade opaca em tela cheia para garantir que a comparação seja executada em primeiro plano e sem qualquer outro app drenando a bateria.
Pausar a execução automática
Se nem o bloqueio de relógio nem o desempenho sustentado forem usados, a biblioteca executa a detecção de limitação térmica automaticamente. Quando ativada, a comparação interna é executada periodicamente para determinar quando a temperatura do dispositivo ficou alta o suficiente para diminuir o desempenho da CPU. Quando um baixo desempenho da CPU for detectado, a biblioteca pausará a execução para permitir que o dispositivo resfrie e repetirá a comparação atual.
Erros de configuração
A biblioteca detecta as condições a seguir para garantir que o projeto e o ambiente estejam configurados de modo a apresentar um desempenho preciso para a versão:
Debuggable
é definido comofalse
.- Um dispositivo físico está sendo utilizado, e não um emulador.
- Os relógios estão bloqueados, caso o dispositivo tenha acesso root.
- O dispositivo está com um nível de bateria suficiente.
Se qualquer uma das verificações acima apresentar uma falha, a comparação gerará um erro para evitar medições imprecisas.
Para suprimir esses erros no formato de avisos e impedir que interrompam a comparação,
transmita o tipo de erros que você quer suprimir em uma lista separada por
vírgulas para o argumento de instrumentação
androidx.benchmark.suppressErrors
:
adb shell am instrument -w -e "androidx.benchmark.suppressErrors" "DEBUGGABLE" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
Você pode definir essa lista no Gradle da seguinte forma:
Groovy
android { defaultConfig { testInstrumentationRunnerArgument 'androidx.benchmark.suppressErrors', 'DEBUGGABLE' } }
Kotlin
android { defaultConfig { testInstrumentationRunnerArguments(mapOf( "androidx.benchmark.suppressErrors" to "DEBUGGABLE" )) } }
Suprimir erros permite que a comparação seja executada em uma configuração
incorreta, mas a saída da comparação será corrompida
ao incluir intencionalmente os erros como prefixos dos nomes de testes. Ou seja, quando uma
comparação depurável for executada com a supressão acima, os nomes de testes receberão DEBUGGABLE_
como
prefixo.
Caracterização de perfil
Você pode criar um perfil de uma comparação para investigar por que o código medido está lento.
Adicione o seguinte ao arquivo build.gradle
do seu módulo de comparação:
Groovy
android { defaultConfig { // must be one of: 'None', 'StackSampling', or 'MethodTracing' testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'StackSampling' } }
Kotlin
android { defaultConfig { // must be one of: 'None', 'StackSampling', or 'MethodTracing' testInstrumentationRunnerArgument = mapOf( "androidx.benchmark.profiling.mode", "StackSampling" ) } }
Quando você executa uma comparação, um arquivo de saída .trace
é copiado para o host, no
diretório ao lado dos resultados JSON. Abra esse arquivo com o Android
Studio usando File > Open para inspecionar os resultados de criação de perfil no CPU Profiler.
Rastreamento de métodos
Com o rastreamento de métodos, a comparação aquecerá antes de capturar o rastreamento de um método, registrando todos os métodos chamados pela própria comparação. Os resultados de desempenho serão afetados significativamente pela sobrecarga da captura de cada entrada/saída do método.
Amostragem de pilhas
Com a amostragem de pilhas, a comparação coletará amostras das pilhas de chamadas após a conclusão do aquecimento. É possível controlar a frequência e a duração da amostragem usando argumentos de instrumentação.
No Android 10 (API 29) e versões mais recentes, a amostragem de pilhas usa o Simpleperf para amostragens de pilhas de chamadas do app, incluindo o código C++. No Android 9 (API 28) e versões anteriores, ela usa Debug.startMethodTracingSampling para capturar amostras de pilha.
Para ler mais sobre como usar o rastreamento e a amostragem de métodos, consulte a configuração de criação de perfil da CPU.
Amostras de comparação
Amostras de código de comparação estão disponíveis nos seguintes projetos:
- Amostras de desempenho (link em inglês)
- Amostras de componentes de arquitetura (link em inglês)
As amostras de projetos incluem:
BenchmarkSample: essa é uma amostra independente, que apresenta como usar um módulo de comparação para medir o código e a IU.
PagingWithNetworkSample: amostra dos Componentes da arquitetura do Android, que mostra como fazer uma comparação do desempenho do
RecyclerView
.WorkManagerSample: amostra dos Componentes da arquitetura do Android, que mostra como fazer uma comparação de workers do
WorkManager
.
Outros recursos
Para saber mais sobre a comparação, consulte os recursos a seguir.
Blogs
- Como combater regressões com comparações em CI (em inglês)
Enviar feedback
Para comunicar problemas ou enviar solicitações de recursos usando a comparação, consulte o rastreador de problemas público.