Inspeccionar la actividad de la CPU y los seguimientos de métodos con CPU Profiler

CPU Profiler te ayuda a inspeccionar el uso de CPU y la actividad de subprocesos de tu app en tiempo real, y a registrar los seguimientos de métodos, para que puedas optimizar y depurar el código de tu app.

Para abrir CPU Profiler, sigue estos pasos:

  1. Haz clic en View > Tool Windows > Android Profiler (también puedes hacer clic en Android Profiler en la barra de herramientas).
  2. Selecciona el dispositivo y el proceso de la app del cual desees generar un perfil en la barra de herramientas del generador de perfiles de Android. Si conectaste un dispositivo mediante USB, pero no lo ves en la lista, asegúrate de haber habilitado la depuración USB.
  3. Haz clic en cualquier parte de la línea de tiempo de CPU para abrir CPU Profiler.

Por qué debes generar perfiles de uso de CPU

Reducir el uso de CPU de tu app tiene muchas ventajas; por ejemplo, proporciona una experiencia de usuario más rápida y fluida, y prolonga la duración de la batería del dispositivo. También permite que tu app tenga un buen rendimiento en diferentes dispositivos más nuevos o antiguos. Puedes usar CPU Profiler para supervisar el uso de CPU y la actividad de subprocesos mientras interactúas con tu app. Sin embargo, para obtener información más detallada sobre cómo ejecuta su código tu app, debes registrar e inspeccionar los seguimientos de métodos.

Para cada subproceso en el proceso de tu app, puedes averiguar los métodos que se ejecutan a lo largo de un período de tiempo y los recursos de CPU que consume cada uno de ellos durante su ejecución. También puedes usar seguimiento de métodos para identificar a los emisores y destinatarios. Un emisor es un método que invoca otro y un destinatario es un método invocado por otro. Puedes usar esta información para determinar los métodos que invocan tareas particulares de alto consumo de recursos con demasiada frecuencia e intentar optimizar el código de tu app para evitar un procesamiento innecesario.

Si deseas recopilar información detallada de nivel de sistema que te permita inspeccionar procesos del sistema nativo y solucionar el bloqueo de la IU ocasionado por la disminución de fotogramas, debes usar systrace.

Como alternativa, si deseas exportar los archivos .trace que captures usando la clase Debug, debes usar Traceview.

Información general de CPU Profiler

Cuando abres CPU Profiler, este comienza a mostrar inmediatamente el uso de CPU y la actividad de subprocesos de tu app. Verás algo similar a lo que se muestra en la figura 1.

Figura 1: CPU Profiler.

