Cómo escribir una Microbenchmark

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Puedes comenzar a usar rápidamente la biblioteca de Microbenchmark si agregas cambios al código de la aplicación. Para lograr una configuración de proyecto adecuada, sigue lo que se indica en la sección Configuración completa, que requiere cambios más complicados en la base de código.

Guía de inicio rápido

En esta sección, se proporciona una guía rápida a fin de probar las comparativas y ejecutar mediciones únicas sin necesidad de mover código a los módulos. Para obtener resultados de rendimiento exactos, estos pasos implican inhabilitar la depuración en tu aplicación, por lo que debes conservar esta configuración en una copia de trabajo local sin confirmar los cambios en el sistema fuente de control.

A fin de generar rápidamente comparativas únicas, completa los siguientes pasos:

  1. Agrega la biblioteca al archivo build.gradle del módulo:

    project_root/module_dir/build.gradle

    Groovy

    dependencies {
        androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.1.0-beta03'
    }
    

    Kotlin

    dependencies {
        androidTestImplementation("androidx.benchmark:benchmark-junit4:1.1.0-beta03")
    }
    
  2. Si deseas inhabilitar la depuración en el manifiesto de prueba, actualiza el elemento <application> a fin de forzar la inhabilitación de la depuración de manera temporal:

    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"/>
    
  3. Para agregar tu comparativa, agrega una instancia de la clase BenchmarkRule en un archivo de prueba del directorio androidTest. Consulta Cómo crear una clase de Microbenchmark para descubrir cómo escribir comparativas.

    En el siguiente fragmento de código se muestra cómo agregar comparativas a una prueba de JUnit:

    Kotlin

    @RunWith(AndroidJUnit4::class)
    class SampleBenchmark {
        @get:Rule
        val benchmarkRule = BenchmarkRule()
    
        @Test
        fun benchmarkSomeWork() {
            benchmarkRule.measureRepeated {
                doSomeWork()
            }
        }
    }
    

    Java

    @RunWith(AndroidJUnit4.class)
    class SampleBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
        @Test
        public void benchmarkSomeWork() {
            final BenchmarkState state = benchmarkRule.getState();
            while (state.keepRunning()) {
                doSomeWork();
            }
        }
    }
    

Si deseas ver cómo escribir una comparativa, avanza a la sección Cómo crear una clase de Microbenchmark.

Configuración completa del proyecto

Para configurar la generación de comparativas regulares en lugar de las comparativas únicas, debes aislarlas en su propio módulo. De esta manera, podrás asegurarte de que su configuración, como establecer debuggable en false, sea independiente de las pruebas regulares.

Debido a que Microbenchmark ejecuta tu código de forma directa, debes colocar el código que quieras comparar en un módulo de Gradle separado y establecer la dependencia en ese módulo, como se muestra en la siguiente figura.

Estructura de la app con módulos de Gradle :app, :microbenchmark y :benchmarkable, que permite que las microcomparativas comparen código en el módulo :benchmarkable.

Para agregar un nuevo módulo de Gradle, puedes usar el asistente de módulos en Android Studio. El asistente crea un módulo configurado previamente para la generación de comparativas, con un directorio de comparativas y el valor debuggable configurado como false.

Bumblebee (o una versión más reciente)

  1. 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.
  2. Selecciona Benchmark en el panel Templates.
  3. Selecciona Microbenchmark como tipo de módulo de comparativas.
  4. Como nombre de módulo, escribe "microbenchmark".
  5. Haz clic en Finish.
  6. Configuración de un nuevo módulo de biblioteca

Arctic Fox

  1. 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.
  2. Selecciona Benchmark en el panel Templates.
  3. Como nombre de módulo, escribe "microbenchmark".
  4. Haz clic en Finish.
  5. Configuración de un nuevo módulo de biblioteca

Después de crear el módulo, cambia su archivo build.gradle y agrega testImplementation al módulo que contiene el código para crear comparativas:

Groovy

dependencies {
    // Note, that the module name may be different
    androidTestImplementation project(':benchmarkable')
}

Kotlin

dependencies {
    // Note, that the module name may be different
    androidTestImplementation(project(":benchmarkable"))
}

Cómo crear una clase de Microbenchmark

Las comparativas son pruebas de instrumentación estándar. Para crear comparativas, usa la clase BenchmarkRule que proporciona la biblioteca. Si deseas comparar actividades, usa ActivityTestRule o ActivityScenarioRule. Usa @UiThreadTest para comparar código de IU.

En el siguiente código se muestran comparativas de ejemplo:

Kotlin

@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun benchmarkSomeWork() {
        benchmarkRule.measureRepeated {
            doSomeWork()
        }
    }
}
    

Java

