⏱️ Lectura: 10 min

Until now, the only way to use SIMD instructions in Go was to write assembly by hand, something that only paid off in the most performance-critical compute cores. Go 1.27 changes that: on September 24, 2026, David Chase and Junyang Shao introduced on the official Go blog an experimental package called simd, the first portable SIMD implementation in Go that runs the same way on amd64, arm64, and wasm.

📑 En este artículo
  1. TL;DR
  2. What happened
  3. Context and history
  4. Technical details of portable SIMD in Go
  5. How to start testing portable SIMD in Go
  6. Impact and analysis
  7. What’s next
  8. Frequently Asked Questions
    1. What is Go’s simd package?
    2. How is it different from archsimd?
    3. What architectures does portable SIMD in Go support today?
    4. How do I enable the simd package in my project?
    5. Is it safe to use simd in production right now?
    6. Does simd replace writing assembly in Go for good?
  9. References

The package doesn’t require maintaining a separate code version for each CPU architecture. The idea isn’t new to the project: Go’s Green Tea garbage collector already uses SIMD instructions to speed up memory scanning for live objects, so the team knows firsthand how much performance is left on the table when the rest of the code doesn’t take advantage of them.

TL;DR

  • Simd, the package introduced on September 24, 2026, runs on amd64, arm64, and wasm without recompiling separate code per architecture.
  • Go 1.26 introduced SIMD only for amd64 with the archsimd package; Go 1.27 added arm64 (NEON) and wasm.
  • David Chase and Junyang Shao designed simd drawing inspiration from Google’s C++ Highway library.
  • The new API supports AVX, AVX2, and AVX512 on amd64, NEON on arm64, and wasm’s SIMD instructions.
  • To enable it, you need to build with the GOEXPERIMENT=simd environment variable.
  • Go’s Green Tea garbage collector already uses SIMD instructions to scan memory for live objects.
  • Vector types are named simd.Uint8s, simd.Float32s, and similar: capitalized, pluralized primitives loaded from slices.
  • Riscv64 supports variable-size vectors between 128 and 65,536 bits, always powers of two, a case the new API abstracts away.

What happened

On September 24, 2026, the official Go blog confirmed that Go 1.27 adds a fully portable simd package, in addition to extending the architecture-dependent archsimd package to arm64 and wasm. Go 1.26, released earlier the same year, had already introduced SIMD for amd64 through archsimd.

The portable simd package in Go pursues four design goals, according to its authors.

  • Cover data-processing algorithms that benefit from a vectorized implementation, without tying them to a fixed vector size.
  • Perform as well as assembly when the source code matches what the hardware supports.
  • Emulate as well as possible when the hardware lacks that instruction or the architecture has no support in archsimd.
  • Be easy to read and understand, even if a language model ends up writing it.

Context and history

Before these APIs, the only entry point to SIMD in Go was writing Go assembly by hand. That was only justified in truly critical compute cores, so much of the software that could benefit from vectorization simply left a large part of the CPU unused.

Go 1.26 opened the door with archsimd for amd64. Go 1.27 extended that same architecture-dependent API to arm64 (specifically NEON) and to wasm. But archsimd, although designed to be as uniform as possible across architectures, still requires writing a separate code branch for each one. The new simd package goes a step further: it removes fixed-size vectors from the type system and only exposes operations that lie at the intersection of all supported platforms, filling the gaps with efficient emulation in terms of other SIMD instructions.

Go 1.26 added SIMD only for amd64 with archsimd; Go 1.27 extended it to arm64 (NEON) and wasm. Foto de CDC en Unsplash

Technical details of portable SIMD in Go

The variation between SIMD architectures is enormous, and not just in which operations they support but in how they represent vectors. Some platforms use fixed-size vectors, between 128 and 512 bits; on others, the vector size isn’t even known at compile time and has to be queried when the program starts.

ArchitectureVector sizesMask modelSupported in simd (2026)
amd64 (AVX/AVX2/AVX512)128, 256, and 512 bitsVector bitmask (AVX/AVX2) or dedicated mask registers (AVX512)Yes
arm64 NEONFixed 128 bitsVector bitmaskYes
arm64 SVE/SVE2Variable, 128-2048 bits (powers of 2)One bit per byte; the least significant bit of each element governsNot yet
wasmFixed 128 bitsVector bitmask (no 64-bit integer comparisons)Yes
riscv64 (RVV)Variable, 128-65536 bits (powers of 2)Dedicated mask registers, like AVX512Not yet
loong64128 and 256 bitsNot documented in the announcementNot yet

Masking (deciding which elements of a vector participate in an operation) also varies greatly. wasm, AVX, AVX2, and NEON don’t have dedicated mask registers: selection is done with boolean operations on regular vectors. AVX512 and RVV do have mask registers, with one bit per element. SVE reserves one bit per vector byte, but only the least significant bit of each element counts. Even basic operations differ: wasm, for example, has no native comparisons for 64-bit integer vectors. The portable simd package in Go hides all that variation and only exposes operations present at the intersection of platforms, emulating the rest.

Here’s a summary of the flow when the compiler processes code that uses the simd package.

flowchart TD
A["Go code using the simd package"] --> B{"Target architecture"}
B -->|"amd64"| C["AVX / AVX2 / AVX512 backend"]
B -->|"arm64"| D["NEON backend"]
B -->|"wasm"| E["wasm SIMD instructions"]
B -->|"other architecture"| F["Emulation with equivalent instructions"]

The same source code compiles to different paths depending on the available backend, and if none applies, it falls back to the emulation path instead of failing.

How to start testing portable SIMD in Go

The package lives behind an experimental flag, so you need a Go 1.27 (or later) toolchain and to enable GOEXPERIMENT when building.

