Macrobenchmark te permite escribir pruebas de rendimiento sobre el inicio y el tiempo de ejecución directamente con tu app en dispositivos que ejecutan Android M (nivel de API 23) o versiones posteriores.
Se recomienda usar Macrobenchmark con la versión más reciente de Android Studio (2021.1.1 o posterior), ya que en esa versión del IDE se incluyen funciones nuevas que están integradas con Macrobenchmark. Los usuarios que tienen versiones anteriores de Android Studio pueden usar la instrucción adicional que aparece más tarde en este tema para trabajar con archivos de registro.
Las pruebas de comparativas se proporcionan mediante la API de la regla MacrobenchmarkRule
de JUnit4 en la biblioteca de 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) }
Las métricas se muestran directamente en Android Studio y como resultados del uso de CI en un archivo JSON.
Configuración de módulo
Las macrocomparativas requieren un módulo com.android.test
independiente en el código de tu app en el que se ejecuten las pruebas que miden la app.
Bumblebee
En Android Studio Bumblebee, hay una plantilla disponible para simplificar la configuración del módulo de macrocomparativas.
Cómo agregar un módulo nuevo
La plantilla del módulo de comparativas crea automáticamente un módulo en el proyecto para medir la app que compila un módulo de app, incluida una comparativa de inicio de muestra.
Si deseas usar la plantilla del módulo a fin de crear un módulo nuevo, haz lo siguiente:
Haz clic con el botón derecho en tu proyecto o módulo desde el panel Project en Android Studio y haz clic en New > Module.
Selecciona Benchmark Module.
Puedes personalizar la aplicación de destino (la app de la que obtendrás comparativas), así como el nombre del paquete y del módulo para el módulo nuevo de macrocomparativas.
Haz clic en Finish.
Arctic Fox
En Arctic Fox, crearás un módulo de biblioteca y lo convertirás en un módulo de prueba.
Cómo agregar un módulo nuevo
Agrega un módulo nuevo a tu proyecto que contenga tus pruebas de Macrobenchmark.
- Haz clic con el botón derecho en tu proyecto o módulo desde el panel Project en Android Studio y haz clic en New > Module.
- Selecciona Android Library en el panel Templates.
- Como nombre de módulo, escribe
macrobenchmark
. - Configura Minimum SDK en API 23: Android M.
- Haz clic en Finish.
Cómo modificar el archivo de Gradle
Personaliza el build.gradle
del módulo de Macrobenchmark como se indica a continuación:
- Cambia el complemento de
com.android.library
acom.android.test
. - Agrega las propiedades adicionales y obligatorias del módulo de prueba en el bloque
android {}
: - Cambia todas las dependencias llamadas
testImplementation
oandroidTestImplementation
aimplementation
. - Agrega una dependencia en la biblioteca de Macrobenchmark:
implementation 'androidx.benchmark:benchmark-macro-junit4:1.1.0-alpha13'
- Después del bloque
android {}
, pero antes del bloquedependencies {}
, agrega lo siguiente:
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' } }
Cómo simplificar la estructura del directorio
En un módulo com.android.test
, hay un solo directorio del código fuente para todas las pruebas. Borra los otros directorios del código fuente, incluidos src/test
y src/androidTest
, ya que no se usan.
Consulta el módulo de Macrobenchmark de muestra como referencia.
Cómo crear una macrocomparativa
Define una clase de prueba nueva en ese módulo completando el nombre de paquete de la 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) } }
Cómo configurar la app
A fin de obtener comparativas de una app (denominada el objetivo de la macrocomparativa), se debe poder crear un perfil de ella para que se pueda leer información de seguimiento detallada. Puedes habilitar esta función en la etiqueta <application>
del AndroidManifest.xml
de la app:
<application ... >
<!-- Profileable to enable Macrobenchmark profiling -->
<!-- Suppress AndroidElementNotAllowed -->
<profileable android:shell="true"/>
...
</application>
Configura la app de la que obtendrás comparativas lo más parecido a la experiencia del usuario posible. Además, configúrala como no depurable y con la reducción activada en lo posible, lo que mejorará el rendimiento. Para ello, generalmente, debes crear una copia de benchmark
de la variante de release
, que tendrá el mismo rendimiento, pero se firma de manera local con claves 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
}
}
Realiza una sincronización de Gradle, abre el panel Build Variants en el lado izquierdo y selecciona la variante benchmark
de la app y del módulo de Macrobenchmark.
De esta manera, te aseguras de que con la ejecución de las comparativas se compile y se pruebe la variante correcta de tu app.
Ejecutar Macrobenchmark en actividades internas requiere un paso adicional. Para obtener comparativas de una actividad interna exported=false
, pasa un setupBlock
a MacrobenchmarkRule.measureRepeated()
a fin de navegar al código sobre el que quieres crear la comparativa y usa el measureBlock
a fin de invocar el inicio de la actividad o la acción de desplazamiento que quieres medir.
Cómo personalizar macrocomparativas
CompilationMode
Las macrocomparativas pueden especificar un CompilationMode
, que define cuánto de la app se debe compilar previamente desde el código de bytes DEX (el formato de código de bytes dentro de un APK) al código máquina (similar a C++ ya compilado).
De forma predeterminada, las macrocomparativas se ejecutan con CompilationMode.DEFAULT
, que, en Android Nougat (API 24) y versiones posteriores, instala un perfil de Baseline (si está disponible) y, en Android Marshmallow (API 23) y versiones anteriores, compila por completo el APK (que es el comportamiento predeterminado del sistema).
Puedes instalar un perfil de Baseline si la aplicación de destino contiene este tipo de perfil y la biblioteca de ProfileInstaller.
En Android Nougat (API 24) y versiones posteriores, puedes personalizar el CompilationMode
para que afecte la cantidad de compilación previa en el dispositivo, a fin de imitar diferentes niveles de compilación anticipada (AOT) o almacenamiento en caché de JIT. Consulta CompilationMode.Full
, CompilationMode.Partial
y CompilationMode.None
.
Esta funcionalidad se basa en comandos de compilación de ART. Para garantizar que no haya interferencias entre las comparativas, cada una borrará los datos de perfil antes de comenzar.
Inicio
A fin de iniciar una actividad, puedes pasar un modo de inicio predefinido (que puede ser COLD
, WARM
o HOT
) a la función measureRepeated()
. Este parámetro cambia la manera en la que se inicia la actividad y el estado del proceso al comienzo de la actividad.
Si quieres obtener más información sobre los tipos de inicio, consulta la documentación de inicio de Android Vitals.
Desplazamiento y animación
A diferencia de la mayoría de las pruebas de IU de Android, las de Macrobenchmark se ejecutan en un proceso independiente de la app. Esto es necesario para realizar acciones como finalizar el proceso de la app y compilarlo con comandos shell.
Puedes controlar tu app con la biblioteca de UI Automator o algún otro mecanismo que pueda controlar la aplicación objetivo del proceso de prueba. Los enfoques como Espresso o ActivityScenario
no funcionarán porque pretenden ejecutarse en un proceso compartido con la app.
En el siguiente ejemplo, se encuentra una RecyclerView
que usa su ID de recurso y se desplaza hacia abajo varias veces:
@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()
}
}
}
Como la prueba especifica una FrameTimingMetric
, se registra e informa el tiempo de los fotogramas como un resumen de nivel superior de la distribución de la latencia de fotogramas: percentil 50, 90, 95 y 99.
No es necesario que tus comparativas se desplacen por la IU; en su lugar, pueden ejecutar una animación, por ejemplo. Tampoco es necesario que usen específicamente UI Automator. Siempre y cuando el sistema de vistas produzca los fotogramas (incluidos los que genera Compose), se recopilarán métricas de rendimiento. Ten en cuenta que los mecanismos del proceso, como Espresso, no funcionarán, dado que la app se debe controlar desde el proceso de la aplicación de prueba.
Cómo ejecutar macrocomparativas
Ejecuta la prueba desde Android Studio para medir el rendimiento de la app en tu dispositivo. Ten en cuenta que debes ejecutar la prueba en un dispositivo físico, no en un emulador, ya que estos no producen números de rendimiento representativos de la experiencia del usuario final.
Consulta la sección Comparativas en CI para obtener información sobre la manera en que debes ejecutar y supervisar comparativas en la integración continua.
También puedes ejecutar todas las comparativas desde la línea de comandos mediante connectedCheck
, como se muestra a continuación:
$ ./gradlew :macrobenchmark:connectedCheck
Errores de configuración
Si la app está mal configurada (es depurable o no permite que se generen perfiles), Macrobenchmark arrojará un error, en lugar de informar una medición incorrecta o incompleta.
Puedes suprimir esos errores mediante el argumento androidx.benchmark.suppressErrors
.
Además, se arrojan errores cuando se intenta medir un emulador o si el dispositivo tiene poca batería, dado que se podría comprometer la disponibilidad principal y la velocidad del reloj.
Cómo inspeccionar un registro
Cada iteración medida captura un registro del sistema diferente. Para abrir estos registros de resultado, haz clic en uno de los vínculos que se incluyen en el panel Test Results, como se muestra en la imagen de la sección Jetpack Macrobenchmark de este tema. Cuando se carga el registro, Android Studio te solicita que selecciones el proceso a analizar. La selección se propaga previamente con el proceso de la app de destino, como se muestra a continuación:
Una vez cargado el archivo de registro, Studio muestra los resultados en la herramienta del Generador de perfiles de CPU:
Cómo acceder manualmente a los archivos de registro
Si utilizas una versión posterior de Android Studio (anterior a 2020.3.1) o usas la herramienta Perfetto para analizar un archivo de registro, necesitarás pasos adicionales.
Primero, extrae el archivo de registro del 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
Ten en cuenta que la ruta de acceso del archivo de salida puede ser diferente si la personalizas con el argumento additionalTestOutputDir
. Puedes buscar registros de rutas de acceso en Logcat para consultar dónde están escritos. Por ejemplo:
I PerfettoCapture: Writing to /storage/emulated/0/Android/data/androidx.benchmark.integration.macrobenchmark.test/cache/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace.
Si, en su lugar, invocas las pruebas mediante la línea de comandos de Gradle (como ./gradlew macrobenchmark:connectedCheck
), puedes copiar los archivos del resultado de la prueba en un directorio de resultados de pruebas, en tu sistema host. Para ello, agrega la siguiente línea al archivo gradle.properties
del proyecto:
android.enableAdditionalTestOutput=true
Los archivos de resultado de las ejecuciones de prueba se muestran en el directorio de compilación de tu proyecto de la siguiente manera:
build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/<device-name>/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace
Cuando tengas el archivo de registro en tu sistema host, puedes abrirlo en Android Studio con las opciones File > Open del menú. Esta opción muestra la vista de la herramienta del generador de perfiles que se proporcionó en la sección anterior.
Como alternativa, puedes usar la herramienta Perfetto, que te permite inspeccionar todos los procesos que se realizan en el dispositivo durante el registro. En cambio, el Generador de perfiles de CPU de Android Studio limita la inspección a un único proceso.
Mejora los datos de registro con eventos personalizados
Te puede resultar útil instrumentar la aplicación con eventos de registro personalizados, que se muestran con el resto del informe de seguimiento y pueden ayudar a identificar problemas específicos de tu app. Si quieres obtener más información sobre la creación de eventos de registro personalizados, consulta la guía Cómo definir eventos personalizados.
Comparativas en CI
Es habitual ejecutar pruebas en la CI sin Gradle, o bien de forma local si usas un sistema de compilación diferente. En esta sección, se explica cómo configurar Macrobenchmark para el uso de CI durante el tiempo de ejecución.
Archivos de resultado: JSON y registros
Macrobenchmark muestra como resultado un archivo JSON y varios archivos de registro: uno por iteración medida de cada bucle MacrobenchmarkRule.measureRepeated
.
Para definir el lugar en el que se escriben estos archivos, pasa el siguiente argumento de instrumentación durante el tiempo de ejecución:
-e additionalTestOutputDir "device_path_you_can_write_to"
Ten en cuenta que para simplificar el proceso, puedes especificar una ruta de acceso en /sdcard/
, pero debes inhabilitar el almacenamiento específico mediante la configuración de requestLegacyExternalStorage
en true
, en tu módulo de Macrobenchmark:
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
También puedes pasar un argumento de instrumentación para omitir el almacenamiento específico en la prueba, como se muestra a continuación:
-e no-isolated-storage 1
Archivo JSON de muestra
A continuación, se muestra un ejemplo de resultado JSON para una única comparativa de inicio:
{
"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
}
]
}
Recursos adicionales
En GitHub, encontrarás un proyecto de ejemplo en el repositorio de Android/muestras de rendimiento.
Si quieres obtener orientación para detectar regresiones de rendimiento, consulta Cómo resolver regresiones con comparativas en CI.
Comentarios
Si deseas informar errores o enviar solicitudes de funciones de Jetpack Macrobenchmark, consulta la Herramienta de seguimiento de errores pública.