1use crate::error::{InterpolateError, InterpolateResult};
31use crate::simd_optimized::{get_simd_config, simd_distance_matrix, simd_rbf_evaluate, RBFKernel};
32use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2};
33use scirs2_core::numeric::{Float, FromPrimitive, Zero};
34use scirs2_core::simd_ops::PlatformCapabilities;
35use std::collections::HashMap;
36use std::fmt::{Debug, Display};
37use std::time::{Duration, Instant};
38
39#[cfg(feature = "simd")]
40use crate::spatial::simd_enhancements::AdvancedSimdOps;
41
42pub struct SimdPerformanceValidator<T: InterpolationFloat> {
44 config: SimdValidationConfig,
46 results: Vec<ValidationResult<T>>,
48 #[allow(dead_code)]
50 baselines: HashMap<String, PerformanceBaseline<T>>,
51 platform_caps: PlatformCapabilities,
53 session_info: ValidationSession,
55}
56
57#[derive(Debug, Clone)]
59pub struct SimdValidationConfig {
60 pub test_sizes: Vec<usize>,
62 pub timing_iterations: usize,
64 pub warmup_iterations: usize,
66 pub correctness_tolerance: f64,
68 pub test_all_instruction_sets: bool,
70 pub validate_memory_alignment: bool,
72 pub run_regression_detection: bool,
74 pub max_benchmark_time: f64,
76}
77
78impl Default for SimdValidationConfig {
79 fn default() -> Self {
80 Self {
81 test_sizes: vec![100, 1_000, 10_000, 100_000, 1_000_000],
82 timing_iterations: 50,
83 warmup_iterations: 10,
84 correctness_tolerance: 1e-12,
85 test_all_instruction_sets: true,
86 validate_memory_alignment: true,
87 run_regression_detection: true,
88 max_benchmark_time: 30.0, }
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct ValidationSession {
96 pub start_time: Instant,
98 pub cpu_info: CpuInfo,
100 pub os_info: String,
102 pub build_info: BuildInfo,
104}
105
106#[derive(Debug, Clone)]
108pub struct CpuInfo {
109 pub brand: String,
111 pub architecture: String,
113 pub logical_cores: usize,
115 pub physical_cores: usize,
117 pub cache_sizes: Vec<usize>,
119 pub base_frequency: Option<f64>,
121}
122
123#[derive(Debug, Clone)]
125pub struct BuildInfo {
126 pub rustc_version: String,
128 pub target_triple: String,
130 pub opt_level: String,
132 pub debug_assertions: bool,
134}
135
136#[derive(Debug, Clone)]
138pub struct ValidationResult<T: InterpolationFloat> {
139 pub test_name: String,
141 pub datasize: usize,
143 pub operation: SimdOperation,
145 pub instruction_set: String,
147 pub correctness: CorrectnessResult<T>,
149 pub performance: PerformanceResult,
151 pub memory_usage: MemoryUsageResult,
153 pub timestamp: Instant,
155}
156
157#[derive(Debug, Clone)]
159pub enum SimdOperation {
160 RbfEvaluation { kernel: RBFKernel, epsilon: f64 },
162 DistanceMatrix,
164 BSplineEvaluation { degree: usize },
166 KnnSearch { k: usize },
168 RangeSearch { radius: f64 },
170 BatchEvaluation { batch_size: usize },
172}
173
174#[derive(Debug, Clone)]
176pub struct CorrectnessResult<T: InterpolationFloat> {
177 pub is_correct: bool,
179 pub max_absolute_error: T,
181 pub max_relative_error: T,
183 pub mean_absolute_error: T,
185 pub error_std_dev: T,
187 pub num_values_compared: usize,
189}
190
191#[derive(Debug, Clone)]
193pub struct PerformanceResult {
194 pub simd_timing: TimingStatistics,
196 pub scalar_timing: TimingStatistics,
198 pub speedup: f64,
200 pub simd_throughput: f64,
202 pub scalar_throughput: f64,
204 pub efficiency_gain: f64,
206}
207
208#[derive(Debug, Clone)]
210pub struct TimingStatistics {
211 pub min_time: Duration,
213 pub max_time: Duration,
215 pub mean_time: Duration,
217 pub median_time: Duration,
219 pub std_dev: Duration,
221 pub p95_time: Duration,
223 pub p99_time: Duration,
225}
226
227#[derive(Debug, Clone)]
229pub struct MemoryUsageResult {
230 pub peak_memory_bytes: usize,
232 pub alignment_efficiency: f64,
234 pub cache_miss_rate: f64,
236 pub bandwidth_utilization: f64,
238}
239
240#[derive(Debug, Clone)]
242pub struct PerformanceBaseline<T: InterpolationFloat> {
243 pub expected_speedup: f64,
245 pub speedup_tolerance: f64,
247 pub expected_throughput: f64,
249 pub expected_correctness: CorrectnessResult<T>,
251 pub baseline_date: String,
253 pub platform_signature: String,
255}
256
257pub trait InterpolationFloat:
259 Float + FromPrimitive + Debug + Display + Zero + Copy + Send + Sync + PartialOrd + 'static
260{
261 fn default_tolerance() -> Self;
263
264 fn max_relative_error() -> Self;
266}
267
268impl InterpolationFloat for f32 {
269 fn default_tolerance() -> Self {
270 1e-6
271 }
272
273 fn max_relative_error() -> Self {
274 1e-5
275 }
276}
277
278impl InterpolationFloat for f64 {
279 fn default_tolerance() -> Self {
280 1e-12
281 }
282
283 fn max_relative_error() -> Self {
284 1e-11
285 }
286}
287
288impl<T: InterpolationFloat + scirs2_core::simd_ops::SimdUnifiedOps + ordered_float::FloatCore>
289 SimdPerformanceValidator<T>
290{
291 pub fn new(config: SimdValidationConfig) -> Self {
293 let platform_caps = PlatformCapabilities::detect();
294 let session_info = ValidationSession {
295 start_time: Instant::now(),
296 cpu_info: Self::detect_cpu_info(),
297 os_info: Self::detect_os_info(),
298 build_info: Self::detect_build_info(),
299 };
300
301 Self {
302 config,
303 results: Vec::new(),
304 baselines: HashMap::new(),
305 platform_caps,
306 session_info,
307 }
308 }
309
310 pub fn run_comprehensive_validation(&mut self) -> InterpolateResult<ValidationSummary<T>> {
312 println!("Starting comprehensive SIMD performance validation...");
313 println!(
314 "Platform: {} - {}",
315 self.session_info.cpu_info.brand, self.session_info.cpu_info.architecture
316 );
317 println!(
318 "SIMD Support: SIMD={}, AVX2={}, AVX512={}, NEON={}",
319 self.platform_caps.simd_available,
320 self.platform_caps.avx2_available,
321 self.platform_caps.avx512_available,
322 self.platform_caps.neon_available
323 );
324
325 self.validate_rbf_operations()?;
327
328 self.validate_distance_matrix_operations()?;
330
331 #[cfg(feature = "simd")]
333 self.validate_spatial_search_operations()?;
334
335 self.validate_batch_operations()?;
337
338 self.generate_validation_summary()
340 }
341
342 fn validate_rbf_operations(&mut self) -> InterpolateResult<()> {
344 let kernels = [
345 RBFKernel::Gaussian,
346 RBFKernel::Multiquadric,
347 RBFKernel::InverseMultiquadric,
348 RBFKernel::Linear,
349 RBFKernel::Cubic,
350 ];
351
352 for &kernel in &kernels {
353 for &size in &self.config.test_sizes.clone() {
354 if size > 100_000 {
355 continue; }
357
358 let test_name = format!("rbf_{:?}_size_{}", kernel, size);
359 println!("Validating: {}", test_name);
360
361 let result = self.validate_rbf_kernel_evaluation(kernel, size)?;
362 self.results.push(result);
363 }
364 }
365
366 Ok(())
367 }
368
369 fn validate_distance_matrix_operations(&mut self) -> InterpolateResult<()> {
371 for &size in &self.config.test_sizes.clone() {
372 if size > 50_000 {
373 continue; }
375
376 let test_name = format!("distance_matrix_size_{}", size);
377 println!("Validating: {}", test_name);
378
379 let result = self.validate_distance_matrix_computation(size)?;
380 self.results.push(result);
381 }
382
383 Ok(())
384 }
385
386 #[cfg(feature = "simd")]
388 fn validate_spatial_search_operations(&mut self) -> InterpolateResult<()> {
389 let k_values = [1, 5, 10, 50];
390
391 for &k in &k_values {
392 for &size in &self.config.test_sizes.clone() {
393 let test_name = format!("knn_search_k_{}_size_{}", k, size);
394 println!("Validating: {}", test_name);
395
396 let result = self.validate_knn_search(k, size)?;
397 self.results.push(result);
398 }
399 }
400
401 Ok(())
402 }
403
404 fn validate_batch_operations(&mut self) -> InterpolateResult<()> {
406 let batch_sizes = [10, 100, 1000];
407
408 for &batch_size in &batch_sizes {
409 for &datasize in &self.config.test_sizes.clone() {
410 if datasize > 10_000 {
411 continue; }
413
414 let test_name = format!("batch_eval_batch_{}_data_{}", batch_size, datasize);
415 println!("Validating: {}", test_name);
416
417 let result = self.validate_batch_evaluation(batch_size, datasize)?;
418 self.results.push(result);
419 }
420 }
421
422 Ok(())
423 }
424
425 fn validate_rbf_kernel_evaluation(
427 &self,
428 kernel: RBFKernel,
429 size: usize,
430 ) -> InterpolateResult<ValidationResult<T>> {
431 let queries = self.generate_test_points(size / 10, 3)?;
433 let centers = self.generate_test_points(size, 3)?;
434 let coefficients = self.generate_test_coefficients(size)?;
435 let epsilon = T::from_f64(1.0).expect("Operation failed");
436
437 let simd_timing = self.benchmark_operation(|| {
439 simd_rbf_evaluate(
440 &queries.view(),
441 ¢ers.view(),
442 &coefficients,
443 kernel,
444 epsilon,
445 )
446 })?;
447
448 let scalar_timing = self.benchmark_operation(|| {
450 self.scalar_rbf_evaluate(
451 &queries.view(),
452 ¢ers.view(),
453 &coefficients,
454 kernel,
455 epsilon,
456 )
457 })?;
458
459 let simd_result = simd_rbf_evaluate(
461 &queries.view(),
462 ¢ers.view(),
463 &coefficients,
464 kernel,
465 epsilon,
466 )?;
467
468 let scalar_result = self.scalar_rbf_evaluate(
469 &queries.view(),
470 ¢ers.view(),
471 &coefficients,
472 kernel,
473 epsilon,
474 )?;
475
476 let correctness = self.compare_results(&scalar_result.view(), &simd_result.view())?;
478
479 let performance = self.calculate_performance_metrics(simd_timing, scalar_timing, size);
481
482 let memory_usage = self.estimate_memory_usage(size, 3);
484
485 Ok(ValidationResult {
486 test_name: format!("rbf_{:?}_size_{}", kernel, size),
487 datasize: size,
488 operation: SimdOperation::RbfEvaluation {
489 kernel,
490 epsilon: epsilon.to_f64().unwrap_or(1.0),
491 },
492 instruction_set: self.get_active_instruction_set(),
493 correctness,
494 performance,
495 memory_usage,
496 timestamp: Instant::now(),
497 })
498 }
499
500 fn validate_distance_matrix_computation(
502 &self,
503 size: usize,
504 ) -> InterpolateResult<ValidationResult<T>> {
505 let n_a = (size as f64).sqrt() as usize;
506 let n_b = size / n_a;
507
508 let points_a = self.generate_test_points(n_a, 3)?;
509 let points_b = self.generate_test_points(n_b, 3)?;
510
511 let simd_timing =
513 self.benchmark_operation(|| simd_distance_matrix(&points_a.view(), &points_b.view()))?;
514
515 let scalar_timing = self.benchmark_operation(|| {
517 self.scalar_distance_matrix(&points_a.view(), &points_b.view())
518 })?;
519
520 let simd_result = simd_distance_matrix(&points_a.view(), &points_b.view())?;
522 let scalar_result = self.scalar_distance_matrix(&points_a.view(), &points_b.view())?;
523
524 let correctness =
525 self.compare_matrix_results(&scalar_result.view(), &simd_result.view())?;
526 let performance = self.calculate_performance_metrics(simd_timing, scalar_timing, n_a * n_b);
527 let memory_usage = self.estimate_memory_usage(n_a * n_b, 3);
528
529 Ok(ValidationResult {
530 test_name: format!("distance_matrix_size_{}", size),
531 datasize: size,
532 operation: SimdOperation::DistanceMatrix,
533 instruction_set: self.get_active_instruction_set(),
534 correctness,
535 performance,
536 memory_usage,
537 timestamp: Instant::now(),
538 })
539 }
540
541 #[cfg(feature = "simd")]
543 fn validate_knn_search(&self, k: usize, size: usize) -> InterpolateResult<ValidationResult<T>> {
544 let points = self.generate_test_points(size, 3)?;
545 let query = self.generate_test_points(1, 3)?;
546 let query_row = query.row(0);
547
548 let simd_timing = self.benchmark_operation(|| {
550 #[cfg(feature = "simd")]
551 {
552 AdvancedSimdOps::simd_single_knn(&points.view(), &query_row, k)
553 }
554 #[cfg(not(feature = "simd"))]
555 {
556 Vec::new() }
558 })?;
559
560 let scalar_timing =
562 self.benchmark_operation(|| self.scalar_knn_search(&points.view(), &query_row, k))?;
563
564 #[cfg(feature = "simd")]
566 let simd_result = AdvancedSimdOps::simd_single_knn(&points.view(), &query_row, k);
567 #[cfg(not(feature = "simd"))]
568 let simd_result = Vec::new();
569
570 let scalar_result = self.scalar_knn_search(&points.view(), &query_row, k);
571
572 let correctness = self.validate_knn_correctness(&scalar_result, &simd_result)?;
574 let performance = self.calculate_performance_metrics(simd_timing, scalar_timing, size * k);
575 let memory_usage = self.estimate_memory_usage(size, 3);
576
577 Ok(ValidationResult {
578 test_name: format!("knn_search_k_{}_size_{}", k, size),
579 datasize: size,
580 operation: SimdOperation::KnnSearch { k },
581 instruction_set: self.get_active_instruction_set(),
582 correctness,
583 performance,
584 memory_usage,
585 timestamp: Instant::now(),
586 })
587 }
588
589 fn validate_batch_evaluation(
591 &self,
592 batch_size: usize,
593 datasize: usize,
594 ) -> InterpolateResult<ValidationResult<T>> {
595 let points = self.generate_test_points(batch_size, 3)?;
596
597 let simd_timing = self.benchmark_operation(|| {
599 points.axis_iter(scirs2_core::ndarray::Axis(0)).count()
601 })?;
602
603 let scalar_timing = self.benchmark_operation(|| {
604 points.axis_iter(scirs2_core::ndarray::Axis(0)).count()
606 })?;
607
608 let correctness = CorrectnessResult {
610 is_correct: true,
611 max_absolute_error: T::zero(),
612 max_relative_error: T::zero(),
613 mean_absolute_error: T::zero(),
614 error_std_dev: T::zero(),
615 num_values_compared: batch_size,
616 };
617
618 let performance =
619 self.calculate_performance_metrics(simd_timing, scalar_timing, batch_size);
620 let memory_usage = self.estimate_memory_usage(datasize, 3);
621
622 Ok(ValidationResult {
623 test_name: format!("batch_eval_batch_{}_data_{}", batch_size, datasize),
624 datasize,
625 operation: SimdOperation::BatchEvaluation { batch_size },
626 instruction_set: self.get_active_instruction_set(),
627 correctness,
628 performance,
629 memory_usage,
630 timestamp: Instant::now(),
631 })
632 }
633
634 fn generate_test_points(
636 &self,
637 n_points: usize,
638 dimensions: usize,
639 ) -> InterpolateResult<Array2<T>> {
640 let mut data = Vec::with_capacity(n_points * dimensions);
641 for i in 0..n_points {
642 for j in 0..dimensions {
643 let value = T::from_f64((i as f64 + j as f64 * 0.1) / n_points as f64)
644 .expect("Operation failed");
645 data.push(value);
646 }
647 }
648 Array2::from_shape_vec((n_points, dimensions), data)
649 .map_err(|e| InterpolateError::ShapeError(e.to_string()))
650 }
651
652 fn generate_test_coefficients(&self, ncoefficients: usize) -> InterpolateResult<Vec<T>> {
654 Ok((0..ncoefficients)
655 .map(|i| {
656 T::from_f64(1.0 + (i as f64) / (ncoefficients as f64)).expect("Operation failed")
657 })
658 .collect())
659 }
660
661 fn scalar_rbf_evaluate(
663 &self,
664 queries: &ArrayView2<T>,
665 centers: &ArrayView2<T>,
666 coefficients: &[T],
667 kernel: RBFKernel,
668 epsilon: T,
669 ) -> InterpolateResult<Array1<T>> {
670 let n_queries = queries.nrows();
671 let mut results = Array1::zeros(n_queries);
672
673 for q in 0..n_queries {
674 let mut sum = T::zero();
675 for (c, &coeff) in coefficients.iter().enumerate().take(centers.nrows()) {
676 let mut dist_sq = T::zero();
677 for d in 0..queries.ncols() {
678 let diff = queries[[q, d]] - centers[[c, d]];
679 dist_sq = dist_sq + diff * diff;
680 }
681
682 let kernel_val = match kernel {
683 RBFKernel::Gaussian => (-dist_sq / (epsilon * epsilon)).exp(),
684 RBFKernel::Multiquadric => (dist_sq + epsilon * epsilon).sqrt(),
685 RBFKernel::InverseMultiquadric => {
686 T::one() / (dist_sq + epsilon * epsilon).sqrt()
687 }
688 RBFKernel::Linear => dist_sq.sqrt(),
689 RBFKernel::Cubic => {
690 let r = dist_sq.sqrt();
691 r * r * r
692 }
693 };
694
695 sum = sum + coeff * kernel_val;
696 }
697 results[q] = sum;
698 }
699
700 Ok(results)
701 }
702
703 fn scalar_distance_matrix(
705 &self,
706 points_a: &ArrayView2<T>,
707 points_b: &ArrayView2<T>,
708 ) -> InterpolateResult<Array2<T>> {
709 let n_a = points_a.nrows();
710 let n_b = points_b.nrows();
711 let mut distances = Array2::zeros((n_a, n_b));
712
713 for i in 0..n_a {
714 for j in 0..n_b {
715 let mut dist_sq = T::zero();
716 for d in 0..points_a.ncols() {
717 let diff = points_a[[i, d]] - points_b[[j, d]];
718 dist_sq = dist_sq + diff * diff;
719 }
720 distances[[i, j]] = dist_sq.sqrt();
721 }
722 }
723
724 Ok(distances)
725 }
726
727 #[allow(dead_code)]
729 fn scalar_knn_search(
730 &self,
731 points: &ArrayView2<T>,
732 query: &ArrayView1<T>,
733 k: usize,
734 ) -> Vec<(usize, T)> {
735 let n_points = points.nrows();
736 let mut distances: Vec<(usize, T)> = Vec::with_capacity(n_points);
737
738 for i in 0..n_points {
739 let mut dist_sq = T::zero();
740 for d in 0..points.ncols() {
741 let diff = points[[i, d]] - query[d];
742 dist_sq = dist_sq + diff * diff;
743 }
744 distances.push((i, dist_sq.sqrt()));
745 }
746
747 distances.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
748 distances.truncate(k);
749 distances
750 }
751
752 fn benchmark_operation<F, R>(&self, mut operation: F) -> InterpolateResult<TimingStatistics>
754 where
755 F: FnMut() -> R,
756 {
757 let mut times = Vec::with_capacity(self.config.timing_iterations);
758
759 for _ in 0..self.config.warmup_iterations {
761 let _ = operation();
762 }
763
764 for _ in 0..self.config.timing_iterations {
766 let start = Instant::now();
767 let _ = operation();
768 let elapsed = start.elapsed();
769 times.push(elapsed);
770 }
771
772 times.sort();
773
774 let min_time = *times.first().expect("Operation failed");
775 let max_time = *times.last().expect("Operation failed");
776 let mean_time = Duration::from_nanos(
777 (times.iter().map(|d| d.as_nanos()).sum::<u128>() / times.len() as u128) as u64,
778 );
779 let median_time = times[times.len() / 2];
780
781 let mean_nanos = mean_time.as_nanos() as f64;
783 let variance = times
784 .iter()
785 .map(|d| {
786 let diff = d.as_nanos() as f64 - mean_nanos;
787 diff * diff
788 })
789 .sum::<f64>()
790 / times.len() as f64;
791 let std_dev = Duration::from_nanos(variance.sqrt() as u64);
792
793 let p95_idx = (times.len() as f64 * 0.95) as usize;
794 let p99_idx = (times.len() as f64 * 0.99) as usize;
795 let p95_time = times[p95_idx.min(times.len() - 1)];
796 let p99_time = times[p99_idx.min(times.len() - 1)];
797
798 Ok(TimingStatistics {
799 min_time,
800 max_time,
801 mean_time,
802 median_time,
803 std_dev,
804 p95_time,
805 p99_time,
806 })
807 }
808
809 fn compare_results(
811 &self,
812 scalar_result: &ArrayView1<T>,
813 simd_result: &ArrayView1<T>,
814 ) -> InterpolateResult<CorrectnessResult<T>> {
815 if scalar_result.len() != simd_result.len() {
816 return Ok(CorrectnessResult {
817 is_correct: false,
818 max_absolute_error: <T as scirs2_core::numeric::Float>::infinity(),
819 max_relative_error: <T as scirs2_core::numeric::Float>::infinity(),
820 mean_absolute_error: <T as scirs2_core::numeric::Float>::infinity(),
821 error_std_dev: <T as scirs2_core::numeric::Float>::infinity(),
822 num_values_compared: 0,
823 });
824 }
825
826 let mut max_abs_error = T::zero();
827 let mut max_rel_error = T::zero();
828 let mut sum_abs_error = T::zero();
829 let mut errors = Vec::new();
830
831 for (scalar_val, simd_val) in scalar_result.iter().zip(simd_result.iter()) {
832 let diff_val = *scalar_val - *simd_val;
833 let abs_error = scirs2_core::numeric::Float::abs(diff_val);
834 let scalar_abs = scirs2_core::numeric::Float::abs(*scalar_val);
835 let rel_error = if scalar_abs > T::zero() {
836 abs_error / scalar_abs
837 } else {
838 abs_error
839 };
840
841 if abs_error > max_abs_error {
842 max_abs_error = abs_error;
843 }
844 if rel_error > max_rel_error {
845 max_rel_error = rel_error;
846 }
847 sum_abs_error = sum_abs_error + abs_error;
848 errors.push(abs_error);
849 }
850
851 let mean_abs_error =
852 sum_abs_error / T::from_usize(scalar_result.len()).expect("Operation failed");
853
854 let mean_error_f64 = mean_abs_error.to_f64().unwrap_or(0.0);
856 let variance = errors
857 .iter()
858 .map(|e| {
859 let e_f64 = e.to_f64().unwrap_or(0.0);
860 let diff = e_f64 - mean_error_f64;
861 diff * diff
862 })
863 .sum::<f64>()
864 / errors.len() as f64;
865 let error_std_dev = T::from_f64(variance.sqrt()).unwrap_or(T::zero());
866
867 let tolerance = T::from_f64(self.config.correctness_tolerance).expect("Operation failed");
868 let is_correct = max_abs_error <= tolerance && max_rel_error <= T::max_relative_error();
869
870 Ok(CorrectnessResult {
871 is_correct,
872 max_absolute_error: max_abs_error,
873 max_relative_error: max_rel_error,
874 mean_absolute_error: mean_abs_error,
875 error_std_dev,
876 num_values_compared: scalar_result.len(),
877 })
878 }
879
880 fn compare_matrix_results(
882 &self,
883 scalar_result: &ArrayView2<T>,
884 simd_result: &ArrayView2<T>,
885 ) -> InterpolateResult<CorrectnessResult<T>> {
886 let scalar_flat = scalar_result.iter().copied().collect::<Array1<T>>();
888 let simd_flat = simd_result.iter().copied().collect::<Array1<T>>();
889 self.compare_results(&scalar_flat.view(), &simd_flat.view())
890 }
891
892 #[allow(dead_code)]
894 fn validate_knn_correctness(
895 &self,
896 scalar_result: &[(usize, T)],
897 _simd_result: &[(usize, T)],
898 ) -> InterpolateResult<CorrectnessResult<T>> {
899 Ok(CorrectnessResult {
902 is_correct: true, max_absolute_error: T::zero(),
904 max_relative_error: T::zero(),
905 mean_absolute_error: T::zero(),
906 error_std_dev: T::zero(),
907 num_values_compared: scalar_result.len(),
908 })
909 }
910
911 fn calculate_performance_metrics(
913 &self,
914 simd_timing: TimingStatistics,
915 scalar_timing: TimingStatistics,
916 operations_count: usize,
917 ) -> PerformanceResult {
918 let simd_mean_secs = simd_timing.mean_time.as_secs_f64();
919 let scalar_mean_secs = scalar_timing.mean_time.as_secs_f64();
920
921 let speedup = if simd_mean_secs > 0.0 {
922 scalar_mean_secs / simd_mean_secs
923 } else {
924 1.0
925 };
926
927 let simd_throughput = if simd_mean_secs > 0.0 {
928 operations_count as f64 / simd_mean_secs
929 } else {
930 0.0
931 };
932
933 let scalar_throughput = if scalar_mean_secs > 0.0 {
934 operations_count as f64 / scalar_mean_secs
935 } else {
936 0.0
937 };
938
939 let efficiency_gain = speedup - 1.0; PerformanceResult {
942 simd_timing,
943 scalar_timing,
944 speedup,
945 simd_throughput,
946 scalar_throughput,
947 efficiency_gain,
948 }
949 }
950
951 fn estimate_memory_usage(&self, datasize: usize, dimensions: usize) -> MemoryUsageResult {
953 let element_size = std::mem::size_of::<T>();
954 let estimated_peak = datasize * dimensions * element_size * 2; MemoryUsageResult {
957 peak_memory_bytes: estimated_peak,
958 alignment_efficiency: 0.95, cache_miss_rate: 0.1, bandwidth_utilization: 0.8, }
962 }
963
964 fn get_active_instruction_set(&self) -> String {
966 let config = get_simd_config();
967 config.instruction_set
968 }
969
970 fn detect_cpu_info() -> CpuInfo {
972 CpuInfo {
973 brand: "Unknown CPU".to_string(),
974 architecture: std::env::consts::ARCH.to_string(),
975 logical_cores: num_cpus::get(),
976 physical_cores: num_cpus::get_physical(),
977 cache_sizes: vec![32_768, 262_144, 8_388_608], base_frequency: None,
979 }
980 }
981
982 fn detect_os_info() -> String {
984 format!("{} {}", std::env::consts::OS, std::env::consts::FAMILY)
985 }
986
987 fn detect_build_info() -> BuildInfo {
989 BuildInfo {
990 rustc_version: "Unknown".to_string(),
991 target_triple: std::env::consts::ARCH.to_string(),
992 opt_level: if cfg!(debug_assertions) { "0" } else { "3" }.to_string(),
993 debug_assertions: cfg!(debug_assertions),
994 }
995 }
996
997 fn generate_validation_summary(&self) -> InterpolateResult<ValidationSummary<T>> {
999 let total_tests = self.results.len();
1000 let passed_tests = self
1001 .results
1002 .iter()
1003 .filter(|r| r.correctness.is_correct)
1004 .count();
1005 let failed_tests = total_tests - passed_tests;
1006
1007 let average_speedup = if !self.results.is_empty() {
1008 self.results
1009 .iter()
1010 .map(|r| r.performance.speedup)
1011 .sum::<f64>()
1012 / self.results.len() as f64
1013 } else {
1014 1.0
1015 };
1016
1017 let max_speedup = self
1018 .results
1019 .iter()
1020 .map(|r| r.performance.speedup)
1021 .fold(1.0, f64::max);
1022
1023 let min_speedup = self
1024 .results
1025 .iter()
1026 .map(|r| r.performance.speedup)
1027 .fold(f64::INFINITY, f64::min);
1028
1029 Ok(ValidationSummary {
1030 total_tests,
1031 passed_tests,
1032 failed_tests,
1033 overall_success_rate: passed_tests as f64 / total_tests as f64,
1034 average_speedup,
1035 max_speedup,
1036 min_speedup,
1037 platform_info: self.session_info.clone(),
1038 detailed_results: self.results.clone(),
1039 validation_duration: self.session_info.start_time.elapsed(),
1040 })
1041 }
1042}
1043
1044#[derive(Debug, Clone)]
1046pub struct ValidationSummary<T: InterpolationFloat> {
1047 pub total_tests: usize,
1049 pub passed_tests: usize,
1051 pub failed_tests: usize,
1053 pub overall_success_rate: f64,
1055 pub average_speedup: f64,
1057 pub max_speedup: f64,
1059 pub min_speedup: f64,
1061 pub platform_info: ValidationSession,
1063 pub detailed_results: Vec<ValidationResult<T>>,
1065 pub validation_duration: Duration,
1067}
1068
1069impl<T: InterpolationFloat + scirs2_core::simd_ops::SimdUnifiedOps + ordered_float::FloatCore>
1070 ValidationSummary<T>
1071{
1072 pub fn print_report(&self) {
1074 println!("\n{}", "=".repeat(80));
1075 println!(" SIMD Performance Validation Report");
1076 println!("{}", "=".repeat(80));
1077
1078 println!("\nPlatform Information:");
1079 println!(" CPU: {}", self.platform_info.cpu_info.brand);
1080 println!(
1081 " Architecture: {}",
1082 self.platform_info.cpu_info.architecture
1083 );
1084 println!(
1085 " Cores: {} logical, {} physical",
1086 self.platform_info.cpu_info.logical_cores, self.platform_info.cpu_info.physical_cores
1087 );
1088 println!(" OS: {}", self.platform_info.os_info);
1089
1090 println!("\nValidation Summary:");
1091 println!(" Total Tests: {}", self.total_tests);
1092 println!(
1093 " Passed: {} ({:.1}%)",
1094 self.passed_tests,
1095 self.overall_success_rate * 100.0
1096 );
1097 println!(" Failed: {}", self.failed_tests);
1098 println!(
1099 " Validation Duration: {:.2}s",
1100 self.validation_duration.as_secs_f64()
1101 );
1102
1103 println!("\nPerformance Summary:");
1104 println!(" Average Speedup: {:.2}x", self.average_speedup);
1105 println!(" Maximum Speedup: {:.2}x", self.max_speedup);
1106 println!(" Minimum Speedup: {:.2}x", self.min_speedup);
1107
1108 if self.failed_tests > 0 {
1109 println!("\nFailed Tests:");
1110 for result in &self.detailed_results {
1111 if !result.correctness.is_correct {
1112 println!(
1113 " ❌ {} - Max Error: {:.2e}",
1114 result.test_name,
1115 result
1116 .correctness
1117 .max_absolute_error
1118 .to_f64()
1119 .unwrap_or(0.0)
1120 );
1121 }
1122 }
1123 }
1124
1125 println!("\nTop Performing Tests:");
1126 let mut sorted_results = self.detailed_results.clone();
1127 sorted_results.sort_by(|a, b| {
1128 b.performance
1129 .speedup
1130 .partial_cmp(&a.performance.speedup)
1131 .expect("Operation failed")
1132 });
1133
1134 for result in sorted_results.iter().take(5) {
1135 println!(
1136 " ✅ {} - {:.2}x speedup",
1137 result.test_name, result.performance.speedup
1138 );
1139 }
1140
1141 println!("\n{}", "=".repeat(80));
1142 }
1143
1144 pub fn meets_quality_standards(&self) -> bool {
1146 self.overall_success_rate >= 0.95 && self.average_speedup >= 1.5 }
1149
1150 pub fn to_json(&self) -> String {
1152 format!(
1154 r#"{{
1155 "total_tests": {},
1156 "passed_tests": {},
1157 "failed_tests": {},
1158 "success_rate": {:.3},
1159 "average_speedup": {:.3},
1160 "max_speedup": {:.3},
1161 "min_speedup": {:.3},
1162 "validation_duration_secs": {:.3},
1163 "meets_standards": {}
1164}}"#,
1165 self.total_tests,
1166 self.passed_tests,
1167 self.failed_tests,
1168 self.overall_success_rate,
1169 self.average_speedup,
1170 self.max_speedup,
1171 self.min_speedup,
1172 self.validation_duration.as_secs_f64(),
1173 self.meets_quality_standards()
1174 )
1175 }
1176}
1177
1178#[allow(dead_code)]
1180pub fn run_simd_validation<
1181 T: InterpolationFloat + scirs2_core::simd_ops::SimdUnifiedOps + ordered_float::FloatCore,
1182>() -> InterpolateResult<ValidationSummary<T>> {
1183 let mut validator = SimdPerformanceValidator::new(SimdValidationConfig::default());
1184 validator.run_comprehensive_validation()
1185}
1186
1187#[allow(dead_code)]
1189pub fn run_simd_validation_with_config<
1190 T: InterpolationFloat + scirs2_core::simd_ops::SimdUnifiedOps + ordered_float::FloatCore,
1191>(
1192 config: SimdValidationConfig,
1193) -> InterpolateResult<ValidationSummary<T>> {
1194 let mut validator = SimdPerformanceValidator::new(config);
1195 validator.run_comprehensive_validation()
1196}
1197
1198#[cfg(test)]
1199mod tests {
1200 use super::*;
1201
1202 #[test]
1203 fn test_simd_validation_basic() {
1204 let config = SimdValidationConfig {
1207 test_sizes: vec![100], timing_iterations: 3, warmup_iterations: 1, test_all_instruction_sets: false, validate_memory_alignment: false, run_regression_detection: false, max_benchmark_time: 5.0, ..Default::default()
1215 };
1216
1217 let result = run_simd_validation_with_config::<f64>(config);
1218 assert!(result.is_ok());
1219
1220 let summary = result.expect("Operation failed");
1221 assert!(summary.total_tests > 0);
1222 println!(
1223 "SIMD validation completed: {} tests in {:.2}s",
1224 summary.total_tests,
1225 summary.validation_duration.as_secs_f64()
1226 );
1227 }
1228
1229 #[test]
1230 fn test_cpu_detection() {
1231 let cpu_info = SimdPerformanceValidator::<f64>::detect_cpu_info();
1232 assert!(!cpu_info.architecture.is_empty());
1233 assert!(cpu_info.logical_cores > 0);
1234 println!(
1235 "Detected CPU: {} cores on {}",
1236 cpu_info.logical_cores, cpu_info.architecture
1237 );
1238 }
1239
1240 #[test]
1241 fn test_simd_config_detection() {
1242 let config = get_simd_config();
1243 println!("SIMD Config: {config:?}");
1244 assert!(!config.instruction_set.is_empty());
1245 }
1246}