simdeez

repository·master·Indexed 18 days ago

https://github.com/arduano/simdeez

A Rust library providing a high-level abstraction over SIMD instruction sets including SSE2, SSE41, AVX2, AVX-512, Neon, and WebAssembly SIMD. It enables portable SIMD code with support for compile-time or runtime dispatch, #[no_std] compatibility, and a pure-Rust SIMD math surface via simdeez::math. Key features include operator overloading, scalar fallbacks, and macros like simd_runtime_generate! and simd_compiletime_generate! for efficient instruction set selection.

Tokens
6.9K
Snippets
13
Records
35
Agent score
64%

What's inside simdeez

  1. Overview of SIMDeez

    master

    SIMDeez is a Rust library that abstracts over various SIMD instruction sets (SSE2, SSE41, AVX2, AVX-512, Neon, and WebAssembly SIMD). It allows developers to write a single SIMD function that can be dispatched either at compile time or automatically at runtime based on the host CPU's capabilities.

    Key features include:

    • Cross-platform abstraction: Supports x86, ARM (Neon), and WebAssembly.
    • Zero runtime overhead: When using compile-time selection.
    • Familiar Syntax: Uses Intel intrinsic naming conventions (e.g., add_ps(a, b) instead of _mm_add_ps(a, b)).
    • Robust Fallbacks: Automatically provides scalar code fallbacks for platforms without SIMD support or for unsupported instructions.
    • Idiomatic Rust: Supports operator overloading (va + vb) and index-based lane access (v[1]).
    • #[no_std] support: Compatible with embedded or restricted environments.
  2. Understand SIMD math implementation and dispatch behavior

    master

    The math surface follows a layered implementation pattern designed for portability and performance:

    1. Portable SIMD: Most f32 families use portable SIMD by default.
    2. Backend Overrides: Specific functions (like f32 log2_u35) may include AVX2 overrides where benchmarks justify them. These are dispatched automatically without changing the public API.
    3. f64 Support: f64 families for log/exp, inverse trig, and binary-misc use SIMD defaults, though some f64 families may remain scalar-reference if SIMD defaults were not justified by local profiling.
    4. Scalar Patching: Exceptional semantics are handled via centralized scalar-lane patching.
  3. Generate SIMD functions with `simd_compiletime_generate!`

    master

    If you want to avoid runtime dispatch overhead and instead select the fastest instruction set available at compile time based on your target features, use the simd_compiletime_generate! macro.

    This macro generates two functions:

    1. [name]<S: Simd>: The generic version.
    2. [name]_compiletime: The fastest instruction set available for the given compile-time feature set.
  4. Run SIMD math benchmarks

    master

    To evaluate the performance of the math surface against different backends (such as scalar, sse2, sse41, avx2, and avx512), use the following Criterion benchmark commands:

    cargo bench --bench simd_math
    cargo bench --bench simd_math_remaining_baseline
  5. Generate SIMD functions with `simd_runtime_generate!`

    master

    To implement a function that automatically selects the fastest available SIMD instruction set at runtime, use the simd_runtime_generate! macro.

    When you wrap a function in this macro, SIMDeez generates several versions:

    • [name]<S: Simd>: The generic version.
    • [name]_scalar: A scalar fallback.
    • [name]_sse2, [name]_sse41, [name]_avx2, [name]_avx512, [name]_neon, [name]_wasm: Specific instruction set versions.
    • [name]_runtime_select: The version that performs runtime feature detection to call the fastest available implementation.

    Implementation Note: When writing the function body, you must operate in terms of the vector width using S::Vf32::WIDTH (or the relevant type's width constant) to ensure compatibility across different instruction sets.

    use simdeez::{prelude::*, simd_runtime_generate};
    
    simd_runtime_generate!(
        fn distance(x1: &[f32], y1: &[f32], x2: &[f32], y2: &[f32]) -> Vec<f32> {
            // ... implementation using S::Vf32::WIDTH and S::Vf32::load_from_slice ...
        }
    );
    
    // Usage:
    let distances = distance_runtime_select(slice1, slice2, slice3, slice4);
  6. How SIMDeez abstracts SIMD instruction sets

    master

    SIMDeez allows you to write a single function that can be compiled into multiple SIMD versions (SSE2, SSE41, AVX2, AVX-512, Neon, WebAssembly SIMD) and a scalar fallback.

    Key features include:

    • Width Abstraction: Unlike stdsimd, SIMDeez can abstract over differing SIMD widths.
    • Selection Modes: You can select the instruction set at runtime (using feature detection), at compile-time, or manually.
    • Syntax: Uses familiar Intel intrinsic naming conventions (e.g., _mm_add_ps becomes add_ps) and supports operator overloading (e.g., va + vb).
    • Compatibility: Works with #[no_std] projects and builds on stable Rust.
    • Math Support: Provides a pure-Rust SIMD math surface via extension traits for functions like sin, cos, exp, log2, etc.
    use simdeez::{prelude::*, simd_runtime_generate};
    
    // Use simd_runtime_generate! to create a version that uses runtime feature detection
    simd_runtime_generate!(
        fn distance(x1: &[f32], y1: &[f32], x2: &[f32], y2: &[f32]) -> Vec<f32> {
            // ... implementation using S::Vf32 ...
        }
    );
    
    // This generates:
    // - distance<S: Simd> (generic)
    // - distance_scalar (fallback)
    // - distance_sse2, distance_sse41, distance_avx2, etc.
    // - distance_runtime_select (picks fastest at runtime)
  7. Migrate from Simd trait to typed SIMD types

    master

    The Simd trait functions are deprecated. To ensure future compatibility and better type safety, you should stop using functions directly on the Simd trait (e.g., ceil_ps, load_epi32, sub_ps) and instead use the methods provided directly on the specific SIMD types:

    • Vf32 (32-bit floating point)
    • Vf64 (64-bit floating point)
    • Vi16 (16-bit integer)
    • Vi32 (32-bit integer)
    • Vi64 (64-bit integer)

    All deprecated Simd trait functions are marked unsafe and should be replaced by the corresponding method on the appropriate typed vector.

  8. Use fast math approximations for SSE2

    master

    When targeting SSE2, simdeez provides 'fast' versions of certain floating-point operations. These are significant performance boosts but come with constraints:

    • fast_round_ps / fast_ceil_ps / fast_floor_ps: These only work correctly on floating-point values small enough to fit within an i32.
    • fast_floor_pd: A faster version of floor for 64-bit floats.

    When using these, ensure your data range is compatible with the i32 constraint to avoid incorrect results.

  9. Use the SimdMathF32 and SimdMathF64 traits for full SIMD math support

    master

    The SimdMathF32 and SimdMathF64 traits provide a unified interface for all SIMD mathematical operations available in the library. Instead of importing individual math families, you can use these blanket traits to access a comprehensive suite of functions on any type that implements the underlying SIMD float traits.

    These traits aggregate several specialized math families:

    • SimdMathF32Core / SimdMathF64Core: Core mathematical operations.
    • SimdMathF32InverseTrig / SimdMathF64InverseTrig: Inverse trigonometric functions.
    • SimdMathF32Hyperbolic / SimdMathF64Hyperbolic: Hyperbolic functions.
    • SimdMathF32InverseHyperbolic / SimdMathF64InverseHyperbolic: Inverse hyperbolic functions.
    • SimdMathF32BinaryMisc / SimdMathF64BinaryMisc: Miscellaneous binary operations.
    use simdeez::{SimdMathF32, SimdFloat32};
    
    // Assuming 'v' is a type implementing SimdFloat32
    fn compute<T: SimdMathF32>(v: T) -> T {
        // All math functions from the sub-traits are available on 'v'
        v.sin() // Example of a core or trig function
    }
  10. Leverage FMA (Fused Multiply-Add) optimizations

    master

    The library provides FMA-style operations that automatically optimize based on the available instruction set:

    • If AVX2 is available, the library uses actual FMA instructions.
    • If AVX2 is not available, it replicates the behavior using a combination of multiply and add/sub instructions.

    This allows you to write code using FMA patterns once and receive optimal performance on both older and newer hardware. The deprecated trait versions are:

    • fmadd_ps / fmadd_pd (Fused Multiply-Add)
    • fnmadd_ps / fnmadd_pd (Fused Negative Multiply-Add)
    • fmsub_ps / fmsub_pd (Fused Multiply-Subtract)
    • fnmsub_ps / fnmsub_pd (Fused Negative Multiply-Subtract)
  11. Use SIMD math functions from `simdeez::math`

    master

    SIMDeez provides a pure-Rust SIMD math surface via simdeez::math (also re-exported in simdeez::prelude). This allows you to perform complex mathematical operations on SIMD vectors.

    Supported families include:

    • Core log/exp: log2_u35, exp2_u35, ln_u35, exp_u35
    • Trigonometric: sin_u35, cos_u35, tan_u35, asin_u35, acos_u35, atan_u35, atan2_u35
    • Hyperbolic: sinh_u35, cosh_u35, tanh_u35, asinh_u35, acosh_u35, atanh_u35
    • Miscellaneous: log10_u35, hypot_u35, fmod
    use simdeez::prelude::*;
    
    fn apply_math<S: Simd>(x: S::Vf32) -> S::Vf32 {
        let y = x.log2_u35();
        y.exp2_u35() + x.ln_u35() + x.exp_u35() + x.sin_u35() + x.cos_u35() + x.tan_u35()
    }
  12. Use SIMD math functions via simdeez::math and simdeez::prelude

    master

    SIMDeez provides a native, pure-Rust SIMD math surface. You can access these functions through the simdeez::math module or by importing simdeez::prelude.

    The available math families include:

    • Core log/exp: log2_u35, exp2_u35, ln_u35, exp_u35
    • Trigonometric and inverse trig: sin_u35, cos_u35, tan_u35, asin_u35, acos_u35, atan_u35, atan2_u35
    • Hyperbolic and inverse hyperbolic: sinh_u35, cosh_u35, tanh_u35, asinh_u35, acosh_u35, atanh_u35
    • Binary miscellaneous: log10_u35, hypot_u35, fmod