La voie vers l'optimisation des performances des compilateurs de petits programmes

Auteur | Marc

Introduction

Le compilateur d'applet est un module de création de compilation dans les outils de développement Baidu, utilisé pour convertir le code d'applet en code d'exécution. En raison du développement commercial, l'ancienne version du compilateur avait des problèmes de compilation lente et d'utilisation élevée de la mémoire. Nous avons effectué une reconstruction à grande échelle du compilateur, adopté une architecture auto-développée et effectué de nombreuses optimisations telles que le multi-threading, le code mise en cache et sourcemap. Les performances et l’utilisation de la mémoire ont été grandement améliorées. Le texte intégral présente les idées de conception et les méthodes d'optimisation de la nouvelle version du compilateur, ainsi que quelques points techniques pouvant être utilisés dans les outils de packaging généraux.

Le texte intégral compte 6 629 mots et le temps de lecture estimé est de 17 minutes.

01 Préface

Les mini-compilateurs de programmes sont nécessaires à toutes les étapes du développement, de la prévisualisation et de la publication des mini-programmes. Par conséquent, les performances du compilateur affecteront directement l'efficacité du développement des développeurs et l'expérience d'utilisation des outils de développement.

Étant donné que l'ancienne version du compilateur (basée sur webpack4) est très lente lors de la création de grands projets et utilise beaucoup de mémoire, les développeurs se sont plaints d'elle. Après beaucoup de recherche et de développement, nous avons finalement adopté une architecture entièrement développée par nous-mêmes pour la nouvelle compilation, effectué de nombreuses optimisations pour la construction de petits projets de programme et résolu fondamentalement les problèmes de l'ancienne compilation.

La figure suivante est une comparaison des délais de construction de certains projets :

La nouvelle version du compilateur permet d'améliorer les performances de 2 à 7 fois par rapport à l'ancienne version et prend en charge des fonctionnalités telles que la compilation en temps réel et le rechargement à chaud. Elle utilise moins de mémoire et crée de meilleurs produits.

Ce qui suit présente le chemin de développement du nouveau compilateur sous les aspects de sélection du framework, de principe de fonctionnement du nouveau compilateur, de performances et de méthodes d'optimisation des produits.

02 Sélection du cadre

Lors de la conception d'une nouvelle version du compilateur, il est nécessaire de clarifier le problème actuel : les performances, et de donner la priorité à la résolution des problèmes de performances. D'autres nouvelles techniques et idées utiles au compilateur sont également implémentées.

L'ancienne version du compilateur basée sur webpack4 présente les problèmes suivants :

  • Les grands projets se construisent trop lentement.

  • Le démarrage du développement est lent et la compilation incrémentielle est lente. Il ne prend en charge que la mise en cache du chargeur, et les bundles sans mise en cache sont également plus lents.

  • Le développement d'extensions basées sur webpack4 nécessite de patcher certains modules pour fonctionner, ce qui rend la maintenance difficile.

  • Une partie du processus du bundle Webpack ne peut pas être optimisée pour la structure du code de l'applet et il existe des versions non valides.

Objectifs de conception nouvellement compilés :

  • Vitesse de compilation complète plus rapide et élimination du processus de construction invalide du webpack.

  • Prend en charge la mise en cache complète pour accélérer la compilation initiale et incrémentielle.

  • Prend en charge la compilation en temps réel, réduisant ainsi le temps de démarrage du développement et de compilation secondaire.

  • Prend en charge l'accélération de la compilation multithread et le rechargement à chaud des pages.

  • Optimisez la structure du produit et réduisez le volume des produits.

2.1 Outils de construction grand public

Vous trouverez ci-dessous les principaux outils de construction front-end que nous avons étudiés. Chaque outil présente des scénarios, des avantages et des inconvénients applicables.

Lors de la conception d'une nouvelle version de l'architecture du compilateur, il convient de se référer aux concepts de conception et aux caractéristiques techniques d'autres outils de construction.

Processus de création du Webpack :

Avantages du Webpack : fonctions complètes, communauté active, forte configurabilité et forte évolutivité.

Inconvénients du Webpack : configuration complexe, vitesse de construction lente et difficulté de développement secondaire.

