Cómo escribir una macrocomparativa

Usa la biblioteca de Macrobenchmark para probar los casos de uso más grandes de tu app, incluido su inicio y las manipulaciones de IU complejas, como desplazarse por una RecyclerView o ejecutar animaciones. Si quieres probar áreas más pequeñas de tu código, consulta la biblioteca de Microbenchmark. En esta página, se muestra cómo configurar la biblioteca de Macrobenchmark.

La biblioteca genera resultados de comparativas tanto en la consola de Android Studio como en un archivo JSON con más detalles. También proporciona archivos de registro que puedes cargar y analizar en Android Studio.

Usa la biblioteca de Macrobenchmark en un entorno de integración continua (CI), como se describe en Cómo obtener comparativas en integración continua.

Puedes usar Macrobenchmark para generar perfiles de Baseline. Primero, configura la biblioteca de Macrobenchmark y, luego, podrás crear un perfil de Baseline.

Configuración del proyecto

Te recomendamos que uses Macrobenchmark con la versión más reciente de Android Studio para las funciones del IDE que se integran con Macrobenchmark.

Configura el módulo de Macrobenchmark

Las macrocomparativas requieren un módulo com.android.test independiente del código de tu app en el que se ejecuten las pruebas que miden la app.

En Android Studio, hay una plantilla disponible para simplificar la configuración del módulo de Macrobenchmark. Esta plantilla 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 para crear un módulo nuevo, haz lo siguiente:

  1. Haz clic con el botón derecho en tu proyecto o módulo desde el panel Project en Android Studio, y luego selecciona New > Module.

  2. Selecciona Benchmark en el panel Templates. 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 Macrobenchmark.

  3. Haz clic en Finish.

Plantilla del módulo de comparativas

Figura 1: Plantilla del módulo de comparativas

Cómo configurar la app

Para obtener comparativas de una app, lo que se conoce como el objetivo de Macrobenchmark, esta debe ser profileable, lo que permite leer información de seguimiento detallada sin afectar el rendimiento. El asistente de módulos agrega la etiqueta <profileable> automáticamente al archivo AndroidManifest.xml de la app.

Asegúrate de que la app de destino incluya ProfilerInstaller 1.3 o una versión posterior, para la que la biblioteca de Macrobenchmark debe habilitar la captura y el restablecimiento de perfiles, y borrar la caché del sombreador.

Configura la app de la que obtendrás comparativas de la forma más parecida posible a la versión de actualización (o producción). Además, configúrala como no depurable y con la reducción activada en lo posible, lo que mejorará el rendimiento. Para ello, en general, debes crear una copia de la variante de la versión, que tendrá el mismo rendimiento, pero se firma de manera local con claves de depuración. Como alternativa, puedes usar initWith para indicar a Gradle que lo haga por ti:

Kotlin

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
    }

    create("benchmark") {
        initWith(getByName("release"))
        signingConfig = signingConfigs.getByName("debug")
    }
}

Groovy

buildTypes {
    val release = getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
        // In real app, this would use its own release keystore
        signingConfig = signingConfigs.getByName("debug")
    }

    create("benchmark") {
        initWith(release)
        signingConfig = signingConfigs.getByName("debug")
        proguardFiles("benchmark-rules.pro")
    }
}

Para asegurarte de que la ejecución de las comparativas compile y pruebe la variante correcta de tu app, como se muestra en la Figura 2, haz lo siguiente:

  1. Realiza una sincronización de Gradle.
  2. Abre el panel Build Variants.
  3. Selecciona la variante de comparativas de la app y del módulo de Macrobenchmark.

Selecciona una variante de comparativas

Figura 2: Selecciona la variante de comparativas.

Cómo configurar una app con varios módulos (opcional)

Si tu app tiene más de un módulo de Gradle, asegúrate de que las secuencias de comandos de compilación sepan qué variante de compilación compilar. Agrega la propiedad matchingFallbacks al tipo de compilación benchmark de tus módulos :macrobenchmark y :app. El resto de los módulos de Gradle puede tener la misma configuración que antes.

Kotlin

create("benchmark") {
    initWith(getByName("release"))
    signingConfig = signingConfigs.getByName("debug")

    matchingFallbacks += listOf("release")
}

Groovy

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
}

De lo contrario, el tipo de compilación benchmark que se agregó recientemente hace que la compilación falle y muestra el siguiente mensaje de error:

> Could not resolve project :shared.
     Required by:
         project :app
      > No matching variant of project :shared was found.
      ...

Cuando selecciones las variantes de compilación de tu proyecto, elige benchmark para los módulos :app y :macrobenchmark, y release para cualquier otro módulo que tengas en tu app, como se muestra en la Figura 3:

Variantes de comparativas para un proyecto de varios módulos con versiones de comparativas y tipos de compilación

