Mejoras en Android 8.0 ART

El tiempo de ejecución de Android (ART) se ha mejorado significativamente en la versión Android 8.0. La siguiente lista resume las mejoras que los fabricantes de dispositivos pueden esperar en ART.

Recolector de basura de compactación concurrente

Como se anunció en Google I/O, ART presenta un nuevo recolector de basura (GC) de compactación concurrente en Android 8.0. Este recopilador compacta el montón cada vez que se ejecuta GC y mientras se ejecuta la aplicación, con solo una breve pausa para procesar las raíces de los subprocesos. Estos son sus beneficios:

  • GC siempre compacta el montón: en promedio, tamaños de montón un 32% más pequeños en comparación con Android 7.0.
  • La compactación permite la asignación de objetos de puntero de relieve local de subprocesos: las asignaciones son un 70 % más rápidas que en Android 7.0.
  • Ofrece tiempos de pausa un 85% más cortos para el punto de referencia H2 en comparación con Android 7.0 GC.
  • Los tiempos de pausa ya no varían según el tamaño del montón; las aplicaciones deberían poder utilizar grandes montones sin preocuparse por los bloqueos.
  • Detalle de implementación de GC - Leer barreras:
    • Las barreras de lectura son una pequeña cantidad de trabajo realizado para cada campo de objeto leído.
    • Están optimizados en el compilador, pero pueden ralentizar algunos casos de uso.

Optimizaciones de bucle

ART emplea una amplia variedad de optimizaciones de bucle en la versión de Android 8.0:

  • Eliminaciones de cheques de límites
    • Estático: se ha demostrado que los rangos están dentro de los límites en tiempo de compilación
    • Dinámico: las pruebas en tiempo de ejecución garantizan que los bucles se mantengan dentro de los límites (de lo contrario, no se opten)
  • Eliminaciones de variables de inducción.
    • Eliminar la inducción muerta
    • Reemplace la inducción que se usa solo después del ciclo por expresiones de forma cerrada
  • Eliminación de código muerto dentro del cuerpo del bucle, eliminación de bucles completos que quedan muertos
  • Reducción de fuerza
  • Transformaciones de bucle: inversión, intercambio, división, desenrollado, unimodular, etc.
  • SIMDización (también llamada vectorización)

El optimizador de bucle reside en su propio paso de optimización en el compilador ART. La mayoría de las optimizaciones de bucles son similares a optimizaciones y simplificaciones en otros lugares. Surgen desafíos con algunas optimizaciones que reescriben el CFG de una manera más elaborada de lo habitual, porque la mayoría de las utilidades de CFG (ver nodes.h) se centran en construir un CFG, no en reescribirlo.

Análisis de jerarquía de clases

ART en Android 8.0 utiliza el análisis de jerarquía de clases (CHA), una optimización del compilador que desvirtualiza las llamadas virtuales en llamadas directas en función de la información generada al analizar las jerarquías de clases. Las llamadas virtuales son costosas ya que se implementan en torno a una búsqueda de vtable y requieren un par de cargas dependientes. Además, las llamadas virtuales no se pueden incluir en línea.

Aquí hay un resumen de las mejoras relacionadas:

  • Actualización dinámica del estado del método de implementación única: al final del tiempo de vinculación de la clase, cuando se ha completado vtable, ART realiza una comparación entrada por entrada con la vtable de la superclase.
  • Optimización del compilador: el compilador aprovechará la información de implementación única de un método. Si un método A.foo tiene establecido un indicador de implementación única, el compilador desvirtualizará la llamada virtual en una llamada directa e intentará alinear la llamada directa como resultado.
  • Invalidación del código compilado: también al final del tiempo de vinculación de la clase cuando se actualiza la información de implementación única, si el método A.foo que anteriormente tenía implementación única pero ese estado ahora está invalidado, todo el código compilado que depende de la suposición de que el método A. foo tiene necesidades de implementación única para invalidar su código compilado.
  • Desoptimización: para el código compilado en vivo que está en la pila, se iniciará la desoptimización para forzar el código compilado invalidado a entrar en modo intérprete para garantizar la corrección. Se utilizará un nuevo mecanismo de desoptimización que es un híbrido de desoptimización sincrónica y asincrónica.

Cachés en línea en archivos .oat

ART ahora emplea cachés en línea y optimiza los sitios de llamadas para los cuales existen suficientes datos. La función de cachés en línea registra información adicional del tiempo de ejecución en perfiles y la utiliza para agregar optimizaciones dinámicas a la compilación anticipada.

Diseño Dex