Processus de création de parcelles :

Avantages de Parcel : Aucune configuration requise, vitesse de construction rapide, prise en charge native du multi-threading et du cache complet, le partage de données entre multi-threads se fait via lmdb, évitant ainsi la surcharge de communication entre threads.

Inconvénients de Parcel : petit écosystème, personnalisation limitée, utilisation intensive des plug-ins Node et mauvaise compatibilité.

Processus de création de Vite :

Avantages de Vite : configuration relativement simple, compilation à la demande, démarrage rapide et bonne expérience lors du développement.

Inconvénients de Vite : L'écosystème est petit et il existe deux ensembles de processus de construction pour le développement et la publication.

Autres plateformes de mini-programmes :

  • WeChat construit de petits programmes basés sur des modules gulp et C++, et pré-construit des modules npm, ce qui offre de meilleures performances et une meilleure expérience de développement.

  • Alipay crée de petits programmes basés sur Webpack et utilise esbuild pour accélérer la compression du code.

  • Le mini programme Douyin utilise un compilateur auto-développé et le processus de construction est relativement simple.

2.2 Nouvelle version du compilateur

Lors de la conception du nouveau cadre de compilation, nous avons tiré les leçons du flux de travail des outils d'empaquetage traditionnels, combinés aux caractéristiques du code du mini programme, et avons décidé de ne pas créer un outil d'empaquetage universel, en nous concentrant sur l'optimisation des performances d'empaquetage du mini programme.

Enfin, nous avons choisi la solution du compilateur auto-développé et effectué de nombreux travaux d'optimisation. Les points d'optimisation de la nouvelle version du compilateur incluent les aspects suivants :

1. Prend en charge la collaboration de plusieurs compilateurs pour découpler la construction de plusieurs types de projets tels que le développement de bibliothèques dynamiques.

2. L'ensemble du processus est mis en cache pendant la phase de compilation, économisant ainsi plus de 90 % du temps de construction secondaire.

Le développement 3.dev utilise par défaut la compilation à la demande pour améliorer les performances de compilation d'une seule page.

4. Prend en charge la compilation multithread Babel et SWC, augmentant la vitesse de compilation complète de 2 à 7 fois.

5. Adoptez la nouvelle version du protocole sourcemap, supprimez l'analyse et la fusion inutiles et réduisez considérablement le temps passé dans la phase de regroupement.

6. Une optimisation du balisage au moment de la construction a été effectuée pour la compilation de modèles js, css et swan afin de réduire le temps de fusion des bundles.

7. Pour la compression et l'obscurcissement js dans les étapes de prévisualisation et de publication, une solution parallèle de terser et esbuild est adoptée. esbuild est utilisé pour générer rapidement des packages de prévisualisation, et terser peut garantir le taux de compression des packages de version.

À en juger par les résultats, le nouveau compilateur a considérablement amélioré la vitesse, l'utilisation des ressources et la maintenabilité par rapport à l'ancienne version.

03 Comment fonctionne la nouvelle version du compilateur

Le flux de traitement du nouveau compilateur est similaire à celui de Parcel. Le compilateur contrôle le flux de traitement et le processeur effectue la conversion du code. Le flux de base est le suivant :

Plusieurs modules importants :

  • Le compilateur CompileEntry est le module d'entrée, comprenant la communication cli, la communication du serveur de développement, l'invocation de commandes, etc.

  • CompileManager est un gestionnaire de compilation, utilisé pour télécharger et gérer les ressources dépendantes et pour la création collaborative de plusieurs compilateurs.

  • Le compilateur est un module de compilateur, utilisé pour compiler le code source du projet en code d'exécution. Il peut y avoir plusieurs compilateurs lors de la construction d'un projet.

  • Le processeur est un processeur unitaire utilisé pour gérer des tâches de compilation individuelles telles que la conversion de code et la fusion de code.

Remarque : Il existe 1 compilateur pour le projet Mini Program App et 2 compilateurs pour les projets de bibliothèque dynamique et d'extension dynamique.

3.1 Compilateur Compilateur

Utilisé pour compiler un seul petit projet de programme et compiler le code original du développeur en code exécutable.

