Les temps de build de Zig s’améliorent
(mitchellh.com)- La sortie de Zig 0.15.1 améliore nettement la vitesse de compilation par rapport à la version précédente
- Les mesures effectuées sur le projet Ghostty montrent une réduction globale des temps d’exécution
- LLVM est encore partiellement utilisé, mais on attend de nouveaux gains de performance avec l’adoption du backend maison
- La compilation incrémentale n’est pas encore entièrement implémentée, mais des avantages de performance apparaissent déjà sur les builds partiels
- Il est très probable qu’un environnement de build encore plus rapide et une meilleure expérience de développement deviennent réalité à l’avenir
Aperçu
Partant de la remarque d’Andrew Kelley selon laquelle « le compilateur est tellement lent qu’il introduit des bugs », Zig poursuit depuis plusieurs années divers efforts structurels avec pour objectif des temps de compilation plus rapides
- L’équipe Zig travaille à supprimer LLVM, à développer son propre backend de génération de code, à construire un linker maison, et à concrétiser à terme une compilation incrémentale
- Les résultats de ce travail de long terme commencent à devenir visibles avec Zig 0.15.1, et l’évolution des temps de build a été mesurée et partagée sur un projet réel, Ghostty
Vitesse de compilation du script de build
- Zig 0.14 : 7 secondes 167 ms
- Zig 0.15 : 1 seconde 702 ms
Il s’agit du temps de build du script build.zig lui-même, un coût initial supporté à chaque fois dans un environnement de build à partir de sources vierges
- La fréquence de recompilation du script de build reste faible, mais cela a un impact direct sur l’expérience d’un utilisateur qui build le projet lui-même pour la première fois
Build complet du binaire sans cache (Ghostty)
- Zig 0.14 : 41 secondes
- Zig 0.15 : 32 secondes
Il s’agit du temps total de build du binaire, y compris le temps de build du script de build
- Avec Zig 0.15, on observe environ 2 secondes de gain supplémentaire, et la différence initiale est aussi clairement visible en temps réel
- Le backend x86_64 maison n’est pas encore pleinement exploité, et la majorité du build continue d’utiliser LLVM
- À l’avenir, lorsque Ghostty pourra être buildé entièrement avec le backend maison, le temps devrait descendre sous les 25 secondes (environ la moitié du niveau initial)
Build incrémental (exécutable Ghostty)
- Zig 0.14 : 19 secondes
- Zig 0.15 : 16 secondes
C’est le temps nécessaire pour relancer le build après une modification significative d’une ligne (ajout d’un appel de log dans le code d’émulation de terminal)
- Il s’agit d’un build partiel dans une situation où le script de build et le graphe de dépendances sont déjà en cache
- La compilation incrémentale n’est pas encore complètement implémentée, mais les gains de performance sont déjà clairement visibles
- En excluant la part liée à LLVM, une réduction à environ 12 secondes semble possible
- Une fois un véritable build incrémental implémenté, on peut même espérer des builds en quelques millisecondes
Build incrémental (libghostty-vt)
- Zig 0.14 : 2 secondes 884 ms
- Zig 0.15 : 975 ms
Mesure du temps nécessaire pour rebuild partiellement uniquement libghostty-vt après une modification d’une ligne
libghostty-vtpeut être buildé entièrement avec le backend x86_64 maison, ce qui reflète directement les améliorations de Zig sans influence de LLVM- Même sans compilation incrémentale, atteindre un temps de build inférieur à une seconde représente déjà un progrès considérable
- Dans le workflow des développeurs, cela améliore l’efficacité grâce à une expérience de retour immédiat
- Les backends x86_64 et aarch64 gagnent progressivement en stabilité, et pourraient être appliqués à l’ensemble de Ghostty dans les prochains mois
Où en est l’amélioration des temps de build
- Le build de Ghostty avec Zig 0.15.1 est clairement plus rapide dans toutes les mesures effectuées
- Même si le backend maison et la compilation incrémentale ne sont pas encore finalisés, les résultats actuels sont déjà très impressionnants
- Des améliorations de vitesse encore plus marquantes sont attendues dans les 1 à 2 prochaines années
- Le choix de Zig apparaît de plus en plus pertinent du point de vue de la vitesse de build
1 commentaires
Avis Hacker News
En 1995, quand j’ai obtenu mon diplôme de fin de lycée, recompiler "Borland Pascal version Turbo Vision for DOS" sur un Intel 486 allait à peu près à cette vitesse ; on retrouve enfin aujourd’hui des temps de compilation vraiment aussi rapides
Turbo Vision est un framework de fenêtres TUI, utilisé pour le développement des IDE Borland Pascal et C++
On peut le voir comme un mode texte réalisant l’équivalent d’un IDE JetBrains en 10 Mo au lieu de 1000 Mo
Turbo Vision sur Wikipédia
LLVM est une sorte de piège
Le bootstrap est vraiment rapide et on obtient gratuitement toutes sortes de passes d’optimisation et la prise en charge de nombreuses plateformes, mais on perd la capacité de régler finement les performances lors de la dernière étape d’optimisation ou de l’édition de liens
Je pense que Cranelift sera bientôt activé dans Rust
Mais si Rust a atteint sa place actuelle, c’est aussi parce qu’il a choisi LLVM au départ
Go a décidé depuis longtemps de ne pas déléguer la génération de code et l’édition de liens à des outils externes, et profite largement de ce choix
J’ai du mal à être d’accord avec l’idée qu’LLVM soit un piège, et Rust est plutôt un bon contre-exemple
En pratique, la partie génération de code via LLVM représente une très petite portion du compilateur, et on peut la remplacer par
codegen_craneliftoucodegen_gccLa dépendance aux intrinsics SIMD spécifiques aux fournisseurs est bien un problème de lock-in, mais c’est un problème de conception du langage
Pour la plupart des langages, commencer avec un backend LLVM est raisonnable
Pour les langages proches de C/C++, le pipeline par défaut de LLVM optimise déjà bien, mais plus un langage a des caractéristiques différentes, plus il faut écrire soi-même son pipeline d’optimisation
Le cas de Go, qui a intégré son propre backend dès le début, semble effectivement être une réussite, mais ce n’est pas un facteur de différenciation particulier, et l’implémentation maison a un coût d’opportunité assez élevé
Les compilateurs de Go et d’Ocaml sont vraiment rapides
Ils ont correctement construit leurs propres bibliothèques dès le départ, et n’ont plus rien à perdre côté vitesse maintenant
Je n’ai plus envie de travailler dans un environnement où une compilation prend plus d’une minute
J’aimerais que chaque projet ait un compilateur dédié au mode
dev, et qu’on n’utilise quelque chose de lourd comme llvm que pour le build finalSi un langage basé sur LLVM vise à remplacer C++, il reste malgré tout dépendant de C++
Un langage doit pouvoir se bootstrapper lui-même
Au début, ces outils pratiques existent, mais ce ne sont que des moyens de gagner du temps, pas des éléments indispensables
Je comprends totalement le choix fait par l’équipe Go
J’espère que Cranelift continuera à progresser
Aujourd’hui, LLVM est fragmenté par une avalanche de forks propres aux différents fournisseurs de CPU, chacun liant ses améliorations spécifiques dans des paquets privés
Dès qu’on utilise un autre frontend de langage ou qu’un bug du compilateur apparaît, la situation devient très pénible
Quelqu’un se demande comment cela peut être un piège si les langages peuvent librement migrer vers d’autres backends
Si la vitesse de compilation gêne le développement, pourquoi ne pas faire un interpréteur ?
La vitesse d’exécution et la vitesse de compilation sont fondamentalement indépendantes
Avec un interpréteur, il devient aussi facile de créer des outils de développement supplémentaires, comme l’instrumentation du code ou le contrôle du runtime
Il existe quelques rares cas où il faut déboguer uniquement un binaire RELEASE optimisé, mais dans la plupart des situations un interpréteur ou un build DEBUG suffit
J’ai entendu dire que Rust se classe par ordre de priorité sécurité, performance, ergonomie, tandis que Zig se classe performance, ergonomie, sécurité
Sous cet angle, améliorer la vitesse de build est cohérent, mais un interpréteur est une alternative plus adaptée quand l’ergonomie passe avant tout
J’aime bien l’approche de Julia
Dans un environnement pleinement interactif, l’interpréteur compile en réalité le code puis l’exécute immédiatement
C’est aussi le cas d’environnements Common Lisp comme SBCL
Si on pousse à l’extrême, vitesse d’exécution et vitesse de compilation sont indépendantes
Entre les deux, il existe une « zone de compromis » où l’on peut rendre un compilateur plus rapide sans perte de performance du binaire
Quelqu’un affirme que le domaine du jeu n’est pas un cas particulier
Jusqu’à présent, la meilleure vitesse de compilateur reste celle de TCC, l’œuvre de Fabrice Bellard
Même sans multithreading ni optimisations complexes, il est incroyablement rapide
J’utilise Clang pour les releases, mais les performances de génération de code de TCC ne sont pas mauvaises non plus
Je pense que Delphi offre une expressivité et une sécurité bien supérieures, tout en gardant une compilation très rapide
Le compilateur Go est lui aussi assez rapide
Je considérais que DMD, capable de compiler à la fois C et D, était le « gold standard »
Quelqu’un espère que TCC prendra en charge C23
Il n’existe pas de « vrai gold standard » unique
vlangest lui aussi assez rapide pour terminer une recompilation en quelques secondes, et le compilateur Go est également extrêmement rapideIl existe aussi des techniques comme le cache de build pour éviter complètement les recompilations ; ce n’est donc l’apanage de personne
En construisant une application avec zig, j’ai été très satisfait des builds incrémentaux
On peut construire un binaire statique unique utilisant diverses bibliothèques comme SQLite ou luau aussi vite qu’en Go
Cela dit, le compilateur self-hosted contient encore pas mal de bugs
Par exemple, SQLite nécessite encore llvm ; voir le ticket ici
Je me demande si Zig peut bien s’intégrer à des systèmes de build comme Bazel ou Buck2
Les scripts de build Zig sont Turing-complets, donc j’ai peur que cela complique le caching et l’automatisation du build dans ce type de systèmes
C’est la même raison pour laquelle, en Rust, on préfère les bibliothèques utilisables sans
build.rsJe me demande si les bibliothèques Zig reposent elles aussi souvent sur des builds personnalisés
Les scripts de build de Zig sont totalement optionnels
On peut compiler ou exécuter directement des fichiers source individuels sans
build.zigOn peut intégrer Zig dans n’importe quel workflow où GCC ou Clang interviennent
À noter que Zig fonctionne aussi comme remplaçant de compilateur C
Article lié
Exemple d’intégration entre Bazel et Zig avec rules_zig
Le projet réel ZML l’utilise également
Projet ZML
Je me demande comment se comparent la compilation et la génération de code de Zig face à TPDE
On dit que c’est 10 à 20 fois plus rapide que LLVM
-O0, mais il semble y avoir des limitesJe trouve la stratégie de Zig audacieuse
Mais je me demande toujours s’il est vraiment judicieux de continuer à utiliser le backend LLVM
LLVM reste compétitif sur la vitesse de compilation et le support des plateformes, mais personne ne le rattrape pour produire le meilleur code machine
Le backend LLVM n’est utilisé que pour les builds Release, et pour les builds debug, l’approche self-hosted est la valeur par défaut sur les plateformes prises en charge
Comme les builds debug sont répétés de nombreuses fois pendant les tests, cette approche est plus rationnelle
Je ne partage pas cette obsession des performances du compilateur
Au fond, c’est un compromis entre les passes d’optimisation — inlining, élimination du code mort, etc. — et le temps de compilation
Un compilateur sans optimisation ne peut être qu’un peu plus rapide de façon linéaire ; pour aller au-delà, les optimisations coûtent forcément du temps
Une « excellente sortie assembleur » n’est pas vraiment un enjeu important
Selon la loi de Proebsting, les progrès des compilateurs sont bien plus lents que l’augmentation des performances matérielles
La conclusion est que des optimisations simples et rapides suffisent largement en pratique
LLVM n’a rien d’absolu en matière d’optimisation, et atteint lui aussi vite ses limites face au temps de compilation
Je crée une bibliothèque Java qui encapsule une bibliothèque C via JNI, et comme le build de bibliothèques dynamiques par plateforme est trop pénible, j’envisage Zig pour du build multiplateforme
Pour un simple shim, il vaudrait mieux utiliser Panama et jextract sur les versions récentes de Java
Zig embarque les headers, les sources de libc et son propre LLVM, et la compilation croisée est vraiment très simple
Au point qu’on n’a pratiquement plus rien à gérer
Quelqu’un demande pourquoi Ghostty ne se build pas actuellement avec le backend self-hosted x86_64
Le compilateur Zig plante à cause d’un bug
C’est encore une technologie récente, donc c’est complexe, mais ce sera sans doute corrigé bientôt
La plupart des utilisateurs de Ghostty sont sur des plateformes aarch64