Dexlayout es una biblioteca introducida en Android 8.0 para analizar archivos dex y reordenarlos según un perfil. Dexlayout tiene como objetivo utilizar información de perfiles en tiempo de ejecución para reordenar secciones del archivo dex durante la compilación de mantenimiento inactivo en el dispositivo. Al agrupar partes del archivo dex a las que se accede con frecuencia juntas, los programas pueden tener mejores patrones de acceso a la memoria desde una localidad mejorada, ahorrando RAM y acortando el tiempo de inicio.

Dado que la información del perfil actualmente está disponible solo después de que se hayan ejecutado las aplicaciones, dexlayout se integra en la compilación en el dispositivo de dex2oat durante el mantenimiento inactivo.

Eliminación de caché de Dex

Hasta Android 7.0, el objeto DexCache poseía cuatro matrices grandes, proporcionales al número de ciertos elementos en DexFile, a saber:

  • cadenas (una referencia por DexFile::StringId),
  • tipos (una referencia por DexFile::TypeId),
  • métodos (un puntero nativo por DexFile::MethodId),
  • campos (un puntero nativo por DexFile::FieldId).

Estas matrices se utilizaron para la recuperación rápida de objetos que resolvimos previamente. En Android 8.0, se eliminaron todas las matrices excepto la matriz de métodos.

Actuación de intérprete

El rendimiento del intérprete mejoró significativamente en la versión Android 7.0 con la introducción de "mterp", un intérprete que presenta un mecanismo central de búsqueda/decodificación/interpretación escrito en lenguaje ensamblador. Mterp sigue el modelo del rápido intérprete Dalvik y admite arm, arm64, x86, x86_64, mips y mips64. Para el código computacional, el mterp de Art es aproximadamente comparable al intérprete rápido de Dalvik. Sin embargo, en algunas situaciones puede ser significativamente, e incluso dramáticamente, más lento:

  1. Invocar rendimiento.
  2. Manipulación de cadenas y otros usuarios habituales de métodos reconocidos como intrínsecos en Dalvik.
  3. Mayor uso de memoria de pila.

Android 8.0 soluciona estos problemas.

Más en línea

Desde Android 6.0, ART puede alinear cualquier llamada dentro de los mismos archivos dex, pero solo puede alinear métodos hoja de diferentes archivos dex. Hubo dos razones para esta limitación:

  1. La inserción desde otro archivo dex requiere usar el caché dex de ese otro archivo dex, a diferencia de la inserción del mismo archivo dex, que podría simplemente reutilizar el caché dex de la persona que llama. El caché dex es necesario en el código compilado para un par de instrucciones como llamadas estáticas, carga de cadenas o carga de clases.
  2. Los mapas de pila solo codifican un índice de método dentro del archivo dex actual.

Para abordar estas limitaciones, Android 8.0:

  1. Elimina el acceso a la caché Dex del código compilado (consulte también la sección "Eliminación de la caché Dex")
  2. Extiende la codificación del mapa de pila.

Mejoras de sincronización

El equipo de ART ajustó las rutas de código MonitorEnter/MonitorExit y redujo nuestra dependencia de las barreras de memoria tradicionales en ARMv8, reemplazándolas con instrucciones más nuevas (adquirir/liberar) cuando sea posible.

Métodos nativos más rápidos

Las llamadas nativas más rápidas a la interfaz nativa de Java (JNI) están disponibles utilizando las anotaciones @FastNative y @CriticalNative . Estas optimizaciones integradas del tiempo de ejecución de ART aceleran las transiciones JNI y reemplazan la notación !bang JNI, ahora obsoleta. Las anotaciones no tienen ningún efecto en los métodos no nativos y solo están disponibles para el código del lenguaje Java de la plataforma en bootclasspath (no hay actualizaciones de Play Store).

La anotación @FastNative admite métodos no estáticos. Úselo si un método accede a un jobject como parámetro o valor de retorno.

La anotación @CriticalNative proporciona una forma aún más rápida de ejecutar métodos nativos, con las siguientes restricciones:

  • Los métodos deben ser estáticos: sin objetos para parámetros, valores de retorno o un this implícito.
  • Sólo los tipos primitivos se pasan al método nativo.
  • El método nativo no utiliza los parámetros JNIEnv y jclass en su definición de función.
  • El método debe registrarse con RegisterNatives en lugar de depender de enlaces JNI dinámicos.

@FastNative puede mejorar el rendimiento del método nativo hasta 3 veces y @CriticalNative hasta 5 veces. Por ejemplo, una transición JNI medida en un dispositivo Nexus 6P:

Invocación de la interfaz nativa de Java (JNI) Tiempo de ejecución (en nanosegundos)
JNI regular 115
!bang JNI 60
@FastNative 35
@CriticalNative 25