"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Amélioration des performances grâce à l'analyse statique, à l'initialisation d'images et à la capture instantanée de tas

Amélioration des performances grâce à l'analyse statique, à l'initialisation d'images et à la capture instantanée de tas

Publié le 2024-11-08
Parcourir:948

Des structures monolithiques au monde des systèmes distribués, le développement d'applications a parcouru un long chemin. L’adoption massive du cloud computing et de l’architecture des microservices a considérablement modifié l’approche de création et de déploiement des applications serveur. Au lieu de serveurs d'applications géants, nous disposons désormais de services indépendants, déployés individuellement, qui entrent en action
au fur et à mesure des besoins.

Cependant, un nouvel acteur susceptible d'avoir un impact sur ce bon fonctionnement pourrait être les 'démarrages à froid'. Les démarrages à froid surviennent lorsque la première requête est traitée sur un travailleur fraîchement généré. Cette situation nécessite l’initialisation du runtime du langage et de la configuration du service avant de traiter la demande réelle. L'imprévisibilité et la lenteur d'exécution associées aux démarrages à froid peuvent enfreindre les accords de niveau de service d'un service cloud. Alors, comment répondre à cette préoccupation croissante ?

Image native : optimisation du temps de démarrage et de l'empreinte mémoire

Pour lutter contre les inefficacités des démarrages à froid, une nouvelle approche a été développée impliquant l'analyse des points d'accès, l'initialisation de l'application au moment de la construction, la capture instantanée du tas et la compilation (AOT) à l'avance. Cette méthode fonctionne dans le cadre d'un monde fermé, exigeant que toutes les classes Java soient prédéterminées et accessibles au moment de la construction. Au cours de cette phase, une analyse complète des points à atteindre détermine tous les éléments de programme accessibles (classes, méthodes, champs) pour garantir que seules les méthodes Java essentielles sont compilées.

Le code d'initialisation de l'application peut s'exécuter pendant le processus de génération plutôt qu'au moment de l'exécution. Cela permet la pré-allocation d'objets Java et la construction de structures de données complexes, qui sont ensuite mises à disposition au moment de l'exécution via un « tas d'images ». Ce tas d'images est intégré à l'exécutable, offrant une disponibilité immédiate au démarrage de l'application. Le
l'exécution itérative de l'analyse des points de destination et de la capture instantanée se poursuit jusqu'à ce qu'un état stable (point fixe) soit atteint, optimisant à la fois le temps de démarrage et la consommation des ressources.

Flux de travail détaillé

L'entrée de notre système est le bytecode Java, qui peut provenir de langages comme Java, Scala ou Kotlin. Le processus traite uniformément l'application, ses bibliothèques, le JDK et les composants de la machine virtuelle pour produire un exécutable natif spécifique à un système d'exploitation et à une architecture, appelé « image native ». Le processus de création comprend une analyse itérative des points d'accès et une capture instantanée du tas jusqu'à ce qu'un point fixe soit atteint, permettant à l'application de participer activement via des rappels enregistrés. Ces étapes sont collectivement connues sous le nom de processus de création d'image native (Figure 1)

Enhancing Performance with Static Analysis, Image Initialization and Heap Snapshotting

Figure 1 – Processus de création d'images natives (source : redhat.com)

Analyse des points à effectuer

Nous utilisons une analyse de points pour vérifier l'accessibilité des classes, des méthodes et des champs pendant l'exécution. L'analyse des points de destination commence par tous les points d'entrée, tels que la méthode principale de l'application, et traverse de manière itérative toutes les méthodes accessibles de manière transitive jusqu'à atteindre un point fixe (Figure 2).

Enhancing Performance with Static Analysis, Image Initialization and Heap Snapshotting

Figure 2 – Points d'analyse

Notre analyse des points d'accès exploite le frontal de notre compilateur pour analyser le bytecode Java dans la représentation intermédiaire de haut niveau du compilateur (IR). Par la suite, l'IR est transformé en un graphe de type-flux. Dans ce graphique, les nœuds représentent des instructions opérant sur des types d'objets, tandis que les arêtes désignent des arêtes d'utilisation dirigée entre les nœuds, pointant de la définition vers l'utilisation. Chaque nœud conserve un état de type, composé d'une liste de types pouvant atteindre le nœud et d'informations nulles. Les états de type se propagent à travers les bords d'utilisation ; si l'état de type d'un nœud change, ce changement est diffusé à tous les usages. Il est important de noter que les états de type ne peuvent que se développer ; de nouveaux types peuvent être ajoutés à un état de type, mais les types existants ne sont jamais supprimés. Ce mécanisme garantit que le
l'analyse converge finalement vers un point fixe, menant à la terminaison.

Exécuter le code d'initialisation

L'analyse des points de destination guide l'exécution du code d'initialisation lorsqu'il atteint un point fixe local. Ce code trouve ses origines dans deux sources distinctes : les initialiseurs de classe et le lot de code personnalisé exécuté au moment de la construction via une interface de fonctionnalités :

  1. Initialiseurs de classe : Chaque classe Java peut avoir un initialiseur de classe indiqué par une méthode , qui initialise les champs statiques. Les développeurs peuvent choisir les classes à initialiser au moment de la construction ou au moment de l'exécution.

  2. Rappels explicites : Les développeurs peuvent implémenter du code personnalisé via des hooks fournis par notre système, s'exécutant avant, pendant ou après les étapes d'analyse.