Como se indica en la figura 1, en la vista predeterminada para CPU Profiler se incluye lo siguiente:

  1. Línea de tiempo de eventos: muestra las actividades en tu app mientras pasan por diferentes estados durante su ciclo de vida. También indica las interacciones de los usuarios con el dispositivo, incluidos los eventos de rotación de pantalla. Para obtener más información acerca de la línea de tiempo de eventos, incluida la forma de habilitarla, consulta Habilitar la generación de perfiles avanzada.
  2. Línea de tiempo de CPU: muestra el uso de CPU de tu app en tiempo real (como un porcentaje del tiempo de CPU total disponible) y la cantidad total de subprocesos que tu app usa. La línea de tiempo también muestra el uso de CPU de otros procesos (como los procesos del sistema u otras apps), por lo que puedes compararlo con el uso que hace tu app. Puedes inspeccionar datos históricos de uso de CPU desplazando tu mouse por el eje horizontal de la línea de tiempo.
  3. Línea de tiempo de actividad de subprocesos: enumera cada subproceso que pertenezca al proceso de tu app e indica su actividad en una línea de tiempo por medio de los colores que se detallan a continuación. Después de registrar un seguimiento de métodos, puedes seleccionar un subproceso de esta línea de tiempo para inspeccionar sus datos en el subpanel de seguimiento.
    • Verde: el subproceso está activo o listo para usar la CPU. Es decir, se encuentra en un estado “de ejecución” o “ejecutable”.
    • Amarillo: el subproceso está activo, pero espera una operación de E/S, como una E/S de disco o red, para poder completar su trabajo.
    • Gris: el subproceso está suspendido y no consume tiempo de CPU. Esto a veces ocurre cuando el subproceso necesita acceso a un recurso que todavía no está disponible. O bien el subproceso entra en suspensión voluntaria, o el kernel suspende el subproceso hasta que el recurso requerido se encuentre disponible.
  4. Configuraciones de registro: te permite seleccionar una de las siguientes opciones para determinar la forma en que el generador de perfiles registra un seguimiento de métodos.
    • Sampled: configuración predeterminada que captura la pila de llamadas de tu app a intervalos frecuentes durante la ejecución de esta. El generador de perfiles compara grupos de datos capturados para producir información sobre el uso de recursos y la sincronización relacionada con la ejecución del código de tu app. Un problema inherente del seguimiento basado en muestras es que si tu app ingresa en un método luego de una captura de la pila de llamadas y sale de este antes de la captura siguiente, el generador de perfiles no registra esa llamada al método. Si te interesa el seguimiento de métodos con ciclos de vida tan cortos, debes usar seguimiento instrumentado.
    • Instrumented: configuración predeterminada que instrumenta tu app en el tiempo de ejecución para registrar una marca de tiempo al comienzo y al fin de cada llamada al método. Las marcas de tiempo se recolectan y se comparan para generar datos de seguimiento de métodos, incluida la información sobre sincronización y el uso de CPU. Ten en cuenta que la sobrecarga asociada con la instrumentación de cada método tiene impacto en el rendimiento del tiempo de ejecución y puede influir en los datos de generación de perfiles. Esto se nota incluso más cuando se trata de métodos con ciclos de vida más cortos. Adicionalmente, si tu app ejecuta una cantidad elevada de métodos en un tiempo reducido, es posible que el generador de perfiles exceda rápidamente su límite de tamaño de archivo y, por lo tanto, no registre ningún otro dato de seguimiento.
    • Edit configurations: te permite cambiar algunos valores predeterminados de las configuraciones de registro muestreadas o instrumentadas, descritas previamente, y guardarlas como una configuración personalizada. Para obtener más información, visita la sección sobre la creación de configuraciones de registro.
  5. Botón de registro: inicia y detiene el registro de un seguimiento de métodos. Para obtener más información, visita la sección relacionada con el registro y la inspección de seguimientos de métodos.

Nota: El generador de perfiles también informa el uso de CPU de los subprocesos que Android Studio y la plataforma Android agregan al proceso de tu app, como JDWP, Profile Saver, Studio:VMStats, Studio:Perfa y Studio:Heartbeat (aunque los nombres exactos que se muestran en la línea de tiempo de actividad de subprocesos pueden variar). Esto significa que el uso de CPU de tu app en la línea de tiempo de CPU también informa el tiempo de CPU que usan estos subprocesos. Puedes ver algunos de estos subprocesos en la línea de tempo de la actividad de subprocesos y también supervisar su actividad. (Sin embargo, debido a que los subprocesos del generador de perfiles ejecutan código nativo, no puedes registrar datos de seguimiento de métodos de los mismos). Android Studio presenta estos datos para que puedas identificar fácilmente los casos en que la actividad de los subprocesos y el uso de CPU se deban realmente del código de tu app.

Registrar e inspeccionar seguimientos de métodos

Para comenzar a registrar un seguimiento de métodos, selecciona en el menú desplegable la configuración de registro Sampled o Instrumented, o una configuración de registro personalizada que hayas creado, y haz clic en Record . Interactúa con tu app y haz clic en Stop recording cuando termines. El generador de perfiles selecciona automáticamente el período de tiempo registrado y presenta su información de seguimiento en el subpanel de seguimiento de métodos, como se muestra en la figura 2. Si deseas inspeccionar el seguimiento de métodos para un subproceso diferente, simplemente selecciónalo en la línea de tiempo de actividad de subprocesos.

