Key points
- Go has no default portable AVX2 intrinsics (no
<immintrin.h>equivalent). Four paths exist to use 256-bit vectors, two official-stable, one official-experimental, one community ecosystem. - Path 1 (stable, Go 1.18+):
GOAMD64=v3makes the gc compiler emit AVX2/BMI1/BMI2/FMA/LZCNT/F16C instructions. Binaries will fail at startup on CPUs lacking AVX2/OSXSAVE. Benchmarks required — official docs say performance "is expected to improve" but not guaranteed; dense math shows 20–40% gains in narrow hot paths. - Path 2 (stable, always available): Hand-written Plan 9 assembly in
.sfiles. Syntax usesY0–Y15for YMM registers, mandatoryVVEX prefix, source-left/destination-right operand order (opposite of Intel). Used bycrypto/sha1,crypto/sha256,crypto/aes,hash/crc32,runtime.maphash. Costs: no inlining across Go/asm boundary, minor async-preemption risk, Plan 9 dialect is niche. - Path 3 (stable, always available): Runtime dispatch via
golang.org/x/sys/cpu(e.g.,cpu.X86.HasAVX2) — checks both CPUID flag *and* OSXSAVE support. Standard pattern:if HasAVX2 { asmImpl } else { genericImpl }. Standard library already uses this forbytes.Equal,strings.Index. - Path 4 (experimental, Go 1.26):
simd/archsimdpackage enabled withGOEXPERIMENT=simd. Provides opaque vector types (Int8x16,Float64x8,Int32x8) with methods compiling to single CPU instructions. amd64-only, API unstable, not covered by Go 1 compatibility. Driven by proposal #73787 (Austin Clements). Rob Pike publicly opposes it on simplicity grounds. Functions panic if hardware lacks the required feature;go vetstatic checks recommended. - AVX-SSE transition penalty: ~25% slowdown when mixing VEX/non-VEX; runtime inserts
VZEROUPPERat boundaries but asm code must do so manually. - AVX-512 throttling: Skylake-X and similar CPUs downclock under AVX-512 thermal load; AVX2 can outperform AVX-512 per-watt.
- GOAMD64=v3/v4 portability: binaries refuse to start on older CPUs or cloud instances; verify minimum instruction set in heterogeneous clusters.
- Experimental package: do not expose
simd/archsimdtypes in public library APIs. - Abstraction penalty: call overhead negates SIMD gains on tiny arrays; only worth it in hot, branchless, large-data inner loops.
- "Just make my binary faster on modern servers, no asm" →
GOAMD64=v3, benchmark, fall back to v1 if no win. - "I have a clear hot loop on large arrays" → Reuse
kelindar/simd; else write Plan 9 asm +x/sys/cpudispatch + generic fallback. - "I need extreme perf and accept experimental API" → Go 1.26 +
GOEXPERIMENT=simd+simd/archsimd, amd64 only, no public exposure,go vetCPU-feature checks. - "I need amd64+arm64 portability" → asm + build tags, or wait for the planned high-level portable SIMD API (C++ Highway-style, not yet started).
- Proposal #73787 is a two-step plan: (1) low-level arch-specific intrinsics — shipped as experimental; (2) high-level portable API — not started.
- The Go toolchain does not emit AVX-512 even under
GOAMD64=v4; v4 only enables *your* asm/experimental packages. - Go 1.26 mentions GC small-object scanning now uses vector instructions on Intel Ice Lake / AMD Zen 4+, invisible to user code.
- Standardization pace will likely be slow given internal debate.
Myth-busting: gc does not auto-vectorize user loops
A common misconception: writing a tight Go for loop does not cause gc to emit SIMD. Two reasons: (1) compile-speed priority — auto-vectorization needs alias/dependency/bounds-check-elimination analysis; (2) Go's mandatory slice bounds checks block most vectorization. SIMD in gc is limited to hand-written stdlib code (math/bits, bytes.Equal, crypto/*). Claims that Go 1.24/1.26 added automatic user-loop vectorization are not corroborated by official release notes (the Go 1.26 compiler chapter only mentions stack-allocated slice optimization).
Verify with: GOSSAFUNC=MyFunc go build (inspect ssa.html) or go tool objdump -s MyFunc ./binary.
Pitfalls
Ecosystem libraries
| Library | Use |
|---|---|
| kelindar/simd | Vectorized Sum/Min/Max/Add/Sub/Mul/Div, amd64 AVX2 + arm64 NEON, generic fallback |
| mmcloughlin/avo | Generate Plan 9 asm from Go code |
| klauspost/compress | Snappy/zstd with hand-written AVX2 asm |
| minio/sha256-simd | Pure-Go + AVX2 SHA-256 |
| grailbio/base/simd | Cross-arch SIMD primitives |
Typical gains: 2×–5×. A Cgo escape hatch (#include <immintrin.h>) exists but breaks pure-Go portability.
Decision tree
Roadmap notes
Sources
Authoritative: Go Wiki *Minimum Requirements*; Go 1.26 release notes; src/internal/cpu/cpu.go; golang.org/x/sys/cpu; src/simd/archsimd/cpu.go (go1.26.2).
Community (cross-verify): colobu on amd64 microarch levels; Tony Bai on native SIMD proposal; CIOage on the Pike debate.
Flagged unreliable: Chinese-blog claims of automatic user-loop vectorization in Go 1.24/1.26 — no official source confirms.