Fork me on GitHub

go-simd

Pure-Go SIMD drop-ins for the standard library โ€” base64, base32, hex, ascii85, utf8, adler32, crc32, crc64, strconv and the primitives under them, with byte-identical output on plain go build.

go-asmgen-generated6 architecturesamd64 ยท arm64 ยท riscv64 ยท loong64 ยท ppc64le ยท s390xllvm-mca cycle-modelled100% coveragehonest benchmarks
zsh
$ go get github.com/go-simd/base64@latest
$ go get github.com/go-simd/base32@latest
$ go get github.com/go-simd/hex@latest
$ go get github.com/go-simd/utf8@latest
$ go get github.com/go-simd/adler32@latest
$ go get github.com/go-simd/strconv@latest
One organisation, 22 repositories

Each package is a drop-in: the same signatures and byte-identical output (and errors) as the stdlib package it accelerates. The hot loop is real SIMD assembly emitted by go-asmgen (v0.5.0) for all six 64-bit Go targets — amd64, arm64, riscv64, loong64, ppc64le (VSX) and s390x (vector facility, big-endian) — and encoded by cmd/asm; the short tail delegates back to the standard library so results match exactly. The numbers are measured and honest โ€” wins, parities, and the cases where SIMD genuinely does not help.

Six architectures, one generator. The amd64/arm64 throughput figures below are native measurements. Most repos are now measured on real IBM z15 (VXE2, native execution, 2026-07-03, -count=6) — the vector-facility kernels post clear wins (e.g. bitpack ~34×, streamvbyte decode ~20×, hex encode ~18×, matchlen ~9.2×, crc64 ~9.1×) and are bit-exact on big-endian (jsonvalidate and ascii85 still await a follow-up z15 run). ppc64le kernels are measured on real POWER9 where a GCC Compile Farm runner is available, otherwise qemu-validated for correctness. Two highlights: base32 gets real SIMD on ppc64le (VSRH) and s390x (VMLHH) precisely where Go's arm64 assembler lacks the register-variable shift and integer vector multiply it needs; and every kernel is byte-for-byte correct on big-endian s390x.

Beyond these six native targets, matchlen-wasm is a proof-of-concept 7th target — wasm-SIMD (v128) — callable from browsers and WASI hosts via //go:wasmimport, since the Go compiler does not emit v128 from Go source. It is a standalone companion repo, not one of the 21 packages below.

highway is a maintained fork of ajroetker/go-highway, not a go-asmgen package: its upstream releases require a tool module at a version that was never published, so no consumer could go mod tidy against them. The fork publishes that module, retracts the inherited broken tags and carries security updates until upstream releases the fix. On amd64 its AVX2/AVX-512 kernels need GOEXPERIMENT=simd; without it, integer kernels take an allocating generic fallback — measure before you rely on it.

Repositories

base64 drop-in

encoding/base64

encode ~17ร— stdlib; beats emmansun/base64 ~5โ€“6% (cycle-model-guided).

CI coverage 100%

base32 drop-in

encoding/base32

encode ~7.9ร— stdlib (AVX2), SIMD decode too, and arm64 NEON encode + decode (decode ~25ร— at 1 MiB). Real SIMD on ppc64le VSRH + s390x VMLHH; ppc64le measured on real POWER9 (decode ~5.5ร—). No prior pure-Go SIMD base32.

CI coverage 100%

hex drop-in

encoding/hex

beats tmthrgd/go-hex both ways โ€” encode 20.4ร—, decode 6.24ร— stdlib.

CI coverage 100%

utf8 drop-in

unicode/utf8

Valid ~19ร— stdlib, edges stuartcarnie/go-simd ~3.5% (Lemire/Keiser).

CI coverage 100%

adler32 drop-in

hash/adler32

~12โ€“14ร— stdlib, but ~7% behind mhr3/adler32-simd โ€” near-parity, honest.

CI coverage 100%

strconv drop-in

strconv (base-10 parse + ParseFloat)

ParseInt/Atoi ~2.2โ€“2.7ร— on long inputs; added SIMD ParseFloat (Eisel-Lemire, ~1.1โ€“1.36ร—); never regresses (delegates short/uncertain).

CI coverage 100%

matchlen primitive

LZ match-finder primitive

~10ร— scalar (NEON); real SIMD on all 6 arches; AVX2 ~2.08ร— SSE2. Measured native: ppc64le POWER9 ~6.3ร—, riscv64 X60 ~5.8ร—, loong64 ~11.4ร— scalar.

CI coverage 100%

matchlen-wasm proof of concept

the 7th target: GOOS=js/wasip1 GOARCH=wasm

Go emits no v128 from source and wasm has no Plan 9 asm dialect, so a naive wasm build of matchlen falls back to scalar. The kernel lives in a companion module compiled from matchlen.wat and is called through //go:wasmimport. Measured on wazero 1.12.0 it is SLOWER below 4 KiB โ€” 0.16x scalar at 64 B, 0.94x at 1 KiB โ€” crosses over at 4 KiB (1.19x) and reaches 1.43x at 64 KiB. The call boundary is the cost, which is why this is a proof of concept and not the default path.

CI coverage 100%

bitpack primitive

FastPFOR / simdcomp bit-packing

~21โ€“32ร— scalar โ€” byte-exact vs Lemire's simdcomp. ppc64le measured on real POWER9: Pack ~7.0ร—, Unpack ~11.3ร— (overturns the earlier ~1.1ร— llvm-mca estimate).

CI coverage 100%

popcount primitive

Hamming weight over []byte