Figura 2: CPU Profiler, luego de registrar un seguimiento de métodos.

  1. Período de tiempo seleccionado: determina la fracción del período de tiempo registrado que deseas inspeccionar en el subpanel de seguimiento. Cuando registras un seguimiento de métodos por primera vez, CPU Profiler selecciona automáticamente la extensión completa de tu registro en la línea de tiempo de CPU. Si deseas inspeccionar datos de seguimiento de métodos solo para una fracción del período de tiempo registrado, puedes hacer clic y arrastrar los bordes de la región resaltada para modificar su extensión.
  2. Marca de tiempo: indica el tiempo de inicio y finalización de un seguimiento de métodos registrado (relativo al momento en el que el generador de perfiles comienza a recolectar información sobre el uso de CPU de tu dispositivo). Puedes hacer clic en la marca de tiempo para seleccionar automáticamente todo el registro como tu período de tiempo seleccionado. Esto resulta particularmente útil si tienes varios registros que deseas alternar.
  3. Subpanel de seguimiento: muestra datos del seguimiento de métodos para el período de tiempo y el subproceso que seleccionaste. Este subpanel aparece solo después de que registres al menos un seguimiento de métodos. En este subpanel, puedes seleccionar la forma en que deseas ver cada seguimiento de pila (por medio de las pestañas de seguimiento) y medir el tiempo de ejecución (por medio del menú desplegable de referencia de tiempo).
  4. Puedes elegir que tu seguimiento de métodos se muestre como un árbol de arriba abajo (pestaña Top Down) o de abajo arriba (pestaña Bottom Up), o como un gráfico de llamadas (pestaña Call Chart) o de llamas (pestaña Flame Chart). Puedes obtener más información sobre cada una de las pestañas del subpanel de seguimiento en las secciones siguientes.
  5. Selecciona una de las siguientes opciones del menú desplegable para determinar la manera en que se mide la información sobre sincronización para cada llamada al método:
    • Wall clock time: la información de sincronización representa el tiempo transcurrido real.
    • Thread time: la información de sincronización representa el tiempo transcurrido real menos cualquier fracción de ese tiempo en la que el subproceso no consuma recursos de CPU. Para cualquier método determinado, el “thread time” de este siempre será menor o igual a su “wall clock time”. Si usas “thread time”, puedes comprender la fracción de uso de CPU real de un subproceso que se destina a un método determinado.

Inspeccionar seguimientos con la pestaña Call Chart

La pestaña Call Chart proporciona una representación gráfica de un seguimiento de métodos, donde el período y la sincronización de una llamada al método (o emisor) se representan en el eje horizontal y sus destinatarios se muestran en el eje vertical. Las llamadas a los métodos a las API del sistema se muestran en naranja, las llamadas a los propios métodos de tu app se muestran en verde y las llamadas de los métodos a API de terceros (incluidas las API de lenguaje java) se muestran en azul. En la figura 3, a continuación, muestra un gráfico de llamadas de ejemplo y se ilustra el concepto de los tipos de tiempo “self”, “children” y “total” para un método determinado. Puedes obtener más información sobre estos conceptos en la sección sobre cómo inspeccionar seguimientos usando árboles de arriba abajo y de abajo arriba.

Figura 3: Ejemplo de gráfico de llamadas en el que se muestran los tiempos “self”, “children” y “total” para el método D.

Sugerencia: Para saltar al código fuente de un método, haz clic con el botón secundario en el método y selecciona Jump to Source. Esto funciona desde todas las pestañas del subpanel de seguimiento.

Inspeccionar seguimientos con la pestaña Flame Chart

La pestaña Flame Chart provee un gráfico de llamadas invertido que agrega pilas de llamadas idénticas. Es decir, los métodos idénticos que comparten la misma secuencia de emisores se agrupan y representan como una sola barra más larga en un gráfico de llamas (en vez de mostrarse como varias barras más cortas, como se ve en un gráfico de llamada). Esto hace que sea más fácil ver los métodos que consumen la mayor cantidad de tiempo. Sin embargo, esto también significa que el eje horizontal ya no representa una línea de tiempo; en lugar de ello, indica la cantidad relativa de tiempo que cada método ocupa para ejecutarse.

Para ilustrar mejor este concepto, observa el gráfico de llamadas de la figura 4, a continuación. Ten en cuenta que el método D hace varias llamadas a B (B1, B2 y B3) y algunas de esas llamadas a B hacen una llamada a C (C1 y C3).

Figura 4: Gráfico de llamadas con varias llamadas métodos que comparten una misma secuencia de emisores.

