#[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
Fallback(Fallback)
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)
The SSE2 instruction set on (32 and 64 bit) x86.
This is the baseline for i686 and x86-64 targets.
Sse4_2(Sse4_2)
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)
Ice Lake-class AVX-512 on (32 and 64 bit) x86.
Avx2(Avx2)
The x86-64-v3 instruction set on (32 and 64 bit) x86, including AVX2 and FMA.
Implementations§
Source§impl Level
impl Level
Sourcepub fn new() -> Self
Available on WebAssembly or crate feature std only.
pub fn new() -> Self
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?
More examples
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}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}Sourcepub fn try_detect() -> Option<Self>
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.
Sourcepub fn is_fallback(self) -> bool
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.
Sourcepub fn as_sse2(self) -> Option<Sse2>
Available on x86 or x86-64 only.
pub fn as_sse2(self) -> Option<Sse2>
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.
Sourcepub fn as_sse4_2(self) -> Option<Sse4_2>
Available on x86 or x86-64 only.
pub fn as_sse4_2(self) -> Option<Sse4_2>
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?
More examples
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}Sourcepub fn as_avx2(self) -> Option<Avx2>
Available on x86 or x86-64 only.
pub fn as_avx2(self) -> Option<Avx2>
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.
Sourcepub fn as_avx512(self) -> Option<Avx512>
Available on x86 or x86-64 only.
pub fn as_avx512(self) -> Option<Avx512>
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.
Sourcepub const fn baseline() -> Self
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:
- 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 useLevel::newandLevel::try_detectreturnsNone); AND - Your caller does not provide a
Level; AND - 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.
Sourcepub const fn fallback() -> Self
Available on crate feature force_support_fallback only.
pub const fn fallback() -> Self
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.