Cómo probar el rendimiento de la IU

La prueba de rendimiento de la interfaz de usuario (IU) te garantiza que la app no solo cumpla con los requisitos funcionales, sino que la interacción del usuario con tu app sea fluida y funcione constantemente a 60 fotogramas por segundo (Why 60 fps?) sin pérdida o retraso de fotogramas (lo que llamamos bloqueo). Este documento explica las herramientas disponibles para medir el rendimiento de la IU y establece un enfoque para integrar las medidas de rendimiento de la IU en tus prácticas de prueba.

Cómo medir el rendimiento de la IU

Para mejorar el rendimiento, primero necesitas poder medir el rendimiento de tu sistema y, luego, diagnosticar e identificar los problemas que puedan surgir debido a las diversas secciones de tu canalización.

La herramienta dumpsys de Android se ejecuta en el dispositivo y vuelca información útil sobre el estado de los servicios del sistema. Al pasar el comando gxinfo a dumpsys, se obtiene un resultado de logcat con información de rendimiento en relación con los fotogramas de animación que ocurren durante la fase de registro.

    > adb shell dumpsys gfxinfo <PACKAGE_NAME>
    

Este comando puede crear múltiples variantes diferentes de datos del intervalo del fotograma.

Incorporación de Frame Stats

En Android 6.0 (API nivel 23), el comando emite un análisis adicional a logcat sobre los datos del fotograma. Estos datos se recopilan en toda la duración del proceso. Por ejemplo:

    Stats since: 752958278148ns
    Total frames rendered: 82189
    Janky frames: 35335 (42.99%)
    90th percentile: 34ms
    95th percentile: 42ms
    99th percentile: 69ms
    Number Missed Vsync: 4706
    Number High input latency: 142
    Number Slow UI thread: 17270
    Number Slow bitmap uploads: 1542
    Number Slow draw: 23342
    

Estas estadísticas de alto nivel representan, en un nivel avanzado, el rendimiento de representación de la app y su estabilidad en muchos fotogramas.

Información precisa del intervalo del fotograma

Con Android 6.0, se incluye un nuevo comando para gfxinfo, framestats, que brinda información extremadamente detallada sobre la latencia de fotogramas recientes, de manera que puedas localizar y depurar errores de manera más precisa.

    >adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats
    

Ese comando muestra información de latencia de fotogramas con marcas de tiempo en nanosegundos de los últimos 120 fotogramas producidos por la app. A continuación, se muestra un ejemplo de la salida sin procesar de los marcos de datos gfxinfo dumpsys de adb:

    0,27965466202353,27965466202353,27965449758000,27965461202353,27965467153286,27965471442505,27965471925682,27965474025318,27965474588547,27965474860786,27965475078599,27965479796151,27965480589068,
    0,27965482993342,27965482993342,27965465835000,27965477993342,27965483807401,27965486875630,27965487288443,27965489520682,27965490184380,27965490568703,27965491408078,27965496119641,27965496619641,
    0,27965499784331,27965499784331,27965481404000,27965494784331,27965500785318,27965503736099,27965504201151,27965506776568,27965507298443,27965507515005,27965508405474,27965513495318,27965514061984,
    0,27965516575320,27965516575320,27965497155000,27965511575320,27965517697349,27965521276151,27965521734797,27965524350474,27965524884536,27965525160578,27965526020891,27965531371203,27965532114484,
    

Cada línea de esta salida representa un fotograma producido por la aplicación. Cada línea tiene un número fijo de columnas que describen el tiempo transcurrido en cada etapa de la canalización de producción de fotogramas. En la siguiente sección, se describe este formato en detalle y se explica qué representa cada columna.

Formato de datos de framestats

