use std::fmt::{self, Debug, Formatter};
use g_math::fixed_point::{FixedPoint, FixedVector, FixedMatrix};
use log::debug;
use crate::constants;
#[derive(Clone, Copy, Debug)]
pub struct CpuFeatures {
pub avx: bool,
pub avx2: bool,
pub sse41: bool,
pub sse42: bool,
}
impl CpuFeatures {
#[cfg(target_arch = "x86_64")]
pub fn detect() -> Self {
Self {
avx: is_x86_feature_detected!("avx"),
avx2: is_x86_feature_detected!("avx2"),
sse41: is_x86_feature_detected!("sse4.1"),
sse42: is_x86_feature_detected!("sse4.2"),
}
}
#[cfg(not(target_arch = "x86_64"))]
pub fn detect() -> Self {
Self {
avx: false,
avx2: false,
sse41: false,
sse42: false,
}
}
}
pub struct SimdOptimization {
cpu_features: CpuFeatures,
}
impl SimdOptimization {
pub fn new() -> Self {
let cpu_features = CpuFeatures::detect();
debug!("Detected CPU features: {:?}", cpu_features);
Self {
cpu_features,
}
}
pub fn cpu_features(&self) -> CpuFeatures {
self.cpu_features
}
pub fn vector_multiply(&self, a: &FixedVector, b: &FixedVector) -> FixedVector {
let len = a.len().min(b.len());
let mut result = FixedVector::new(len);
for i in 0..len {
result[i] = a[i] * b[i];
}
result
}
pub fn matrix_vector_multiply(&self, m: &FixedMatrix, v: &FixedVector) -> FixedVector {
assert_eq!(m.cols(), v.len(), "Matrix columns must match vector length");
let mut result = FixedVector::new(m.rows());
for i in 0..m.rows() {
let mut sum = FixedPoint::from_int(0);
for j in 0..m.cols() {
sum = sum + (m.get(i, j) * v[j]);
}
result[i] = sum;
}
result
}
pub fn hyperbolic_distance(&self,
_disk_radius: FixedPoint,
p1: &FixedVector,
p2: &FixedVector) -> FixedPoint {
let euclidean_distance = p1.distance_to(p2);
let p1_norm_sq = p1.dot(p1);
let p2_norm_sq = p2.dot(p2);
let one = FixedPoint::from_int(1);
let two = FixedPoint::from_int(2);
let denominator = one - p1_norm_sq * p2_norm_sq;
if denominator.abs() < constants::epsilon() {
return two * constants::safe_atanh(constants::near_boundary());
}
let ratio = euclidean_distance / denominator.sqrt();
let safe_ratio = if ratio > constants::near_boundary() {
constants::near_boundary()
} else {
ratio
};
two * constants::safe_atanh(safe_ratio)
}
pub fn euclidean_distance(&self, v1: &FixedVector, v2: &FixedVector) -> FixedPoint {
v1.distance_to(v2)
}
pub fn vector_norm_squared(&self, v: &FixedVector) -> FixedPoint {
v.dot(v)
}
}
impl Debug for SimdOptimization {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("SimdOptimization")
.field("cpu_features", &self.cpu_features)
.finish()
}
}
pub struct PerformanceMonitor {
timings: std::collections::HashMap<String, Vec<std::time::Duration>>,
start_times: std::collections::HashMap<String, std::time::Instant>,
}
impl PerformanceMonitor {
pub fn new() -> Self {
Self {
timings: std::collections::HashMap::new(),
start_times: std::collections::HashMap::new(),
}
}
pub fn start(&mut self, operation: &str) {
self.start_times.insert(
operation.to_string(),
std::time::Instant::now()
);
}
pub fn stop(&mut self, operation: &str) {
if let Some(start_time) = self.start_times.remove(operation) {
let duration = start_time.elapsed();
self.timings
.entry(operation.to_string())
.or_insert_with(Vec::new)
.push(duration);
}
}
pub fn average(&self, operation: &str) -> Option<std::time::Duration> {
if let Some(timings) = self.timings.get(operation) {
if timings.is_empty() {
return None;
}
let total = timings.iter().sum::<std::time::Duration>();
let count = timings.len() as u32;
Some(total / count)
} else {
None
}
}
pub fn min(&self, operation: &str) -> Option<std::time::Duration> {
if let Some(timings) = self.timings.get(operation) {
timings.iter().min().copied()
} else {
None
}
}
pub fn max(&self, operation: &str) -> Option<std::time::Duration> {
if let Some(timings) = self.timings.get(operation) {
timings.iter().max().copied()
} else {
None
}
}
pub fn stats(&self, operation: &str) -> Option<(std::time::Duration, std::time::Duration, std::time::Duration)> {
if let (Some(avg), Some(min), Some(max)) = (
self.average(operation),
self.min(operation),
self.max(operation),
) {
Some((avg, min, max))
} else {
None
}
}
pub fn reset(&mut self) {
self.timings.clear();
self.start_times.clear();
}
pub fn all_stats(&self) -> std::collections::HashMap<String, (std::time::Duration, std::time::Duration, std::time::Duration)> {
let mut result = std::collections::HashMap::new();
for operation in self.timings.keys() {
if let Some(stats) = self.stats(operation) {
result.insert(operation.clone(), stats);
}
}
result
}
}
pub fn format_duration_us(duration: std::time::Duration) -> String {
format!("{} µs", duration.as_micros())
}
pub fn format_duration_ms(duration: std::time::Duration) -> String {
format!("{:.2} ms", duration.as_micros() as f64 / 1000.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_features_detection() {
let features = CpuFeatures::detect();
println!("Detected CPU features: {:?}", features);
}
#[test]
fn test_degenerate_distance_saturates_not_sentinel() {
let simd = SimdOptimization::new();
let boundary = FixedVector::from_f32_slice(&[0.99999, 0.0]);
let d = simd.hyperbolic_distance(FixedPoint::from_int(1), &boundary, &boundary);
let saturated = FixedPoint::from_int(2) * constants::safe_atanh(constants::near_boundary());
assert!(
d <= saturated + constants::epsilon(),
"degenerate distance {} exceeded the saturated model max {}",
d, saturated
);
}
#[test]
fn test_simd_vector_multiply() {
let simd = SimdOptimization::new();
let a = FixedVector::from_f32_slice(&[1.0, 2.0, 3.0, 4.0]);
let b = FixedVector::from_f32_slice(&[5.0, 6.0, 7.0, 8.0]);
let product = simd.vector_multiply(&a, &b);
let mut expected = FixedVector::new(4);
for i in 0..4 {
expected[i] = a[i] * b[i];
}
for i in 0..4 {
assert!((product[i] - expected[i]).abs() < constants::epsilon(),
"Mismatch at index {}", i);
}
}
#[test]
fn test_simd_matrix_vector_multiply() {
let simd = SimdOptimization::new();
let mut m = FixedMatrix::new(2, 3);
m.set(0, 0, FixedPoint::from_int(1));
m.set(0, 1, FixedPoint::from_int(2));
m.set(0, 2, FixedPoint::from_int(3));
m.set(1, 0, FixedPoint::from_int(4));
m.set(1, 1, FixedPoint::from_int(5));
m.set(1, 2, FixedPoint::from_int(6));
let v = FixedVector::from_f32_slice(&[7.0, 8.0, 9.0]);
let product = simd.matrix_vector_multiply(&m, &v);
let tolerance = FixedPoint::from_int(1) / FixedPoint::from_int(100);
assert!(product.len() == 2);
assert!((product[0] - FixedPoint::from_int(50)).abs() < tolerance);
assert!((product[1] - FixedPoint::from_int(122)).abs() < tolerance);
}
#[test]
fn test_hyperbolic_distance() {
let simd = SimdOptimization::new();
let origin = FixedVector::from_f32_slice(&[0.0, 0.0]);
let point = FixedVector::from_f32_slice(&[0.5, 0.0]);
let disk_radius = FixedPoint::from_int(1);
let distance = simd.hyperbolic_distance(disk_radius, &origin, &point);
let expected = FixedPoint::from_int(2) * constants::safe_atanh(constants::half());
let tolerance = FixedPoint::from_int(1) / FixedPoint::from_int(10);
assert!((distance - expected).abs() < tolerance);
}
#[test]
fn test_performance_monitor() {
let mut monitor = PerformanceMonitor::new();
monitor.start("test_op");
std::thread::sleep(std::time::Duration::from_millis(10));
monitor.stop("test_op");
let (avg, min, max) = monitor.stats("test_op").unwrap();
assert!(avg.as_millis() >= 9 && avg.as_millis() <= 20);
assert!(min.as_millis() >= 9 && min.as_millis() <= 20);
assert!(max.as_millis() >= 9 && max.as_millis() <= 20);
monitor.reset();
assert!(monitor.stats("test_op").is_none());
}
#[test]
fn test_format_duration() {
let duration = std::time::Duration::from_micros(1234);
assert_eq!(format_duration_us(duration), "1234 µs");
assert_eq!(format_duration_ms(duration), "1.23 ms");
}
}