Ce qui se passe
Debian Code Search indexe tout le code source d'origine libre présent dans Debian. Comme tout moteur de recherche, il repose sur un index inversé, donc sur de longues listes d'identifiants de documents qu'il faut décoder vite. Depuis 2019, l'index positionnel tient sur disque — c'est ce qui permet de le loger sur un serveur Hetzner à deux SSD d'1 To — et 78,2 % des requêtes le sollicitent.
Ce décodage passait par TurboPFor, une bibliothèque C, appelée via cgo pendant sept ans. En août 2026, cette dernière dépendance a disparu.
Ce qui l'a rendu possible
Go 1.26, sorti en février 2026, introduit le paquet expérimental
simd/archsimd. Trois réserves l'accompagnent, et elles comptent :
- il faut compiler avec
GOEXPERIMENT=simd; - l'API n'est pas considérée comme stable ;
- seul amd64 est couvert, avec des types vectoriels de 128, 256 et 512 bits
(
Int8x16,Float64x8…).
Jusque-là, faire du SIMD en Go voulait dire écrire de l'assembleur à la main, le générer avec Avo, ou passer par cgo. Aucune de ces trois voies ne se relit confortablement.
La progression, étape par étape
| Étape | Résultat |
|---|---|
Encodeur Go pur, sans SIMD (commit e920dc7) |
76 % de la vitesse du C |
| + SIMD et spécialisation par largeur de bits (2-3 commits) | dépasse le C |
| + popcount positionnel sur le balayage de blocs | ×2 supplémentaire |
Deux détails de méthode valent d'être volés. D'abord GOAMD64, le niveau de
micro-architecture : l'auteur recommande v3 par défaut en 2026 — pour que
bits.OnesCount8 compile en POPCNT plutôt qu'en table de correspondance — ce
qui exige un Haswell (2013) ou un Zen 1 (2017) au minimum. DCS lui-même compile
en v4, qui suppose AVX512, donc Zen 4 ou Zen 5. Poser
export GOAMD64=v4 dans le Makefile évite surtout de mesurer les replis lents
dans ses propres benchmarks.
Ensuite le PGO, et c'est le passage le plus instructif. Activer la
Profile-Guided Optimization a d'abord fait perdre 13 % en moyenne
géométrique — un résultat contre-intuitif que l'auteur ne balaie pas. Le
coupable n'est pas l'inlining mais l'alignement : Go applique
PCALIGNMAX(64, 31) aux boucles chaudes, et ce remplissage déplace les
instructions à d'autres adresses. Compiler avec -gcflags=all=-d=alignhot=0
restaure la performance, ce qui identifie la cause sans ambiguïté. C'était de la
malchance, pas une règle.
Ce que l'auteur refuse de faire
C'est ce qui rend les chiffres exploitables. À armes égales — les mêmes noyaux AVX512 et le même popcount positionnel rétroportés dans le C — Go redevient ≈1,4× plus lent. L'écart tient à trois choses :
- les vérifications de bornes, qu'il ne désactivera pas parce qu'elles protègent ;
- les
NOPinsérés pour les marqueurs d'inlining mid-stack ; - l'absence de ciblage CPU fin : Go connaît
GOARCHetGOAMD64, pas « Zen 4 », si bien qu'unXORL CX, CXémis avant chaquePOPCNTpour contourner une dépendance de sortie des Intel Sandy Bridge/Skylake pénalise aussi les AMD.
Il refuse également d'ajouter du SIMD partout où ce serait possible : chaque instruction vectorielle rend le code plus difficile à comprendre et à modifier.
À retenir
Le décodage atteint 7 instructions par cycle sur une machine qui plafonne à 8. Si vous traînez une dépendance cgo uniquement pour du calcul vectoriel, la sortie existe désormais — au prix d'une API instable, d'amd64 uniquement, et d'un code plus dense. L'auteur précise avoir mené ce travail avec un agent de code, tout en refusant de « vibe-coder » Debian Code Search : les gains sont mesurés, les commits relus.
Source : Michael Stapelberg