#vector #abstraction #simd #operations #cross-platform #explicit #idiomatic

no-std generic-simd

编写显式跨平台SIMD操作的安全且惯用的零成本抽象

1 个不稳定版本

0.1.0 2020年9月7日

#28 in #explicit

MIT/Apache

110KB
3K SLoC

generic-simd

Build Status Rustc Version 1.42+ License Crates.io Rust Documentation

generic-simd提供了编写显式跨平台SIMD操作的安全且惯用的零成本抽象。

许可证

generic-simd以MIT许可证和Apache许可证(版本2.0)的条款进行分发。

有关详细信息,请参阅LICENSE-APACHELICENSE-MIT


lib.rs:

generic-simd提供了编写显式跨平台SIMD操作的安全且惯用的零成本抽象。

支持的架构

所有架构都通过标量回退支持,但以下指令集也得到支持

  • SSE4.1 (x86/x86-64)
  • AVX (x86/x86-64)
  • NEON (aarch64,需nightly cargo功能)
  • SIMD128 (wasm32,需nightly cargo功能和simd128目标功能)

各种架构特定类型在arch模块中可用。

抽象

通过vector模块中的特性行提供向量抽象。使用这些特性行的泛型能够利用任何支持的指令集。

以下示例执行输入切片的向量加速求和

use generic_simd::{
    arch::Token,
    dispatch,
    scalar::ScalarExt,
    slice::SliceExt,
    vector::NativeVector,
};

// This function provides a generic implementation for any instruction set.
// Here we use the "native" vector type, i.e. the widest vector directly supported by the
// architecture.
#[inline]
fn sum_impl<T>(token: T, input: &[f32]) -> f32
where
    T: Token,
    f32: ScalarExt<T> + core::iter::Sum<NativeVector<f32, T>>,
{
    // Use aligned loads in this example, which may be better on some architectures.
    let (start, vectors, end) = input.align_native(token);

    // Sum across the vector lanes, plus the unaligned portions
    vectors.iter().copied().sum::<f32>() + start.iter().chain(end).sum::<f32>()
}

// This function selects the best instruction set at runtime.
// The "dispatch" macro compiles this function for each supported architecture.
#[dispatch(token)]
fn sum(input: &[f32]) -> f32 {
    sum_impl(token, input)
}

assert_eq!(sum(&[1f32; 10]), 10.);

向量适配器

不同的指令集提供不同宽度的向量,因此提供了适配器来创建特定宽度的向量,而不考虑架构。这些适配器在shim模块中可用。

例如,以下函数使用4个f64的数组执行数组数组结构操作,而不考虑指令集

use generic_simd::{
    arch::Token,
    dispatch,
    scalar::Scalar,
    slice::Slice,
    vector::{Signed, Vector, width},
};

// Equivalent to an array of 4 2-dimensional coordinates,
// but with a vectorizable memory layout.
struct Coordinates {
    x: [f64; 4],
    y: [f64; 4],
}

// A generic mean implementation for any instruction set.
fn mean_impl<T>(token: T, input: &[Coordinates]) -> (f64, f64)
where
    T: Token,
    f64: Scalar<T, width::W4>,
    <f64 as Scalar<T, width::W4>>::Vector: Signed,
{
    let mut xsum = f64::zeroed(token);
    let mut ysum = f64::zeroed(token);

    for Coordinates { x, y } in input {
        // read the arrays into vectors
        xsum += x.read(token);
        ysum += y.read(token);
    }

    // sum across the vector lanes
    (
        xsum.iter().sum::<f64>() / (input.len() * 4) as f64,
        ysum.iter().sum::<f64>() / (input.len() * 4) as f64,
    )
}

// Selects the best instruction set at runtime.
#[dispatch(token)]
fn mean(input: &[Coordinates]) -> (f64, f64) {
    mean_impl(token, input)
}

依赖项

~1.5MB
~36K SLoC