Voici les API fournies pour l'intégration à notre système.

API passive (interroge l'état actuel de l'analyse)

boolean isReachable(Class> clazz);

boolean isReachable(Field field);

boolean isReachable(Executable method);

Pour plus d'informations, reportez-vous à QueryReachabilityAccess

API active (enregistre les rappels pour les changements de statut d'analyse) :

void registerReachabilityHandler(Consumer callback, Object... elements);

void registerSubtypeReachabilityHandler(BiConsumer> callback, Class> baseClass);

void registerMethodOverrideReachabilityHandler(BiConsumer callback, Executable baseMethod);

Pour plus d'informations, reportez-vous à BeforeAnalysisAccess

Au cours de cette phase, l'application peut exécuter du code personnalisé tel que l'allocation d'objets et l'initialisation de structures de données plus volumineuses. Il est important de noter que le code d'initialisation peut accéder à l'état actuel de l'analyse des points d'accès, permettant ainsi des requêtes concernant l'accessibilité des types, des méthodes ou des champs. Ceci est accompli à l'aide des différentes méthodes isReachable() fournies par DurantAnalysisAccess. En exploitant ces informations, l'application peut construire des structures de données optimisées pour les segments accessibles de l'application.

Instantané de tas

Enfin, la capture instantanée de tas construit un graphe d'objets en suivant des pointeurs racine comme des champs statiques pour créer une vue complète de tous les objets accessibles. Ce graphique remplit ensuite le champ
de l'image native. tas d'images, garantissant que l'état initial de l'application est chargé efficacement au démarrage.

Pour générer la fermeture transitive des objets accessibles, l'algorithme parcourt les champs d'objets, lisant leurs valeurs par réflexion. Il est essentiel de noter que le générateur d'images fonctionne dans l'environnement Java. Seuls les champs d'instance marqués comme « lus » par l'analyse des points de destination sont pris en compte lors de ce parcours. Par exemple, si une classe a deux champs d'instance mais que l'un d'eux n'est pas marqué comme lu, l'objet accessible via le champ non marqué est exclu du tas d'image.

Lorsque vous rencontrez une valeur de champ dont la classe n'a pas été préalablement identifiée par l'analyse des points de destination, la classe est enregistrée en tant que type de champ. Cet enregistrement garantit que lors des itérations ultérieures de l'analyse des points à atteindre, le nouveau type est propagé à toutes les lectures de champs et utilisations transitives dans le graphe de flux de types.

L'extrait de code ci-dessous décrit l'algorithme de base pour la capture instantanée du tas :

Declare List worklist := []
Declare Set reachableObjects := []

Function BuildHeapSnapshot(PointsToState pointsToState)
For Each field in pointsToState.getReachableStaticObjectFields()
Call AddObjectToWorkList(field.readValue())
End For

    For Each method in pointsToState.getReachableMethods()
        For Each constant in method.embeddedConstants()
            Call AddObjectToWorkList(constant)
        End For
    End For

    While worklist.isNotEmpty
        Object current := Pop from worklist
        If current Object is an Array
            For Each value in current
                Call AddObjectToWorkList(value)
                Add current.getClass() to pointsToState.getObjectArrayTypes()
            End For
        Else
            For Each field in pointsToState.getReachableInstanceObjectFields(current.getClass())
                Object value := field.read(current)
                Call AddObjectToWorkList(value)
                Add value.getClass() to pointsToState.getFieldValueTypes(field)
            End For
        End If
    End While
    Return reachableObjects
End Function

En résumé, l'algorithme de capture instantanée du tas construit efficacement un instantané du tas en parcourant systématiquement les objets accessibles et leurs champs. Cela garantit que seuls les objets pertinents sont inclus dans le tas d'images, optimisant ainsi les performances et l'empreinte mémoire de l'image native.

Conclusion

En conclusion, le processus de capture instantanée du tas joue un rôle essentiel dans la création d'images natives. En parcourant systématiquement les objets accessibles et leurs champs, l'algorithme de capture instantanée du tas construit un graphe d'objets qui représente la fermeture transitive des objets accessibles à partir de pointeurs racine tels que les champs statiques. Ce graphe d'objets est ensuite intégré dans l'image native en tant que tas d'images, servant de tas initial au démarrage de l'image native.

Tout au long du processus, l'algorithme s'appuie sur l'état de l'analyse des points à effectuer pour déterminer quels objets et champs sont pertinents pour être inclus dans le tas d'images. Les objets et les champs marqués comme « lus » par l'analyse des points de destination sont pris en compte, tandis que les entités non marquées sont exclues. De plus, lorsqu'il rencontre des types inédits, l'algorithme les enregistre pour propagation dans les itérations ultérieures de l'analyse des points à atteindre.

Dans l'ensemble, la capture d'instantanés de tas optimise les performances et l'utilisation de la mémoire des images natives en garantissant que seuls les objets nécessaires sont inclus dans le tas d'images. Cette approche systématique améliore l'efficacité et la fiabilité de l'exécution d'images natives.

Déclaration de sortie Cet article est reproduit à l'adresse : https://dev.to/yanev/enhancing-performance-with-static-analysis-image-initialization-and-heap-snapshotting-263f?1 En cas de violation, veuillez contacter study_golang@163. .com pour le supprimer
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3