Skip to content

Go 1.27 simd package brings portable, emulated SIMD

Go 1.27's experimental simd package runs one vector code path on AVX-512, NEON, wasm and RISC-V, and emulates it where hardware SIMD is missing.

By Tech AI Wire Team

4 min read

XLinkedIn
The inner-product example from the Go blog's SIMD post, a Go function that loads float32 slices into simd.Float32s vectors and calls MulAdd.
Photo: The Go Blog

The Go team has explained how its new portable SIMD package works, in a post on the Go blog dated September 24, 2026. Go 1.27 ships an experimental simd package that lets one piece of Go code use vector instructions on many different CPUs. That matters because until Go 1.26, the only way to reach those instructions from Go was to write assembly by hand.

SIMD stands for single instruction, multiple data. It is a CPU feature that applies one operation to a whole batch of values at once, such as adding eight pairs of numbers in a single step. It speeds up work like compression, cryptography, image processing and machine learning.

From assembly to two experimental packages

Go now has two SIMD packages, and both are still experiments. The first, archsimd, arrived in Go 1.26. It is architecture-dependent: it exposes each CPU family's own instructions, so code written for Intel chips does not run on Arm.

The second is the new portable simd package in Go 1.27. The post's authors, David Chase and Junyang Shao, describe it as "an experimental platform-agnostic SIMD API." Phoronix notes that the design is modeled on Highway, Google's C++ library for portable vector code.

Both packages sit behind the same switch. You build with the environment variable GOEXPERIMENT=simd to turn them on.

Why portable SIMD is hard

CPU families disagree on almost everything about vectors. The Go blog lists the vector widths each platform supports.

PlatformVector instructionsVector width
amd64AVX, AVX2, AVX-512128, 256 and 512 bits
arm64NEON128 bits
arm64 (planned for Go 1.28)SVE128 to 2,048 bits
wasmWebAssembly SIMD128 bits
riscv64RVV128 to 65,536 bits

The post also names loong64, ppc64 and s390x as supported. Some chips only reveal their vector width when a program starts, so code cannot assume a size at build time.

The simd package solves this by leaving the width out of the types. Vector types are named like capitalized plural primitives: simd.Float32s, simd.Int8s, simd.Uint64s. A Float32s holds as many 32-bit floats as the current CPU can process at once. Masks, the true-or-false values that comparisons produce, get matching types such as Mask32s.

How it runs fast without knowing the width

The compiler does the specializing. According to the Go blog, it rewrites functions that use simd into variants for each vector width, labeled like @simd128. The program then runs the variant that fits the machine, with no dispatch overhead inside the hot loop.

A GODEBUG setting controls the choice at run time. simd=0 turns vector code off, and simd=128, simd=256 or simd=512 cap the width. No recompiling is needed to test each path.

Where a platform has no suitable instructions, every operation is emulated. The authors list their goals for the package. It should be "as efficient as assembly language when the source code operations match the underlying hardware." It should be "emulated as well as possible" otherwise. And it should be "easy to read and understand (even/especially if an LLM ends up writing the code)."

What is missing in Go 1.27

The first release has clear gaps. There is no horizontal reduction, which means no built-in way to sum all the elements of one vector. The blog's own example writes a small scalar loop for that step and says a ReduceSum operation will come in the next release.

Arm's SVE instructions are also scheduled for Go 1.28, Phoronix reports, along with more operations. Some operations are unavailable on some architectures today.

What this means for developers

If your Go service has a hot loop over slices of numbers, try the package now on a branch. Good candidates are checksums, parsing, distance calculations for vector search and image filters. The portable API lets you write that loop once instead of once per CPU family.

Benchmark every width, not just your laptop's. Run the same benchmark with GODEBUG=simd=0, simd=128, simd=256 and simd=512. The zero setting gives you the scalar baseline for free, and the others show whether wider vectors actually pay off on your data.

Keep it out of anything you cannot rebuild quickly. Both SIMD packages are gated behind GOEXPERIMENT, and experimental APIs can change between releases. Plan for the missing ReduceSum too, since any code that sums a vector needs a temporary scalar loop until Go 1.28.

This is the same trade other ecosystems are making. Go already had one real-world win: Debian Code Search dropped its last C dependency using the older archsimd package. Rust took the library route this month, when Fearless SIMD 1.0 shipped a stable, safe SIMD crate. Go is building the capability into its toolchain, with the compiler doing the per-CPU work.

Sources

  1. Platform-independent SIMD in Go - The Go Blog
  2. Go's Improving SIMD Support, Platform-Independent SIMD Interface - Phoronix

Related articles

A terminal showing a large C++ project compiling, with the CMake percentage counter partway through and object files scrolling past.
Coding

LLVM debates building ClangIR by default

An LLVM RFC proposes compiling ClangIR into Clang by default. Nobody would use it without a flag, but some estimates put build times at more than double.

The daily brief

Three to five stories a day, and what each one means for the people who build software. Free, no spam.