Ce qui se passe
Go 1.27 embarque un paquet expérimental simd portable : un calcul
vectoriel s'écrit une fois et s'exécute sur amd64, arm64 et WebAssembly, avec
une émulation partout ailleurs. Il s'ajoute à simd/archsimd, apparu en
Go 1.26 pour amd64 et étendu en 1.27 à arm64 (NEON) et à wasm, qui expose les
instructions propres à chaque architecture.
Pourquoi un second paquet
Le SIMD applique une même opération à tout un vecteur : huit paires de
float64 additionnées en une instruction, par exemple. Avant ces paquets, Go
n'y accédait que par l'assembleur, réservé aux noyaux de calcul les plus
critiques.
Or les architectures divergent sur presque tout :
- tailles : 128 bits fixes sur wasm, 128, 256 ou 512 sur amd64, de 128 à 2 048 bits avec SVE sur arm64, jusqu'à 65 536 sur riscv64 ;
- masques : registres dédiés sur AVX-512, simples vecteurs sur AVX2 ou NEON ;
- opérations : wasm ne compare pas les entiers 64 bits, la multiplication sans retenue manque ici ou là.
archsimd reflète ces écarts ; simd les masque. Ses types n'ont pas de taille
(simd.Float32s, simd.Uint8s…), l'API se limite au socle commun, et les trous
sont comblés par émulation — y compris la multiplication sans retenue, en temps
constant pour rester utilisable en cryptographie.
Comment ça marche
Le compilateur réécrit chaque fonction qui manipule des types simd en
plusieurs copies spécialisées (128, 256, 512 bits, ou émulation) et choisit
la bonne au démarrage selon le processeur. Les appels entre fonctions
spécialisées ne repassent pas par ce choix. Un exemple, écrit pour cette fiche :
vec/scale.goGo//go:build goexperiment.simd
package vec
import "simd"
// Scale multiplie chaque élément de xs par k, sur place.
func Scale(xs []float32, k float32) {
f := simd.BroadcastFloat32s(k)
n := f.Len()
i := 0
for ; i+n <= len(xs); i += n {
simd.LoadFloat32s(xs[i : i+n]).Mul(f).Store(xs[i : i+n])
}
if i < len(xs) {
v, _ := simd.LoadFloat32sPart(xs[i:])
v.Mul(f).StorePart(xs[i:])
}
}Quand une opération manque, ToArch() et les fonctions …FromArch permettent
de descendre vers archsimd pour une architecture donnée — à condition
d'écrire aussi la variante émulée pour les autres.
Comment s'y prendre
- Compiler avec
GOEXPERIMENT=simd, comme pourarchsimd. - Tester les chemins avec
GODEBUG:simd=0force l'émulation ;simd=128impose des vecteurs de 128 bits et panique d'emblée si le matériel ne suit pas ;256et512s'en servent si possible ;+128,+256et+512tolèrent un jeu d'instructions incomplet (Raspberry Pi sans PMULL, émulation amd64 d'Apple Silicon sans VPCLMULQDQ).
Les limites
C'est une expérience. Pas de somme des éléments d'un vecteur en 1.27 :
ReduceSum arrive en 1.28, avec OnesCount, d'autres opérations de masque et
de permutation, et, espère l'équipe, SVE. Riscv64, ppc64, s390x et loong64 ont
leurs propres extensions, attendues dans archsimd d'ici un an environ.
Source : Platform-independent SIMD in Go, The Go Blog, 25 septembre 2026.