pub mod distance;
pub mod quantization;
pub use distance::{
l2_distance, cosine_distance, dot_product, l2_distance_squared,
};
#[derive(Debug, Clone, Copy)]
pub struct CpuFeatures {
pub avx2: bool,
pub avx512f: bool,
pub sse42: bool,
}
impl CpuFeatures {
#[cfg(target_arch = "x86_64")]
pub fn detect() -> Self {
Self {
avx2: is_x86_feature_detected!("avx2"),
avx512f: is_x86_feature_detected!("avx512f"),
sse42: is_x86_feature_detected!("sse4.2"),
}
}
#[cfg(not(target_arch = "x86_64"))]
pub fn detect() -> Self {
Self {
avx2: false,
avx512f: false,
sse42: false,
}
}
pub fn description(&self) -> String {
let mut features = Vec::new();
if self.avx512f {
features.push("AVX-512");
}
if self.avx2 {
features.push("AVX2");
}
if self.sse42 {
features.push("SSE4.2");
}
if features.is_empty() {
"Scalar (no SIMD)".to_string()
} else {
features.join(", ")
}
}
}
pub fn cpu_features() -> CpuFeatures {
static FEATURES: std::sync::OnceLock<CpuFeatures> = std::sync::OnceLock::new();
*FEATURES.get_or_init(CpuFeatures::detect)
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn test_cpu_detection() {
let features = cpu_features();
println!("Detected CPU features: {}", features.description());
#[cfg(target_arch = "x86_64")]
{
println!("AVX2: {}", features.avx2);
println!("AVX-512: {}", features.avx512f);
println!("SSE4.2: {}", features.sse42);
}
#[cfg(not(target_arch = "x86_64"))]
{
assert!(!features.avx2);
assert!(!features.avx512f);
assert!(!features.sse42);
}
}
#[test]
fn test_feature_description() {
let features = cpu_features();
let desc = features.description();
assert!(!desc.is_empty());
println!("Features: {}", desc);
}
}