Debido a que B1, B2 y B3 comparten la misma secuencia de emisores (A → D → B), se agrupan como se muestra a continuación. De manera similar, C1 y C3 se agrupan porque comparten la misma secuencia de emisores (A → D → B → C). Ten en cuenta que C2 no se incluye porque tiene una secuencia diferente de emisores (A → D → C).

Figura 5: Agregación de métodos idénticos que comparten la misma pila de llamadas.

Las llamadas a los métodos agregadas se usan para crear el gráfico de llamas, como se muestra en la figura 6. Ten en cuenta que, para cualquier llamada al método en un gráfico de llamas, los destinatarios que consumen la mayor cantidad de tiempo de CPU aparecen primeros.

Figura 6: Representación en gráfico de llamas del gráfico de llamadas que se muestra en la figura 4.

Inspeccionar seguimientos mediante árboles de arriba abajo y abajo arriba

En la pestaña Top Down se muestra una lista de llamadas a métodos en la cual, al expandirse el nodo de un método, se muestran los destinatarios. En la figura 7, se muestra un gráfico de arriba abajo para el gráfico de llamadas de la figura 3. Cada flecha del gráfico apunta de un emisor a un destinatario.

Como se muestra en la figura 7, al expandirse el nodo para el método A en la pestaña “Top Down” se muestran sus destinatarios, los métodos B y D. Luego de eso, al expandirse el nodo para el método D se muestran sus destinatarios, los métodos B y C, y así sucesivamente. Como sucede con la pestaña Flame chart, en el árbol de arriba abajo se agrega la información de seguimiento para métodos idénticos que compartan la misma pila de llamadas. Es decir, en la pestaña Flame chart se proporciona una representación gráfica de la pestaña Top down.

En la pestaña Top Down se proporciona la siguiente información para que sea más fácil describir el tiempo de CPU que se ocupa en cada llamada al método (los tiempos también se representan como un porcentaje del tiempo total del subproceso durante el período de tiempo seleccionado):

  • Self: cantidad de tiempo que la llamada al método ocupó en la ejecución de su propio código y no del de sus destinatarios, como se muestra en la figura 3 para el método D.
  • Children: cantidad de tiempo que la llamada al método ocupó en la ejecución del código de sus destinatarios y no de su propio código, como se muestra en la figura 3 para el método D.
  • Total: suma de los tiempos Self y Children del método. Representa la cantidad total de tiempo que la app ocupó en la ejecución de una llamada al método, como se muestra en la figura 3 para el método D.

Figura 7: Árbol de arriba abajo.

Figura 8: Árbol de abajo arriba para el método C de la figura 7.

En la pestaña Bottom Up se muestra una lista de llamadas a métodos, en la cual al expandirse el nodo de un método se muestran sus destinatarios. En la figura 8, con el seguimiento de ejemplo que se muestra en la figura 7, se proporciona un árbol de abajo arriba para el método C. Al abrirse el nodo para el método C en el árbol de abajo arriba, se muestra cada uno de sus emisores únicos, los métodos B y D. Ten en cuenta que, aunque B llama a C dos veces, B aparece una sola vez cuando se expande el nodo para el método C en el árbol de abajo arriba. Luego, al expandirse el nodo para B se muestran sus emisores, los métodos A y D.

La pestaña Bottom Up es útil para ordenar los métodos según los que más (o menos) tiempo de CPU consuman. Además, puedes inspeccionar cada nodo para determinar los emisores ocupan más tiempo de CPU invocando esos métodos. En comparación con el árbol de arriba abajo, la información de sincronización para cada método en un árbol de abajo arriba tiene que ver con el método que está en la parte superior de cada árbol (nodo superior). El tiempo de CPU también se representa como un porcentaje del tiempo total del subproceso durante ese registro. La tabla siguiente ayuda a comprender la manera en que debe interpretarse la información de sincronización para el nodo superior y sus métodos emisores (subnodos).