Figura 3: Variantes de comparativas para un proyecto de varios módulos con versiones de comparativas y tipos de compilación.

Para obtener más información, consulta Cómo usar la administración de dependencias con reconocimiento de variantes.

Cómo configurar las variantes de producto (opcional)

Si configuraste más de una variante de producto en la app, configura el módulo :macrobenchmark para que sepa qué tipo de variante compilar y comparar.

En los ejemplos de esta página, se usan las dos variantes de producto del módulo :app, demo y production, como se muestra en el siguiente fragmento:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
        // ...
    }
    create("production") {
        dimension = "environment"
        // ...
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
        // ...
    }

    production {
        dimension 'environment'
        // ...
    }
}

Sin esta configuración, es posible que recibas un error de compilación similar al de varios módulos de Gradle:

Could not determine the dependencies of task ':macrobenchmark:connectedBenchmarkAndroidTest'.
> Could not determine the dependencies of null.
   > Could not resolve all task dependencies for configuration ':macrobenchmark:benchmarkTestedApks'.
      > Could not resolve project :app.
        Required by:
            project :macrobenchmark
         > The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'benchmark', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.3.0'. However we cannot choose between the following variants of project :app:
             - demoBenchmarkRuntimeElements
             - productionBenchmarkRuntimeElements
           All of them match the consumer attributes:
           ...

Las dos secciones siguientes son formas de configurar las comparativas con más de una variante de producto.

Utiliza missingDimensionStrategy

La especificación de missingDimensionStrategy en el defaultConfig del módulo :macrobenchmark le indica al sistema de compilación que recurra a la dimensión de variantes. Especifica qué dimensiones usar si no las encuentras en el módulo. En el siguiente ejemplo, se usa la variante production como dimensión predeterminada:

Kotlin

defaultConfig {
    missingDimensionStrategy("environment", "production")
}

Groovy

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

De esta manera, el módulo :macrobenchmark solo puede compilar y comparar las variantes de productos especificadas, lo que es útil si sabes que solo una variante de producto tiene la configuración adecuada para obtener comparativas.

Define variantes de producto en el módulo :macrobenchmark

Si deseas compilar y comparar otras variantes de producto, defínelas en el módulo :macrobenchmark. Especifícala del mismo modo que en el módulo :app, pero solo asigna productFlavors a una dimension. No se requiere ningún otro parámetro de configuración:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
    }
    create("production") {
        dimension = "environment"
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
    }

    production {
        dimension 'environment'
    }
}

Una vez definido y sincronizado el proyecto, elige la variante de compilación relevante del panel Build Variants, como se muestra en la Figura 4:

Variantes de comparativas para el proyecto con variantes de producto que muestran el módulo productionBenchmark y la opción release seleccionados

Figura 4: Variantes de comparativas para el proyecto con variantes de producto que muestran el módulo "productionBenchmark" y la opción "release" seleccionados.

Para obtener más información, consulta Cómo corregir errores de compilación relacionados con la coincidencia de variantes.

Cómo crear una clase de macrocomparativas

Las pruebas de comparativas se proporcionan a través de la API de la regla MacrobenchmarkRule de JUnit4 en la biblioteca de Macrobenchmark. Dicha biblioteca contiene un método measureRepeated que te permite especificar varias condiciones para ejecutar y comparar la app de destino.

Como mínimo, debes especificar el packageName de la app de destino, las metrics que deseas medir y la cantidad de iterations que debe ejecutar la comparativa.

Kotlin

@LargeTest
@RunWith(AndroidJUnit4::class)
class SampleStartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = TARGET_PACKAGE,
        metrics = listOf(StartupTimingMetric()),
        iterations = DEFAULT_ITERATIONS,
        setupBlock = {
            // Press home button before each run to ensure the starting activity isn't visible.
            pressHome()
        }
    ) {
        // starts default launch activity
        startActivityAndWait()
    }
}

Java

@LargeTest
@RunWith(AndroidJUnit4.class)
public class SampleStartupBenchmark {
    @Rule
    public MacrobenchmarkRule benchmarkRule = new MacrobenchmarkRule();

    @Test
    public void startup() {
        benchmarkRule.measureRepeated(
            /* packageName */ TARGET_PACKAGE,
            /* metrics */ Arrays.asList(new StartupTimingMetric()),
            /* iterations */ 5,
            /* measureBlock */ scope -> {
                // starts default launch activity
                scope.startActivityAndWait();
                return Unit.INSTANCE;
            }
        );
    }
}

Si deseas conocer todas las opciones para personalizar tus comparativas, consulta la sección Cómo personalizar las comparativas.

Cómo ejecutar las comparativas

Ejecuta la prueba desde Android Studio para medir el rendimiento de la app en tu dispositivo. Puedes ejecutar las comparativas de la misma manera que ejecutas cualquier otra @Test con la acción del margen junto a tu clase o método de prueba, como se muestra en la Figura 5.

