Skip to main content

SIMD - Experimental helpers

This page lists all operations on slices, available in the exp/simd sub-package. These helpers use Go 1.27's portable simd package, built with the GOEXPERIMENT=simd flag. The same code compiles to AVX/AVX2/AVX512 on amd64, NEON on arm64, and SIMD128 on wasm, picking the widest vector the CPU supports at runtime, with an automatic fallback to the scalar lo.* implementation everywhere else (including GODEBUG=simd=0).

Help improve this documentation

This documentation is still new and evolving. If you spot any mistakes, unclear explanations, or missing details, please open an issue.

Your feedback helps us improve!

Unstable API

SIMD helpers are experimental. The API may break in the future.

Performanceโ€‹

Benchmarks show that running SIMD operations on small datasets (below one vector's worth of data) is slower, since the fixed vector setup cost dominates:

BenchmarkSumInt8/small/lo-2 344053036 3.576 ns/op
BenchmarkSumInt8/small/simd-2 48484386 24.80 ns/op

But much faster on large datasets, where the win scales with the CPU's vector width (up to 16 int32 lanes on AVX-512, 4 on NEON):

BenchmarkSumInt8/xlarge/lo-2 466400 2576 ns/op
BenchmarkSumInt8/xlarge/simd-2 13143464 92.25 ns/op

See BENCHMARK.md for a full amd64 (AVX-512) vs arm64 (NEON) comparison across every helper.

  • Sums the values in a collection using SIMD instructions when available (AVX/AVX2/AVX512 on amd64, NEON on arm64, SIMD128 on wasm), falling back to a plain scalar loop everywhere else.

    sum := simd.SumInt32([]int32{1000000, 2000000, 3000000})
    // 6000000
    sum := simd.SumFloat32([]float32{1.1, 2.2, 3.3, 4.4})
    // 11
    // Empty collection returns 0
    sum := simd.SumUint16([]uint16{})
    // 0
    Similar:
    Prototypes:
    func SumInt8[T ~int8](collection []T) T
    func SumInt16[T ~int16](collection []T) T
    func SumInt32[T ~int32](collection []T) T
    func SumInt64[T ~int64](collection []T) T
    func SumUint8[T ~uint8](collection []T) T
    func SumUint16[T ~uint16](collection []T) T
    func SumUint32[T ~uint32](collection []T) T
    func SumUint64[T ~uint64](collection []T) T
    func SumFloat32[T ~float32](collection []T) T
    func SumFloat64[T ~float64](collection []T) T
  • Calculates the arithmetic mean of a collection using SIMD instructions when available, falling back to a plain scalar loop everywhere else.

    mean := simd.MeanInt16([]int16{10, 20, 30, 40})
    // 25
    mean := simd.MeanFloat32([]float32{1.0, 2.0, 3.0, 4.0})
    // 2.5
    // Empty collection returns 0
    mean := simd.MeanUint32([]uint32{})
    // 0
    Similar:
    Prototypes:
    func MeanInt8[T ~int8](collection []T) T
    func MeanInt16[T ~int16](collection []T) T
    func MeanInt32[T ~int32](collection []T) T
    func MeanInt64[T ~int64](collection []T) T
    func MeanUint8[T ~uint8](collection []T) T
    func MeanUint16[T ~uint16](collection []T) T
    func MeanUint32[T ~uint32](collection []T) T
    func MeanUint64[T ~uint64](collection []T) T
    func MeanFloat32[T ~float32](collection []T) T
    func MeanFloat64[T ~float64](collection []T) T
  • Finds the minimum value in a collection using SIMD instructions when available, falling back to a plain scalar loop everywhere else.

    min := simd.MinInt32([]int32{100, 50, 200, 75})
    // 50
    min := simd.MinFloat32([]float32{3.5, 1.2, 4.8, 2.1})
    // 1.2
    // Empty collection returns 0
    min := simd.MinUint16([]uint16{})
    // 0
    Prototypes:
    func MinInt8[T ~int8](collection []T) T
    func MinInt16[T ~int16](collection []T) T
    func MinInt32[T ~int32](collection []T) T
    func MinInt64[T ~int64](collection []T) T
    func MinUint8[T ~uint8](collection []T) T
    func MinUint16[T ~uint16](collection []T) T
    func MinUint32[T ~uint32](collection []T) T
    func MinUint64[T ~uint64](collection []T) T
    func MinFloat32[T ~float32](collection []T) T
    func MinFloat64[T ~float64](collection []T) T
  • Finds the maximum value in a collection using SIMD instructions when available, falling back to a plain scalar loop everywhere else.

    max := simd.MaxInt32([]int32{100, 50, 200, 75})
    // 200
    max := simd.MaxFloat32([]float32{3.5, 1.2, 4.8, 2.1})
    // 4.8
    // Empty collection returns 0
    max := simd.MaxUint16([]uint16{})
    // 0
    Prototypes:
    func MaxInt8[T ~int8](collection []T) T
    func MaxInt16[T ~int16](collection []T) T
    func MaxInt32[T ~int32](collection []T) T
    func MaxInt64[T ~int64](collection []T) T
    func MaxUint8[T ~uint8](collection []T) T
    func MaxUint16[T ~uint16](collection []T) T
    func MaxUint32[T ~uint32](collection []T) T
    func MaxUint64[T ~uint64](collection []T) T
    func MaxFloat32[T ~float32](collection []T) T
    func MaxFloat64[T ~float64](collection []T) T
  • Clamps each element in a collection between mn and mx using SIMD instructions when available, falling back to a plain scalar loop everywhere else. If mn > mx, every element clamps to mn.

    result := simd.ClampInt8([]int8{1, 5, 10, 15, 20}, 5, 15)
    // []int8{5, 5, 10, 15, 15}
    result := simd.ClampFloat32([]float32{0.5, 1.5, 2.5, 3.5}, 1.0, 3.0)
    // []float32{1.0, 1.5, 2.5, 3.0}
    // Empty collection returns an empty collection
    result := simd.ClampUint32([]uint32{}, 10, 100)
    // []uint32{}
    Prototypes:
    func ClampInt8[T ~int8, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampInt16[T ~int16, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampInt32[T ~int32, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampInt64[T ~int64, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampUint8[T ~uint8, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampUint16[T ~uint16, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampUint32[T ~uint32, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampUint64[T ~uint64, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampFloat32[T ~float32, Slice ~[]T](collection Slice, mn, mx T) Slice
    func ClampFloat64[T ~float64, Slice ~[]T](collection Slice, mn, mx T) Slice
  • Transforms a collection using an iteratee function and sums the result using SIMD instructions when available, falling back to a plain scalar loop everywhere else.

    type Person struct {
    Name string
    Age int8
    }

    people := []Person{
    {Name: "Alice", Age: 25},
    {Name: "Bob", Age: 30},
    {Name: "Charlie", Age: 35},
    }

    sum := simd.SumByInt8(people, func(p Person) int8 {
    return p.Age
    })
    // 90
    type Product struct {
    Name string
    Price float32
    Stock int32
    }

    products := []Product{
    {Name: "Widget", Price: 10.50, Stock: 5},
    {Name: "Gadget", Price: 20.00, Stock: 3},
    {Name: "Tool", Price: 15.75, Stock: 2},
    }

    sum := simd.SumByFloat32(products, func(p Product) float32 {
    return p.Price * float32(p.Stock)
    })
    // 152.5
    // Empty collection returns 0
    type Item struct {
    Count int64
    }

    sum := simd.SumByInt64([]Item{}, func(i Item) int64 {
    return i.Count
    })
    // 0
    Similar:
    Prototypes:
    func SumByInt8[T any, R ~int8](collection []T, iteratee func(item T) R) R
    func SumByInt16[T any, R ~int16](collection []T, iteratee func(item T) R) R
    func SumByInt32[T any, R ~int32](collection []T, iteratee func(item T) R) R
    func SumByInt64[T any, R ~int64](collection []T, iteratee func(item T) R) R
    func SumByUint8[T any, R ~uint8](collection []T, iteratee func(item T) R) R
    func SumByUint16[T any, R ~uint16](collection []T, iteratee func(item T) R) R
    func SumByUint32[T any, R ~uint32](collection []T, iteratee func(item T) R) R
    func SumByUint64[T any, R ~uint64](collection []T, iteratee func(item T) R) R
    func SumByFloat32[T any, R ~float32](collection []T, iteratee func(item T) R) R
    func SumByFloat64[T any, R ~float64](collection []T, iteratee func(item T) R) R
  • Transforms a collection using an iteratee function and calculates the arithmetic mean of the result using SIMD instructions when available, falling back to a plain scalar loop everywhere else.

    type Person struct {
    Name string
    Age int8
    }

    people := []Person{
    {Name: "Alice", Age: 20},
    {Name: "Bob", Age: 30},
    {Name: "Charlie", Age: 40},
    }

    mean := simd.MeanByInt8(people, func(p Person) int8 {
    return p.Age
    })
    // 30
    type Product struct {
    Name string
    Price float32
    }

    products := []Product{
    {Name: "Widget", Price: 10.50},
    {Name: "Gadget", Price: 20.00},
    {Name: "Tool", Price: 15.75},
    }

    mean := simd.MeanByFloat32(products, func(p Product) float32 {
    return p.Price
    })
    // 15.4167
    // Empty collection returns 0
    type Item struct {
    Count int64
    }

    mean := simd.MeanByInt64([]Item{}, func(i Item) int64 {
    return i.Count
    })
    // 0
    Similar:
    Prototypes:
    func MeanByInt8[T any, R ~int8](collection []T, iteratee func(item T) R) R
    func MeanByInt16[T any, R ~int16](collection []T, iteratee func(item T) R) R
    func MeanByInt32[T any, R ~int32](collection []T, iteratee func(item T) R) R
    func MeanByInt64[T any, R ~int64](collection []T, iteratee func(item T) R) R
    func MeanByUint8[T any, R ~uint8](collection []T, iteratee func(item T) R) R
    func MeanByUint16[T any, R ~uint16](collection []T, iteratee func(item T) R) R
    func MeanByUint32[T any, R ~uint32](collection []T, iteratee func(item T) R) R
    func MeanByUint64[T any, R ~uint64](collection []T, iteratee func(item T) R) R
    func MeanByFloat32[T any, R ~float32](collection []T, iteratee func(item T) R) R
    func MeanByFloat64[T any, R ~float64](collection []T, iteratee func(item T) R) R
  • Checks if a target value is present in a collection using SIMD instructions when available, falling back to a plain scalar loop everywhere else.

    found := simd.ContainsInt32([]int32{1, 2, 3, 4, 5}, 3)
    // true
    found := simd.ContainsFloat32([]float32{1.1, 2.2, 3.3, 4.4}, 3.3)
    // true
    // Empty collection returns false
    found := simd.ContainsInt16([]int16{}, 5)
    // false
    Prototypes:
    func ContainsInt8[T ~int8](collection []T, target T) bool
    func ContainsInt16[T ~int16](collection []T, target T) bool
    func ContainsInt32[T ~int32](collection []T, target T) bool
    func ContainsInt64[T ~int64](collection []T, target T) bool
    func ContainsUint8[T ~uint8](collection []T, target T) bool
    func ContainsUint16[T ~uint16](collection []T, target T) bool
    func ContainsUint32[T ~uint32](collection []T, target T) bool
    func ContainsUint64[T ~uint64](collection []T, target T) bool
    func ContainsFloat32[T ~float32](collection []T, target T) bool
    func ContainsFloat64[T ~float64](collection []T, target T) bool