devoir du travail :

1. Créez un contexte d'exécution et fournissez la configuration, le traitement des fichiers fs, la surveillance de l'observateur, l'enregistreur et d'autres modules à utiliser par le processeur.

2. Compilation complète et deuxième compilation lorsque les fichiers sont modifiés ; la deuxième compilation ici passe également par le processus de compilation complet, mais la plupart d'entre elles utilisent les résultats mis en cache.

3. Gérez, planifiez et exécutez l'unité de traitement du processeur.

4. Conservez les dépendances du processeur et le cache des résultats.

Caractéristiques:

1. Implémentez la mise en cache complète du processus et écrivez les paramètres d'entrée et les résultats de sortie de chaque processeur dans le cache. Avec la mise en cache, le temps de compilation secondaire peut être réduit de 90 %.

2. Prend en charge la compilation à la demande. Chaque compilation de page unique à la demande, compilation incrémentielle et compilation complète suit toutes le même flux de traitement du processeur.

3. Calculez automatiquement les dépendances des paramètres de cache via le mécanisme proxy, éliminant ainsi le besoin de générer manuellement des hachages de cache pour chaque processeur, réduisant ainsi les bogues par rapport au webpack ou au colis.

4. Seule la dépendance du processeur est conservée et le ModuleGraph n'est pas conservé, ce qui simplifie le processus de traitement.

Concernant la mise en cache complète du processus, chaque packageur a son propre plan de mise en œuvre. Le principe de base est de générer un hachage unique pour l'unité de traitement en fonction des paramètres d'entrée et des dépendances actuels. Si les hachages sont cohérents, les résultats seront cohérents.

Puisque webpack et parcelle maintiennent ModuleGraph, le calcul et la réutilisation du cache seront plus compliqués. Le compilateur d'applet effectue uniquement des calculs basés sur les paramètres d'entrée du processeur et les dépendances d'appel.

3.2 Processeur de l'unité de traitement

Le processeur présente les caractéristiques suivantes :

1. Lorsque les paramètres d'entrée sont cohérents, la sortie est garantie d'être cohérente. L'entrée et la sortie doivent être sérialisables en json, réalisant ainsi un cache complet du processeur.

2. L'URI dans le processeur est l'ID de build. Si l'ID est cohérent au cours d'une seule build, les résultats du traitement seront cohérents. Par exemple, lors du traitement du fichier app.js, l'URI est : js:app.js. L'avantage est que le chemin de traitement des ressources du processeur peut être unifié.

3. Les processeurs prennent en charge les appels mutuels : processWith appelle et continue l'exécution, processWithResult appelle et attend le résultat de retour.

Remarque : Les paramètres d'entrée incluent ici l'uri, la configuration de l'application et contextFreeData.

Plusieurs processeurs couramment utilisés :

1.JS Processor convertit le code es6 en code es5, qui est le module le plus chronophage.

2.Swan Processor convertit le code du modèle de cygne en code js de la couche de vue.

3.Css Processor utilise postcss pour gérer la conversion d'unités, la collecte de dépendances et d'autres travaux en CSS.

4. Le processeur de bundle fusionne les fichiers traités par le transformateur précédent selon l'algorithme de bundle et génère les résultats.

Flux de travail du processeur :

Le flux de traitement du processeur doit passer par le processus de transformation -> bundle. Dans le mini-programme, les ensembles de modèles js, css et swan peuvent être traités séparément et en parallèle. Ceci est différent du mode de traitement de webpack, et est similaire au pipeline de colis.

3.3 Méthodes d'optimisation des performances et des produits

3.3.1 Optimisation de la compilation multicœur

Étant donné que la vitesse d'initialisation et l'efficacité de communication du module multi-thread dans Node sont meilleures que celles du multi-processus, la nouvelle compilation choisit d'utiliser le multi-threading pour l'optimisation multicœur.

