pub mod arch;
pub mod matmul;
pub mod ops;
pub mod quant;
use std::sync::OnceLock;
use crate::simd::quant::QuantizedTensor;
static SIMD_BACKEND: OnceLock<Box<dyn SimdBackend>> = OnceLock::new();
#[derive(Debug, Clone, Copy, Default)]
pub struct CpuFeatures {
pub avx: bool,
pub avx2: bool,
pub avx512f: bool,
pub avx512bw: bool,
pub avx512vl: bool,
pub fma: bool,
pub neon: bool,
pub sve: bool,
}
impl CpuFeatures {
#[cfg(target_arch = "x86_64")]
pub fn detect() -> Self {
Self {
avx: std::arch::is_x86_feature_detected!("avx"),
avx2: std::arch::is_x86_feature_detected!("avx2"),
avx512f: std::arch::is_x86_feature_detected!("avx512f"),
avx512bw: std::arch::is_x86_feature_detected!("avx512bw"),
avx512vl: std::arch::is_x86_feature_detected!("avx512vl"),
fma: std::arch::is_x86_feature_detected!("fma"),
neon: false,
sve: false,
}
}
#[cfg(target_arch = "aarch64")]
pub fn detect() -> Self {
Self {
avx: false,
avx2: false,
avx512f: false,
avx512bw: false,
avx512vl: false,
fma: false,
neon: std::arch::is_aarch64_feature_detected!("neon"),
sve: std::arch::is_aarch64_feature_detected!("sve"),
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
pub fn detect() -> Self {
Self::default()
}
pub fn has_avx2_fma(&self) -> bool {
self.avx2 && self.fma
}
pub fn has_avx512(&self) -> bool {
self.avx512f && self.avx512bw && self.avx512vl
}
pub fn best_level(&self) -> &'static str {
if self.has_avx512() {
"AVX-512"
} else if self.has_avx2_fma() {
"AVX2+FMA"
} else if self.neon {
"NEON"
} else {
"Scalar"
}
}
}
pub trait SimdBackend: Send + Sync {
fn name(&self) -> &'static str;
fn required_features(&self) -> CpuFeatures;
fn q4_gemv(
&self,
weight: &QuantizedTensor,
x: &[f32],
out: &mut [f32],
);
fn q4_gemm(
&self,
weight: &QuantizedTensor,
x: &[f32],
out: &mut [f32],
m: usize, k: usize, n: usize, );
fn rms_norm(&self, x: &[f32], weight: &[f32], eps: f32, out: &mut [f32]);
fn rms_norm_inplace(&self, x: &mut [f32], weight: &[f32], eps: f32);
fn fused_swiglu(&self, gate: &[f32], up: &[f32], out: &mut [f32]);
fn apply_rope(
&self,
q: &mut [f32],
k: &mut [f32],
cos: &[f32],
sin: &[f32],
head_dim: usize,
num_heads: usize,
);
fn softmax(&self, x: &[f32], out: &mut [f32]);
fn softmax_inplace(&self, x: &mut [f32]);
fn silu(&self, x: &[f32], out: &mut [f32]);
fn mul(&self, a: &[f32], b: &[f32], out: &mut [f32]);
fn add(&self, a: &[f32], b: &[f32], out: &mut [f32]);
fn dot(&self, a: &[f32], b: &[f32]) -> f32;
}
pub fn init_simd_backend() -> &'static dyn SimdBackend {
SIMD_BACKEND.get_or_init(|| {
let features = CpuFeatures::detect();
#[cfg(target_arch = "x86_64")]
{
if features.has_avx512() {
tracing::info!("SIMD backend: AVX-512");
return Box::new(arch::avx512::Avx512Backend::new());
}
if features.has_avx2_fma() {
tracing::info!("SIMD backend: AVX2+FMA");
return Box::new(arch::avx2::Avx2Backend::new());
}
}
#[cfg(target_arch = "aarch64")]
{
if features.neon {
tracing::info!("SIMD backend: NEON");
return Box::new(arch::neon::NeonBackend::new());
}
}
tracing::info!("SIMD backend: Scalar (fallback)");
Box::new(arch::scalar::ScalarBackend::new())
}).as_ref()
}
pub fn get_simd_backend() -> &'static dyn SimdBackend {
init_simd_backend()
}
pub fn is_simd_available() -> bool {
SIMD_BACKEND.get().is_some()
}
pub fn cpu_features() -> CpuFeatures {
CpuFeatures::detect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_feature_detection() {
let features = CpuFeatures::detect();
println!("CPU Features: {:?}", features);
println!("Best SIMD level: {}", features.best_level());
#[cfg(target_arch = "x86_64")]
{
println!("AVX: {}, AVX2: {}, FMA: {}", features.avx, features.avx2, features.fma);
}
#[cfg(target_arch = "aarch64")]
{
assert!(features.neon, "NEON should be available on AArch64");
}
}
#[test]
fn test_simd_backend_init() {
let backend = get_simd_backend();
println!("Initialized backend: {}", backend.name());
let features = CpuFeatures::detect();
let name = backend.name();
if features.has_avx512() {
assert!(name.contains("512") || name == "Scalar");
} else if features.has_avx2_fma() {
assert!(name.contains("AVX2") || name == "Scalar");
}
}
}