Ejecuta macrocomparativas con la acción del margen junto a la clase de prueba

Figura 5: Ejecuta macrocomparativas con la acción del margen junto a la clase de prueba.

Puedes ejecutar todas las comparativas en un módulo de Gradle desde la línea de comandos ejecutando el comando connectedCheck:

./gradlew :macrobenchmark:connectedCheck

Puedes realizar una sola prueba ejecutando lo siguiente:

./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startup

Consulta Cómo obtener comparativas en integración continua para obtener información sobre la ejecución y supervisión de comparativas en integración continua.

Resultados de comparativas

Después de generar una comparativa con éxito, las métricas se muestran directamente en Android Studio y como resultados del uso de CI en un archivo JSON. Cada iteración medida captura un registro del sistema diferente. Para abrir estos resultados de seguimiento, haz clic en los vínculos del panel Test Results, como se muestra en la Figura 6:

Resultados de macrocomparativas del inicio

Figura 6: Resultados de macrocomparativas del inicio.

Cuando se carga el registro, Android Studio te solicita que selecciones el proceso a analizar. La selección se prepropaga con el proceso de la app de destino, como se muestra en la Figura 7:

Selección del proceso de registro de Studio

Figura 7: Selección del proceso de registro de Studio.

Una vez cargado el archivo de registro, Studio muestra los resultados en la herramienta del Generador de perfiles de CPU:

Registro de Studio

Figura 8: Registro de Studio.

Los informes de JSON y cualquier seguimiento de perfiles también se copian automáticamente del dispositivo al host. Están escritos en la máquina anfitrión en la siguiente ubicación:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/

Accede a los archivos de registro de forma manual

Si deseas utilizar la herramienta Perfetto para analizar un archivo de registro, debes seguir algunos pasos adicionales. Perfetto 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.

Si invocas las pruebas desde Android Studio o desde la línea de comandos de Gradle, los archivos de registro se copian automáticamente del dispositivo al host. Están escritos en la máquina anfitrión en la siguiente ubicación:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace

Cuando tengas el archivo de registro en tu sistema host, podrás abrirlo en Android Studio seleccionando File > Open en el menú. Esta opción muestra la vista de la herramienta del generador de perfiles que se proporcionó en la sección anterior.

Errores de configuración

Si la app está mal configurada (es depurable o no permite que se generen perfiles), Macrobenchmark mostrará un error, en lugar de informar una medición incorrecta o incompleta. Puedes suprimir esos errores con el argumento androidx.benchmark.suppressErrors.

Macrobenchmark también muestra errores cuando se intenta medir un emulador o si el dispositivo tiene poca batería, lo que puede comprometer la disponibilidad principal y la velocidad del reloj.

Cómo personalizar las comparativas

La función measureRepeated acepta varios parámetros que influyen en qué métricas recopila la biblioteca, cómo se inicia y compila tu app, y cuántas iteraciones ejecutará la comparativa.

Captura las métricas

Las métricas son el tipo principal de información que se extrae de tus comparativas. Las métricas disponibles son las siguientes:

Para obtener más información sobre las métricas, consulta Cómo capturar métricas de Macrobenchmark.

Mejora los datos de registro con eventos personalizados

Podría resultarte útil instrumentar tu app con eventos de registro personalizados, que se muestren con el resto del informe de registro y puedan 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 el artículo Cómo definir eventos personalizados.

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 instala un perfil de Baseline (si está disponible) en Android 7 (nivel de API 24) y versiones posteriores. Si usas Android 6 (nivel de API 23) o versiones anteriores, el modo de compilación compila por completo el APK como el comportamiento predeterminado del sistema.

Puedes instalar un perfil de Baseline si la app de destino contiene este tipo de perfil y la biblioteca de ProfileInstaller.

En Android 7 y versiones posteriores, puedes personalizar el CompilationMode para que afecte la cantidad de compilación previa en el dispositivo, y así imitar diferentes niveles de compilación anticipada (AOT) o almacenamiento en caché de JIT. Consulta CompilationMode.Full, CompilationMode.Partial, CompilationMode.None y CompilationMode.Ignore.

Esta función 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.

StartupMode

Para iniciar una actividad, puedes pasar un modo de inicio predefinido: COLD, WARM o HOT. Este parámetro cambia la manera en la que se inicia la actividad y el estado del proceso al comienzo de la prueba.

Para obtener más información sobre los tipos de inicio, consulta Tiempo de inicio de la app.

Ejemplos

Hay un proyecto de muestra disponible en la Muestra de macrocomparativas del repositorio en GitHub.

Envía comentarios

Si deseas informar errores o enviar solicitudes de funciones de Jetpack Macrobenchmark, consulta la Herramienta de seguimiento de errores pública.