A biblioteca Macrobenchmark possibilita que você crie testes de desempenho de inicialização e execução diretamente no app em dispositivos com o Android M (API 23) ou versões mais recentes.
É recomendável usar a Macrobenchmark com a versão mais nova do Android Studio (2021.1.1 ou mais recente). Há novos recursos nessa versão do ambiente de desenvolvimento integrado à Macrobenchmark. Os usuários de versões anteriores do Android Studio podem usar a instrução extra mostrada mais adiante nesta página para trabalhar com arquivos de rastreamento.
O teste de comparação é fornecido pela API de regra MacrobenchmarkRule
do JUnit4 na biblioteca Macrobenchmark:
Kotlin
@get:Rule val benchmarkRule = MacrobenchmarkRule() @Test fun startup() = benchmarkRule.measureRepeated( packageName = "mypackage.myapp", metrics = listOf(StartupTimingMetric()), iterations = 5, startupMode = StartupMode.COLD ) { // this = MacrobenchmarkScope pressHome() val intent = Intent() intent.setPackage("mypackage.myapp") intent.setAction("mypackage.myapp.myaction") startActivityAndWait(intent) }
Java
@Rule MacrobenchmarkRule benchmarkRule = MacrobenchmarkRule() @Test void startup() = benchmarkRule.measureRepeated( "mypackage.myapp", // packageName listOf(StartupTimingMetric()), // metrics 5, // iterations StartupMode.COLD // startupMode ) { scope -> scope.pressHome() Intent intent = Intent() intent.setPackage("mypackage.myapp") intent.setAction("mypackage.myapp.myaction") scope.startActivityAndWait(intent) }
As métricas são exibidas diretamente no Android Studio e também são geradas para o uso de CI em um arquivo JSON.
Configuração do módulo
A biblioteca Macrobenchmark exige um com.android.test
módulo separado do código do app, responsável por executar os que medem o desempenho
do app.
Bumblebee
No Android Studio Bumblebee, há um modelo disponível para simplificar a configuração do módulo da Macrobenchmark.
Adicionar um novo módulo
O modelo de módulo Benchmark cria automaticamente um módulo no projeto para medir o aplicativo criado por um módulo, incluindo um exemplo de inicialização da Benchmark.
Para usar o modelo do módulo a fim de criar um novo módulo, siga as etapas abaixo:
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.
Selecione Benchmark Module.
É possível personalizar o aplicativo de destino (o aplicativo a ser comparado), bem como o nome do pacote e do novo módulo Macrobenchmark.
Clique em Finish.
Arctic Fox
No Arctic Fox, você criará um módulo de biblioteca e o converterá em um módulo de teste.
Adicionar um novo módulo
Adicione um novo módulo ao projeto. Esse módulo contém testes da Macrobenchmark.
- 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.
- Selecione Android Library no painel Templates.
- Digite
macrobenchmark
como o nome do módulo. - Defina o Minimum SDK como API 23: Android M.
- Clique em Finish.
Modificar o arquivo do Gradle
Personalize o build.gradle
do módulo Macrobenchmark da seguinte maneira:
- Mude o plug-in de
com.android.library
paracom.android.test
. - Adicione outras propriedades obrigatórias do módulo de teste no bloco
android {}
: - Mude todas as dependências chamadas
testImplementation
ouandroidTestImplementation
paraimplementation
. - Adicione uma dependência à biblioteca Macrobenchmark:
implementation 'androidx.benchmark:benchmark-macro-junit4:1.1.0-alpha13'
- Após o bloco
android {}
, mas antes dodependencies {}
, adicione:
targetProjectPath = ":app" // Note that your module name may be different // Enable the benchmark to run separately from the app process experimentalProperties["android.experimental.self-instrumenting"] = true buildTypes { // Declare a build type (release) to match the target app's build type release { debuggable = true } }
androidComponents { beforeVariants(selector().all()) { // Enable only the benchmark buildType, since we only want to measure // release-like build performance (should match app buildType) enabled = buildType == 'benchmark' } }
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.
Consulte o módulo de exemplo da Macrobenchmark (link em inglês) para referência.
Criar uma Macrobenchmark
Defina uma nova classe de teste nesse módulo, preenchendo o nome do pacote do app:
@RunWith(AndroidJUnit4::class) class SampleStartupBenchmark { @get:Rule val benchmarkRule = MacrobenchmarkRule() @Test fun startup() = benchmarkRule.measureRepeated( packageName = "mypackage.myapp", metrics = listOf(StartupTimingMetric()), iterations = 5, startupMode = StartupMode.COLD ) { // this = MacrobenchmarkScope pressHome() val intent = Intent() intent.setPackage("mypackage.myapp") intent.setAction("mypackage.myapp.myaction") startActivityAndWait(intent) } }
Configurar o app
Para avaliar um app, chamado de destino da Macrobenchmark, ele
precisa ser capaz de criar um perfil para ler informações detalhadas sobre o trace. É
possível ativar isso na tag <application>
do AndroidManifest.xml
do app:
<application ... >
<!-- Profileable to enable Macrobenchmark profiling -->
<!-- Suppress AndroidElementNotAllowed -->
<profileable android:shell="true"/>
...
</application>
Configure o app comparado para que fique o mais próximo possível da experiência
do usuário. 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 de benchmark
da
variante release
, que terá o mesmo desempenho, mas será assinada localmente com
chaves debug
:
buildTypes {
benchmark {
// duplicate any release build type settings for measurement accuracy,
// such as "minifyEnabled" and "proguardFiles" in this block
debuggable false
signingConfig signingConfigs.debug
}
}
Sincronize com o Gradle, abra o painel Build Variants à esquerda e
selecione a variante benchmark
do app e do módulo Macrobenchmark.
Isso vai garantir que a comparação crie e teste a variante correta do
app:
A execução da Macrobenchmark em atividades internas requer mais uma etapa. Para comparar
uma atividade interna exported=false
, transmita um setupBlock
para
MacrobenchmarkRule.measureRepeated()
para ir até o código que será comparado. Use o measureBlock
para invocar a ação de inicialização ou rolagem para
fazer a medição.
Personalizar a Macrobenchmark
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
CompilationMode.DEFAULT
,
que no Android Nougat (API de nível 24) e versões mais recentes, instala um perfil de referência (se
disponível). Já no Android Marshmallow (API de nível 23) e versões anteriores, ele compila completamente o
APK, que é o comportamento padrão do sistema.
Você poderá instalar um perfil de referência se o aplicativo de destino tiver um perfil de referência e a biblioteca ProfileInstaller.
No Android Nougat (API de nível 24) ou versões mais recentes, é possível personalizar a classe CompilationMode
para afetar a quantidade de pré-compilação no dispositivo para 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 do perfil antes de começar para garantir que não haja interferência entre comparações.
Inicialização
Para executar uma atividade, você pode transmitir um modo de inicialização predefinido
(COLD
, WARM
ou HOT
)
para a função measureRepeated()
. 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.
Rolagem e animação
Diferentemente da maioria dos testes de IU do Android, os testes da Macrobenchmark acontecem em um processo separado do próprio app. Isso é necessário para ativar ações como eliminar o processo do app e compilá-lo usando comandos do shell.
Você pode direcionar o app usando a biblioteca
UI Automator ou outro mecanismo que controle o
app de destino no processo de teste. Abordagens como Espresso ou
ActivityScenario
não funcionarão porque geralmente são executadas em um processo
compartilhado com o app.
O exemplo a seguir encontra uma RecyclerView
usando o próprio ID de recurso e rola
para baixo várias vezes:
@Test
fun measureScroll() {
benchmarkRule.measureRepeated(
packageName = "mypackage.myapp",
metrics = listOf(FrameTimingMetric()),
iterations = 5,
setupBlock = {
// before starting to measure, navigate to the UI to be measured
val intent = Intent()
intent.action = ACTION
startActivityAndWait(intent)
}
) {
val recycler = device.findObject(By.res("mypackage.myapp", "recycler_id"))
// Set gesture margin to avoid triggering gesture nav
// with input events from automation.
recycler.setGestureMargin(device.displayWidth / 5)
// Scroll down several times
for (i in 1..10) {
recycler.scroll(Direction.DOWN, 2f)
device.waitForIdle()
}
}
}
Conforme o teste especifica uma FrameTimingMetric
, o tempo dos frames é
gravado e relatado como um resumo de alto nível da distribuição de tempo para a renderização do frame:
50º, 90º, 95º e 99º percentis.
Seu comparativo não precisa rolar a IU. Em vez disso, ele pode, por exemplo, exibir uma animação. Ele também não precisa usar o IU Automator especificamente. Desde que os frames estejam sendo produzidos pelo sistema de visualização, o que inclui frames produzidos pelo Compose, as métricas de desempenho serão coletadas. Os mecanismos do processo, como o Espresso, não funcionarão porque o app precisa ser gerado pelo processo do app de teste.
Executar a Macrobenchmark
Faça o teste no Android Studio para medir o desempenho do app no dispositivo. É necessário realizar o teste em um dispositivo físico, e não em um emulador, porque ele não produz números de desempenho do produto que representem a experiência do usuário final.
Consulte a seção Comparações na CI para ver informações sobre como fazer e monitorar comparações na integração contínua.
Também é possível fazer todas as comparações na linha de comando usando o
comando connectedCheck
:
$ ./gradlew :macrobenchmark:connectedCheck
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
gerará um erro, em vez de relatar uma medição incorreta ou incompleta.
É possível suprimir esses erros com o argumento
androidx.benchmark.suppressErrors
.
Erros também são gerados ao tentar medir um emulador ou em um dispositivo com bateria fraca, porque isso pode comprometer a disponibilidade e a velocidade do relógio.
Inspecionar um trace
Cada iteração medida captura um rastreamento do sistema separado. É possível abrir os resultados de traces clicando em um dos links no painel Test Results, como mostrado na imagem da seção Jetpack Macrobenchmark deste tópico. Quando o trace é 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:
Após o arquivo de trace ser carregado, o Studio mostra os resultados na ferramenta CPU Profiler:
Acessar arquivos de trace manualmente
Se você estiver usando uma versão mais antiga do Android Studio (anterior a 2020.3.1) ou a ferramenta Perfetto para analisar um arquivo de trace, há outras etapas envolvidas.
Primeiro, extraia o arquivo de trace do dispositivo:
# The following command pulls all files ending in .perfetto-trace from the directory
# hierarchy starting at the root /storage/emulated/0/Android.
$ adb shell find /storage/emulated/0/Android/ -name "*.perfetto-trace" \
| tr -d '\r' | xargs -n1 adb pull
O caminho do arquivo de saída poderá ser diferente se você personalizá-lo com o
argumento additionalTestOutputDir
. Você pode procurar registros de caminho de trace no logcat
para ver onde eles estão gravados. Exemplo:
I PerfettoCapture: Writing to /storage/emulated/0/Android/data/androidx.benchmark.integration.macrobenchmark.test/cache/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace.
Se você invocar os testes usando a linha de comando do Gradle, como
./gradlew macrobenchmark:connectedCheck
, os arquivos de resultados de teste
poderão ser copiados em um diretório de saída de teste no sistema host. Para fazer isso, adicione esta
linha ao arquivo gradle.properties
do projeto:
android.enableAdditionalTestOutput=true
Os arquivos de resultado das execuções de teste são exibidos no diretório do build do seu projeto da seguinte forma:
build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/<device-name>/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace
Quando tiver o arquivo de trace no sistema host, você poderá abri-lo no Android Studio em File > Open no menu. Isso mostra a visualização da ferramenta do criador de perfil exibida na seção anterior.
Outra alternativa é usar a ferramenta Perfetto. O Perfetto possibilita inspecionar todos os processos que ocorrem em todo o dispositivo durante o trace, enquanto o CPU Profiler do Android Studio limita a inspeção a um único processo.
Melhorar os dados de trace com eventos personalizados
Pode ser útil instrumentar seu aplicativo com eventos de trace personalizados, que são vistos com o restante do relatório de trace e podem ajudar a apontar problemas específicos do app. Para saber mais sobre como criar eventos de trace personalizados, consulte o guia Definir eventos personalizados.
Como fazer comparações na CI
É comum fazer testes na CI sem o Gradle ou localmente se você estiver usando um sistema de compilação diferente. Nesta seção, explicamos como configurar a Macrobenchmark para uso na CI no momento da execução.
Arquivos de resultados: JSON e traces
A Macrobenchmark gera um arquivo JSON e vários arquivos de trace: um por iteração
medida de cada loop MacrobenchmarkRule.measureRepeated
.
Você pode definir onde esses arquivos serão gravados, transmitindo o seguinte argumento de instrumentação no momento da execução:
-e additionalTestOutputDir "device_path_you_can_write_to"
Para simplificar, você pode especificar um caminho em /sdcard/
, mas é necessário
desativar
o armazenamento com escopo definindo requestLegacyExternalStorage
como true
no seu
módulo Macrobenchmark:
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
Ou transmita um argumento de instrumentação para ignorar o armazenamento com escopo durante o teste:
-e no-isolated-storage 1
Exemplo de JSON
Veja a seguir um exemplo de saída JSON para uma única comparação de inicialização:
{
"context": {
"build": {
"device": "walleye",
"fingerprint": "google/walleye/walleye:10/QQ3A.200805.001/6578210:userdebug/dev-keys",
"model": "Pixel 2",
"version": {
"sdk": 29
}
},
"cpuCoreCount": 8,
"cpuLocked": false,
"cpuMaxFreqHz": 2457600000,
"memTotalBytes": 3834605568,
"sustainedPerformanceModeEnabled": false
},
"benchmarks": [
{
"name": "startup",
"params": {},
"className": "androidx.benchmark.integration.macrobenchmark.SampleStartupBenchmark",
"totalRunTimeNs": 77969052767,
"metrics": {
"startupMs": {
"minimum": 228,
"maximum": 283,
"median": 242,
"runs": [
238,
283,
256,
228,
242
]
}
},
"warmupIterations": 3,
"repeatIterations": 5,
"thermalThrottleSleepSeconds": 0
}
]
}
Outros recursos
Um projeto de exemplo (link em inglês) está disponível como parte do repositório Android/performance-samples no GitHub.
Para ver orientações sobre como detectar regressões de desempenho, consulte Como combater regressões com os comparativos de mercado na CI (link em inglês).
Feedback
Para comunicar problemas ou enviar solicitações de recursos para a MacroBenchmark Jetpack, consulte o Issue Tracker público.