Skip to main content

Level

Enum Level 

Source
#[non_exhaustive]
pub enum Level { Fallback(Fallback), Sse2(Sse2), Sse4_2(Sse4_2), Avx512(Avx512), Avx2(Avx2), }
Expand description

The level enum with the specific SIMD capabilities available.

The contained values serve as a proof that the associated target feature is available.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Fallback(Fallback)

Available on crate feature force_support_fallback or neither AArch64 and target feature neon, nor target feature fxsr and target feature sse2 and (x86 or x86-64), nor WebAssembly and target feature simd128 only.

Scalar fallback level, i.e. no supported SIMD features are to be used.

This variant is absent on targets that supports a higher baseline (aarch64-*, i686-*, x86_64-*, WASM with SIMD) unless the force_support_fallback Cargo feature is enabled. Instead of matching on this variant, call is_fallback which is always available.

This can be created with Level::fallback.

§

Sse2(Sse2)

Available on x86 or x86-64 only.

The SSE2 instruction set on (32 and 64 bit) x86.

This is the baseline for i686 and x86-64 targets.

§

Sse4_2(Sse4_2)

Available on x86 or x86-64 only.

The SSE4.2 instruction set on (32 and 64 bit) x86, plus popcnt and cmpxchg16b. Also known as x86-64-v2.

All production CPUs with SSE4.2 also support the other two extensions, so it is safe to require them.

§

Avx512(Avx512)

Available on x86 or x86-64 only.

Ice Lake-class AVX-512 on (32 and 64 bit) x86.

§

Avx2(Avx2)

Available on x86 or x86-64 only.

The x86-64-v3 instruction set on (32 and 64 bit) x86, including AVX2 and FMA.

Implementations§

Source§

impl Level

Source

pub fn new() -> Self

Available on WebAssembly or crate feature std only.

Return the best SIMD level available on the CPU. This value should be passed to dispatch.

On x86 and x86-64 targets, this detects the available CPU features on the first call and caches the result. Other targets return their strongest statically supported level. This may change in the future if runtime-detected levels for other platforms are added.

This function requires the standard library on targets other than wasm32. On wasm32, the available level is known statically, so the standard library isn’t required.

On x86-64, it is sometimes possible to detect the available features on #[no_std] by parsing the output of cpuid instruction, but this function does not do that. If you do this, you can create the SIMD token via assume_supported and then get the level from it.

Libraries that use SIMD on #[no_std] should let the user pass the appropriate SIMD level the user detected through other means (e.g. cpuid), to avoid using the fallback level when a better SIMD level is available in hardware.

Examples found in repository?
examples/srgb.rs (line 89)
88fn main() {
89    let level = Level::new();
90    let rgba = [0.1, -0.2, 0.001, 0.4];
91    let srgb = dispatch!(level, simd=> to_srgb(simd, rgba));
92    println!("{srgb:?}");
93}
More examples
Hide additional examples
examples/disable_avx2_for_one_function.rs (line 39)
38fn main() {
39    let level = Level::new();
40    let inp = [
41        0.1, -0.2, 0.001, 0.4, 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.,
42    ];
43    let mut out = [0.; 16];
44    dispatch!(level, simd => disable_avx2(simd, &inp, &mut out));
45
46    println!("{out:?}");
47}
examples/sigmoid.rs (line 37)
36fn main() {
37    let level = Level::new();
38    let inp = [
39        0.1, -0.2, 0.001, 0.4, 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14.,
40    ];
41    let mut out = [0.; 18];
42    // dispatch! selects the best implementation for the CPU we're running on
43    dispatch!(level, simd => sigmoid(simd, &inp, &mut out));
44
45    println!("{out:?}");
46}
examples/sigmoid_generic.rs (line 44)
43fn main() {
44    let level = Level::new();
45    let input_f32 = [
46        0.1_f32, -0.2, 0.001, 0.4, 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13.,
47    ];
48    let input_f64 = input_f32.map(f64::from);
49    let mut output_f32 = [0.0; 17];
50    let mut output_f64 = [0.0; 17];
51
52    sigmoid(level, &input_f32, &mut output_f32);
53    sigmoid(level, &input_f64, &mut output_f64);
54
55    println!("f32: {output_f32:?}");
56    println!("f64: {output_f64:?}");
57}
examples/gain_generic.rs (line 15)
14fn main() {
15    let level = Level::new();
16    dispatch!(level, simd => {
17        // f32 vectors can be any length
18        let samples = f32x4::from_slice(simd, &[0.1, -0.2, 0.3, -0.4]);
19        let output = apply_gain(samples, 0.5);
20        println!("f32x4: {output:?}");
21
22        let samples = f32x16::from_slice(
23            simd,
24            &[
25                0.1, -0.2, 0.3, -0.4, 0.5, -0.6, 0.7, -0.8,
26                0.9, -1.0, 1.1, -1.2, 1.3, -1.4, 1.5, -1.6,
27            ],
28        );
29        let output = apply_gain(samples, 0.5);
30        println!("f32x16: {output:?}");
31
32        // f64 vectors work too through the same helper
33        let samples = f64x2::from_slice(simd, &[0.1, -0.2]);
34        let output = apply_gain(samples, 0.5);
35        println!("f64x2: {output:?}");
36    });
37}
Source

pub fn try_detect() -> Option<Self>

Get the target feature level suitable for this run.

