Ce qui se passe
Dans Git, une référence est un pointeur nommé vers un objet (une branche, une étiquette). Le backend
historique, dit files, stocke les références empaquetées dans un unique fichier trié,
packed-refs. Problème : supprimer une entrée oblige à réécrire tout le fichier. Sur un dépôt géant,
c'est lent. Celui de GitLab dépasse 15 millions de références ; packed-refs peut atteindre plusieurs
gigaoctets. Sur un fichier de 700 Mo, une seule suppression prenait 2,14 s :
❯ eza -l .git/packed-refs
.rw-r--r-- 700M karthik 8 Nov 15:16 .git/packed-refs
❯ time git update-ref -d refs/heads/branch-0000499999
Executed in 2.14 secsD'où vient le gain
Karthik Nayak, qui travaille sur Git chez GitLab, ne cherchait pas cette optimisation. Pour mesurer le
coût de la frontière entre C et Rust (l'objectif à terme : Git 3.0 rendra Rust obligatoire), il avait
réécrit le backend files en Rust, puis comparé les deux. Surprise, la version Rust supprimait 17,7 %
plus vite (30,6 ms contre 37,1 ms, à 100 000 références).
La cause tenait dans une inutilité du code C. Quand il recopie une référence qu'il ne modifie pas, il
la reconstruisait ligne par ligne avec fprintf() et oid_to_hex(), là où la version Rust écrivait les
octets tels quels. Le correctif écrit les enregistrements intacts directement, avec fwrite() :
if (cmp < 0) {
- /* Pass the old reference through. */
- if (write_packed_entry(out, iter->ref.name,
- iter->ref.oid, iter->ref.peeled_oid))
+ size_t len = packed_iter->pos - packed_iter->record_start;
+ if (fwrite(packed_iter->record_start, len, 1, out) != 1)
goto write_error;Résultat : 18 % de gain (54,9 ms contre 66,5 ms), à égalité avec la version Rust (55,2 ms). La leçon dépasse le correctif : l'hypothèse « Rust est plus rapide » était fausse, c'était du travail superflu côté C — et il a fallu mesurer pour le voir.
Ce que ça change
- Le correctif est fusionné (commit
fc70f528) et arrivera dans Git v2.98. Oui, Git saute de v2.56 à v2.98, en préparation de Git 3.0, qui suivra deux versions plus tard. - Il ne concerne que le backend
files, encore le défaut. Le nouveau backendreftable(introduit en Git 2.45, défaut en Git 3.0) stocke les références dans des tables binaires et remplace déjà la suppression par une simple pierre tombale — pas de réécriture.
Autrement dit : si vous migrez vers reftable, le problème disparaît ; d'ici là, vos suppressions sur un
gros dépôt en files seront sensiblement plus rapides.
Source : Karthik Nayak, en anglais.