Debido a que el bloque de datos se emite en formato CSV, es muy sencillo pegarlo en tu herramienta de hoja de cálculo preferida, o recopilar y redistribuir con un script. En la siguiente tabla, se explica el formato de las columnas de los datos de salida. Las marcas de tiempo están en nanosegundos.

  • FLAGS
    • La latencia total de fotogramas de las filas con "0" en la columna FLAGS se puede calcular restando la columna INTENDED_VSYNC de la columna FRAME_COMPLETED.
    • Si el resultado no es cero, se debe ignorar la fila, ya que se determina que el fotograma contiene un valor atípico de rendimiento, donde se espera que el diseño y la imagen tarden más de 16 ms. Razones por las que puede suceder esto:
      • Se cambió el diseño de la ventana (ya sea el primer fotograma de la aplicación, o luego de una rotación).
      • También es posible que se haya omitido el fotograma, en cuyo caso, alguno de los valores tendrá marcas de tiempo no utilizables. Se puede omitir un fotograma si, por ejemplo, se ejecuta a más de 60 FPS, o si no había nada desfasado en pantalla. Esto no necesariamente indica que la app tenga algún problema.
  • INTENDED_VSYNC
    • Es el punto de partida previsto del fotograma. Si este valor es diferente de VSYNC, el subproceso de IU se encontraba ocupado, lo que evitó la respuesta a la señal vsync de manera oportuna.
  • VSYNC
    • Es el valor de tiempo que se utilizó en todos los objetos de escucha de vsync y las imágenes para el fotograma (devoluciones de llamada del fotograma Choreographer, animaciones, View.getDrawingTime(), etc.).
    • Para obtener más información sobre VSYNC y cómo influye en tu aplicación, consulta el video Understanding VSYNC.
  • OLDEST_INPUT_EVENT
    • Es la marca de tiempo del evento de entrada más antiguo de la cola de entrada, o Long.MAX_VALUE, en caso de que el fotograma no tenga ningún evento de esta índole.
    • Este valor está diseñado principalmente con el fin de trabajar en la plataforma y tiene utilidad limitada para los desarrolladores de apps.
  • NEWEST_INPUT_EVENT
    • Es la marca de tiempo del evento de entrada más reciente de la cola de entrada, o 0, en caso de que el fotograma no tenga ningún evento de esta índole.
    • Este valor está diseñado principalmente con el fin de trabajar en la plataforma y tiene utilidad limitada para los desarrolladores de apps.
    • Sin embargo, se puede obtener una idea general sobre la cantidad de latencia que agrega la app consultando (FRAME_COMPLETED - NEWEST_INPUT_EVENT).
  • HANDLE_INPUT_START
    • Es la marca de tiempo en que se enviaron eventos de entrada a la app.
    • Al observar el tiempo entre esto y ANIMATION_START, se puede medir cuánto tiempo dedicó la app a la administración de eventos de entrada.
    • Si este valor es alto (superior a 2 ms), esto significa que la app dedica tiempo poco común al proceso de los eventos de entrada, como View.onTouchEvent(), lo que indica que este proceso se debe optimizar o descargar a otro subproceso. Ten en cuenta que se espera y es aceptable que este valor sea alto en algunas situaciones, como cuando eventos de clic inician nuevas actividades o situaciones similares.
  • ANIMATION_START
    • Es la marca de tiempo en la que se ejecutaron las animaciones registradas con Choreographer.
    • Al observar el tiempo entre esto y PERFORM_TRANVERSALS_START, se puede determinar cuánto tiempo llevó evaluar todos los mecanismos de animación (los más comunes son ObjectAnimator, ViewPropertyAnimator y Transitions) que se estén ejecutando.
    • Si este valor es alto (superior a 2 ms), controla si tu app escribió alguna animación personalizada o qué campos está animando ObjectAnimators, y asegúrate de que sean adecuados para una animación.
    • Para obtener más información sobre Choreographer, consulta el video For Butter or Worse.
  • PERFORM_TRAVERSALS_START
    • Si a este valor le restas DRAW_START, puedes saber cuánto tardaron en completarse las fases de medición y diseño. (Nota: Durante el desplazamiento o la animación, este número deberá ser cercano a cero).
    • Para obtener más información sobre las fases de medición y diseño de la canalización de representación, consulta el video Invalidations, Layouts and Performance.
  • DRAW_START
    • Es el momento en que comenzó la fase de dibujo de performTraversals. Este es el punto inicial de grabación de la listas de visualización de cualquier vista invalidada.
    • El tiempo entre esto y SYNC_START muestra cuánto se tardó en llamar a View.draw() en todas las vistas invalidadas en el árbol.
    • Para obtener más información sobre el modelo de dibujo, consulta los videos Hardware Acceleration o Invalidations, Layouts and Performance.
  • SYNC_QUEUED
    • Es el momento en el que se envió una solicitud de sincronización a RenderThread.
    • Esto marca el punto en el cual se envió un mensaje para iniciar la fase de sincronización al RenderThread. Si el tiempo entre este y SYNC_START es considerable (alrededor de >0,1 ms), significa que el RenderThread se encontraba ocupado trabajando en un fotograma diferente. Esto se utiliza internamente para diferenciar entre el fotograma que está haciendo demasiado trabajo y excede el límite de 16 ms, y el fotograma que se está demorando debido a que el fotograma anterior excede el límite de 16 ms.
  • SYNC_START
    • Es el momento en que comenzó la fase de sincronización del dibujo.
    • Si el tiempo entre esto e ISSUE_DRAW_COMMANDS_START es muy alto (superior a 0.4 ms o similar), generalmente significa que se dibujaron muchos mapas de bits que se deben subir a la GPU.
    • Para obtener más información sobre la fase de sincronización, consulta el video Profile GPU Rendering.
  • ISSUE_DRAW_COMMANDS_START
    • Es el momento en que el procesador de hardware comenzó a enviar comandos de dibujo a la GPU.
    • El tiempo entre esto y FRAME_COMPLETED permite obtener una idea general sobre cuánto trabajo le genera la app a la GPU. Aquí aparecen los problemas como superposición o efectos de renderización ineficientes.
  • SWAP_BUFFERS
    • Es el momento en que se llamó a eglSwapBuffers, generalmente de poca importancia fuera del trabajo en plataforma.
  • FRAME_COMPLETED
    • ¡Todo listo! El tiempo total dedicado al trabajo en este fotograma se puede calcular haciendo FRAME_COMPLETED - INTENDED_VSYNC.

