La biblioteca de Jetpack Benchmark te permite obtener rápidamente comparativas de tu código basado en Kotlin o en Java dentro de Android Studio. La biblioteca se ocupa de la preparación, mide el rendimiento del código y los recuentos de asignaciones, y genera resultados de comparativas en la consola de Android Studio y en un archivo JSON con más detalles.
Entre los casos de uso se incluye desplazar un RecyclerView
, realizar búsquedas en bases de datos y medir las partes lentas de tu código que desees agilizar.
Puedes usar la biblioteca en un entorno de integración continua (CI), como se describe en Cómo ejecutar comparativas en la integración continua.
Si todavía no implementaste AndroidX en un proyecto del que quieres generar comparativas, consulta Cómo migrar un proyecto existente con Android Studio.
Guía de inicio rápido
En esta sección, se proporciona un conjunto de pasos rápidos para generar comparativas sin necesidad de mover código a los módulos. Debido a que los pasos implican inhabilitar la depuración para obtener resultados de rendimiento precisos, no se confirmarán los cambios en el sistema de control del código fuente, aunque podría ser útil cuando quieras ejecutar mediciones únicas.
Para generar rápidamente comparativas únicas, completa los siguientes pasos:
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-alpha13' }
Kotlin
dependencies { androidTestImplementation("androidx.benchmark:benchmark-junit4:1.1.0-alpha13") }
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, como se muestra a continuación: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"/>
Para agregar tu comparativa, agrega una instancia de la clase
BenchmarkRule
en un archivo de prueba del directorioandroidTest
. Consulta Cómo escribir comparativas para obtener más información.En el siguiente fragmento de código se muestra cómo agregar comparativas a una prueba de JUnit:
Kotlin
@RunWith(AndroidJUnit4::class) class MyBenchmark { @get:Rule val benchmarkRule = BenchmarkRule() @Test fun benchmarkSomeWork() = benchmarkRule.measureRepeated { doSomeWork() } }
Java
@RunWith(AndroidJUnit4.class) class MyBenchmark { @Rule public BenchmarkRule benchmarkRule = new BenchmarkRule(); @Test public void benchmarkSomeWork() { final BenchmarkState state = benchmarkRule.getState(); while (state.keepRunning()) { doSomeWork(); } } }
Cuándo generar comparativas
Antes de escribir comparativas, se recomienda generar el perfil de tu código. De esta forma, podrás encontrar operaciones costosas que sería conveniente optimizar. También puede revelar el motivo de la lentitud, ya que muestra lo que sucede cuando se ejecutan las operaciones. Algunos ejemplos pueden ser ejecutarse en un subproceso de baja prioridad, suspenderse por requerir acceso al disco o llamar de manera inesperada a una función costosa, como la decodificación de un mapa de bits.
Si agregas puntos de seguimiento personalizados mediante la API de TraceCompat (o el wrapper -ktx
) como se muestra a continuación, podrás verlos en Systrace o en el Generador de perfiles de CPU de Android Studio:
Kotlin
fun proccessBitmap(bitmap: Bitmap): Bitmap { trace("processBitmap") { // perform work on bitmap... } }
Java
public Bitmap processBitmap(Bitmap bitmaptest) { TraceCompat.beginSection("processBitmap"); try { // perform work on bitmap... } finally { TraceCompat.endSection(); } }
Qué comparar
Las comparativas son especialmente útiles para el trabajo de la CPU que se ejecuta muchas veces en la app. Algunos ejemplos adecuados son el desplazamiento de RecyclerView
, las conversiones o el procesamiento de datos y los fragmentos de código que se usan de manera repetida.
Es difícil utilizar las comparativas para medir otros tipos de código. Debido a que las comparativas se ejecutan en un bucle, el código que no se ejecute con frecuencia o que tenga un rendimiento diferente cuando se lo llame varias veces, podría no ser apropiado para generar comparativas.
Almacenamiento en caché
Evita medir solo la caché. Por ejemplo, las comparativas de diseño de una vista personalizada podrían medir solo el rendimiento de la caché de diseño. Para evitar que esto suceda, puedes pasar diferentes parámetros de diseño en cada bucle. En otros casos, como cuando se mide el rendimiento del sistema de archivos, podría resultar difícil, ya que el SO almacena en caché el sistema de archivos mientras está en un bucle.
Código ejecutado con poca frecuencia
Es poco probable que Android Runtime (ART) compile mediante JIT el código que se ejecuta una vez durante el inicio de la app. Por lo tanto, generar comparativas de este código con microcomparativas, en las que se ejecuta en un bucle cerrado, no es una manera realista de medir el rendimiento.
Para comparar este tipo de código, recomendamos Jetpack Macrobenchmark, que admite la medición de interacciones generales de usuario, como rendimiento durante el inicio de la app y el desplazamiento.
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 y los ajustes de debuggable
en false
sean independientes de las pruebas regulares.
Antes de agregar el módulo de comparativas, coloca el código y los recursos que deseas comparar en un módulo de biblioteca si aún no están en uno.
Nuestras muestras brindan ejemplos de cómo configurar un proyecto de esta manera.
Cómo definir propiedades de Android Studio
Si usas Android Studio 3.5, debes configurar manualmente las propiedades de Android Studio a fin de habilitar la compatibilidad con el asistente del módulo de comparativas. Esto no es necesario para Android Studio 3.6 o versiones posteriores.
Si deseas habilitar la plantilla de Android Studio para comparativas, haz lo siguiente:
En Android Studio 3.5, haz clic en Help > Edit Custom Properties.
Agrega la siguiente línea en el archivo que se abre:
npw.benchmark.template.module=true
Guarda el archivo y ciérralo.
Reinicia Android Studio.
Cómo crear un módulo nuevo
La plantilla del módulo de comparativas configura automáticamente los ajustes para la generación de comparativas.
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 y selecciona New > Module.
Selecciona Benchmark Module y haz clic en Next.
Figura 1: Módulo de comparativas
Escribe un nombre para el módulo, selecciona el idioma y haz clic en Finish.
Se crea un módulo configurado previamente para la generación de comparativas, con un directorio de comparativas y el valor
debuggable
configurado comofalse
.
Cómo escribir comparativas
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 ViewBenchmark { @get:Rule val benchmarkRule = BenchmarkRule() @Test fun simpleViewInflate() { val context = ApplicationProvider.getApplicationContext<Context>() val inflater = LayoutInflater.from(context) val root = FrameLayout(context) benchmarkRule.measureRepeated { inflater.inflate(R.layout.test_simple_view, root, false) } } }
Java
@RunWith(AndroidJUnit4::class) public class ViewBenchmark { @Rule public BenchmarkRule benchmarkRule = new BenchmarkRule(); @Test public void simpleViewInflate() { Context context = ApplicationProvider.getApplicationContext<Context>(); final BenchmarkState state = benchmarkRule.getState(); LayoutInflater inflater = LayoutInflater.from(context); FrameLayout root = new FrameLayout(context); while (state.keepRunning()) { inflater.inflate(R.layout.test_simple_view, root, false); } } }
Puedes inhabilitar la sincronización para secciones de código que no quieras medir, como se muestra en el siguiente código de ejemplo:
Kotlin
@Test fun bitmapProcessing() = benchmarkRule.measureRepeated { val input: Bitmap = runWithTimingDisabled { constructTestBitmap() } processBitmap(input) }
Java
@Test public void bitmapProcessing() { final BenchmarkState state = benchmarkRule.getState(); while (state.keepRunning()) { state.pauseTiming(); Bitmap input = constructTestBitmap(); state.resumeTiming(); processBitmap(input); } }
Consulta Cómo ejecutar las comparativas para obtener más información.
Cómo ejecutar las comparativas
En Android Studio, ejecuta tus comparativas como lo harías con cualquier @Test
. En Android Studio 3.4 y versiones posteriores, puedes ver el resultado que se envió a la consola.
Para ejecutar las comparativas, en el módulo, navega a benchmark/src/androidTest
y presiona Control
+ Shift
+ F10
(Command
+ Shift
+ R
en Mac). Los resultados de las comparativas aparecerán en la consola, como se muestra en la figura 2:
Figura 2: Resultado de comparativas en Android Studio
Desde la línea de comandos, ejecuta el comando connectedCheck
regular:
./gradlew benchmark:connectedCheck
Cómo recopilar los datos
Hay un informe de comparativas completo con métricas adicionales e información sobre el dispositivo disponible en formato JSON.
El complemento de Gradle androidx.benchmark
habilita el resultado de JSON de manera predeterminada. Si deseas habilitar manualmente los resultados de JSON en entornos de compilación que no sean de Gradle, debes pasar un argumento de instrumentación, androidx.benchmark.output.enable
, configurado en true
.
El siguiente es un ejemplo en el que se usa el comando adb shell am instrument
:
adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
De manera predeterminada, el informe de JSON está escrito en el dispositivo en la carpeta de descargas compartida externa del APK de prueba que, por lo general, se encuentra en:
/storage/emulated/0/Download/app_id-benchmarkData.json
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
.
adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" -e "additionalTestOutputDir" "/path_to_a_directory_on_device_test_has_write_permissions_to/" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
Los informes de JSON se copian del dispositivo al host. 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
Android Gradle Plugin 3.6 y versiones posteriores
Cuando ejecutas comparativas desde la línea de comandos con Gradle, en los proyectos que usan el complemento de Gradle para Android 3.6 y versiones posteriores, puedes agregar la siguiente marca al archivo gradle.properties
del proyecto:
android.enableAdditionalTestOutput=true
De esta manera, se habilita la función experimental del complemento de Gradle para Android a fin de extraer informes de comparativas de dispositivos con nivel de API 16 y posterior:
Android Gradle Plugin 3.5 y versiones anteriores
En el caso de los usuarios de versiones anteriores del complemento de Gradle para Android, el complemento androidx.benchmark
de Gradle se ocupa de la copia de informes de comparativas de JSON del dispositivo al host.
Cuando uses el complemento de Android para Gradle 3.5 o una versión anterior, y especifiques el nivel de API 29, deberás agregar una marca al manifiesto de Android en el directorio androidTest
de tus comparativas a fin de habilitar el comportamiento de almacenamiento externo heredado.
<manifest ... > <!-- This attribute is "false" by default on apps targeting Android 10. --> <application android:requestLegacyExternalStorage="true" ... > ... </application> </manifest>
Consulta Cómo inhabilitar temporalmente el almacenamiento específico para obtener más información.
Estabilidad del reloj
En los dispositivos móviles, los relojes cambian de manera dinámica del estado alto (para el rendimiento) al estado bajo (a fin de ahorrar energía cuando el dispositivo se calienta). Estos relojes variables pueden hacer que los números de tus comparativas varíen en gran medida, por lo que la biblioteca proporciona maneras de abordar este problema.
Bloquear relojes (requiere permisos de administrador)
La mejor manera de estabilizar el rendimiento consiste en bloquear los relojes. De esta manera, se garantiza que los relojes nunca estén lo suficientemente altos para calentar el dispositivo, o bajos si las comparativas no están usando la CPU por completo. Aunque esta es la mejor manera de garantizar un rendimiento estable, la mayoría de los dispositivos no la admiten, ya que se necesitan permisos de administrador de adb
.
Si quieres bloquear tus relojes, agrega el complemento auxiliar proporcionado en la ruta de clase del proyecto de nivel superior en el archivo build.gradle
principal:
Groovy
buildscript { ... dependencies { ... classpath "androidx.benchmark:benchmark-gradle-plugin:1.1.0-alpha13" } }
Kotlin
buildscript { ... dependencies { ... classpath("androidx.benchmark:benchmark-gradle-plugin:1.1.0-alpha13") } }
Aplica el complemento en el build.gradle
de tu módulo de comparativas:
Groovy
plugins { id 'com.android.app' id 'androidx.benchmark' } ...
Kotlin
plugins { id("com.android.app") id("androidx.benchmark") ... }
De esta manera, se agregan tareas de comparativas de Gradle a tu proyecto, como ./gradlew lockClocks
y ./gradlew unlockClocks
. Usa estas tareas para bloquear y desbloquear la CPU de un dispositivo mediante adb.
Si tienes varios dispositivos visibles para adb, usa la variable de entorno ANDROID_SERIAL
a fin de especificar en qué dispositivo debería operar la tarea de Gradle:
ANDROID_SERIAL=device-id-from-adb-devices ./gradlew lockClocks
Modo de rendimiento sostenido
Window.setSustainedPerformanceMode()
es una función compatible con algunos dispositivos que permite que una app opte por una frecuencia de CPU máxima más baja. Cuando se ejecuta en dispositivos compatibles, la biblioteca Benchmark usa una combinación de esta API y el lanzamiento de su propia actividad para evitar los modelos térmicos y estabilizar los resultados.
Esta funcionalidad está habilitada de forma predeterminada por el conjunto testInstrumentationRunner
del complemento de Gradle. Si deseas usar un ejecutor personalizado, puedes crear una subclase de AndroidBenchmarkRunner
y usarla como testInstrumentationRunner
.
El ejecutor lanza una actividad opaca en pantalla completa para garantizar que las comparativas se ejecuten en primer plano y no se superponga ninguna otra app.
Pausado de la ejecución automática
Si no se usan ni el bloqueo de relojes ni el rendimiento sostenido, la biblioteca realiza una detección automática de modelos térmicos. Cuando están habilitadas, las comparativas internas se ejecutan periódicamente a fin de determinar cuándo la temperatura del dispositivo subió tanto como para disminuir el rendimiento de la CPU. Cuando se detecta un rendimiento reducido de la CPU, la biblioteca pausa la ejecución para permitir que el dispositivo se enfríe y vuelve a intentar ejecutar las comparativas actuales.
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
se configura enfalse
.- Se está utilizando un dispositivo físico, no un emulador.
- Los relojes están bloqueados si el dispositivo tiene permisos de administrador.
- El nivel de batería en el dispositivo es suficiente.
Si alguna de las comprobaciones anteriores falla, las comparativas mostrarán un error a fin de evitar mediciones imprecisas.
Para hacer que estos problemas pasen como advertencias y evitar que muestren un error y detengan las comparativas, pasa el tipo de errores que quieras suprimir en una lista separada por comas al argumento de instrumentación androidx.benchmark.suppressErrors
, como se muestra a continuación:
adb shell am instrument -w -e "androidx.benchmark.suppressErrors" "DEBUGGABLE" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
Se puede configurar en Gradle de la siguiente manera:
Groovy
android { defaultConfig { testInstrumentationRunnerArgument 'androidx.benchmark.suppressErrors', 'DEBUGGABLE' } }
Kotlin
android { defaultConfig { testInstrumentationRunnerArguments(mapOf( "androidx.benchmark.suppressErrors" to "DEBUGGABLE" )) } }
Ten en cuenta que suprimir errores permite que las comparativas se ejecuten en un estado de configuración incorrecta, pero el resultado de las comparativas se dañará intencionalmente anteponiendo nombres de pruebas con el error. Es decir que ejecutar comparativas que se pueden depurar con la supresión antes mencionada antepondrá DEBUGGABLE_
a los nombres de pruebas.
Cómo generar perfiles
Puedes generar perfiles de comparativas para analizar el motivo por el que el código medido se ejecuta lentamente.
Agrega lo siguiente al archivo build.gradle
de tu módulo de comparativas:
Groovy
android { defaultConfig { // must be one of: 'None', 'StackSampling', or 'MethodTracing' testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'StackSampling' } }
Kotlin
android { defaultConfig { // must be one of: 'None', 'StackSampling', or 'MethodTracing' testInstrumentationRunnerArgument = mapOf( "androidx.benchmark.profiling.mode", "StackSampling" ) } }
Cuando ejecutes una comparativa, se copiará un archivo .trace
de salida al host en el directorio junto con los resultados JSON. Ábrelo con Android Studio haciendo clic en File > Open a fin de inspeccionar los resultados de la generación de perfiles en el Generador de perfiles de CPU.
Registro de métodos
Con el registro de métodos, la comparativa se preparará antes de capturar un seguimiento de métodos, de modo que se registrará todos los métodos que llame tu comparativa. Ten en cuenta que los resultados de rendimiento se verán afectados de manera significativa por la sobrecarga de capturar la entrada/salida de cada método.
Muestreo de pila
Con el muestreo de pila, la comparativa tomará muestras de pilas de llamadas después de que se complete la preparación. Puedes controlar la frecuencia de muestreo y la duración de muestreo mediante argumentos de instrumentación.
En Android 10 (API 29) y versiones posteriores, el muestreo de pila usa Simpleperf para tomar muestras de pilas de llamadas de apps, incluido el código de C++. En Android 9 (API 28) y versiones anteriores, usa Debug.startMethodTracingSampling para capturar muestras de pilas.
A fin de obtener más información sobre el seguimiento y el muestreo de métodos, consulta la configuración para la generación de perfiles de CPU.
Muestras de comparativas
El código de comparativas de muestra está disponible en los siguientes proyectos:
Algunos de los proyectos de muestra incluyen lo siguiente:
BenchmarkSample: Muestra independiente que explica cómo usar un módulo de comparativas para medir el código y la IU
PagingWithNetworkSample : Muestra de componentes de la arquitectura de Android que explica cómo generar comparativas del rendimiento de
RecyclerView
WorkManagerSample: Muestra de componentes de la arquitectura de Android que explica cómo generar comparativas de los trabajadores de
WorkManager
Recursos adicionales
Para obtener más información sobre las comparativas, consulta los siguientes recursos adicionales.
Blogs
Cómo enviar comentarios
Si deseas informar errores o enviar solicitudes de funciones cuando usas las comparativas, consulta la herramienta de seguimiento de problemas de acceso público.