@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
    @Rule
    public BenchmarkRule benchmarkRule = new BenchmarkRule();

    @Test
    public void benchmarkSomeWork() {
        final BenchmarkState state = benchmarkRule.getState();
        while (state.keepRunning()) {
            doSomeWork();
        }
    }
}
    

Cómo inhabilitar la sincronización

Puedes inhabilitar la sincronización para secciones de código que no quieras medir con el bloque runWithTimingDisabled{}. Por lo general, estas secciones representan algún código que necesitas ejecutar en cada iteración de la comparativa.

Kotlin

// using random with the same seed, so that it generates the same data every run
private val random = Random(0)

// create the array once and just copy it in benchmarks
private val unsorted = IntArray(10_000) { random.nextInt() }

@Test
fun benchmark_quickSort() {
    // ...
    benchmarkRule.measureRepeated {
        // copy the array with timing disabled to measure only the algorithm itself
        listToSort = runWithTimingDisabled { unsorted.copyOf() }

        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort)
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(listToSort.isSorted)
}
    

Java

private final int[] unsorted = new int[10000];

public SampleBenchmark() {
    // using random with the same seed, so that it generates the same data every run
    Random random = new Random(0);

    // create the array once and just copy it in benchmarks
    Arrays.setAll(unsorted, (index) -> random.nextInt());
}

@Test
public void benchmark_quickSort() {
    final BenchmarkState state = benchmarkRule.getState();

    int[] listToSort = new int[0];

    while (state.keepRunning()) {
        
        // copy the array with timing disabled to measure only the algorithm itself
        state.pauseTiming();
        listToSort = Arrays.copyOf(unsorted, 10000);
        state.resumeTiming();
        
        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort);
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(SortingAlgorithmsKt.isSorted(listToSort));
}
    

Intenta minimizar la cantidad de trabajo realizado dentro del bloque measureRepeated, así como dentro de runWithTimingDisabled. El bloque measureRepeated se ejecuta varias veces y puede afectar el tiempo general necesario para ejecutar la comparativa. Si necesitas verificar algunos resultados de una comparativa, puedes confirmar el último resultado en lugar de hacerlo en cada iteración de la comparativa.

Cómo ejecutar las comparativas

En Android Studio, ejecuta tus comparativas como lo harías con cualquier @Test mediante la acción del margen junto a tu clase o método de prueba, como se muestra en la siguiente imagen.

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

Como alternativa, desde la línea de comandos, ejecuta connectedCheck para ejecutar todas las pruebas del módulo de Gradle especificado:

./gradlew benchmark:connectedCheck

También puedes ejecutar una sola prueba:

./gradlew benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.benchmark.SampleBenchmark#benchmarkSomeWork

Resultados de comparativas

Después de ejecutar Microbenchmark con éxito, se muestran las métricas directamente en Android Studio, así como un informe de comparativas completo con información adicional del dispositivo y métricas disponibles en formato JSON. El complemento de Gradle androidx.benchmark habilita el resultado de JSON de forma predeterminada.

Resultados de Microbenchmarks

De forma predeterminada, el informe de JSON se escribe en el disco del dispositivo, en la carpeta externa del APK de prueba de contenido multimedia compartido que, por lo general, se encuentra en: /storage/emulated/0/Android/media/app_id/app_id-benchmarkData.json.

Los informes de JSON también se copian automáticamente del dispositivo al host. Estos están escritos en la máquina anfitrión en la siguiente string:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/app_id-benchmarkData.json

Errores de configuración

La biblioteca detecta las siguientes condiciones a fin de garantizar que tu proyecto y entorno estén configurados para un rendimiento acorde a un lanzamiento:

  • Debuggable está configurado en false.
  • Se está usando un dispositivo físico (no se admiten emuladores).
  • Los relojes están bloqueados si el dispositivo tiene permisos de administrador.
  • El nivel de batería en el dispositivo es suficiente (es de al menos 25%).

Si alguna de las verificaciones anteriores falla, las comparativas informan un error para desalentar las mediciones imprecisas.

Para hacer que algunos tipos de error específicos pasen como advertencias y evitar que detengan las comparativas, pasa el tipo de error en una lista separada por comas al argumento de instrumentación androidx.benchmark.suppressErrors.

Esto se puede configurar desde tu secuencia de comandos de Gradle como se muestra a continuación:

Groovy

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Kotlin

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Ten en cuenta que suprimir errores permite que las comparativas se ejecuten en un estado de configuración incorrecta, pero se le cambiará intencionalmente el nombre del resultado de las comparativas anteponiendo nombres de pruebas con el error. Por ejemplo, ejecutar una comparativa depurable con la supresión del fragmento anterior antepone DEBUGGABLE_ a los nombres de prueba.