Ejemplos de medición y análisis de rendimiento

En estos ejemplos, se muestra cómo usar el registro del sistema con Macrobenchmark, junto con la generación de perfiles de memoria, a fin de medir y mejorar determinados tipos de problemas de rendimiento.

Cómo depurar el inicio de apps con systrace

Se recomienda usar registros de systrace para depurar en el tiempo de inicio. Systrace es un sistema que utiliza código instrumentado previamente para mostrar el tiempo que tardan en ejecutarse determinados eventos. Estos registros te permiten ver lo que sucede en tu aplicación o, incluso, en otros procesos del sistema. Las bibliotecas de Jetpack y de la plataforma de Android tienen instrumentación en muchos eventos clave de una aplicación que se registran según corresponde. También puedes instrumentar aplicaciones con tus propios registros personalizados, que se mostrarán en las mismas herramientas de visualización de systrace, para brindar un panorama general de lo que sucedió en la aplicación.

Cómo usar systrace o Perfetto

Si quieres obtener más información sobre el uso básico de systrace, consulta el siguiente video: Debugging Application Performance.

Para analizar el tiempo de inicio, primero debes comprender lo que sucede durante esa etapa. Si deseas obtener más detalles de los que se incluyen en esta página, en la documentación sobre el tiempo de inicio de apps, encontrarás una descripción general del proceso de inicio de aplicaciones.

Las etapas del inicio de una app son las siguientes:

  • Iniciar el proceso
  • Inicializar objetos de aplicaciones genéricos
  • Crear e inicializar una actividad
  • Aumentar el diseño
  • Dibujar el primer fotograma

Los tipos de inicio incluyen las siguientes etapas:

  • Inicio en frío: Esto ocurre cuando se inicia la aplicación por primera vez desde el inicio o cuando el usuario, o el sistema, finaliza el proceso de la app. El inicio crea un proceso nuevo sin un estado guardado.
  • Inicio semicaliente: Se produce cuando la aplicación ya se estaba ejecutando en segundo plano, pero la actividad debe volver a crearse y enviarse al primer plano. La actividad se puede recrear mientras se reutiliza el proceso existente o se puede volver a crear el proceso con el estado guardado. La biblioteca de prueba de Macrobenchmark admite pruebas de inicio en caliente consistentes mediante la primera opción.
  • Inicio en caliente rápido: Esto ocurre cuando todavía se están ejecutando el proceso y la actividad, y solo necesitan estar en primer plano. Es posible que se vuelvan a crear algunos objetos, según sea necesario, y que se renderice la nueva actividad en primer plano. Esta es la etapa de inicio más corta.

Te recomendamos que captures registros del sistema con la app de registros del sistema incluida en el dispositivo que esté disponible en las Opciones para desarrolladores. Si quieres usar herramientas de línea de comandos, Perfetto está disponible para Android 10 (nivel de API 29) y versiones posteriores, mientras que los dispositivos con versiones anteriores deben usar systrace.

Ten en cuenta que el término "primer fotograma" no es del todo correcto, dado que el modo en el que las aplicaciones manejan el inicio después de crear la actividad inicial puede variar de manera significante. Algunas aplicaciones continúan con el proceso de aumento en varios fotogramas, mientras que otras inician inmediatamente una segunda actividad.

Se recomienda que incluyas una llamada a reportFullyDrawn (disponible en Android 10 y versiones posteriores), siempre que sea posible, cuando se completa el inicio desde la perspectiva de la aplicación.

Estos son algunos datos que debes buscar en los registros del sistema:

Contención de supervisión
Figura 1: La competencia de recursos protegidos por supervisión puede generar un retraso importante en el inicio de la app.

Transacciones de Binder síncronas
Figura 2: Busca transacciones innecesarias en la ruta crítica de tu aplicación.

Recolección simultánea de elementos no utilizados
Figura 3: La recolección simultánea de elementos no utilizados es común y tiene un impacto relativamente bajo, pero, si aparece a menudo, te recomendamos que la analices con el generador de perfiles de memoria de Android Studio.

E/S en el inicio
Figura 4: Revisa la E/S durante el inicio y busca bloqueos largos.

Con lo que se muestra en la figura 4, ten en cuenta que los otros procesos que realizan la E/S al mismo tiempo pueden generar contención de E/S, por lo que debes asegurarte de que no se ejecuten otros procesos.

La actividad significativa en otros subprocesos puede interferir con el de la IU, por lo que debes prestar atención a las tareas en segundo plano durante el inicio. Ten en cuenta que los dispositivos pueden tener configuraciones de CPU diferentes; por lo tanto, la cantidad de subprocesos que se pueden ejecutar en paralelo puede variar según el dispositivo.

