use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Backend {
Avx512,
Avx2Fma,
Neon,
Portable,
}
impl fmt::Display for Backend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Backend::Avx512 => "avx512",
Backend::Avx2Fma => "avx2+fma",
Backend::Neon => "neon",
Backend::Portable => "portable",
};
f.write_str(s)
}
}
#[must_use]
pub fn dense_backend(len: usize) -> Backend {
#[cfg(target_arch = "x86_64")]
{
if len >= crate::dense::MIN_DIM_AVX512 && std::arch::is_x86_feature_detected!("avx512f") {
return Backend::Avx512;
}
if len >= crate::MIN_DIM_SIMD
&& std::arch::is_x86_feature_detected!("avx2")
&& std::arch::is_x86_feature_detected!("fma")
{
return Backend::Avx2Fma;
}
}
#[cfg(target_arch = "aarch64")]
{
if len >= crate::MIN_DIM_SIMD {
return Backend::Neon;
}
}
let _ = len;
Backend::Portable
}
#[must_use]
pub fn slot_backend(len: usize) -> Backend {
#[cfg(target_arch = "x86_64")]
{
if len >= crate::slot::MIN_SLOTS_AVX512 && std::arch::is_x86_feature_detected!("avx512f") {
return Backend::Avx512;
}
if len >= crate::slot::MIN_SLOTS_SIMD && std::arch::is_x86_feature_detected!("avx2") {
return Backend::Avx2Fma;
}
}
#[cfg(target_arch = "aarch64")]
{
if len >= crate::slot::MIN_SLOTS_SIMD {
return Backend::Neon;
}
}
let _ = len;
Backend::Portable
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_vectors_are_portable() {
assert_eq!(dense_backend(1), Backend::Portable);
assert_eq!(dense_backend(15), Backend::Portable);
assert_eq!(slot_backend(1), Backend::Portable);
}
#[test]
fn simd_lengths_dispatch_on_supported_archs() {
let b = dense_backend(768);
#[cfg(target_arch = "aarch64")]
assert_eq!(b, Backend::Neon);
#[cfg(target_arch = "x86_64")]
assert_ne!(b, Backend::Neon);
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
assert_eq!(b, Backend::Portable);
}
#[test]
fn display_names_are_stable() {
assert_eq!(Backend::Avx512.to_string(), "avx512");
assert_eq!(Backend::Portable.to_string(), "portable");
}
}