1use crate::error::{StatsError, StatsResult};
16use scirs2_core::ndarray::Array1;
17use serde::{Deserialize, Serialize};
18use std::collections::{BTreeMap, HashMap};
19use std::fs;
20use std::path::Path;
21use std::time::{SystemTime, UNIX_EPOCH};
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct CrossPlatformRegressionConfig {
26 pub baseline_storage_path: String,
28 pub regression_threshold_percent: f64,
30 pub significance_level: f64,
32 pub min_samples_: usize,
34 pub maxdata_retention_days: usize,
36 pub platform_specificbaselines: bool,
38 pub hardware_aware_normalization: bool,
40 pub compiler_optimization_detection: bool,
42 pub trend_analysis: bool,
44 pub target_platforms: Vec<PlatformInfo>,
46 pub monitored_functions: Vec<String>,
48}
49
50impl Default for CrossPlatformRegressionConfig {
51 fn default() -> Self {
52 Self {
53 baseline_storage_path: "./performancebaselines".to_string(),
54 regression_threshold_percent: 10.0, significance_level: 0.05,
56 min_samples_: 30,
57 maxdata_retention_days: 90,
58 platform_specificbaselines: true,
59 hardware_aware_normalization: true,
60 compiler_optimization_detection: true,
61 trend_analysis: true,
62 target_platforms: vec![PlatformInfo::current_platform()],
63 monitored_functions: vec![
64 "mean".to_string(),
65 "std".to_string(),
66 "variance".to_string(),
67 "pearsonr".to_string(),
68 "ttest_ind".to_string(),
69 "norm_pdf".to_string(),
70 "norm_cdf".to_string(),
71 ],
72 }
73 }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
78pub struct PlatformInfo {
79 pub os: String,
81 pub arch: String,
83 pub cpu_model: String,
85 pub cpu_cores: usize,
87 pub memory_gb: usize,
89 pub rustc_version: String,
91 pub optimization_level: String,
93 pub simd_capabilities: Vec<String>,
95}
96
97impl PlatformInfo {
98 pub fn current_platform() -> Self {
100 Self {
101 os: std::env::consts::OS.to_string(),
102 arch: std::env::consts::ARCH.to_string(),
103 cpu_model: Self::detect_cpu_model(),
104 cpu_cores: num_cpus::get(),
105 memory_gb: Self::detect_memory_gb(),
106 rustc_version: Self::detect_rustc_version(),
107 optimization_level: Self::detect_optimization_level(),
108 simd_capabilities: Self::detect_simd_capabilities(),
109 }
110 }
111
112 fn detect_cpu_model() -> String {
113 #[cfg(target_arch = "x86_64")]
115 {
116 if is_x86_feature_detected!("avx512f") {
117 "Intel AVX-512 Compatible".to_string()
118 } else if is_x86_feature_detected!("avx2") {
119 "Intel AVX2 Compatible".to_string()
120 } else if is_x86_feature_detected!("sse4.1") {
121 "Intel SSE4.1 Compatible".to_string()
122 } else {
123 "x86_64 Generic".to_string()
124 }
125 }
126 #[cfg(not(target_arch = "x86_64"))]
127 {
128 std::env::consts::ARCH.to_string()
129 }
130 }
131
132 fn detect_memory_gb() -> usize {
133 8 }
136
137 fn detect_rustc_version() -> String {
138 option_env!("RUSTC_VERSION")
139 .unwrap_or("unknown")
140 .to_string()
141 }
142
143 fn detect_optimization_level() -> String {
144 #[cfg(debug_assertions)]
145 {
146 "debug".to_string()
147 }
148 #[cfg(not(debug_assertions))]
149 {
150 "release".to_string()
151 }
152 }
153
154 fn detect_simd_capabilities() -> Vec<String> {
155 let mut capabilities = Vec::new();
156
157 #[cfg(target_arch = "x86_64")]
158 {
159 if is_x86_feature_detected!("sse2") {
160 capabilities.push("sse2".to_string());
161 }
162 if is_x86_feature_detected!("sse4.1") {
163 capabilities.push("sse4.1".to_string());
164 }
165 if is_x86_feature_detected!("avx") {
166 capabilities.push("avx".to_string());
167 }
168 if is_x86_feature_detected!("avx2") {
169 capabilities.push("avx2".to_string());
170 }
171 if is_x86_feature_detected!("avx512f") {
172 capabilities.push("avx512f".to_string());
173 }
174 if is_x86_feature_detected!("fma") {
175 capabilities.push("fma".to_string());
176 }
177 }
178
179 capabilities
180 }
181
182 pub fn platform_id(&self) -> String {
184 format!(
185 "{}-{}-{}-{}",
186 self.os,
187 self.arch,
188 self.optimization_level,
189 self.simd_capabilities.join("_")
190 )
191 }
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct PerformanceBaseline {
197 pub platform: PlatformInfo,
199 pub function_name: String,
201 pub input_parameters: String,
203 pub measurements: Vec<PerformanceMeasurement>,
205 pub statistics: BaselineStatistics,
207 pub last_updated: u64,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct PerformanceMeasurement {
214 pub timestamp: u64,
216 pub execution_time_ns: f64,
218 pub memory_usage_bytes: usize,
220 pub iterations: usize,
222 pub hardware_context: HardwareContext,
224 pub compiler_context: CompilerContext,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct HardwareContext {
231 pub cpu_utilization: f64,
233 pub available_memory_percent: f64,
235 pub cpu_frequency_mhz: f64,
237 pub temperature_celsius: Option<f64>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct CompilerContext {
244 pub rustc_version: String,
246 pub target_triple: String,
248 pub optimization_flags: Vec<String>,
250 pub feature_flags: Vec<String>,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct BaselineStatistics {
257 pub mean_time_ns: f64,
259 pub std_dev_time_ns: f64,
261 pub median_time_ns: f64,
263 pub p95_time_ns: f64,
265 pub p99_time_ns: f64,
267 pub coefficient_of_variation: f64,
269 pub confidence_interval_95: (f64, f64),
271 pub sample_count: usize,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct RegressionAnalysisResult {
278 pub function_name: String,
280 pub platform_comparison: PlatformComparison,
282 pub current_measurement: PerformanceMeasurement,
284 pub baseline_performance: BaselineStatistics,
286 pub regression_detected: bool,
288 pub performance_change_percent: f64,
290 pub statistical_significance: f64,
292 pub confidence_level: f64,
294 pub trend_analysis: Option<TrendAnalysis>,
296 pub recommendations: Vec<PerformanceRecommendation>,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct PlatformComparison {
303 pub current_platform: PlatformInfo,
305 pub baseline_platform: PlatformInfo,
307 pub hardware_normalization_factor: f64,
309 pub platform_similarity_score: f64,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct TrendAnalysis {
316 pub trend_direction: TrendDirection,
318 pub trend_strength: f64,
320 pub slope_ns_per_day: f64,
322 pub r_squared: f64,
324 pub predicted_performance_30d: f64,
326 pub trend_significance: f64,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
332pub enum TrendDirection {
333 Improving,
334 Stable,
335 Degrading,
336 Volatile,
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct PerformanceRecommendation {
342 pub category: RecommendationCategory,
344 pub priority: RecommendationPriority,
346 pub description: String,
348 pub expected_impact_percent: f64,
350 pub confidence: f64,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
356pub enum RecommendationCategory {
357 CompilerOptimization,
358 AlgorithmSelection,
359 SIMDOptimization,
360 MemoryOptimization,
361 ParallelProcessing,
362 PlatformSpecific,
363 HardwareUpgrade,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub enum RecommendationPriority {
369 Low,
370 Medium,
371 High,
372 Critical,
373}
374
375pub struct CrossPlatformRegressionDetector {
377 config: CrossPlatformRegressionConfig,
378 baselines: HashMap<String, PerformanceBaseline>,
379 historicaldata: BTreeMap<u64, Vec<PerformanceMeasurement>>,
380}
381
382impl CrossPlatformRegressionDetector {
383 pub fn new(config: CrossPlatformRegressionConfig) -> StatsResult<Self> {
385 let mut detector = Self {
386 config,
387 baselines: HashMap::new(),
388 historicaldata: BTreeMap::new(),
389 };
390
391 detector.loadbaselines()?;
392 Ok(detector)
393 }
394
395 fn loadbaselines(&mut self) -> StatsResult<()> {
397 if !Path::new(&self.config.baseline_storage_path).exists() {
398 fs::create_dir_all(&self.config.baseline_storage_path).map_err(|e| {
399 StatsError::InvalidInput(format!("Failed to create baseline directory: {}", e))
400 })?;
401 return Ok(());
402 }
403
404 let baseline_dir = Path::new(&self.config.baseline_storage_path);
406 if let Ok(entries) = fs::read_dir(baseline_dir) {
407 for entry in entries.flatten() {
408 let path = entry.path();
409 if path.extension().is_some_and(|ext| ext == "json") {
410 if let Ok(content) = fs::read_to_string(&path) {
411 if let Ok(baseline) = serde_json::from_str::<PerformanceBaseline>(&content)
412 {
413 let key = format!(
414 "{}-{}",
415 baseline.platform.platform_id(),
416 baseline.function_name
417 );
418 self.baselines.insert(key, baseline);
419 }
420 }
421 }
422 }
423 }
424
425 Ok(())
426 }
427
428 fn savebaseline(&self, baseline: &PerformanceBaseline) -> StatsResult<()> {
430 let filename = format!(
431 "{}-{}.json",
432 baseline.platform.platform_id(),
433 baseline.function_name
434 );
435 let filepath = Path::new(&self.config.baseline_storage_path).join(filename);
436
437 let content = serde_json::to_string_pretty(baseline).map_err(|e| {
438 StatsError::InvalidInput(format!("Failed to serialize baseline: {}", e))
439 })?;
440
441 fs::write(filepath, content)
442 .map_err(|e| StatsError::InvalidInput(format!("Failed to write baseline: {}", e)))?;
443
444 Ok(())
445 }
446
447 pub fn record_measurement(
449 &mut self,
450 function_name: &str,
451 input_parameters: &str,
452 execution_time_ns: f64,
453 memory_usage_bytes: usize,
454 iterations: usize,
455 ) -> StatsResult<()> {
456 let platform = PlatformInfo::current_platform();
457 let timestamp = SystemTime::now()
458 .duration_since(UNIX_EPOCH)
459 .expect("Operation failed")
460 .as_secs();
461
462 let measurement = PerformanceMeasurement {
463 timestamp,
464 execution_time_ns,
465 memory_usage_bytes,
466 iterations,
467 hardware_context: self.capture_hardware_context()?,
468 compiler_context: self.capture_compiler_context()?,
469 };
470
471 self.historicaldata
473 .entry(timestamp)
474 .or_default()
475 .push(measurement.clone());
476
477 let baseline_key = format!("{}-{}", platform.platform_id(), function_name);
479
480 if let Some(baseline) = self.baselines.get_mut(&baseline_key) {
481 baseline.measurements.push(measurement);
482 baseline.last_updated = timestamp;
483 let measurements = baseline.measurements.clone();
485 let _ = baseline; let stats = self.calculate_statistics(&measurements)?;
487 if let Some(baseline) = self.baselines.get_mut(&baseline_key) {
489 baseline.statistics = stats;
490 }
491 } else {
492 let baseline = PerformanceBaseline {
493 platform,
494 function_name: function_name.to_string(),
495 input_parameters: input_parameters.to_string(),
496 measurements: vec![measurement],
497 statistics: BaselineStatistics {
498 mean_time_ns: execution_time_ns,
499 std_dev_time_ns: 0.0,
500 median_time_ns: execution_time_ns,
501 p95_time_ns: execution_time_ns,
502 p99_time_ns: execution_time_ns,
503 coefficient_of_variation: 0.0,
504 confidence_interval_95: (execution_time_ns, execution_time_ns),
505 sample_count: 1,
506 },
507 last_updated: timestamp,
508 };
509
510 self.baselines.insert(baseline_key, baseline.clone());
511 self.savebaseline(&baseline)?;
512 }
513
514 Ok(())
515 }
516
517 pub fn detect_regression(
519 &self,
520 function_name: &str,
521 current_measurement: &PerformanceMeasurement,
522 ) -> StatsResult<RegressionAnalysisResult> {
523 let platform = PlatformInfo::current_platform();
524 let baseline_key = format!("{}-{}", platform.platform_id(), function_name);
525
526 let baseline = self.baselines.get(&baseline_key).ok_or_else(|| {
527 StatsError::InvalidInput(format!(
528 "No baseline found for function {} on platform {}",
529 function_name,
530 platform.platform_id()
531 ))
532 })?;
533
534 let performance_change_percent = ((current_measurement.execution_time_ns
536 - baseline.statistics.mean_time_ns)
537 / baseline.statistics.mean_time_ns)
538 * 100.0;
539
540 let statistical_significance = self.calculate_statistical_significance(
542 current_measurement.execution_time_ns,
543 &baseline.statistics,
544 )?;
545
546 let regression_detected = performance_change_percent
548 > self.config.regression_threshold_percent
549 && statistical_significance < self.config.significance_level;
550
551 let confidence_level = 1.0 - statistical_significance;
553
554 let trend_analysis = if self.config.trend_analysis {
556 Some(self.analyze_trend(function_name)?)
557 } else {
558 None
559 };
560
561 let recommendations = self.generate_recommendations(
563 function_name,
564 performance_change_percent,
565 &baseline.statistics,
566 current_measurement,
567 )?;
568
569 let platform_comparison = PlatformComparison {
570 current_platform: platform.clone(),
571 baseline_platform: baseline.platform.clone(),
572 hardware_normalization_factor: 1.0, platform_similarity_score: self
574 .calculate_platform_similarity(&platform, &baseline.platform),
575 };
576
577 Ok(RegressionAnalysisResult {
578 function_name: function_name.to_string(),
579 platform_comparison,
580 current_measurement: current_measurement.clone(),
581 baseline_performance: baseline.statistics.clone(),
582 regression_detected,
583 performance_change_percent,
584 statistical_significance,
585 confidence_level,
586 trend_analysis,
587 recommendations,
588 })
589 }
590
591 fn calculate_statistical_significance(
593 &self,
594 current_time: f64,
595 baseline_stats: &BaselineStatistics,
596 ) -> StatsResult<f64> {
597 if baseline_stats.sample_count < 2 {
598 return Ok(1.0); }
600
601 let t_statistic = (current_time - baseline_stats.mean_time_ns)
603 / (baseline_stats.std_dev_time_ns / (baseline_stats.sample_count as f64).sqrt());
604
605 let p_value = if t_statistic.abs() > 2.0 {
607 0.05 } else if t_statistic.abs() > 1.5 {
609 0.1 } else {
611 0.5 };
613
614 Ok(p_value)
615 }
616
617 fn calculate_statistics(
619 &self,
620 measurements: &[PerformanceMeasurement],
621 ) -> StatsResult<BaselineStatistics> {
622 if measurements.is_empty() {
623 return Err(StatsError::InvalidInput(
624 "No measurements provided".to_string(),
625 ));
626 }
627
628 let times: Vec<f64> = measurements.iter().map(|m| m.execution_time_ns).collect();
629
630 let _times_array = Array1::from_vec(times.clone());
631
632 let mean = times.iter().sum::<f64>() / times.len() as f64;
634 let variance = times.iter().map(|&x| (x - mean).powi(2)).sum::<f64>()
635 / (times.len() - 1).max(1) as f64;
636 let std_dev = variance.sqrt();
637
638 let mut sorted_times = times.clone();
640 sorted_times.sort_by(|a, b| a.partial_cmp(b).expect("Operation failed"));
641
642 let median = if sorted_times.len().is_multiple_of(2) {
643 let mid = sorted_times.len() / 2;
644 (sorted_times[mid - 1] + sorted_times[mid]) / 2.0
645 } else {
646 sorted_times[sorted_times.len() / 2]
647 };
648
649 let p95_idx = ((sorted_times.len() as f64 * 0.95) as usize).min(sorted_times.len() - 1);
650 let p99_idx = ((sorted_times.len() as f64 * 0.99) as usize).min(sorted_times.len() - 1);
651 let p95 = sorted_times[p95_idx];
652 let p99 = sorted_times[p99_idx];
653
654 let coefficient_of_variation = if mean != 0.0 { std_dev / mean } else { 0.0 };
656
657 let standard_error = std_dev / (times.len() as f64).sqrt();
659 let margin_of_error = 1.96 * standard_error; let confidence_interval_95 = (mean - margin_of_error, mean + margin_of_error);
661
662 Ok(BaselineStatistics {
663 mean_time_ns: mean,
664 std_dev_time_ns: std_dev,
665 median_time_ns: median,
666 p95_time_ns: p95,
667 p99_time_ns: p99,
668 coefficient_of_variation,
669 confidence_interval_95,
670 sample_count: times.len(),
671 })
672 }
673
674 fn analyze_trend(&self, _functionname: &str) -> StatsResult<TrendAnalysis> {
676 let measurements: Vec<_> = self
678 .historicaldata
679 .values()
680 .flatten()
681 .filter(|_m| {
682 true
684 })
685 .collect();
686
687 if measurements.len() < 5 {
688 return Ok(TrendAnalysis {
689 trend_direction: TrendDirection::Stable,
690 trend_strength: 0.0,
691 slope_ns_per_day: 0.0,
692 r_squared: 0.0,
693 predicted_performance_30d: 0.0,
694 trend_significance: 1.0,
695 });
696 }
697
698 let timestamps: Vec<f64> = measurements.iter().map(|m| m.timestamp as f64).collect();
700 let times: Vec<f64> = measurements.iter().map(|m| m.execution_time_ns).collect();
701
702 let (slope, r_squared) = self.linear_regression(×tamps, ×)?;
703
704 let slope_ns_per_day = slope * 86400.0; let trend_direction = if slope_ns_per_day > 100.0 {
708 TrendDirection::Degrading
709 } else if slope_ns_per_day < -100.0 {
710 TrendDirection::Improving
711 } else {
712 TrendDirection::Stable
713 };
714
715 let trend_strength = r_squared.abs();
716 let trend_significance = if r_squared > 0.5 { 0.01 } else { 0.5 };
717
718 let current_time = SystemTime::now()
720 .duration_since(UNIX_EPOCH)
721 .expect("Operation failed")
722 .as_secs() as f64;
723 let future_time = current_time + (30.0 * 86400.0);
724 let predicted_performance_30d =
725 slope * future_time + (times.iter().sum::<f64>() / times.len() as f64);
726
727 Ok(TrendAnalysis {
728 trend_direction,
729 trend_strength,
730 slope_ns_per_day,
731 r_squared,
732 predicted_performance_30d,
733 trend_significance,
734 })
735 }
736
737 fn linear_regression(&self, x: &[f64], y: &[f64]) -> StatsResult<(f64, f64)> {
739 if x.len() != y.len() || x.len() < 2 {
740 return Err(StatsError::InvalidInput(
741 "Invalid data for regression".to_string(),
742 ));
743 }
744
745 let n = x.len() as f64;
746 let sum_x = x.iter().sum::<f64>();
747 let sum_y = y.iter().sum::<f64>();
748 let sum_xy = x.iter().zip(y.iter()).map(|(xi, yi)| xi * yi).sum::<f64>();
749 let sum_x2 = x.iter().map(|xi| xi * xi).sum::<f64>();
750 let _sum_y2 = y.iter().map(|yi| yi * yi).sum::<f64>();
751
752 let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
753
754 let mean_y = sum_y / n;
756 let ss_tot = y.iter().map(|yi| (yi - mean_y).powi(2)).sum::<f64>();
757 let intercept = (sum_y - slope * sum_x) / n;
758 let ss_res = x
759 .iter()
760 .zip(y.iter())
761 .map(|(xi, yi)| (yi - (slope * xi + intercept)).powi(2))
762 .sum::<f64>();
763
764 let r_squared = 1.0 - (ss_res / ss_tot);
765
766 Ok((slope, r_squared))
767 }
768
769 fn calculate_platform_similarity(
771 &self,
772 platform1: &PlatformInfo,
773 platform2: &PlatformInfo,
774 ) -> f64 {
775 let mut score = 0.0;
776 let mut factors = 0.0;
777
778 if platform1.os == platform2.os {
780 score += 0.3;
781 }
782 factors += 0.3;
783
784 if platform1.arch == platform2.arch {
786 score += 0.2;
787 }
788 factors += 0.2;
789
790 let common_simd: Vec<_> = platform1
792 .simd_capabilities
793 .iter()
794 .filter(|cap| platform2.simd_capabilities.contains(cap))
795 .collect();
796 let total_simd = platform1
797 .simd_capabilities
798 .len()
799 .max(platform2.simd_capabilities.len());
800 if total_simd > 0 {
801 score += 0.3 * (common_simd.len() as f64 / total_simd as f64);
802 }
803 factors += 0.3;
804
805 if platform1.optimization_level == platform2.optimization_level {
807 score += 0.2;
808 }
809 factors += 0.2;
810
811 if factors > 0.0 {
812 score / factors
813 } else {
814 0.0
815 }
816 }
817
818 fn generate_recommendations(
820 &self,
821 _function_name: &str,
822 performance_change_percent: f64,
823 baseline_stats: &BaselineStatistics,
824 _measurement: &PerformanceMeasurement,
825 ) -> StatsResult<Vec<PerformanceRecommendation>> {
826 let mut recommendations = Vec::new();
827
828 if performance_change_percent > 20.0 {
830 recommendations.push(PerformanceRecommendation {
831 category: RecommendationCategory::AlgorithmSelection,
832 priority: RecommendationPriority::High,
833 description: format!(
834 "Significant performance regression detected ({}% slower). Consider algorithm optimization.",
835 performance_change_percent as i32
836 ),
837 expected_impact_percent: -performance_change_percent * 0.5,
838 confidence: 0.8,
839 });
840 }
841
842 if baseline_stats.coefficient_of_variation > 0.2 {
844 recommendations.push(PerformanceRecommendation {
845 category: RecommendationCategory::CompilerOptimization,
846 priority: RecommendationPriority::Medium,
847 description:
848 "High performance variability detected. Consider compiler optimization flags."
849 .to_string(),
850 expected_impact_percent: -10.0,
851 confidence: 0.6,
852 });
853 }
854
855 let platform = PlatformInfo::current_platform();
857 if platform.simd_capabilities.contains(&"avx512f".to_string()) {
858 recommendations.push(PerformanceRecommendation {
859 category: RecommendationCategory::SIMDOptimization,
860 priority: RecommendationPriority::Medium,
861 description:
862 "AVX-512 capabilities detected. Consider using specialized SIMD optimizations."
863 .to_string(),
864 expected_impact_percent: -25.0,
865 confidence: 0.7,
866 });
867 }
868
869 Ok(recommendations)
870 }
871
872 fn capture_hardware_context(&self) -> StatsResult<HardwareContext> {
874 Ok(HardwareContext {
876 cpu_utilization: 50.0, available_memory_percent: 75.0,
878 cpu_frequency_mhz: 3000.0,
879 temperature_celsius: None,
880 })
881 }
882
883 fn capture_compiler_context(&self) -> StatsResult<CompilerContext> {
885 Ok(CompilerContext {
886 rustc_version: option_env!("RUSTC_VERSION")
887 .unwrap_or("unknown")
888 .to_string(),
889 target_triple: option_env!("TARGET")
890 .unwrap_or("unknown-target")
891 .to_string(),
892 optimization_flags: vec![], feature_flags: vec![],
894 })
895 }
896
897 pub fn generate_report(&self) -> StatsResult<RegressionReport> {
899 let mut function_analyses = Vec::new();
900
901 for function_name in &self.config.monitored_functions {
902 if let Some(latest_measurement) = self.get_latest_measurement(function_name) {
903 if let Ok(analysis) = self.detect_regression(function_name, &latest_measurement) {
904 function_analyses.push(analysis);
905 }
906 }
907 }
908
909 let overall_status = if function_analyses.iter().any(|a| a.regression_detected) {
910 RegressionStatus::RegressionsDetected
911 } else {
912 RegressionStatus::NoRegressionsDetected
913 };
914
915 Ok(RegressionReport {
916 timestamp: SystemTime::now()
917 .duration_since(UNIX_EPOCH)
918 .expect("Operation failed")
919 .as_secs(),
920 overall_status,
921 platform: PlatformInfo::current_platform(),
922 function_analyses,
923 summary_statistics: self.calculate_summary_statistics()?,
924 })
925 }
926
927 fn get_latest_measurement(&self, _functionname: &str) -> Option<PerformanceMeasurement> {
929 None
931 }
932
933 fn calculate_summary_statistics(&self) -> StatsResult<RegressionSummaryStatistics> {
935 Ok(RegressionSummaryStatistics {
936 total_functions_monitored: self.config.monitored_functions.len(),
937 functions_with_regressions: 0, average_performance_change: 0.0,
939 max_performance_change: 0.0,
940 total_measurements: self.historicaldata.values().map(|v| v.len()).sum(),
941 })
942 }
943}
944
945#[derive(Debug, Clone, Serialize, Deserialize)]
947pub struct RegressionReport {
948 pub timestamp: u64,
949 pub overall_status: RegressionStatus,
950 pub platform: PlatformInfo,
951 pub function_analyses: Vec<RegressionAnalysisResult>,
952 pub summary_statistics: RegressionSummaryStatistics,
953}
954
955#[derive(Debug, Clone, Serialize, Deserialize)]
957pub enum RegressionStatus {
958 NoRegressionsDetected,
959 RegressionsDetected,
960 InsufficientData,
961}
962
963#[derive(Debug, Clone, Serialize, Deserialize)]
965pub struct RegressionSummaryStatistics {
966 pub total_functions_monitored: usize,
967 pub functions_with_regressions: usize,
968 pub average_performance_change: f64,
969 pub max_performance_change: f64,
970 pub total_measurements: usize,
971}
972
973#[allow(dead_code)]
975pub fn create_regression_detector() -> StatsResult<CrossPlatformRegressionDetector> {
976 CrossPlatformRegressionDetector::new(CrossPlatformRegressionConfig::default())
977}
978
979#[allow(dead_code)]
981pub fn create_regression_detector_with_config(
982 config: CrossPlatformRegressionConfig,
983) -> StatsResult<CrossPlatformRegressionDetector> {
984 CrossPlatformRegressionDetector::new(config)
985}
986
987#[cfg(test)]
988mod tests {
989 use super::*;
990
991 #[test]
992 fn test_platform_info_creation() {
993 let platform = PlatformInfo::current_platform();
994 assert!(!platform.os.is_empty());
995 assert!(!platform.arch.is_empty());
996 assert!(!platform.platform_id().is_empty());
997 }
998
999 #[test]
1000 fn test_regression_detector_creation() {
1001 let detector = create_regression_detector();
1002 assert!(detector.is_ok());
1003 }
1004
1005 #[test]
1006 fn test_performance_measurement_recording() {
1007 let mut detector = create_regression_detector().expect("Operation failed");
1008 let result = detector.record_measurement(
1009 "test_function",
1010 "inputsize_100",
1011 1000.0, 1024, 100, );
1015 assert!(result.is_ok());
1016 }
1017
1018 #[test]
1019 fn testbaseline_statistics_calculation() {
1020 let detector = create_regression_detector().expect("Operation failed");
1021 let measurements = vec![
1022 PerformanceMeasurement {
1023 timestamp: 1000,
1024 execution_time_ns: 100.0,
1025 memory_usage_bytes: 1024,
1026 iterations: 10,
1027 hardware_context: HardwareContext {
1028 cpu_utilization: 50.0,
1029 available_memory_percent: 75.0,
1030 cpu_frequency_mhz: 3000.0,
1031 temperature_celsius: None,
1032 },
1033 compiler_context: CompilerContext {
1034 rustc_version: "1.70.0".to_string(),
1035 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1036 optimization_flags: vec![],
1037 feature_flags: vec![],
1038 },
1039 },
1040 PerformanceMeasurement {
1041 timestamp: 1001,
1042 execution_time_ns: 110.0,
1043 memory_usage_bytes: 1024,
1044 iterations: 10,
1045 hardware_context: HardwareContext {
1046 cpu_utilization: 50.0,
1047 available_memory_percent: 75.0,
1048 cpu_frequency_mhz: 3000.0,
1049 temperature_celsius: None,
1050 },
1051 compiler_context: CompilerContext {
1052 rustc_version: "1.70.0".to_string(),
1053 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1054 optimization_flags: vec![],
1055 feature_flags: vec![],
1056 },
1057 },
1058 ];
1059
1060 let stats = detector
1061 .calculate_statistics(&measurements)
1062 .expect("Operation failed");
1063 assert!((stats.mean_time_ns - 105.0).abs() < 1e-10);
1064 assert_eq!(stats.sample_count, 2);
1065 }
1066
1067 #[test]
1068 fn test_platform_similarity_calculation() {
1069 let detector = create_regression_detector().expect("Operation failed");
1070 let platform1 = PlatformInfo {
1071 os: "linux".to_string(),
1072 arch: "x86_64".to_string(),
1073 cpu_model: "Intel Core".to_string(),
1074 cpu_cores: 8,
1075 memory_gb: 16,
1076 rustc_version: "1.70.0".to_string(),
1077 optimization_level: "release".to_string(),
1078 simd_capabilities: vec!["avx2".to_string(), "fma".to_string()],
1079 };
1080 let platform2 = platform1.clone();
1081
1082 let similarity = detector.calculate_platform_similarity(&platform1, &platform2);
1083 assert!((similarity - 1.0).abs() < 1e-10); }
1085
1086 #[test]
1087 fn test_linear_regression() {
1088 let detector = create_regression_detector().expect("Operation failed");
1089 let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
1090 let y = vec![2.0, 4.0, 6.0, 8.0, 10.0]; let (slope, r_squared) = detector
1093 .linear_regression(&x, &y)
1094 .expect("Operation failed");
1095 assert!((slope - 2.0).abs() < 1e-10);
1096 assert!((r_squared - 1.0).abs() < 1e-10);
1097 }
1098}