Self Children Total
Método en la parte superior del árbol de abajo arriba (nodo superior) Representa la cantidad total de tiempo que el método ocupó en la ejecución de su propio código y no del de sus destinatarios. En comparación con el árbol de arriba abajo, esta información de sincronización representa una suma de todas las llamadas a este método durante el registro. Representa la cantidad total de tiempo que el método ocupó en la ejecución del código de sus destinatarios y no de su propio código. En comparación con el árbol de arriba abajo, esta información de sincronización representa la suma de todas las llamadas a los destinatarios de este método sobre la duración del registro. Suma de los tiempos “self” y “children”.
Métodos emisores (subnodos) Representa el tiempo “self” total del destinatario cuando el emisor lo llama. Si usamos el árbol de arriba abajo de la figura 8 como ejemplo, el tiempo “self” para el método B sería igual a la suma de los tiempos “self” para cada ejecución del método C cuando B lo llama. Representa el tiempo “children” total del destinatario cuando el emisor lo invoca. Si usamos el árbol de abajo arriba de la figura 8 como ejemplo, el tiempo “children” para el método B sería igual a la suma de los tiempos “children” para cada ejecución del método C cuando B lo llama. Suma de los tiempos “self” y “children”.

Nota: Para un registro en particular, Android Studio deja de recolectar datos nuevos cuando el generador de perfiles alcanza el límite de tamaño de archivo (esto, sin embargo, no detiene el registro). Normalmente, esto sucede mucho más rápido cuando se llevan a cabo seguimientos instrumentados, ya que este tipo de seguimiento recolecta más datos en un período de tiempo más corto en comparación con un seguimiento muestreado. Si extiendes el período de tiempo de inspección a un período del registro que ocurrió luego de alcanzar el límite, los datos de sincronización del subpanel de seguimiento no cambian (ya que no hay nuevos datos disponibles). Además, en el subpanel de seguimiento se muestra NaN para la información de sincronización cuando seleccionas únicamente la parte de un registro que no tenga datos disponibles.

Crear configuraciones de registro

Puedes seleccionar una de las configuraciones de registro que Android Studio te proporciona, como Sampled o Instrumented, o puedes crear la tuya. Para crear o editar una configuración personalizada, o inspeccionar una configuración predeterminada existente, abre el diálogo CPU Recording Configurations seleccionando Edit configurations en el menú desplegable de configuraciones de registro.

Figura 9: El diálogo CPU Recording Configurations te permite crear o editar una configuración de registro personalizada o inspeccionar una configuración predeterminada existente.

Puedes inspeccionar los ajustes de una configuración existente seleccionándola en el subpanel izquierdo o crear una configuración de registro nueva de la siguiente manera:

  1. Haz clic en Add en la parte superior izquierda del diálogo. Con esto se crea una configuración nueva con algunos ajustes predeterminados.
  2. Asigna un nombre a tu configuración.
  3. Selecciona Sampled o Instrumented en la sección Trace Technology. Cada una de estas opciones funciona como se describe en Información general de CPU Profiler.
  4. Para configuraciones de registros muestreadas, especifica Sampling interval con un valor en microsegundos (μs). Este valor representa la duración entre cada muestra de la pila de llamadas de tu app. Ten en cuenta que cuanto menor sea la duración que especifiques, más rápido alcanzarás el límite de tamaño de archivo para los datos registrados.
  5. Especifica File size limit con un valor en megabytes (MB) para los datos registrados que se escriben en el dispositivo conectado. Si detienes el registro, Android Studio analiza estos datos y los muestra en la ventana del generador de perfiles. Por lo tanto, si aumentas el límite y registras una gran cantidad de datos, Android Studio necesita mucho más tiempo para analizar el archivo y puede bloquearse.

    Nota: Si usas un dispositivo conectado que cuente con el nivel 26 de API o un nivel superior, no habrá límite para el tamaño de archivo de los datos de seguimiento y este valor se ignorará. Sin embargo, de todos modos debes tener cuidado con la cantidad de datos que el dispositivo recolecta luego de cada registro, ya que Android Studio podría tener dificultades para analizar archivos de seguimiento grandes. Por ejemplo, si registras un seguimiento muestreado con un intervalo de muestreo corto o un seguimiento instrumentado mientras tu app llama a muchos métodos en un período breve, generarás rápidamente archivos de seguimiento muy grandes.

  6. Haz clic en Apply o OK. Si modificaste otras configuraciones de registro, esos cambios también se aplican.

Una vez que se crea una nueva configuración, esta se selecciona automáticamente en el menú desplegable de configuraciones de registro y puedes usarla para tu próximo registro.