size-dependent: ~9โ€“12ร— in-cache, converges to memory-bound out-of-cache. Measured native: loong64 LSX ~6.1ร—, ppc64le POWER9 ~2.9ร— scalar (riscv64 stays scalar โ€” no per-element vector popcount in Go's RVV asm).

CI coverage 100%

histogram primitive

byte-value histogram

multi-table scalar โ€” measured, SIMD does NOT help (no scatter on AVX2/NEON).

CI coverage 100%

crc64 drop-in

hash/crc64

first pure-Go SIMD crc64 (stdlib + all Go libs are scalar); CLMUL folding, arm64 ~4.2ร— stdlib; all 6 arches (riscv64/loong64 scalar).

CI coverage 100%

crc32 drop-in

hash/crc32

arm64-only, deliberately: stdlib IEEE path is already hardware-assisted on amd64/ppc64le/s390x, so only arm64's serial CRC32X fallback is worth beating; PMULL/PMULL2 fold-by-eight does that, every other arch and non-IEEE polynomial just calls hash/crc32.

CI coverage 100%

ascii85 drop-in

encoding/ascii85

first SIMD ascii85, SIMD encode AND decode; real SIMD on all 6 arches incl. an arm64 NEON kernel on Go 1.27. Measured native: ppc64le POWER9 ~2.9ร— encode, riscv64 X60 ~4.0ร—, loong64 ~4.3ร—; arm64 NEON ~3.0ร— / fused-scalar ~1.9ร— on stable Go.

CI coverage 100%

floats primitive

Dot/Sum/Min/Max/Distance/CosineSimilarity (float32 + float64); Axpy/Xpby/AxpyDot/Scale (float64)

only pure-Go lib with the full vector-search API on all 6 arches (gonum/vek/kelindar are amd64-centric or partial); arm64 ~2ร— scalar. Since v0.4.0 the BLAS-1 updates Axpy/Xpby/AxpyDot/Scale, bit-identical to math.FMA on every arch: AVX2 3.0โ€“7.7ร—, M4 NEON 1.6โ€“5.3ร— over the scalar loop at n=1e3โ€“1e6. ppc64le measured on real POWER9: f32 VSX kernel ~1.55โ€“1.61ร— over naive, f64 routes to the gc-autovectorized scalar loop (it beats VSX there โ€” honest).

CI coverage 100%

xxhash primitive

XXH3-64

first Go XXH3 on all 6 SIMD arches, bit-exact vs official vectors. Honest: zeebo faster on arm64 (~22 vs ~15 GB/s), amd64 block-at-a-time kernel reaches ~0.94ร— zeebo. Where zeebo has no kernel we win big: riscv64 RVV ~2.5โ€“3.5ร— zeebo on real X60 silicon; ppc64le near-parity (~0.91ร—) on real POWER9.

CI coverage 100%

streamvbyte primitive

Stream VByte uint32 codec

first Go port on all 6 arches (others amd64-only); full codec โ€” SIMD encode and decode, byte-identical to lemire/streamvbyte; decode arm64 ~10ร— scalar. Measured native: ppc64le POWER9 decode ~11.6ร—, riscv64 X60 ~4.5ร—, loong64 ~11.8ร— scalar.

CI coverage 100%

ascii drop-in

ASCII case (ToUpper/ToLower/EqualFold)

branchless vector case-fold, byte-identical to bytes/strings; real SIMD on all 6 arches incl. arm64 (multiply-free sign-bit predicate); arm64 ~4.9ร— stdlib (ToUpper).

CI coverage 100%

levenshtein primitive

edit distance (Distance)

Myers bit-parallel edit distance, 62โ€“142ร— vs agnivade/levenshtein on long inputs. Honest: pure-Go bit-parallel, not vector-SIMD (word-serial column, no lane parallelism); endian-clean on all 6 arches.

CI coverage 100%

bitset primitive

bulk []uint64 set ops

SIMD And/Or/AndNot/Xor + fused Count/IntersectionCount; amd64 Count ~3โ€“3.9ร—. Honest: logical ops are bandwidth-bound (converge to scalar out-of-cache), popcount wins across the board.

CI coverage 100%

int8dot primitive

int8 quantized dot (Dot/DotUint8/DotU8S8)

INT8 multiply-accumulate for ML embeddings, bit-exact vs scalar; amd64 AVX2 ~4ร—; arm64 NEON kernel on Go 1.27 (~2.4ร—; scalar on stable Go โ‰ค 1.26). Measured native: riscv64 X60 ~9.1ร— (biggest RVV win in the suite), loong64 ~8.9ร— (DotUint8 ~13.8ร—), ppc64le POWER9 ~4.1ร—.

CI coverage 100%

jsonvalidate drop-in

encoding/json.Valid

byte-identical verdict to encoding/json.Valid; only SIMD JSON validator on all 6 arches (simdjson-go is amd64-only); SIMD string/space scans, ~1.4ร— on string-heavy JSON (number-/structure-heavy stays scalar). Measured native on string-heavy: ppc64le POWER9 ~8.5ร— stdlib, riscv64 X60 ~5.2ร—.

CI coverage 100%

Every kernel is generated by go-asmgen (v0.5.0) over a shared ABI0 layout (amd64 SSE2/AVX2, arm64 NEON, loong64 LSX/LASX, riscv64 RVV, ppc64le VSX, s390x vector facility), cycle-modelled with llvm-mca, validated on real AVX2 / native arm64 / QEMU riscv64+loong64+ppc64le+s390x, and held at 100% statement coverage as a CI gate. The headline benchmarks come from native CI; s390x is measured on real IBM z15 (VXE2, 2026-07-03) and ppc64le on real POWER9 where a GCC Compile Farm runner is available.