Should be used in libraries if they wish to handle the case where target features cannot be detected at runtime. Most users should prefer new. This is discussed in more detail in new’s documentation.

Source

pub fn is_fallback(self) -> bool

Check whether this is the Fallback level; that is, whether no better feature level could be statically or dynamically detected. This is useful if there’s a scalarized version of your algorithm that runs faster if SIMD isn’t supported.

This method is always available, even when the fallback backend is not compiled. In that case, it always returns false.

Source

pub fn as_sse2(self) -> Option<Sse2>

Available on x86 or x86-64 only.

If this is a proof that SSE2 (or better) is available, access that instruction set.

See Sse2::assume_supported for the exact list of CPU features this token enables.

This method should be preferred over matching against the Sse2 variant of self, because if the CPU supports a superset of SSE2 (e.g. SSE4.2, AVX2, or AVX-512), this method will return the SSE2 token even if that “better” instruction set is available.

This can be used in combination with the kernel macro to safely access level-specific SIMD intrinsics.

Source

pub fn as_sse4_2(self) -> Option<Sse4_2>

Available on x86 or x86-64 only.

If this is a proof that x86-64-v2 feature set (or better) is available, access that instruction set.

See Sse4_2::assume_supported for the exact list of CPU features this token enables.

This method should be preferred over matching against the Sse4_2 variant of self, because if the CPU supports a superset of SSE4.2 (e.g. AVX2 or AVX-512), this method will return the SSE4.2 token even if that “better” instruction set is available.

This can be used in combination with the kernel macro to safely access level-specific SIMD intrinsics.

Examples found in repository?
examples/disable_avx2_for_one_function.rs (line 16)
11fn disable_avx2<S: Simd>(simd: S, x: &[f32], out: &mut [f32]) {
12    let level = simd.level();
13    match level {
14        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
15        // Downgrade AVX2 to SSE4.2 when calling `sigmoid()`
16        Level::Avx2(_) => sigmoid(level.as_sse4_2().unwrap(), x, out),
17        _ => sigmoid(simd, x, out),
18    }
19}
More examples
Hide additional examples
examples/srgb.rs (line 55)
53fn copy_alpha<S: Simd>(a: f32x4<S>, b: f32x4<S>) -> f32x4<S> {
54    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
55    if let Some(sse4_2) = a.simd.level().as_sse4_2() {
56        return copy_alpha_sse4_2(sse4_2, a.into(), b.into()).simd_into(a.simd);
57    }
58
59    #[cfg(target_arch = "aarch64")]
60    if let Some(neon) = a.simd.level().as_neon() {
61        return copy_alpha_neon(neon, a.into(), b.into()).simd_into(a.simd);
62    }
63
64    let mut result = a;
65    result[3] = b[3];
66    result
67}
Source

pub fn as_avx2(self) -> Option<Avx2>

Available on x86 or x86-64 only.

If this is a proof that the x86-64-v3 feature set (or better) is available, access that instruction set.

See Avx2::assume_supported for the exact list of CPU features this token enables.

This method should be preferred over matching against the Avx2 variant of self, because if the CPU supports a superset of AVX2 (e.g. AVX-512), this method will return the AVX2 token even if that “better” instruction set is available.

This can be used in combination with the kernel macro to safely access level-specific SIMD intrinsics.

Source

pub fn as_avx512(self) -> Option<Avx512>

Available on x86 or x86-64 only.

If this is a proof that the Ice Lake AVX-512 feature set is available, access that instruction set.

See Avx512::assume_supported for the exact list of CPU features this token enables.

This can be used in combination with the kernel macro to safely access level-specific SIMD intrinsics.

Source

pub const fn baseline() -> Self

Get the strongest statically supported SIMD level.

That is, if your compilation run ambiently declares that a target feature is enabled, this method will take that into account. In most cases, you should use Level::new or Level::try_detect. This method is mainly useful for libraries, where:

  1. Your crate features request that you not use the standard library, i.e. doesn’t enable your "std" crate feature reason (so you can’t use Level::new and Level::try_detect returns None); AND
  2. Your caller does not provide a Level; AND
  3. The library doesn’t want to panic when it can’t find a SIMD level.

Note that in these cases, the library should clearly inform the integrator that it is using a fallback and so not getting optimal performance (e.g. by panicking if debug_assertions are enabled, and emitting a log with the “error” level otherwise). The messages given should also provide actionable fixes, such as pointing to the entry-point which provides a Level, or your "std" feature.

Note that this is unaffected by the force-support-fallback feature. Instead, you should use Level::fallback if you require the fallback level.

Source

pub const fn fallback() -> Self

Available on crate feature force_support_fallback only.

Create a scalar fallback level, which uses no SIMD instructions.

This is primarily intended for tests; most users should prefer Level::new or Level::baseline.

Note that enabling the scalar fallback does not mean that the fallback branch will not contain SIMD instructions. This is because the “ambient” compilation environment has SIMD instructions available, which may be utilised by LLVM to auto-vectorise that path.

Trait Implementations§

Source§

impl Clone for Level

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Level

Source§

impl Debug for Level

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Level

§

impl RefUnwindSafe for Level

§

impl Send for Level

§

impl Sync for Level

§

impl Unpin for Level

§

impl UnsafeUnpin for Level

§

impl UnwindSafe for Level

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(_simd: S, value: T) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.