Il existe 2 options pour la compilation multithread :

  • Option 1 : planification multithread basée sur les processeurs. Étant donné que les processeurs prennent en charge les appels mutuels, le traitement réel sera très compliqué et entraînera des coûts de communication.

  • L'ancien compilateur créait un chargeur de threads de travail basé sur Webpack et l'amélioration des performances était limitée (10 % à 15 %).

  • Parcel est une meilleure solution basée sur le cache public lmdb pour éliminer la communication entre les threads et garantir l'efficacité de la lecture et de l'écriture.

  • Option 2 : effectuez uniquement une planification multithread pour la traduction js, avec seulement deux coûts de communication.

  • Utilisez jest-worker et babel transform pour la traduction multithread js ou utilisez swc multi-thread pour la traduction js.

Étant donné que la majeure partie du temps de construction est consacrée à la traduction js (il existe de nombreuses dépendances node_modules dans js, qui doivent toutes être converties), la conversion des modules CSS et Swan prend moins de temps.

La dernière option 2 ne fait que la traduction js multi-thread. Le processus de traitement est simple et les avantages sont meilleurs. L'amélioration globale est la suivante :

  • En utilisant la traduction Babel multithread Jest-Worker, 4 threads peuvent augmenter la vitesse de plus de 1 fois.

  • En utilisant swc pour la traduction js, 4 threads augmentent la vitesse de plus de 4 fois.

Multi-thread du processeur JS :

dans:

uri : Construire l'ID du processeur

contextFreeData : données immuables dans une seule version, telles que les éléments de configuration dans app.json

arguments de contexte : paramètres globaux, tels que le commutateur d'expérience d'optimisation, le commutateur multi-thread, etc.

L'interface de conversion unifiée du transformateur est stipulée dans le traitement de conversion js.Basé sur l'interface, trois types de processeurs sont implémentés : babel monothread, babel multithread et swc conversion, et la commutation de processeur peut être effectuée à tout moment.

Des paramètres flexibles peuvent être définis pour différents environnements de compilation :

1. Dans les outils de développement, les développeurs peuvent basculer entre les modes de compilation multi-threading et swc en fonction de la configuration de la machine pour améliorer l'efficacité.

2. Le pipeline de compilation cloud active par défaut la compilation multithread pour améliorer les performances.

3. webIDE ouvre un seul thread par défaut pour réduire la consommation de ressources.

3.3.2 Optimisation de la compilation SWC

Par rapport à l'ancienne compilation, le mode multithread du nouveau compilateur a été amélioré d'environ 1 fois. Pendant le développement des développeurs, la première compilation de certaines grandes pages de projet est encore un peu lente, prenant plus de 10 secondes, principalement en js transformer.

swc est maintenant fondamentalement mature dans la traduction js, et la plupart des scénarios peuvent augmenter la vitesse de traduction de plus de 4. Par conséquent, la prise en charge de la traduction multithread swc a été ajoutée et la première compilation de grandes pages de projet peut être contrôlée en 5 secondes.

Deux plug-ins swc doivent être écrits pour s'adapter à la traduction swc :

  • @swanide/swc-require-rename extraira les informations de chemin du module dans require/import/export pour faciliter l'analyse ultérieure des dépendances du module dans js.

  • @swanide/swc-web-debug effectue un traitement d'instrumentation sur le code js pour prendre en charge le débogage des points d'arrêt dans le débogage de la machine réelle.

L'amélioration des performances apportée par la compilation swc est énorme, et certains problèmes ont également été découverts lors de l'utilisation :

1. Il y a une fuite de mémoire dans swc. Si le nombre de compilations complètes est trop important pendant la phase de développement, cela entraînera une utilisation élevée de la mémoire et le compilateur devra être redémarré manuellement.

2. Le plug-in swc prend en charge moins d'API et certaines fonctions faciles à implémenter avec babel sont difficiles à gérer dans swc.

3. Puisque swc utilise rust pour écrire des plug-ins, les plug-ins ne peuvent pas être utilisés entre différentes versions de @swc/core. Les plug-ins Swc doivent être générés pour différentes plates-formes, ce qui sera plus difficile à déployer.

En utilisation réelle, pour certains scénarios que swc ne peut pas bien gérer, il sera rétrogradé en babel pour le traitement.

3.3.3 Compression du code et mise en cache d'exécution