# Linux and macOS (bash/zsh)
GOEXPERIMENT=simd go build ./...

# Windows (PowerShell)
$env:GOEXPERIMENT = "simd"
go build ./...

# Windows (cmd.exe)
set GOEXPERIMENT=simd
go build ./...

With that variable active, the compiler enables the simd import path in addition to the per-architecture archsimd packages. A minimal example, adapting the inner-product pattern shown in the official blog, would look like this.

package vectormath

import "simd"

// innerProduct computes the dot product of x and y using the
// experimental simd package (requires building with GOEXPERIMENT=simd).
func innerProduct(x, y []float32) float32 {
	var acc simd.Float32s
	n := len(x)
	width := acc.Len() // actual vector width for this CPU, not fixed in the code

	i := 0
	for ; i+width <= n; i += width {
		vx := simd.LoadFloat32sSlice(x[i:])
		vy := simd.LoadFloat32sSlice(y[i:])
		acc = acc.Add(vx.Mul(vy))
	}

	sum := acc.Sum() // reduces the accumulated vector to a single float32
	for ; i < n; i++ {
		sum += x[i] * y[i]
	}
	return sum
}

This function walks through x and y width elements at a time (the actual vector width the CPU exposes at runtime, not a fixed number in the code) and accumulates products with vector instructions; the remainder, if n isn’t a multiple of width, is added element by element at the end. On an amd64 CPU with AVX2, width would be 8 float32s per vector; on an arm64 with NEON, 4.

⚠️ Heads up: The simd package is experimental and lives behind the GOEXPERIMENT flag. Its API can still change between Go versions, so it’s best to avoid betting critical production code on the exact shape of its functions for now.

To confirm the compiler actually emitted vector instructions, it helps to look at the generated assembly and search for SIMD mnemonics, and check whether the binary was flagged with the experimental flag.

go build -gcflags="-S" ./vectormath 2>&1 | grep -iE "vpaddps|vfmadd|vmovups"

go version -m ./mibinario | grep GOEXPERIMENT

If the second command shows GOEXPERIMENT=simd in the build metadata, the binary was compiled with the package enabled.

simd is enabled by building with GOEXPERIMENT=simd, just like other experimental Go features. Foto de ThisisEngineering en Unsplash

Impact and analysis

The arrival of portable SIMD in Go lowers the barrier to speeding up compute-intensive tasks, such as cryptography, data processing, and AI inference, without condemning the project to maintaining assembly per architecture. Previously, writing SIMD by hand in Go meant multiplying the maintenance work by every supported architecture; now that cost is absorbed by the compiler and the simd package’s emulation layer.

💭 Key point: Go’s Green Tea garbage collector already uses SIMD instructions to speed up memory scanning for live objects. The improvement isn’t just for application code. It also benefits the runtime.

The design has an explicit cost. By exposing only the intersection of operations across platforms, the simd package doesn’t allow squeezing out exotic instructions specific to a single architecture, something that hand-written assembly or using archsimd directly can achieve. For a hyper-specific compute core, where every cycle counts and the binary only runs on one type of CPU, archsimd (or pure assembly) remains the faster option; portable simd in Go makes sense when the same binary has to perform well across several architectures without tripling the source code.

What’s next

The simd package remains gated behind GOEXPERIMENT, which in Go’s usual practice means its API can still change, merge, or even get dropped before becoming stable, as happened with other experimental language features. Today it covers amd64, arm64, and wasm; architectures with variable-size vectors like riscv64 (RVV) and arm64 with SVE, or with smaller fixed support like loong64, PowerPC, and s390x, remain outside the initial scope.

The Go team hasn’t published a stable-graduation date for simd, but the project’s usual pattern is to leave a feature behind GOEXPERIMENT for several versions while it gets production feedback before enabling it by default.

📖 Summary on Telegram: View summary

Try it yourself: build any numeric function of your own with GOEXPERIMENT=simd go build ./… on a Go 1.27 toolchain and compare the resulting assembly against the version without the flag.

Frequently Asked Questions

What is Go’s simd package?

It’s an experimental API, introduced in Go 1.27, that exposes vector operations without tying them to a specific architecture or a fixed vector size. The compiler picks the appropriate backend (AVX/AVX2/AVX512 on amd64, NEON on arm64, wasm’s SIMD) or emulates the operation if needed.

How is it different from archsimd?

archsimd, added in Go 1.26 for amd64 and extended in 1.27 to arm64 and wasm, exposes the hardware almost unfiltered: you have to write a separate code branch per architecture. simd, on the other hand, is agnostic to platform and vector size, and the same source code runs on every supported architecture.

What architectures does portable SIMD in Go support today?

According to the September 24, 2026 announcement, simd covers AVX, AVX2, and AVX512 on amd64, NEON on arm64, and wasm’s SIMD instructions. Architectures like riscv64, loong64, PowerPC, or s390x aren’t yet covered by the portable package.

How do I enable the simd package in my project?

You need to build with the GOEXPERIMENT=simd environment variable, for example with GOEXPERIMENT=simd go build ./…, using a Go 1.27 or later toolchain.

Is it safe to use simd in production right now?

It’s experimental: it lives behind GOEXPERIMENT precisely because its API can still change. It’s best to test it in your own benchmarks before betting critical code on its current form.

Does simd replace writing assembly in Go for good?

Not in every case. For hyper-specific cores that only run on one architecture and need to squeeze out every instruction, archsimd or direct Go assembly still perform better than the portable layer, which trades some specificity for portability.

References

📱 Enjoying this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.

Imagen destacada: Foto de Mathew Schwartz en Unsplash

Categories: Noticias Tech

Andrés Morales

Developer and AI researcher. Writes about language models, frameworks, developer tooling, and open source releases. Covers ML papers, the tech startup ecosystem, and programming trends.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.