use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SimdLevel {
Scalar,
Sse2,
Sse41,
Avx2,
Avx512,
Neon,
Sve,
WasmSimd128,
}
impl fmt::Display for SimdLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SimdLevel::Scalar => write!(f, "Scalar"),
SimdLevel::Sse2 => write!(f, "SSE2"),
SimdLevel::Sse41 => write!(f, "SSE4.1"),
SimdLevel::Avx2 => write!(f, "AVX2"),
SimdLevel::Avx512 => write!(f, "AVX-512"),
SimdLevel::Neon => write!(f, "NEON"),
SimdLevel::Sve => write!(f, "SVE"),
SimdLevel::WasmSimd128 => write!(f, "WASM SIMD128"),
}
}
}
#[derive(Debug, Clone)]
pub struct SimdCapabilities {
pub has_sse2: bool,
pub has_sse41: bool,
pub has_avx2: bool,
pub has_avx512f: bool,
pub has_fma: bool,
pub has_neon: bool,
pub has_sve: bool,
pub has_wasm_simd128: bool,
pub max_vector_width_bytes: usize,
pub best_level: SimdLevel,
}
impl SimdCapabilities {
pub fn detect() -> Self {
let mut caps = SimdCapabilities {
has_sse2: false,
has_sse41: false,
has_avx2: false,
has_avx512f: false,
has_fma: false,
has_neon: false,
has_sve: false,
has_wasm_simd128: false,
max_vector_width_bytes: 0,
best_level: SimdLevel::Scalar,
};
#[cfg(target_arch = "x86_64")]
{
caps.detect_x86_64();
}
#[cfg(target_arch = "aarch64")]
{
caps.detect_aarch64();
}
#[cfg(target_arch = "wasm32")]
{
caps.detect_wasm();
}
if caps.max_vector_width_bytes == 0 {
caps.max_vector_width_bytes = 4;
}
caps
}
#[cfg(target_arch = "x86_64")]
fn detect_x86_64(&mut self) {
self.has_sse2 = true;
self.best_level = SimdLevel::Sse2;
self.max_vector_width_bytes = 16;
if is_x86_feature_detected!("sse4.1") {
self.has_sse41 = true;
self.best_level = SimdLevel::Sse41;
}
if is_x86_feature_detected!("fma") {
self.has_fma = true;
}
if is_x86_feature_detected!("avx2") {
self.has_avx2 = true;
self.best_level = SimdLevel::Avx2;
self.max_vector_width_bytes = 32; }
if is_x86_feature_detected!("avx512f") {
self.has_avx512f = true;
self.best_level = SimdLevel::Avx512;
self.max_vector_width_bytes = 64; }
}
#[cfg(target_arch = "aarch64")]
fn detect_aarch64(&mut self) {
self.has_neon = true;
self.best_level = SimdLevel::Neon;
self.max_vector_width_bytes = 16;
#[cfg(target_feature = "sve")]
{
self.has_sve = true;
self.best_level = SimdLevel::Sve;
self.max_vector_width_bytes = 32; }
}
#[cfg(target_arch = "wasm32")]
fn detect_wasm(&mut self) {
#[cfg(target_feature = "simd128")]
{
self.has_wasm_simd128 = true;
self.best_level = SimdLevel::WasmSimd128;
self.max_vector_width_bytes = 16; }
}
pub fn f32_lane_count(&self) -> usize {
self.max_vector_width_bytes / std::mem::size_of::<f32>()
}
pub fn has_simd(&self) -> bool {
self.best_level != SimdLevel::Scalar
}
pub fn summary(&self) -> String {
let mut features = Vec::new();
if self.has_sse2 {
features.push("SSE2");
}
if self.has_sse41 {
features.push("SSE4.1");
}
if self.has_avx2 {
features.push("AVX2");
}
if self.has_avx512f {
features.push("AVX-512F");
}
if self.has_fma {
features.push("FMA");
}
if self.has_neon {
features.push("NEON");
}
if self.has_sve {
features.push("SVE");
}
if self.has_wasm_simd128 {
features.push("WASM SIMD128");
}
if features.is_empty() {
"Scalar only (no SIMD)".to_string()
} else {
format!(
"Best: {} | Features: {} | Vector width: {} bytes ({} f32 lanes)",
self.best_level,
features.join(", "),
self.max_vector_width_bytes,
self.f32_lane_count()
)
}
}
}
impl fmt::Display for SimdCapabilities {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.summary())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_returns_valid_capabilities() {
let caps = SimdCapabilities::detect();
assert!(caps.max_vector_width_bytes >= 4);
assert!(caps.f32_lane_count() >= 1);
}
#[test]
fn test_summary_not_empty() {
let caps = SimdCapabilities::detect();
let summary = caps.summary();
assert!(!summary.is_empty());
}
#[test]
fn test_display_impl() {
let caps = SimdCapabilities::detect();
let display = format!("{caps}");
assert!(!display.is_empty());
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_x86_64_has_sse2() {
let caps = SimdCapabilities::detect();
assert!(caps.has_sse2);
assert!(caps.has_simd());
}
#[cfg(target_arch = "aarch64")]
#[test]
fn test_aarch64_has_neon() {
let caps = SimdCapabilities::detect();
assert!(caps.has_neon);
assert!(caps.has_simd());
}
}