Puedes utilizar esta información de distintas maneras. Un método de visualización simple pero eficaz es el histograma que muestra la distribución de los tiempos del fotograma (FRAME_COMPLETED - INTENDED_VSYNC) en distintos bloques de latencia; ve la siguiente figura. Este gráfico indica brevemente que la mayoría de los fotogramas estuvieron muy bien, es decir, por debajo del límite de 16 ms (marcado en rojo). Sin embargo, algunos fotogramas estuvieron muy por encima del límite. En el histograma, podemos observar los cambios con el correr del tiempo para ver la creación de los cambios totales o los nuevos valores atípicos. También puedes graficar la latencia de entrada, el tiempo dedicado al diseño o cualquier otra medición interesante similar sobre las marcas de tiempo en los datos.

Volcado simple de la latencia de fotogramas

Si el procesamiento de la GPU del perfil (o procesamiento de la HWUI del perfil) está configurado como In adb shell dumpsys gfxinfo en las Opciones para desarrolladores, el comando adb shell dumpsys gfxinfo mostrará la información de sincronización de los 120 fotogramas más recientes, dividida en algunas categorías diferentes con valores separados por tabulaciones. Esta información puede resultar útil para indicar qué partes de la canalización del dibujo podrían funcionar lento en un nivel alto.

Al igual que la sección de framestats anterior, es muy sencillo pegar esta información en tu herramienta de hoja de cálculo preferida, o recolectarla y analizarla con una secuencia de comandos. En el siguiente gráfico, se detalla dónde pasaron tiempo muchos de los fotogramas generados por la app.

Es el resultado de ejecutar gfxinfo, copiar la salida, pegarla en una aplicación de hoja de cálculo y graficar los datos en forma de barras apiladas.

Cada barra vertical representa un fotograma de animación; su altura representa la cantidad de milisegundos que le llevó calcular ese fotograma de animación. Cada segmento de color de la barra representa una etapa diferente de la canalización de representación, de manera que puedas observar qué partes de tu app pueden estar creando un cuello de botella. Para obtener más información sobre la canalización de representación y cómo optimizarla, consulta el video Invalidations Layouts and Performance.

Control del período de recopilación de datos

Los intervalos de framestats y del fotograma simple recopilan datos durante un período muy breve: aproximadamente dos segundos de la representación. Para poder controlar este período con precisión, por ejemplo para limitar los datos a una animación en particular, puedes restablecer todos los contadores y agregar los datos recopilados.

    >adb shell dumpsys gfxinfo <PACKAGE_NAME> reset
    

Esto se puede usar junto con los comandos de volcado para recopilar y restablecer a una cadencia normal, a fin de capturar continuamente períodos de fotogramas de menos de dos segundos.

Cómo diagnosticar las regresiones de rendimiento

Identificar regresiones es un buen primer paso para localizar problemas y mantener la app funcionando correctamente. Sin embargo, dumpsys solo identifica la existencia y la gravedad relativa de los problemas. Todavía debes diagnosticar la causa particular de los problemas de rendimiento y encontrar las soluciones adecuadas. Para esto, es sumamente recomendable que utilices la herramienta systrace.

Recursos adicionales

Para obtener más información sobre el funcionamiento de la canalización de procesamiento de Android, los problemas comunes que puedes encontrar y cómo solucionarlos, es posible que algunos de los siguientes recursos te resulten útiles:

Cómo automatizar las pruebas de rendimiento de la IU

Un enfoque para realizar la prueba de rendimiento de la IU es solicitar a un evaluador que realice una serie de operaciones de usuario en la app de destino para identificar visualmente bloqueos, o bien, pasar mucho tiempo utilizando un enfoque basado en alguna herramienta para encontrar bloqueos. Sin embargo, este enfoque manual tiene sus riesgos, ya que la habilidad humana para percibir cambios en los índices de los fotogramas varía de manera alarmante. Además, este proceso lleva mucho tiempo, es tedioso y propenso a errores.

Un método más eficiente es registrarse y analizar las mediciones de rendimiento clave a partir de pruebas automatizadas de IU. Android 6.0 incluye nuevas capacidades de registro que facilitan la determinación de la cantidad y gravedad de bloqueos en las animaciones de tu app, y pueden utilizarse para crear un proceso estricto a fin de determinar tu rendimiento actual y realizar un seguimiento de futuros objetivos de rendimiento.

Recursos adicionales

Para obtener más información sobre este tema, consulta los siguientes recursos.

Codelabs