veilletech.fr
26 sept. Feed du jour
#07 GO Article

Go 1.27 : un SIMD portable, écrit une seule fois

Le vecteur n'a plus de taille : c'est le processeur qui la choisit.

Go 1.27 ajoute un paquet expérimental simd portable : on écrit un calcul vectoriel une fois, et le compilateur le spécialise pour 128, 256 ou 512 bits selon le processeur, avec une émulation là où le matériel manque. Il complète archsimd, propre à chaque architecture, et s'active avec GOEXPERIMENT=simd.

3 min de lectureavancévidéo 1:25
Partager
Sommaire6 sections
  1. Ce qui se passe
  2. Pourquoi un second paquet
  3. Comment ça marche
  4. Comment s'y prendre
  5. Les limites
  6. À retenir

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 :

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

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.