Le runtime Android (ART) a été considérablement amélioré dans la version Android 8.0. La liste ci-dessous résume les améliorations auxquelles les fabricants d’appareils peuvent s’attendre dans ART.
Récupérateur de place à compactage simultané
Comme annoncé lors de Google I/O, ART propose un nouveau garbage collector (GC) à compactage simultané dans Android 8.0. Ce collecteur compacte le tas à chaque fois que GC s'exécute et pendant l'exécution de l'application, avec une seule courte pause pour le traitement des racines des threads. Voici ses avantages :
- GC compacte toujours le tas : des tailles de tas 32 % plus petites en moyenne par rapport à Android 7.0.
- Le compactage permet l'allocation d'objets de pointeur de bosse local de thread : les allocations sont 70 % plus rapides que dans Android 7.0.
- Offre des temps de pause 85 % plus petits pour le benchmark H2 par rapport à Android 7.0 GC.
- Les temps de pause ne s'adaptent plus à la taille du tas ; les applications devraient pouvoir utiliser de gros tas sans se soucier des erreurs.
- Détails de la mise en œuvre du GC - Lire les obstacles :
- Les barrières de lecture représentent une petite quantité de travail effectué pour chaque lecture de champ d’objet.
- Ceux-ci sont optimisés dans le compilateur, mais peuvent ralentir certains cas d'utilisation.
Optimisations de boucle
Une grande variété d'optimisations de boucles sont utilisées par ART dans la version Android 8.0 :
- Éliminations du contrôle des limites
- Statique : il est prouvé que les plages sont dans les limites au moment de la compilation
- Dynamique : les tests d'exécution garantissent que les boucles restent dans les limites (sinon, désactivez)
- Éliminations des variables d'induction
- Supprimer l'induction morte
- Remplacer l'induction utilisée uniquement après la boucle par des expressions de forme fermée
- Élimination du code mort à l'intérieur du corps de la boucle, suppression des boucles entières qui deviennent mortes
- Réduction de la force
- Transformations de boucles : inversion, interchangement, fractionnement, déroulement, unimodulaire, etc.
- SIMDisation (également appelée vectorisation)
L'optimiseur de boucle réside dans sa propre passe d'optimisation dans le compilateur ART. La plupart des optimisations de boucles sont similaires aux optimisations et simplifications ailleurs. Des défis surviennent avec certaines optimisations qui réécrivent le CFG d'une manière plus élaborée que d'habitude, car la plupart des utilitaires CFG (voir nodes.h) se concentrent sur la construction d'un CFG, pas sur sa réécriture.
Analyse de la hiérarchie des classes
ART dans Android 8.0 utilise Class Hierarchy Analysis (CHA), une optimisation du compilateur qui dévirtualise les appels virtuels en appels directs en fonction des informations générées par l'analyse des hiérarchies de classes. Les appels virtuels sont coûteux car ils sont implémentés autour d’une recherche de table virtuelle et nécessitent quelques charges dépendantes. De plus, les appels virtuels ne peuvent pas être intégrés.
Voici un résumé des améliorations associées :
- Mise à jour dynamique de l'état de la méthode à implémentation unique - À la fin du temps de liaison des classes, lorsque la table virtuelle a été remplie, ART effectue une comparaison entrée par entrée avec la table virtuelle de la super classe.
- Optimisation du compilateur - Le compilateur tirera parti des informations d'implémentation unique d'une méthode. Si une méthode A.foo a un indicateur d'implémentation unique, le compilateur dévirtualisera l'appel virtuel en un appel direct et tentera en outre d'intégrer l'appel direct en conséquence.
- Invalidation du code compilé - Également à la fin du temps de liaison des classes lorsque les informations d'implémentation unique sont mises à jour, si la méthode A.foo qui avait auparavant une implémentation unique mais que ce statut est maintenant invalidé, tout le code compilé qui dépend de l'hypothèse que la méthode A. foo a besoin d'une implémentation unique pour que son code compilé soit invalidé.
- Désoptimisation - Pour le code compilé en direct sur la pile, la désoptimisation sera lancée pour forcer le code compilé invalidé en mode interpréteur afin de garantir l'exactitude. Un nouveau mécanisme de désoptimisation hybride de désoptimisation synchrone et asynchrone sera utilisé.
Caches en ligne dans les fichiers .oat
ART utilise désormais des caches en ligne et optimise les sites d'appel pour lesquels suffisamment de données existent. La fonctionnalité de cache en ligne enregistre des informations d'exécution supplémentaires dans les profils et les utilise pour ajouter des optimisations dynamiques à la compilation anticipée.
Disposition Dex
Dexlayout est une bibliothèque introduite dans Android 8.0 pour analyser les fichiers dex et les réorganiser selon un profil. Dexlayout vise à utiliser les informations de profilage d'exécution pour réorganiser les sections du fichier dex lors de la compilation de maintenance inactive sur l'appareil. En regroupant les parties du fichier dex qui sont souvent consultées ensemble, les programmes peuvent avoir de meilleurs modèles d'accès à la mémoire grâce à une localité améliorée, économisant ainsi de la RAM et réduisant le temps de démarrage.
Étant donné que les informations de profil ne sont actuellement disponibles qu'après l'exécution des applications, dexlayout est intégré à la compilation sur l'appareil de dex2oat pendant la maintenance inactive.
Suppression du cache Dex
Jusqu'à Android 7.0, l'objet DexCache possédait quatre grands tableaux, proportionnels au nombre de certains éléments dans le DexFile, à savoir :
- chaînes (une référence par DexFile::StringId),
- types (une référence par DexFile::TypeId),
- méthodes (un pointeur natif par DexFile::MethodId),
- champs (un pointeur natif par DexFile::FieldId).
Ces tableaux ont été utilisés pour une récupération rapide des objets que nous avons précédemment résolus. Dans Android 8.0, tous les tableaux ont été supprimés à l'exception du tableau des méthodes.
Performance de l'interprète
Les performances de l'interpréteur se sont considérablement améliorées dans la version Android 7.0 avec l'introduction de "mterp" - un interpréteur doté d'un mécanisme de récupération/décodage/interprétation de base écrit en langage assembleur. Mterp est calqué sur l'interpréteur rapide Dalvik et prend en charge arm, arm64, x86, x86_64, mips et mips64. Pour le code informatique, le mterp d'Art est à peu près comparable à l'interpréteur rapide de Dalvik. Cependant, dans certaines situations, cela peut être considérablement – et même considérablement – plus lent :
- Invoquez les performances.
- Manipulation de chaînes et autres grands utilisateurs de méthodes reconnues comme intrinsèques à Dalvik.
- Utilisation plus élevée de la mémoire de la pile.
Android 8.0 résout ces problèmes.
Plus d'inline
Depuis Android 6.0, ART peut intégrer n'importe quel appel dans les mêmes fichiers dex, mais ne peut intégrer que des méthodes feuille à partir de différents fichiers dex. Il y avait deux raisons à cette limitation :
- L'insertion à partir d'un autre fichier dex nécessite d'utiliser le cache dex de cet autre fichier dex, contrairement à l'insertion du même fichier dex, qui pourrait simplement réutiliser le cache dex de l'appelant. Le cache dex est nécessaire dans le code compilé pour quelques instructions telles que les appels statiques, le chargement de chaînes ou le chargement de classes.
- Les cartes de pile codent uniquement un index de méthode dans le fichier dex actuel.
Pour remédier à ces limitations, Android 8.0 :
- Supprime l'accès au cache Dex du code compilé (voir également la section "Suppression du cache Dex")
- Étend le codage de la carte de pile.
Améliorations de la synchronisation
L'équipe ART a ajusté les chemins de code MonitorEnter/MonitorExit et a réduit notre dépendance aux barrières de mémoire traditionnelles sur ARMv8, en les remplaçant par des instructions plus récentes (acquisition/libération) lorsque cela est possible.
Méthodes natives plus rapides
Des appels natifs plus rapides vers Java Native Interface (JNI) sont disponibles à l’aide des annotations @FastNative
et @CriticalNative
. Ces optimisations d'exécution ART intégrées accélèrent les transitions JNI et remplacent la notation !bang JNI désormais obsolète. Les annotations n'ont aucun effet sur les méthodes non natives et ne sont disponibles que pour le code du langage Java de la plate-forme sur le bootclasspath
(pas de mise à jour du Play Store).
L'annotation @FastNative
prend en charge les méthodes non statiques. Utilisez-le si une méthode accède à un jobject
en tant que paramètre ou valeur de retour.
L'annotation @CriticalNative
fournit un moyen encore plus rapide d'exécuter des méthodes natives, avec les restrictions suivantes :
- Les méthodes doivent être statiques : aucun objet pour les paramètres, les valeurs de retour ou un
this
implicite. - Seuls les types primitifs sont transmis à la méthode native.
- La méthode native n'utilise pas les paramètres
JNIEnv
etjclass
dans sa définition de fonction. - La méthode doit être enregistrée auprès de
RegisterNatives
au lieu de s'appuyer sur une liaison JNI dynamique.
@FastNative
peut améliorer les performances des méthodes natives jusqu'à 3x et @CriticalNative
jusqu'à 5x. Par exemple, une transition JNI mesurée sur un appareil Nexus 6P :
Appel de l'interface native Java (JNI) | Temps d'exécution (en nanosecondes) |
---|---|
JNI régulier | 115 |
!bang JNI | 60 |
@FastNative | 35 |
@CriticalNative | 25 |