Usa Simpleperf para evaluar el rendimiento de un dispositivo. Simpleperf es una herramienta de generación de perfiles nativa para apps y procesos nativos en Android. Usa el Generador de perfiles de CPU para inspeccionar el uso de la CPU y la actividad de subprocesos de la app en tiempo real.
Existen dos indicadores de rendimiento visibles para el usuario:
- Rendimiento predecible y perceptible. ¿La interfaz de usuario (IU) descarta fotogramas o se renderiza de forma coherente a 60 FPS? ¿El audio se reproduce sin artefactos ni estallidos? ¿Cuánto dura el retraso entre el momento en que el usuario toca la pantalla y el momento en que se muestra el efecto en la pantalla?
- Tiempo requerido para operaciones más largas (como abrir apps).
El primero es más notable que el segundo. Por lo general, los usuarios notan el bloqueo, pero no podrán distinguir entre 500 ms y 600 ms de tiempo de inicio de la app, a menos que estén mirando dos dispositivos en paralelo. La latencia táctil se nota de inmediato y contribuye de manera significativa a la percepción de un dispositivo.
Como resultado, en un dispositivo rápido, la canalización de la IU es lo más importante del sistema, además de lo necesario para mantener la canalización de la IU funcional. Esto significa que la canalización de la IU debe anular cualquier otro trabajo que no sea necesario para una IU fluida. Para mantener una IU fluida, la sincronización en segundo plano, la entrega de notificaciones y tareas similares deben retrasarse si se puede ejecutar el trabajo de la IU. Es aceptable intercambiar el rendimiento de operaciones más largas (tiempo de ejecución de HDR+, inicio de la app, etc.) para mantener una IU fluida.
Capacidad en comparación con el jitter
Cuando se considera el rendimiento del dispositivo, la capacidad y el jitter son dos métricas significativas.
Capacidad
La capacidad es la cantidad total de un recurso que el dispositivo posee durante un período determinado. Estos pueden ser recursos de CPU, recursos de GPU, recursos de E/S, recursos de red, ancho de banda de memoria o cualquier métrica similar. Cuando se examina el rendimiento del sistema completo, puede ser útil abstraer los componentes individuales y suponer una sola métrica que determine el rendimiento (especialmente cuando se ajusta un dispositivo nuevo, ya que es probable que las cargas de trabajo que se ejecutan en ese dispositivo sean fijas).
La capacidad de un sistema varía según los recursos de procesamiento en línea. Cambiar la frecuencia de la CPU o la GPU es el medio principal para cambiar la capacidad, pero existen otros, como cambiar la cantidad de núcleos de CPU en línea. En consecuencia, la capacidad de un sistema corresponde al consumo de energía. Cambiar la capacidad siempre genera un cambio similar en el consumo de energía.
La capacidad requerida en un momento determinado está determinada en gran medida por la app en ejecución. Como resultado, la plataforma puede hacer poco para ajustar la capacidad requerida para una carga de trabajo determinada, y los medios para hacerlo se limitan a las mejoras del entorno de ejecución (framework de Android, ART, Bionic, compilador o controladores de GPU, kernel).
Jitter
Si bien la capacidad requerida para una carga de trabajo es fácil de ver, el jitter es un concepto más difuso. Para obtener una buena introducción al jitter como impedimento para los sistemas rápidos, te recomendamos que leas el artículo titulado The Case of the Missing Supercomputer Performance: Achieving Optimal Performance on the 8,192 processors of ASCI Q. (es una investigación sobre por qué el supercomputador ASCI Q no logró el rendimiento esperado y es una excelente introducción a la optimización de sistemas grandes).
En esta página, se usa el término jitter para describir lo que el documento ASCI Q denomina ruido. El jitter es el comportamiento aleatorio del sistema que impide que se ejecute el trabajo perceptible. A menudo, es trabajo que se debe ejecutar, pero es posible que no tenga requisitos de tiempo estrictos que lo hagan ejecutarse en un momento determinado. Debido a que es aleatorio, es muy difícil refutar la existencia de jitter para una carga de trabajo determinada. También es muy difícil probar que una fuente conocida de jitter fue la causa de un problema de rendimiento en particular. Las herramientas que se usan con mayor frecuencia para diagnosticar las causas del jitter (como el seguimiento o el registro) pueden introducir su propio jitter.
Entre las fuentes de jitter que se experimentan en las implementaciones reales de Android, se incluyen las siguientes:
- Retraso del programador
- Controladores de interrupción
- El código del controlador se ejecuta durante demasiado tiempo con la anulación o las interrupciones inhabilitadas.
- Softirqs de larga duración
- Contención de bloqueo (app, framework, controlador de kernel, bloqueo de Binder, bloqueo de mmap)
- Contención de descriptores de archivos en la que un subproceso de baja prioridad mantiene el bloqueo de un archivo, lo que impide que se ejecute un subproceso de alta prioridad
- Ejecutar código crítico de la IU en colas de trabajo donde podría retrasarse
- Transiciones de inactividad de la CPU
- Registro
- Retrasos de E/S
- Creación de procesos innecesarios (por ejemplo, transmisiones de
CONNECTIVITY_CHANGE
) - Intercambio de la caché de páginas causado por memoria libre insuficiente
La cantidad de tiempo requerida para un período determinado de jitter puede disminuir o no a medida que aumenta la capacidad. Por ejemplo, si un controlador deja inhabilitadas las interrupciones mientras espera una operación de lectura desde un bus I2C, tardará una cantidad de tiempo fija, independientemente de si la CPU está a 384 MHz o 2 GHz. Aumentar la capacidad no es una solución viable para mejorar el rendimiento cuando hay jitter. Como resultado, los procesadores más rápidos no suelen mejorar el rendimiento en situaciones con restricciones de jitter.
Por último, a diferencia de la capacidad, el jitter se encuentra casi por completo dentro del dominio del proveedor del sistema.
Consumo de memoria
Tradicionalmente, el consumo de memoria se considera el culpable del rendimiento deficiente. Si bien el consumo en sí no es un problema de rendimiento, puede causar jitter a través de la sobrecarga de lowmemorykiller, los reinicios del servicio y el intercambio de la caché de páginas. Reducir el consumo de memoria puede evitar las causas directas de un rendimiento deficiente, pero es posible que haya otras mejoras específicas que también eviten esas causas (por ejemplo, fijar el framework para evitar que se page cuando se page poco después).
Cómo analizar el rendimiento inicial del dispositivo
Partir de un sistema funcional, pero con un rendimiento bajo, y tratar de corregir su comportamiento analizando casos individuales de rendimiento bajo visibles para el usuario no es una estrategia sólida. Debido a que el rendimiento deficiente suele no ser reproducible fácilmente (es decir, jitter) o un problema de la app, hay demasiadas variables en el sistema completo que impiden que esta estrategia sea eficaz. Como resultado, es muy fácil identificar mal las causas y realizar mejoras menores, mientras se pierden oportunidades sistémicas para corregir el rendimiento en todo el sistema.
En su lugar, usa el siguiente enfoque general cuando muestres un dispositivo nuevo:
- Haz que el sistema se inicie en la IU con todos los controladores en ejecución y algunos parámetros de configuración básicos del regulador de frecuencia (si cambias la configuración del regulador de frecuencia, repite todos los pasos que se indican a continuación).
- Asegúrate de que el kernel admita el punto de seguimiento
sched_blocked_reason
, así como otros puntos de seguimiento en la canalización de la pantalla que indiquen cuándo se entrega la trama a la pantalla. - Toma registros largos de toda la canalización de la IU (desde la recepción de entradas a través de una IRQ hasta la limpieza final) mientras ejecutas una carga de trabajo ligera y coherente (por ejemplo, UiBench o la prueba de bola en TouchLatency).
- Corrige las caídas de fotogramas detectadas en la carga de trabajo liviana y coherente.
- Repite los pasos 3 y 4 hasta que puedas ejecutar sin perder fotogramas durante más de 20 segundos a la vez.
- Continúa con otras fuentes de bloqueo visibles para el usuario.
Estas son otras acciones sencillas que puedes realizar al principio de la activación del dispositivo:
- Asegúrate de que tu kernel tenga el parche de punto de seguimiento sched_blocked_reason. Este punto de seguimiento se habilita con la categoría de seguimiento de sched en systrace y proporciona la función responsable de suspender cuando ese subproceso entra en suspensión ininterrumpida. Es fundamental para el análisis de rendimiento, ya que el modo de suspensión ininterrumpida es un indicador muy común de jitter.
- Asegúrate de tener un registro suficiente para la GPU y las canalizaciones de visualización. En los SoC de Qualcomm recientes, los puntos de seguimiento se habilitan con lo siguiente:
adb shell "echo 1 > /d/tracing/events/kgsl/enable"
adb shell "echo 1 > /d/tracing/events/mdss/enable"
Estos eventos permanecen habilitados cuando ejecutas systrace para que puedas ver información adicional en el registro sobre la canalización de visualización (MDSS) en la sección mdss_fb0
. En los SoC de Qualcomm, no verás información adicional sobre la GPU en la vista estándar de systrace, pero los resultados están presentes en el seguimiento (para obtener más información, consulta Información sobre systrace).
Lo que deseas de este tipo de seguimiento de pantalla es un solo evento que indique directamente que se entregó un fotograma a la pantalla. A partir de ahí, puedes determinar si alcanzaste el tiempo de fotogramas correctamente. Si el evento Xn ocurre en menos de 16.7 ms después del evento Xn-1 (suponiendo una pantalla de 60 Hz), significa que no hubo bloqueos. Si tu SOC no proporciona esos indicadores, trabaja con tu proveedor para obtenerlos. La depuración del jitter es muy difícil sin un indicador definitivo de finalización de la trama.
Usa comparativas sintéticas
Las comparativas sintéticas son útiles para garantizar que la funcionalidad básica de un dispositivo esté presente. Sin embargo, no es útil tratar las comparativas como un proxy del rendimiento percibido del dispositivo.
Según las experiencias con los SoC, las diferencias en el rendimiento de las comparativas sintéticas entre los SoC no se correlacionan con una diferencia similar en el rendimiento perceptible de la IU (cantidad de fotogramas perdidos, tiempo de fotogramas del percentil 99, etcétera). Las comparativas sintéticas son comparativas solo de capacidad. El jitter afecta el rendimiento medido de estas comparativas solo robando tiempo a la operación masiva de la comparativa. Como resultado, las puntuaciones de comparativas sintéticas son, en su mayoría, irrelevantes como métrica del rendimiento percibido por el usuario.
Considera dos SoC que ejecutan la comparativa X que renderiza 1,000 fotogramas de la IU y informa el tiempo de renderización total (cuanto menor sea la puntuación, mejor).
- El SOC 1 renderiza cada fotograma de la comparativa X en 10 ms y obtiene una puntuación de 10,000.
- SOC 2 renderiza el 99% de los fotogramas en 1 ms, pero el 1% en 100 ms y obtiene una puntuación de 19,900, una puntuación mucho mejor.
Si la comparativa es indicativa del rendimiento real de la IU, el SOC 2 sería inutilizable. Suponiendo una frecuencia de actualización de 60 Hz, SOC 2 tendría un fotograma irregular cada 1.5 s de operación. Mientras tanto, el SOC 1 (el más lento según la comparativa X) sería perfectamente fluido.
Cómo usar los informes de errores
Los informes de errores a veces son útiles para el análisis de rendimiento, pero, como son muy pesados, rara vez son útiles para depurar problemas de bloqueos esporádicos. Pueden proporcionar algunas sugerencias sobre lo que estaba haciendo el sistema en un momento determinado, en especial si el bloqueo se produjo alrededor de una transición de la app (que se registra en un informe de errores). Los informes de errores también pueden indicar cuando hay un problema más general con el sistema que podría reducir su capacidad efectiva (como la limitación térmica o la fragmentación de la memoria).
Cómo usar TouchLatency
Varios ejemplos de comportamiento incorrecto provienen de TouchLatency, que es la carga de trabajo periódica preferida que se usa para Pixel y Pixel XL. Está disponible en frameworks/base/tests/TouchLatency
y tiene dos modos: latencia táctil y bola rebotante (para cambiar de modo, haz clic en el botón de la esquina superior derecha).
La prueba de la pelota rebotante es exactamente tan simple como parece: una pelota rebota por la pantalla para siempre, independientemente de la entrada del usuario. Por lo general, también es de lejos la prueba más difícil de ejecutar de forma perfecta, pero cuanto más se acerque a ejecutarse sin perder fotogramas, mejor será tu dispositivo. La prueba de bola rebotante es difícil porque es una carga de trabajo trivial, pero perfectamente coherente, que se ejecuta a una velocidad de reloj muy baja (esto supone que el dispositivo tiene un regulador de frecuencia; si, en cambio, el dispositivo se ejecuta con relojes fijos, reduce la velocidad de la CPU o la GPU a un valor cercano al mínimo cuando ejecutes la prueba de bola rebotante por primera vez). A medida que el sistema entra en estado inactivo y los relojes se acercan al estado inactivo, aumenta el tiempo requerido de CPU/GPU por fotograma. Puedes ver la bola y ver cómo se producen interrupciones, y también podrás ver los fotogramas perdidos en systrace.
Debido a que la carga de trabajo es muy coherente, puedes identificar la mayoría de las fuentes de jitter con mucha más facilidad que en la mayoría de las cargas de trabajo visibles para el usuario haciendo un seguimiento de lo que se ejecuta exactamente en el sistema durante cada fotograma perdido en lugar de la canalización de la IU. Los relojes más bajos amplifican los efectos del jitter, ya que aumentan la probabilidad de que cualquier jitter cause una disminución de fotogramas. Como resultado, cuanto más cerca esté TouchLatency de 60 FPS, es menos probable que tengas comportamientos del sistema incorrectos que provoquen interrupciones esporádicas y difíciles de reproducir en apps más grandes.
Como el jitter suele ser (pero no siempre) invariante a la velocidad de reloj, usa una prueba que se ejecute a velocidades de reloj muy bajas para diagnosticar el jitter por los siguientes motivos:
- No todo el jitter es invariante a la velocidad de reloj; muchas fuentes solo consumen tiempo de CPU.
- El regulador debe obtener el tiempo de fotogramas promedio cerca de la fecha límite reduciendo el tiempo de reloj, de modo que el tiempo dedicado a ejecutar trabajos que no son de la IU pueda llevarlo al límite para que descarte un fotograma.