Cómo obtener comparativas en integración continua

Puedes ejecutar comparativas en integración continua (CI) para hacer un seguimiento del rendimiento a lo largo del tiempo y reconocer regresiones (o mejoras) de rendimiento antes de que se publique tu app. En esta página, se proporciona información básica sobre las comparativas en CI.

Antes de comenzar a realizar comparativas en CI, considera cómo difiere la captura y evaluación de los resultados de las pruebas regulares.

Resultados aproximados

Aunque las comparativas son pruebas instrumentadas, los resultados no son solo aprobatorios o reprobatorios. Las comparativas proporcionan mediciones de tiempo para el dispositivo determinado en el que se ejecutan. Graficar los resultados a lo largo del tiempo te permite supervisar los cambios y observar el ruido en el sistema de medición.

Usa dispositivos reales

Ejecuta comparativas en dispositivos Android físicos. Si bien pueden ejecutarse en emuladores, no es recomendable en absoluto, ya que no representarán una experiencia del usuario realista y proporcionarán números vinculados a las capacidades del hardware y el SO del host. Considera usar dispositivos reales o un servicio que te permita ejecutar pruebas en ellos, como Firebase Test Lab.

Ejecuta las comparativas

Ejecutar las comparativas como parte de la canalización de CI puede ser diferente a ejecutarlas de forma local desde Android Studio. De forma local, normalmente se ejecutan las pruebas de integración de Android con una tarea connectedCheck de Gradle. Esta tarea compila automáticamente tu APK y el APK de prueba, y ejecuta las pruebas en los dispositivos conectados al servidor de CI. Cuando se ejecuta en CI, por lo general, este flujo debe dividirse en fases separadas.

Compilación

Para la Biblioteca de microcomparativas, ejecuta la tarea de Gradle assemble[VariantName]AndroidTest, que crea tu APK de prueba que contiene el código de la aplicación y el código que probaste.

De manera alternativa, la Biblioteca de macrocomparativas requiere que compiles tu APK de destino y el APK de prueba por separado. Por lo tanto, ejecuta las tareas de Gradle :app:assemble[VariantName] y :macrobenchmark:assemble[VariantName].

Instalación y ejecución

Generalmente, estos pasos se realizan sin necesidad de ejecutar tareas de Gradle. Ten en cuenta que se pueden abstraer dependiendo de si usas un servicio que te permite ejecutar pruebas en dispositivos reales.

Para la instalación, usa el comando adb install y especifica el APK de prueba o el APK de destino.

Ejecuta el comando de instrumento adb shell am para ejecutar todas las comparativas.

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

Cuando utilices la biblioteca de Macrobenchmark, usa el objeto androidx.test.runner.AndroidJUnitRunner normal como ejecutor de instrumentación.

Puedes pasar los mismos argumentos de instrumentación que en la configuración de Gradle con el argumento -e. Si quieres conocer todas las opciones de argumentos de instrumentación, consulta Argumentos de instrumentación de microcomparativas o Cómo agregar argumentos de instrumentación para Macrobenchmark.

Por ejemplo, puedes configurar el argumento dryRunMode para ejecutar microcomparativas como parte de tu proceso de verificación de solicitud de extracción. Con esta marca habilitada, las microcomparativas se ejecutan solo en un bucle único, lo que permite verificar que se ejecuten de forma correcta, pero que no tarden demasiado en ejecutarse.

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

Si quieres obtener más información para ejecutar pruebas de instrumentación desde la línea de comandos, consulta Cómo ejecutar pruebas con ADB.

Bloquea relojes

El complemento de Gradle para microcomparativas proporciona el comando ./gradlew lockClocks para bloquear los relojes de la CPU de un dispositivo con permisos de administrador. Esto es útil para garantizar la estabilidad cuando tienes acceso a dispositivos con permisos de administrador, como compilaciones "userdebug". Puedes replicar esta acción con la secuencia de comandos de shell lockClocks.sh, que está disponible en la fuente de la biblioteca.

Puedes ejecutar la secuencia de comandos directamente desde un host de Linux o Mac, o bien enviarla al dispositivo con algunos 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

Si ejecutas la secuencia de comandos de shell directamente en un host, este envía los comandos a un dispositivo conectado.

Para obtener más información sobre por qué es útil bloquear los relojes de la CPU, consulta cómo obtener comparativas coherentes.

Recopila los resultados

Las bibliotecas de comparativas generan mediciones en JSON, junto con registros de la generación de perfiles, en un directorio en el dispositivo Android después de cada ejecución de comparativas. La biblioteca de Macrobenchmark genera varios archivos de registro de perfetto: uno por iteración medida de cada bucle MacrobenchmarkRule.measureRepeated. Sin embargo, la de Microbenchmark crea solo un archivo de registro para todas las iteraciones de cada BenchmarkRule.measureRepeated. Los archivos de registro de generación de perfiles también se envían a este mismo directorio.

Guarda y ubica los archivos

Si ejecutas comparativas con Gradle, estos archivos se copian automáticamente en el directorio de salidas de tu computadora host en build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/.

Si ejecutas de manera directa con el comando adb, debes extraer los archivos de forma manual. De forma predeterminada, los informes se guardan en el dispositivo dentro del directorio de contenido multimedia del almacenamiento externo de la app probada. Para mayor comodidad, la biblioteca muestra la ruta de acceso del archivo en Logcat. Ten en cuenta que la carpeta de salida puede ser diferente según la versión de Android en la que se ejecuten las comparativas.

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

También puedes configurar la ubicación en la que se guardan los informes de comparativas en el dispositivo. Para ello, usa el argumento de instrumentación additionalTestOutputDir. La app debe poder escribir en esta carpeta.

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

En Android 10 (nivel de API 29) y versiones posteriores, las pruebas de tu app se ejecutan en una zona de pruebas de almacenamiento de forma predeterminada, lo que evita que la app acceda a archivos fuera del directorio específico de la app. Para poder guardar en un directorio global, como /sdcard/Download, pasa el siguiente argumento de instrumentación:

-e no-isolated-storage true

También debes permitir de manera explícita las opciones de almacenamiento heredado en el manifiesto de tus comparativas:

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

Consulta Cómo inhabilitar temporalmente el almacenamiento específico para obtener más información.

Recupera los archivos

Para obtener los archivos generados del dispositivo, usa el comando adb pull, que extrae el archivo especificado en el directorio actual de tu host:

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

Para recuperar todos los benchmarkData de una carpeta especificada, verifica el siguiente fragmento:

# 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

Los archivos de registro (.trace o .perfetto-trace) se guardan en la misma carpeta que benchmarkData.json, por lo que puedes recopilarlos de la misma manera.

Ejemplo de datos de comparativas

Las bibliotecas de comparativas generan archivos JSON que contienen información sobre el dispositivo en el que se ejecutaron las comparativas y las comparativas reales que ejecutó. El siguiente fragmento representa el archivo JSON que se genera:

{
    "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
        }
    ]
}

Recursos adicionales