Optimisation bas niveau et Zig
(alloc.dev)- L’optimisation bas niveau peut être mise en œuvre facilement avec le langage Zig
- Le compilateur effectue bien les optimisations dans la plupart des cas, mais il faut parfois exprimer plus clairement l’intention du programmeur pour obtenir de meilleures performances
- Zig prend en charge la génération de code hautes performances et un métaprogrammation puissante grâce à l’exécution à la compilation (
comptime) - Par rapport à Rust, Zig permet des optimisations plus fines grâce aux annotations et à une structure de code explicite
- Pour des opérations répétitives comme la comparaison de chaînes,
comptimepeut générer un code assembleur supérieur à celui d’une fonction ordinaire
Optimisation et Zig
Comme le rappelle un avertissement bien connu, « tout est possible, mais ce qui est intéressant ne s’obtient pas facilement » : l’optimisation des programmes reste toujours une préoccupation majeure des développeurs. Qu’il s’agisse de réduire le coût de l’infrastructure cloud, d’améliorer la latence ou de simplifier un système, l’optimisation du code est indispensable. Cet article explique surtout les concepts d’optimisation bas niveau dans Zig ainsi que les points forts du langage.
Peut-on faire confiance au compilateur ?
- On entend souvent le conseil « faites confiance au compilateur », mais dans la pratique il arrive que le compilateur se comporte différemment des attentes ou viole les spécifications du langage
- Les langages de haut niveau rendent plus difficile l’expression claire de l’intention et imposent donc certaines limites en matière de performances
- Les langages de bas niveau, grâce au caractère explicite du code, permettent au compilateur de connaître les informations nécessaires à l’optimisation ; par exemple, si l’on compare une fonction
maxArrayen JavaScript et en Zig, Zig transmet dès la compilation — et non à l’exécution — des informations claires sur les types, l’alignement ou encore la présence d’alias - Si l’on écrit la même opération
maxArrayen Zig et en Rust, on obtient presque le même code assembleur haute performance, mais mieux l’intention est exprimée, meilleur est le résultat de l’optimisation - Comme on ne peut pas toujours faire confiance aux performances du compilateur, il faut, dans les sections critiques, vérifier directement le code et le résultat de la compilation pour chercher des pistes d’optimisation
Le rôle de Zig
- Grâce à son explicitation précise, à ses nombreuses fonctions intégrées, à ses pointeurs et annotations, à
comptimeet à un comportement illégal bien défini, Zig permet de produire du code optimisé sans information abstraite superflue - Rust garantit par défaut, grâce à son modèle mémoire, l’absence d’alias entre arguments, alors qu’en Zig il faut ajouter soi-même des annotations comme
noalias - Si l’on se base uniquement sur LLVM IR, le niveau d’optimisation de Zig est lui aussi élevé
- Surtout,
comptime(exécution à la compilation) est un outil d’optimisation extrêmement puissant dans Zig
Qu’est-ce que comptime ?
- Le
comptimede Zig est utilisé pour la génération de code, l’intégration de constantes, la création de structures génériques basées sur les types, etc., et joue un rôle important dans l’amélioration des performances à l’exécution - Il permet d’implémenter de la métaprogrammation
- Contrairement aux macros de C/C++ ou au système de macros de Rust,
comptimen’a pas de syntaxe distincte : c’est du code ordinaire - Le code
comptimene modifie pas directement l’AST ; il permet d’inspecter, de refléter et de générer à la compilation pour tous les types - La souplesse de
comptimea influencé l’évolution d’autres langages comme Rust et s’intègre naturellement au langage Zig
Les limites de comptime
- Certaines fonctionnalités de macro, comme le token-pasting, ne peuvent pas être remplacées par le
comptimede Zig - Zig privilégie la lisibilité du code ; il n’autorise donc pas la création de variables ou la définition de macros hors de leur portée
- En contrepartie, le
comptimede Zig offre de nombreux usages en métaprogrammation, comme la réflexion sur les types, l’implémentation de DSL ou l’optimisation du parsing de chaînes
Optimiser la comparaison de chaînes avec comptime
- Une fonction classique de comparaison de chaînes peut être implémentée dans n’importe quel langage, mais dans Zig, lorsque l’une des deux chaînes est une constante connue à la compilation, il est possible de générer un code assembleur plus efficace
- Par exemple, si une chaîne vaut toujours
"Hello!\n", on peut exploiter une optimisation consistant à la comparer non pas octet par octet, mais par blocs plus larges - En utilisant
comptime, on peut générer à la compilation du code haute performance exploitant des vecteurs SIMD, un traitement par blocs et des optimisations sur les octets restants - Cette approche permet d’implémenter non seulement des comparaisons répétitives de chaînes, mais aussi divers mappings fondés sur des données statiques, des tables de hachage parfaites, des parseurs d’AST, et d’autres composants orientés performance
Conclusion
- Zig convient très bien à l’optimisation bas niveau et permet d’implémenter directement des performances de tout premier plan grâce à une structure de code explicite et à la puissance de
comptime - Même comparé à d’autres langages comme Rust, les capacités de programmation à la compilation et l’explicitation de Zig constituent un avantage majeur pour le développement de logiciels hautes performances
- Les capacités d’optimisation de Zig devraient devenir un atout concurrentiel encore plus important à l’avenir
1 commentaires
Commentaire Hacker News
bun, au point d’être admiratif.bunm’a énormément simplifié la vie.uv, basé sur Rust, procure une expérience similairefor(;;);doit réellement rester infinie, etloop {}en Rust devrait l’être aussi. Mais les développeurs LLVM semblent parfois croire qu’ils ne construisent qu’un compilateur C++, si bien que quand Rust demande « merci de me laisser une boucle infinie », LLVM répond « impossible en C++, donc optimisation ! », ce qui provoque des problèmes. En somme, une mauvaise optimisation a été appliquée au mauvais langagecomptime, on peut tout à fait faire en C de l’inlining et de l’unrolling de comparaisons de chaînes. Exemple liéTypedArray, car le coût d’initialisation est élevé et cela ne devient intéressant qu’en cas de réutilisation fréquente. L’article disait aussi que le code JS était gonflé, mais c’est largement parce que le JIT n’ose pas faire confiance aux vérifications de longueur de tableau et ajoute des gardes ; en réalité, tout le monde écrit des boucles du typei < x.length, ce qui permet l’optimisation JIT. C’est donc un peu pinailler, même si la nuance existecomptimeque les compilateurs C++ ne peuvent pas vraiment anticiperpurchase.calculate_tax().await.map_err(|e| TaxCalculationError { source: e })?;est rempli d’intention, mais il est impossible de prédire à quoi ressemblera réellement le code machine-march=native, optimisation à l’échelle du programme entier, etc.). En réalité, en C aussi, des indices d’optimisation commeunreachablesont possibles via des extensions de langage, et Clang est lui aussi très agressif pour le constant folding. Autrement dit, la différence entrecomptimeen Zig et la génération de code en C vient souvent des réglages d’optimisation du compilateur. TL;DR : si C est lent, il faut d’abord vérifier les options du compilateur. Au bout du compte, le cœur de l’optimisation reste LLVMcomptimeet la compilation de programme entier que ce à quoi je m’attendais en lisant l’article d’origine. Je partage cet avis. À noter que Virgil prenait déjà en charge dès 2006 l’usage d’un langage entier au compile-time et la compilation de programme entier. Virgil ne cible pas LLVM, donc les comparaisons de vitesse reviennent au fond à comparer les backends. Grâce à cette approche, Virgil peut appliquer des optimisations très puissantes : lier statiquement à l’avance les appels de méthodes (devirtualize), éliminer autant que possible les champs/objets inutilisés, propager des constantes jusque dans les objets alloués sur le heap via les champs, ou encore spécialiser parfaitementforen Zig me paraît beaucoup trop brouillonne. Le fait de devoir placer deux listes côte à côte et aligner les positions me fait mal aux yeux rien qu’à regarder. Je pense que c’est une erreur des langages récents de déverser autant de syntaxe « magique » et de symboles spéciaux. J’aurais du mal à regarder ça pendant des heures