Consulta también la guía sobre fuentes comunes de bloqueo.

Cómo usar el Generador de perfiles de memoria de Android Studio

El Generador de perfiles de memoria de Android Studio es una herramienta potente que permite reducir la presión de memoria que se puede producir debido a fugas de memoria o patrones de uso incorrecto. Proporciona una visualización en vivo de las asignaciones y recopilaciones de objetos.

Si necesitas solucionar problemas de memoria en tu app, puedes usar el Generador de perfiles de memoria para realizar un seguimiento de las causas y la frecuencia de las recolecciones de elementos no utilizados, y de las posibles fugas de memoria que podrían provocar un aumento constante del montón con el tiempo.

La generación de perfiles de memoria de la app se puede desglosar en los siguientes pasos:

1. Detecta problemas de memoria

Para detectar problemas de memoria, comienza por registrar una sesión de perfilamiento de memoria para tu app. A continuación, busca un objeto cuya huella de memoria esté en aumento y que, eventualmente, active un evento de recolección de elementos no utilizados.

Recuento de objetos en aumento
Figura 5: Es el Generador de perfiles de memoria que muestra asignaciones de objetos en aumento durante el tiempo.

Recolecciones de elementos no utilizados
Figura 6: El Generador de perfiles de memoria muestra eventos de recolección de elementos no utilizados. {.:image-caption}

Cuando hayas identificado un caso de uso que agrega presión de memoria, comienza a analizar las causas raíz.

2. Diagnostica hotspots de presión de memoria

Selecciona un rango en el cronograma para visualizar el tamaño aplanado y las asignaciones.

Visualiza asignaciones y el tamaño aplanado
Figura 7: El Generador de perfiles de memoria muestra asignaciones y tamaños para un rango seleccionado en el cronograma.

Existen varias maneras de ordenar estos datos. En las secciones siguientes, se brindan algunos ejemplos de la forma en que cada vista puede ayudarte a analizar los problemas.

Ordenar por clase

La organización por clase es útil para buscar clases que generan objetos que deberían almacenarse en caché o volver a utilizarse desde una reducción de memoria.

Por ejemplo, imagina que ves una app que crea 2,000 objetos de clase por segundo llamados "Vertex". Esto aumentaría el recuento de asignaciones a 2,000 por segundo, y lo verías cuando ordenes los datos por clase. ¿Deberías volver a usar esos objetos para evitar la creación de elementos no utilizados? Si la respuesta es sí, es probable que necesites implementar una reducción de memoria.

Ordenar por pila de llamadas

La organización por pila de llamadas resulta útil cuando hay una ruta de acceso reciente en la que se asigna memoria; por ejemplo, dentro de un bucle o una función específica que realiza muchas tareas de asignación. La visualización por pila de llamadas te permitirá identificar esos hotspots de asignación.

Tamaño aplanado o retenido

El tamaño aplanado solo hace un seguimiento de la memoria del objeto en sí, por lo que es más útil para rastrear clases simples que estén compuestas principalmente de primitivas.

El tamaño retenido muestra la cantidad total de memoria asignada directamente por el objeto, así como otros objetos asignados a los que solo hace referencia el objeto. Resulta útil para hacer un seguimiento de la presión de memoria causada por objetos complejos que requieren asignación de otros objetos y no solo de campos primitivos. Para obtener este valor, crea un volcado de memoria con el Generador de perfiles de memoria. Los objetos asignados en ese montón se agregan a la pantalla.

Volcado de la memoria completa
Figura 8: Puedes crear un volcado de memoria en cualquier momento haciendo clic en el botón Dump Java heap, en la barra de herramientas del Generador de perfiles de memoria.

se agregó como columna
Figura 9: Cuando se crea un volcado de memoria, aparece una columna que muestra asignaciones de objetos en ese montón.

3. Mide el impacto de una optimización

Una mejora de la optimización de memoria que es fácil de medir es la recolección de elementos no utilizados. Cuando una optimización reduce la presión de memoria, deberías ver menos recolecciones de elementos no utilizados (GC, por sus siglas en inglés). Para medir esto, mide el tiempo entre las GC en el cronograma del Generador de perfiles. Deberías ver duraciones más largas entre las GC después de las optimizaciones de memoria.

El mayor impacto de las mejoras de memoria de este tipo se nota de las siguientes maneras:

  • Si la app no experimenta presión de memoria constante, se cerrará con menor frecuencia debido a problemas de falta de memoria.
  • Una menor cantidad de GC mejora las métricas de bloqueo. Esto se debe a que las GCs generan contención de CPU, lo que puede provocar el diferimiento de las tareas en procesamiento durante la recolección.