Lors de la phase de développement, le code compilé n'est pas compressé et peut être exécuté dans le simulateur. En raison de la limitation de la taille du package lors de la phase de version préliminaire, la compression du code est nécessaire pour réduire la taille du produit.

Il existe trois outils de compression de code facultatifs :

1.terser a un taux de compression élevé, un petit volume de produit et la vitesse la plus lente.

2.swc se compresse rapidement, le support mangle est incomplet et le taux de compression est faible.

3.esbuild a la compression la plus rapide (plus de 10 fois plus rapide que terser), prend en charge mangle et le taux de compression du code n'est pas aussi bon que terser.

Finalement, après comparaison et réflexion, le schéma de compression suivant a été sélectionné :

1. Puisqu'il n'y a pas besoin de sourcemap dans la phase de prévisualisation, supprimez le sourcemap et utilisez esbuild pour la compression du code afin d'améliorer la vitesse de prévisualisation (une grande amélioration pour les scènes de prévisualisation automatique).

2. Utilisez terser pour la compression multithread pendant la phase de publication et conservez le plan source.

La mise en cache d'exécution signifie que les résultats intermédiaires du processus de génération sont mis en cache en mémoire, y compris les résultats du traitement du processeur et les résultats de la compression du code, ce qui permet d'économiser la majeure partie du temps de reconstruction lors de la deuxième génération. Étant donné que les chaînes et les objets json sont conservés dans le cache, il y a une économie de mémoire de 40 à 60 % par rapport à l'ancien compilateur basé sur webpack, ce qui se situe dans une plage acceptable en termes d'utilisation de la mémoire.

3.3.4 Optimisation du traitement des modèles Swan

L'ancien traitement du modèle Swan utilise Swan-Loader pour la conversion du modèle. Étant donné que la portée de l'importation du modèle n'est pas correctement gérée lors de la conception, la balise <template> et la fonction de filtre de filtre ne peuvent être intégrées que dans le code de la page. Si le modèle est largement utilisé dans le modèle et le filtre, la taille finale du code généré sera très grande.

Le nouveau compilateur corrige la relation de portée d'importation, modifie les modes de génération de modèle et de filtre dans le produit compilé de en ligne à exiger une référence, puis fusionne le code dans l'étape du bundle afin que les mêmes modules puissent être réutilisés, ce qui comble un grand trou. .

Nouveau flux de traitement du modèle de cygne du compilateur :

Les produits possibles après le traitement d'un seul fichier Swan par le processeur sont :

  • composant module de composants, utilisé pour générer des pages et des composants personnalisés

  • module de modèle

  • fonction de filtre de filtre, fonction de filtre sjs

  • code intermédiaire du document transformé

Convertissez les modèles de cygne en différents types de modules js et maintenez les dépendances pour faciliter un contrôle plus raffiné lors de la fusion de code ultérieure.

Pour des raisons historiques, le module modèle ne peut pas être généré directement lorsque import/include contient des références sjs ou de modèle. Il doit être généré dans le modèle d'entrée final. La nouvelle compilation fournit également des options de compilation statique de modèles, qui limiteront strictement la portée de l'importation et généreront directement le code du module de modèle. Pour les petits projets de programme générés par taro, cela peut économiser environ 30 % de la taille du produit.

3.3.5 Optimisation du plan source

Étant donné que le compilateur doit prendre en charge le débogage du code js et le suivi des erreurs d'exécution, les sourcesmaps doivent être générées pendant les étapes de développement et de publication.

Lors de la génération de code dans Webpack, les sourcesmaps doivent être fusionnées et calculées. Pour les projets plus importants, la fusion des sourcesmaps prendra beaucoup de temps et les sourcesmaps doivent être recalculées à chaque fois qu'elles sont recompilées.

Au cours de la recherche, j'ai constaté que les outils de développement du navigateur prennent très bien en charge la carte d'index du protocole sourcemap. Le nouveau compilateur a effectué une optimisation de la fusion de la carte source basée sur le protocole de la carte d'index. Le calcul précédent de fusion de la carte source multi-fichiers est devenu un calcul. pour générer une carte de décalage et épisser le contenu.De cette façon, le temps pris par le bundle js est passé de quelques secondes à des dizaines de secondes à un temps fixe de moins de 3 secondes.

