veilletech.fr
13 sept. Feed du jour
#06 MESURE Article

Seize des vingt-quatre minutes étaient le lieur

Avant de changer de langage, regardez qui tient la fin du build.

Bun annonçait une compilation plus de 5× plus rapide après son passage de Zig à Rust. Un ingénieur de Perfetto a écrit buildprof, un traceur qui enregistre tous les processus et accès fichiers d'un build, pour voir ce que ce chiffre contenait. Réponse : ld.lld tournait seul plus de 16 minutes à la fin, en LTO complet. Passé en ThinLTO, l'ancien build tombe de 24 à 15 minutes ; le reste de l'écart est structurel.

3 min de lectureavancévidéo 1:11
Partager
Sommaire5 sections
  1. Ce qui se passe
  2. Comment buildprof s'y prend
  3. Ce que la trace a montré
  4. Ce que ça change
  5. À retenir

Ce qui se passe

Jarred Sumner, architecte de Bun, annonçait un build Linux plus de 5× plus rapide après le passage de Zig à Rust. Un détail de l'annonce était passé presque inaperçu : l'ancien build utilisait Full LTO, le nouveau ThinLTO. Lalit Maganti, qui travaille sur Perfetto, a voulu savoir quelle part du gain venait de là — et a écrit l'outil qui manquait pour le dire.

Les chiffres se reproduisent. Rejeu des builds CI Linux x64 de Bun 1.3.14 et 1.4.0 sur une VM de 6 cœurs / 12 threads :

Build Linux x64 Ère Zig Ère Rust
Médiane CI annoncée par Bun 30 min 06 s 5 min 37 s
Rejeu sur une seule machine 24 min 24 s 5 min 40 s

Comment buildprof s'y prend

Il se place devant la commande existante, sans intégration à écrire :

Terminal
buildprof -- make -j16
buildprof -- cargo build
buildprof -- ninja -C out/target
buildprof -- ./dev/custom-build-script.sh

Un build ressemble à un seul programme, mais c'est un arbre de processus : Cargo lance rustc, qui lance cc, qui lance collect2, qui lance ld.lld. Du point de vue du système, tous les systèmes de build font la même chose — lancer des processus. C'est donc la seule couche à observer, et elle couvre Cargo, Ninja, CMake, Make et Zig sans cas particulier, scripts maison compris.

L'enregistrement utilise ptrace, l'interface des débogueurs, qui signale fork, exec et exit ; un filtre seccomp intercepte en plus les seuls appels système nécessaires aux accès fichiers. eBPF aurait demandé CAP_BPF, CAP_PERFMON et l'accrochage à des points de trace instables. L'interface est un dérivé de celle de Perfetto.

Ce que la trace a montré

Sur le build de l'ère Zig, ld.lld tournait seul, à la fin, plus de seize minutes — environ les deux tiers du build total. Sa ligne de commande, capturée automatiquement, portait bien Full LTO.

Changer le réglage pour Bun seul n'a pas suffi : WebKit, gros morceau du build, restait en Full LTO. Les deux passés en ThinLTO, le build Zig descend de 24 à 15 minutes, dont encore 7 de lien.

Et l'écart restant n'est pas le langage : il est structurel. Rust répartit la compilation sur plus de 90 crates, là où le build Zig faisait tout passer par un seul module.

Ce que ça change

Les outils existants ne couvraient chacun qu'une tranche : ninjatracing ne voit pas les scripts au-dessus de Ninja, et les timings de Cargo ne couvraient que 1 min 51 s des 5 min 40 s du build CI, sans regarder dans un build.rs.

Le surcoût de buildprof dépend surtout du nombre de fichiers ouverts :

Build Sans traçage Processus seuls Processus + fichiers
ripgrep / Cargo 12,27 s 12,30 s 12,43 s
Redis / Make 26,78 s 27,04 s 31,89 s

L'option --no-file-events retire le traçage des fichiers et garde la frise des processus.

À retenir

LalitMaganti/buildprof est publié sous Apache-2.0, pour Linux uniquement ; macOS et Windows sont envisagés, ainsi que npm, Gradle, Bazel et le calcul du chemin critique. Et la leçon est indépendante de l'outil : une annonce de gain de compilation qui change le langage et le réglage de LTO dans le même mouvement ne dit pas lequel des deux a payé.