Une chose intéressante est que le débogueur js de vscode ne prenait en charge le débogage de la carte d'index que le 22 juin (la carte d'index a été publiée en 2011) et les actions de Microsoft étaient légèrement plus lentes.

3.3.6 Travaux de suivi

Lors de la promotion du nouveau compilateur une fois le développement terminé, une méthode de promotion progressive est adoptée :

Dans la première étape , les nouveaux et anciens compilateurs d'outils de développement coexistent, le développement et l'aperçu utilisent le nouveau compilateur et la version utilise l'ancien compilateur.

Dans la deuxième étape , toutes les versions préliminaires et versions internes du pipeline utilisent la nouvelle compilation.

Dans la troisième étape , tous les outils de développement sont basculés vers le nouveau compilateur.

Il reste encore quelques problèmes de compatibilité mineurs après la compilation et la mise en ligne de la nouvelle version. Il est nécessaire d'exposer les problèmes le plus tôt possible avant de pouvoir publier un remplacement complet.

Pour les petits projets de programme, la nouvelle compilation a effectué beaucoup de travail d'optimisation, et certains travaux d'optimisation n'ont pas encore été terminés, notamment :

Rechargement à chaud HMR : pendant le développement, étant donné que le cadre d'exécution et les outils de développement nécessitent une adaptation de l'interface, le débogage prend beaucoup de temps pour répondre aux attentes.

Élimination du code d'arborescence : Pour les modules es6, le code d'arborescence peut être éliminé lors de l'étape de transformation.

scope-hoisting scope hoisting : théoriquement faisable, l'effet de réduction du code reste à vérifier.

Étant donné que la nouvelle version du compilateur doit être entièrement compatible avec les résultats de construction de l'ancienne version du compilateur, il reste encore de la place pour l'optimisation dans le scénario de packaging groupé. Nous pouvons faire davantage d'optimisation des produits packagés en conjonction avec le runtime cadre dans les travaux ultérieurs.

04 Résumé

La nouvelle version du compilateur adopte une solution de packaging auto-développée. Par rapport à l'ancien compilateur basé sur webpack, il a obtenu d'énormes améliorations de performances, résolvant complètement les problèmes de compilation lente et d'utilisation élevée des ressources. Il présente également de bons avantages en termes de performances par rapport au compilateurs de concurrents.

Certaines méthodes d'optimisation introduites par la nouvelle compilation, telles que la traduction swc, la compression esbuild et l'optimisation du sourcemap, peuvent également être utilisées dans la construction d'autres projets frontaux et avoir un effet d'accélération.

Dans le projet du nouveau compilateur, chaque étudiant a travaillé très dur et a apporté de nombreuses idées merveilleuses, et la plupart des problèmes rencontrés ont été résolus efficacement. Nous continuerons à adhérer aux deux directions de performances et d'optimisation des produits, et à améliorer continuellement l'expérience des développeurs et l'efficacité d'exécution.

--FIN--

Lecture recommandée

Pratique d'optimisation de la taille du paquet iOS Baidu APP 50M (6) Méthode de nettoyage inutile

Stratégie d'interception et de répartition des problèmes en temps réel basée sur des scénarios en ligne anormaux

Planification de lecture parallèle SSD extrêmement optimisée

La pratique de la création et de la publication de textes IA sur l'application Baidu

DeeTune : Conception et application du framework réseau Baidu basé sur eBPF

Amende de 200 yuans et plus d'un million de yuans confisqués You Yuxi : L'importance des documents chinois de haute qualité Le serveur de migration hard-core de Musk Solon pour JDK 21, les fils virtuels sont incroyables ! ! ! Le contrôle de la congestion TCP sauve Internet Flutter pour OpenHarmony est là La période LTS du noyau Linux sera restaurée de 6 ans à 2 ans Go 1.22 corrigera l'erreur de variable de boucle for Svelte a construit une "nouvelle roue" - les runes Google fête son 25e anniversaire
{{o.name}}
{{m.nom}}

Acho que você gosta

Origin my.oschina.net/u/4939618/blog/10114374
Recomendado
Clasificación