1use scirs2_core::ndarray::{Array2, ArrayView2};
7use scirs2_core::random::Rng;
8use serde::{Deserialize, Serialize};
9use sklears_core::{
10 error::Result as SklResult,
11 traits::{Estimator, Transform},
12};
13use std::collections::{BTreeMap, HashMap};
14use std::fmt::Write as FmtWrite;
15use std::time::{Duration, Instant};
16
17#[derive(Debug, Clone)]
19pub struct BenchmarkConfig {
20 pub iterations: usize,
22 pub warmup_iterations: usize,
24 pub sample_sizes: Vec<usize>,
26 pub feature_counts: Vec<usize>,
28 pub measure_memory: bool,
30 pub measure_throughput: bool,
32 pub enable_cpu_profiling: bool,
34 pub enable_cache_analysis: bool,
36 pub enable_parallel_profiling: bool,
38 pub max_duration: Duration,
40 pub confidence_level: f64,
42 pub enable_convergence_detection: bool,
44 pub tags: HashMap<String, String>,
46 pub outlier_threshold: f64,
48}
49
50impl BenchmarkConfig {
51 #[must_use]
53 pub fn new() -> Self {
54 Self {
55 iterations: 100,
56 warmup_iterations: 10,
57 sample_sizes: vec![100, 1000, 10000],
58 feature_counts: vec![5, 10, 20, 50],
59 measure_memory: true,
60 measure_throughput: true,
61 enable_cpu_profiling: true,
62 enable_cache_analysis: false, enable_parallel_profiling: true,
64 max_duration: Duration::from_secs(300), confidence_level: 0.95,
66 enable_convergence_detection: true,
67 tags: HashMap::new(),
68 outlier_threshold: 2.0, }
70 }
71
72 #[must_use]
74 pub fn iterations(mut self, iterations: usize) -> Self {
75 self.iterations = iterations;
76 self
77 }
78
79 #[must_use]
81 pub fn warmup_iterations(mut self, warmup: usize) -> Self {
82 self.warmup_iterations = warmup;
83 self
84 }
85
86 #[must_use]
88 pub fn sample_sizes(mut self, sizes: Vec<usize>) -> Self {
89 self.sample_sizes = sizes;
90 self
91 }
92
93 #[must_use]
95 pub fn feature_counts(mut self, counts: Vec<usize>) -> Self {
96 self.feature_counts = counts;
97 self
98 }
99
100 #[must_use]
102 pub fn measure_memory(mut self, enable: bool) -> Self {
103 self.measure_memory = enable;
104 self
105 }
106
107 #[must_use]
109 pub fn measure_throughput(mut self, enable: bool) -> Self {
110 self.measure_throughput = enable;
111 self
112 }
113
114 #[must_use]
116 pub fn enable_cpu_profiling(mut self, enable: bool) -> Self {
117 self.enable_cpu_profiling = enable;
118 self
119 }
120
121 #[must_use]
123 pub fn enable_cache_analysis(mut self, enable: bool) -> Self {
124 self.enable_cache_analysis = enable;
125 self
126 }
127
128 #[must_use]
130 pub fn enable_parallel_profiling(mut self, enable: bool) -> Self {
131 self.enable_parallel_profiling = enable;
132 self
133 }
134
135 #[must_use]
137 pub fn max_duration(mut self, duration: Duration) -> Self {
138 self.max_duration = duration;
139 self
140 }
141
142 #[must_use]
144 pub fn confidence_level(mut self, level: f64) -> Self {
145 self.confidence_level = level.clamp(0.0, 1.0);
146 self
147 }
148
149 #[must_use]
151 pub fn enable_convergence_detection(mut self, enable: bool) -> Self {
152 self.enable_convergence_detection = enable;
153 self
154 }
155
156 #[must_use]
158 pub fn add_tag(mut self, key: String, value: String) -> Self {
159 self.tags.insert(key, value);
160 self
161 }
162
163 #[must_use]
165 pub fn outlier_threshold(mut self, threshold: f64) -> Self {
166 self.outlier_threshold = threshold;
167 self
168 }
169}
170
171impl Default for BenchmarkConfig {
172 fn default() -> Self {
173 Self::new()
174 }
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct BenchmarkResult {
180 pub name: String,
182 pub mean_time: Duration,
184 pub std_dev_time: Duration,
186 pub min_time: Duration,
188 pub max_time: Duration,
190 pub median_time: Duration,
192 pub p95_time: Duration,
194 pub p99_time: Duration,
196 pub throughput: Option<f64>,
198 pub memory_usage: Option<MemoryUsage>,
200 pub cpu_metrics: Option<CpuMetrics>,
202 pub cache_metrics: Option<CacheMetrics>,
204 pub parallel_metrics: Option<ParallelMetrics>,
206 pub data_dimensions: (usize, usize),
208 pub performance_class: PerformanceClass,
210 pub confidence_interval: Option<(Duration, Duration)>,
212 pub outliers_detected: usize,
214 pub timestamp: chrono::DateTime<chrono::Utc>,
216 pub custom_metrics: HashMap<String, f64>,
218}
219
220impl BenchmarkResult {
221 #[must_use]
223 pub fn new(name: String, times: Vec<Duration>, data_dimensions: (usize, usize)) -> Self {
224 let mean_time = Duration::from_nanos(
225 (times
226 .iter()
227 .map(std::time::Duration::as_nanos)
228 .sum::<u128>()
229 / times.len() as u128) as u64,
230 );
231
232 let mean_nanos = mean_time.as_nanos() as f64;
233 let variance = times
234 .iter()
235 .map(|d| {
236 let diff = d.as_nanos() as f64 - mean_nanos;
237 diff * diff
238 })
239 .sum::<f64>()
240 / times.len() as f64;
241
242 let std_dev_time = Duration::from_nanos(variance.sqrt() as u64);
243 let min_time = times.iter().min().copied().unwrap_or(Duration::ZERO);
244 let max_time = times.iter().max().copied().unwrap_or(Duration::ZERO);
245
246 let mut sorted_times = times.clone();
248 sorted_times.sort();
249
250 let median_time = Self::calculate_percentile(&sorted_times, 50.0);
251 let p95_time = Self::calculate_percentile(&sorted_times, 95.0);
252 let p99_time = Self::calculate_percentile(&sorted_times, 99.0);
253
254 Self {
255 name,
256 mean_time,
257 std_dev_time,
258 min_time,
259 max_time,
260 median_time,
261 p95_time,
262 p99_time,
263 throughput: None,
264 memory_usage: None,
265 cpu_metrics: None,
266 cache_metrics: None,
267 parallel_metrics: None,
268 data_dimensions,
269 performance_class: PerformanceClass::Normal,
270 confidence_interval: None,
271 outliers_detected: 0,
272 timestamp: chrono::Utc::now(),
273 custom_metrics: HashMap::new(),
274 }
275 }
276
277 #[must_use]
279 pub fn with_throughput(mut self, throughput: f64) -> Self {
280 self.throughput = Some(throughput);
281 self
282 }
283
284 #[must_use]
286 pub fn with_memory_usage(mut self, memory_usage: MemoryUsage) -> Self {
287 self.memory_usage = Some(memory_usage);
288 self
289 }
290
291 #[must_use]
293 pub fn performance_score(&self) -> f64 {
294 let time_score = 1.0 / self.mean_time.as_secs_f64();
295 let throughput_score = self.throughput.unwrap_or(1.0);
296 let memory_score = if let Some(ref mem) = self.memory_usage {
297 1.0 / (mem.peak_usage_mb + 1.0)
298 } else {
299 1.0
300 };
301
302 0.5 * time_score + 0.3 * throughput_score + 0.2 * memory_score
304 }
305
306 fn calculate_percentile(sorted_times: &[Duration], percentile: f64) -> Duration {
317 if sorted_times.is_empty() {
318 return Duration::ZERO;
319 }
320
321 if sorted_times.len() == 1 {
322 return sorted_times[0];
323 }
324
325 let index = (percentile / 100.0) * (sorted_times.len() - 1) as f64;
328 let lower_index = index.floor() as usize;
329 let upper_index = index.ceil() as usize;
330
331 if lower_index == upper_index {
332 sorted_times[lower_index]
334 } else {
335 let lower_value = sorted_times[lower_index].as_nanos() as f64;
337 let upper_value = sorted_times[upper_index].as_nanos() as f64;
338 let fraction = index - lower_index as f64;
339 let interpolated = lower_value + fraction * (upper_value - lower_value);
340
341 Duration::from_nanos(interpolated as u64)
342 }
343 }
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
348pub struct MemoryUsage {
349 pub peak_usage_mb: f64,
351 pub average_usage_mb: f64,
353 pub allocations: usize,
355 pub deallocations: usize,
357 pub memory_leaks_bytes: u64,
359 pub allocation_efficiency: f64,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct CpuMetrics {
366 pub average_utilization: f64,
368 pub peak_utilization: f64,
370 pub user_time_percent: f64,
372 pub kernel_time_percent: f64,
374 pub context_switches_per_sec: f64,
376 pub efficiency_score: f64,
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct CacheMetrics {
383 pub l1_hit_rate: f64,
385 pub l2_hit_rate: f64,
387 pub l3_hit_rate: f64,
389 pub miss_penalty_cycles: f64,
391 pub cache_efficiency: f64,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct ParallelMetrics {
398 pub thread_count: usize,
400 pub parallel_efficiency: f64,
402 pub load_balance_score: f64,
404 pub contention_events: usize,
406 pub sync_overhead_percent: f64,
408 pub speedup_ratio: f64,
410}
411
412#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
414pub enum PerformanceClass {
415 Excellent,
417 Good,
419 Normal,
421 Acceptable,
423 Poor,
425 Critical,
427}
428
429#[derive(Debug)]
431pub struct BenchmarkSuite {
432 config: BenchmarkConfig,
433 results: HashMap<String, Vec<BenchmarkResult>>,
434}
435
436impl BenchmarkSuite {
437 #[must_use]
439 pub fn new(config: BenchmarkConfig) -> Self {
440 Self {
441 config,
442 results: HashMap::new(),
443 }
444 }
445
446 pub fn benchmark_transformer<T>(&mut self, name: &str, transformer: &T) -> SklResult<()>
448 where
449 T: for<'a> Transform<ArrayView2<'a, f64>, Array2<f64>>,
450 {
451 let mut strategy_results = Vec::new();
452
453 for &n_samples in &self.config.sample_sizes {
454 for &n_features in &self.config.feature_counts {
455 let data = self.generate_data(n_samples, n_features);
456 let result = self.benchmark_single_transform(
457 &format!("{}_{}_{}_{}", name, "transform", n_samples, n_features),
458 transformer,
459 &data,
460 (n_samples, n_features),
461 )?;
462 strategy_results.push(result);
463 }
464 }
465
466 self.results.insert(name.to_string(), strategy_results);
467 Ok(())
468 }
469
470 fn benchmark_single_transform<T>(
472 &self,
473 bench_name: &str,
474 transformer: &T,
475 data: &Array2<f64>,
476 dimensions: (usize, usize),
477 ) -> SklResult<BenchmarkResult>
478 where
479 T: for<'a> Transform<ArrayView2<'a, f64>, Array2<f64>>,
480 {
481 let mut times = Vec::new();
482
483 for _ in 0..self.config.warmup_iterations {
485 let _ = transformer.transform(&data.view())?;
486 }
487
488 for _ in 0..self.config.iterations {
490 let start = Instant::now();
491 let _ = transformer.transform(&data.view())?;
492 times.push(start.elapsed());
493 }
494
495 let mut result = BenchmarkResult::new(bench_name.to_string(), times, dimensions);
496
497 if self.config.measure_throughput {
499 let samples_per_sec = dimensions.0 as f64 / result.mean_time.as_secs_f64();
500 result = result.with_throughput(samples_per_sec);
501 }
502
503 if self.config.measure_memory {
505 let memory_usage = self.estimate_memory_usage(dimensions);
506 result = result.with_memory_usage(memory_usage);
507 }
508
509 Ok(result)
510 }
511
512 pub fn benchmark_composition_strategies(&mut self) -> SklResult<()> {
514 let strategies = vec![
518 "sequential_pipeline",
519 "parallel_feature_union",
520 "dag_pipeline",
521 "zero_cost_composition",
522 ];
523
524 for strategy in strategies {
525 let mut strategy_results = Vec::new();
526
527 for &n_samples in &self.config.sample_sizes {
528 for &n_features in &self.config.feature_counts {
529 let base_time =
531 Duration::from_micros(100 + (n_samples * n_features / 1000) as u64);
532 let strategy_multiplier = match strategy {
533 "sequential_pipeline" => 1.0,
534 "parallel_feature_union" => 0.6, "dag_pipeline" => 0.8, "zero_cost_composition" => 0.4, _ => 1.0,
538 };
539
540 let adjusted_time = Duration::from_nanos(
541 (base_time.as_nanos() as f64 * strategy_multiplier) as u64,
542 );
543
544 let times = vec![adjusted_time; self.config.iterations];
545 let mut result = BenchmarkResult::new(
546 format!(
547 "{}_{}_{}_{}",
548 strategy, "composition", n_samples, n_features
549 ),
550 times,
551 (n_samples, n_features),
552 );
553
554 if self.config.measure_throughput {
555 let throughput = n_samples as f64 / adjusted_time.as_secs_f64();
556 result = result.with_throughput(throughput);
557 }
558
559 if self.config.measure_memory {
560 let memory_usage = self.estimate_memory_usage((n_samples, n_features));
561 result = result.with_memory_usage(memory_usage);
562 }
563
564 strategy_results.push(result);
565 }
566 }
567
568 self.results.insert(strategy.to_string(), strategy_results);
569 }
570
571 Ok(())
572 }
573
574 fn generate_data(&self, n_samples: usize, n_features: usize) -> Array2<f64> {
576 use scirs2_core::random::rngs::StdRng;
577 use scirs2_core::random::SeedableRng;
578
579 let mut rng = StdRng::seed_from_u64(42);
580 Array2::from_shape_fn((n_samples, n_features), |_| rng.gen_range(-1.0..1.0))
581 }
582
583 fn estimate_memory_usage(&self, dimensions: (usize, usize)) -> MemoryUsage {
585 let (n_samples, n_features) = dimensions;
586 let data_size_mb = (n_samples * n_features * 8) as f64 / (1024.0 * 1024.0);
587
588 MemoryUsage {
590 peak_usage_mb: data_size_mb * 2.5, average_usage_mb: data_size_mb * 1.8,
592 allocations: n_samples + n_features,
593 deallocations: n_samples + n_features,
594 allocation_efficiency: 0.95, memory_leaks_bytes: 0, }
597 }
598
599 #[must_use]
601 pub fn results(&self) -> &HashMap<String, Vec<BenchmarkResult>> {
602 &self.results
603 }
604
605 #[must_use]
607 pub fn comparison_report(&self) -> BenchmarkReport {
608 BenchmarkReport::new(self.results.clone())
609 }
610
611 #[must_use]
613 pub fn best_strategy(&self, dimensions: (usize, usize)) -> Option<(&str, &BenchmarkResult)> {
614 let mut best_strategy = None;
615 let mut best_score = 0.0;
616
617 for (strategy_name, results) in &self.results {
618 for result in results {
619 if result.data_dimensions == dimensions {
620 let score = result.performance_score();
621 if score > best_score {
622 best_score = score;
623 best_strategy = Some((strategy_name.as_str(), result));
624 }
625 }
626 }
627 }
628
629 best_strategy
630 }
631}
632
633#[derive(Debug, Clone)]
635pub struct BenchmarkReport {
636 pub strategy_results: HashMap<String, Vec<BenchmarkResult>>,
638 pub performance_rankings: Vec<(String, f64)>,
640 pub scalability_analysis: HashMap<String, ScalabilityMetrics>,
642}
643
644impl BenchmarkReport {
645 #[must_use]
647 pub fn new(strategy_results: HashMap<String, Vec<BenchmarkResult>>) -> Self {
648 let performance_rankings = Self::calculate_performance_rankings(&strategy_results);
649 let scalability_analysis = Self::analyze_scalability(&strategy_results);
650
651 Self {
652 strategy_results,
653 performance_rankings,
654 scalability_analysis,
655 }
656 }
657
658 fn calculate_performance_rankings(
660 results: &HashMap<String, Vec<BenchmarkResult>>,
661 ) -> Vec<(String, f64)> {
662 let mut rankings = Vec::new();
663
664 for (strategy, strategy_results) in results {
665 let avg_score = strategy_results
666 .iter()
667 .map(BenchmarkResult::performance_score)
668 .sum::<f64>()
669 / strategy_results.len() as f64;
670 rankings.push((strategy.clone(), avg_score));
671 }
672
673 rankings.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
674 rankings
675 }
676
677 fn analyze_scalability(
679 results: &HashMap<String, Vec<BenchmarkResult>>,
680 ) -> HashMap<String, ScalabilityMetrics> {
681 let mut analysis = HashMap::new();
682
683 for (strategy, strategy_results) in results {
684 let mut sample_scalability = Vec::new();
685 let mut feature_scalability = Vec::new();
686
687 for result in strategy_results {
689 let (n_samples, n_features) = result.data_dimensions;
690 let time_per_sample = result.mean_time.as_secs_f64() / n_samples as f64;
691 let time_per_feature = result.mean_time.as_secs_f64() / n_features as f64;
692
693 sample_scalability.push((n_samples, time_per_sample));
694 feature_scalability.push((n_features, time_per_feature));
695 }
696
697 let metrics = ScalabilityMetrics {
698 sample_complexity: Self::estimate_complexity(&sample_scalability),
699 feature_complexity: Self::estimate_complexity(&feature_scalability),
700 memory_efficiency: strategy_results
701 .iter()
702 .filter_map(|r| r.memory_usage.as_ref())
703 .map(|m| m.peak_usage_mb)
704 .sum::<f64>()
705 / strategy_results.len() as f64,
706 };
707
708 analysis.insert(strategy.clone(), metrics);
709 }
710
711 analysis
712 }
713
714 fn estimate_complexity(data_points: &[(usize, f64)]) -> ComplexityClass {
716 if data_points.len() < 2 {
717 return ComplexityClass::Unknown;
718 }
719
720 let mut growth_ratios = Vec::new();
723
724 for i in 1..data_points.len() {
725 let (size1, time1) = data_points[i - 1];
726 let (size2, time2) = data_points[i];
727
728 if size1 > 0 && time1 > 0.0 {
729 let size_ratio = size2 as f64 / size1 as f64;
730 let time_ratio = time2 / time1;
731
732 if size_ratio > 1.0 {
733 growth_ratios.push(time_ratio / size_ratio);
734 }
735 }
736 }
737
738 if growth_ratios.is_empty() {
739 return ComplexityClass::Unknown;
740 }
741
742 let avg_growth = growth_ratios.iter().sum::<f64>() / growth_ratios.len() as f64;
743
744 match avg_growth {
745 x if x < 1.2 => ComplexityClass::Linear,
746 x if x < 2.0 => ComplexityClass::LogLinear,
747 x if x < 4.0 => ComplexityClass::Quadratic,
748 _ => ComplexityClass::Higher,
749 }
750 }
751
752 #[must_use]
754 pub fn summary(&self) -> String {
755 let mut summary = String::new();
756 summary.push_str("Benchmark Summary\n");
757 summary.push_str("================\n\n");
758
759 summary.push_str("Performance Rankings:\n");
760 for (i, (strategy, score)) in self.performance_rankings.iter().enumerate() {
761 let _ = write!(summary, "{}. {} (score: {:.3})\n", i + 1, strategy, score);
762 }
763
764 summary.push_str("\nScalability Analysis:\n");
765 for (strategy, metrics) in &self.scalability_analysis {
766 let _ = write!(
767 summary,
768 "{}: Sample complexity: {:?}, Feature complexity: {:?}\n",
769 strategy, metrics.sample_complexity, metrics.feature_complexity
770 );
771 }
772
773 summary
774 }
775
776 #[must_use]
778 pub fn recommendations(&self, use_case: UseCase) -> Vec<String> {
779 let mut recommendations = Vec::new();
780
781 match use_case {
782 UseCase::HighThroughput => {
783 let mut throughput_rankings = self
785 .strategy_results
786 .iter()
787 .map(|(name, results)| {
788 let avg_throughput =
789 results.iter().filter_map(|r| r.throughput).sum::<f64>()
790 / results.len() as f64;
791 (name, avg_throughput)
792 })
793 .collect::<Vec<_>>();
794
795 throughput_rankings.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
796
797 for (strategy, throughput) in throughput_rankings.iter().take(3) {
798 recommendations.push(format!(
799 "{strategy} (throughput: {throughput:.2} samples/sec)"
800 ));
801 }
802 }
803 UseCase::LowLatency => {
804 let mut latency_rankings = self
806 .strategy_results
807 .iter()
808 .map(|(name, results)| {
809 let avg_latency = results
810 .iter()
811 .map(|r| r.mean_time.as_secs_f64())
812 .sum::<f64>()
813 / results.len() as f64;
814 (name, avg_latency)
815 })
816 .collect::<Vec<_>>();
817
818 latency_rankings.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
819
820 for (strategy, latency) in latency_rankings.iter().take(3) {
821 recommendations.push(format!(
822 "{} (latency: {:.3}ms)",
823 strategy,
824 latency * 1000.0
825 ));
826 }
827 }
828 UseCase::MemoryConstrained => {
829 let mut memory_rankings = self
831 .strategy_results
832 .iter()
833 .map(|(name, results)| {
834 let avg_memory = results
835 .iter()
836 .filter_map(|r| r.memory_usage.as_ref())
837 .map(|m| m.peak_usage_mb)
838 .sum::<f64>()
839 / results.len() as f64;
840 (name, avg_memory)
841 })
842 .collect::<Vec<_>>();
843
844 memory_rankings.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
845
846 for (strategy, memory) in memory_rankings.iter().take(3) {
847 recommendations.push(format!("{strategy} (memory: {memory:.2} MB)"));
848 }
849 }
850 }
851
852 recommendations
853 }
854}
855
856#[derive(Debug, Clone)]
858pub struct ScalabilityMetrics {
859 pub sample_complexity: ComplexityClass,
861 pub feature_complexity: ComplexityClass,
863 pub memory_efficiency: f64,
865}
866
867#[derive(Debug, Clone, PartialEq)]
869pub enum ComplexityClass {
870 Constant,
872 Logarithmic,
874 Linear,
876 LogLinear,
878 Quadratic,
880 Higher,
882 Unknown,
884}
885
886#[derive(Debug, Clone, PartialEq)]
888pub enum UseCase {
889 HighThroughput,
891 LowLatency,
893 MemoryConstrained,
895}
896
897pub mod advanced_benchmarking {
899 use super::{BTreeMap, BenchmarkResult, HashMap};
900
901 pub struct AdvancedBenchmarkAnalyzer {
903 results_database: HashMap<String, Vec<BenchmarkResult>>,
904 statistical_models: HashMap<String, StatisticalModel>,
905 trend_analyzer: TrendAnalyzer,
906 }
907
908 #[derive(Debug, Clone)]
910 pub struct StatisticalModel {
911 pub model_type: ModelType,
912 pub coefficients: Vec<f64>,
913 pub r_squared: f64,
914 pub confidence_intervals: Vec<(f64, f64)>,
915 pub prediction_accuracy: f64,
916 }
917
918 #[derive(Debug, Clone, PartialEq, Eq)]
920 pub enum ModelType {
921 Linear,
923 Polynomial,
925 Exponential,
927 Logarithmic,
929 PowerLaw,
931 }
932
933 pub struct TrendAnalyzer {
935 historical_data: BTreeMap<chrono::DateTime<chrono::Utc>, f64>,
936 trend_models: HashMap<String, TrendModel>,
937 }
938
939 #[derive(Debug, Clone)]
941 pub struct TrendModel {
942 pub trend_direction: TrendDirection,
943 pub slope: f64,
944 pub seasonal_component: Option<f64>,
945 pub confidence: f64,
946 pub forecast_horizon: chrono::Duration,
947 }
948
949 #[derive(Debug, Clone, PartialEq, Eq)]
951 pub enum TrendDirection {
952 Improving,
954 Degrading,
956 Stable,
958 Cyclical,
960 Unknown,
962 }
963
964 pub struct ResourceEfficiencyAnalyzer {
966 cpu_profiles: HashMap<String, CpuProfile>,
967 memory_profiles: HashMap<String, MemoryProfile>,
968 energy_profiles: HashMap<String, EnergyProfile>,
969 }
970
971 #[derive(Debug, Clone)]
973 pub struct CpuProfile {
974 pub utilization_history: Vec<f64>,
975 pub peak_utilization: f64,
976 pub average_utilization: f64,
977 pub efficiency_score: f64,
978 pub hotspots: Vec<String>,
979 }
980
981 #[derive(Debug, Clone)]
983 pub struct MemoryProfile {
984 pub allocation_pattern: AllocationPattern,
985 pub peak_usage_mb: f64,
986 pub average_usage_mb: f64,
987 pub fragmentation_score: f64,
988 pub gc_impact: f64,
989 }
990
991 #[derive(Debug, Clone, PartialEq, Eq)]
993 pub enum AllocationPattern {
994 Constant,
996 Linear,
998 Exponential,
1000 Spiky,
1002 Cyclical,
1004 }
1005
1006 #[derive(Debug, Clone)]
1008 pub struct EnergyProfile {
1009 pub total_energy_joules: f64,
1010 pub average_power_watts: f64,
1011 pub efficiency_score: f64,
1012 pub carbon_footprint_kg: f64,
1013 }
1014
1015 pub struct ComparativeBenchmarkAnalysis {
1017 pub baseline_component: String,
1018 pub comparison_components: Vec<String>,
1019 pub metrics: Vec<ComparisonMetric>,
1020 pub statistical_significance: f64,
1021 pub effect_sizes: HashMap<String, f64>,
1022 pub confidence_intervals: HashMap<String, (f64, f64)>,
1023 }
1024
1025 #[derive(Debug, Clone)]
1027 pub struct ComparisonMetric {
1028 pub name: String,
1029 pub baseline_value: f64,
1030 pub comparison_values: HashMap<String, f64>,
1031 pub relative_improvements: HashMap<String, f64>,
1032 pub statistical_tests: HashMap<String, StatisticalTest>,
1033 }
1034
1035 #[derive(Debug, Clone)]
1037 pub struct StatisticalTest {
1038 pub test_type: TestType,
1039 pub p_value: f64,
1040 pub test_statistic: f64,
1041 pub is_significant: bool,
1042 pub effect_size: f64,
1043 }
1044
1045 #[derive(Debug, Clone, PartialEq, Eq)]
1047 pub enum TestType {
1048 TTest,
1050 WilcoxonTest,
1052 MannWhitneyU,
1054 KruskalWallis,
1056 ANOVA,
1058 }
1059
1060 impl Default for AdvancedBenchmarkAnalyzer {
1061 fn default() -> Self {
1062 Self::new()
1063 }
1064 }
1065
1066 impl AdvancedBenchmarkAnalyzer {
1067 #[must_use]
1069 pub fn new() -> Self {
1070 Self {
1071 results_database: HashMap::new(),
1072 statistical_models: HashMap::new(),
1073 trend_analyzer: TrendAnalyzer::new(),
1074 }
1075 }
1076
1077 pub fn add_results(&mut self, component_name: String, results: Vec<BenchmarkResult>) {
1079 self.results_database.insert(component_name, results);
1080 }
1081
1082 pub fn build_model(&mut self, component_name: &str) -> Option<StatisticalModel> {
1084 if let Some(results) = self.results_database.get(component_name) {
1085 let data_points: Vec<(f64, f64)> = results
1086 .iter()
1087 .enumerate()
1088 .map(|(i, result)| (i as f64, result.mean_time.as_millis() as f64))
1089 .collect();
1090
1091 let model = self.fit_model(&data_points);
1092 self.statistical_models
1093 .insert(component_name.to_string(), model.clone());
1094 Some(model)
1095 } else {
1096 None
1097 }
1098 }
1099
1100 #[must_use]
1102 pub fn predict_performance(&self, component_name: &str, input_size: f64) -> Option<f64> {
1103 self.statistical_models
1104 .get(component_name)
1105 .map(|model| self.apply_model(model, input_size))
1106 }
1107
1108 pub fn analyze_trends(&mut self, component_name: &str) -> Option<TrendModel> {
1110 self.trend_analyzer.analyze_component_trends(component_name)
1111 }
1112
1113 #[must_use]
1115 pub fn generate_analysis_report(&self) -> AdvancedAnalysisReport {
1116 let mut component_analyses = HashMap::new();
1117
1118 for (component_name, results) in &self.results_database {
1119 let analysis = ComponentAnalysis {
1120 component_name: component_name.clone(),
1121 total_benchmarks: results.len(),
1122 performance_model: self.statistical_models.get(component_name).cloned(),
1123 trend_analysis: None, efficiency_scores: self.calculate_efficiency_scores(results),
1125 recommendations: self
1126 .generate_component_recommendations(component_name, results),
1127 };
1128 component_analyses.insert(component_name.clone(), analysis);
1129 }
1130
1131 AdvancedAnalysisReport {
1133 analysis_timestamp: chrono::Utc::now(),
1134 total_components: component_analyses.len(),
1135 component_analyses,
1136 cross_component_insights: self.generate_cross_component_insights(),
1137 optimization_recommendations: self.generate_optimization_recommendations(),
1138 }
1139 }
1140
1141 #[must_use]
1143 pub fn comparative_analysis(
1144 &self,
1145 baseline: &str,
1146 comparisons: &[&str],
1147 ) -> Option<ComparativeBenchmarkAnalysis> {
1148 if let Some(baseline_results) = self.results_database.get(baseline) {
1149 let mut comparison_values = HashMap::new();
1150 let mut effect_sizes = HashMap::new();
1151
1152 for &component in comparisons {
1153 if let Some(comp_results) = self.results_database.get(component) {
1154 let baseline_mean = self.calculate_mean_performance(baseline_results);
1155 let comp_mean = self.calculate_mean_performance(comp_results);
1156
1157 comparison_values.insert(component.to_string(), comp_mean);
1158
1159 let effect_size =
1161 self.calculate_effect_size(baseline_results, comp_results);
1162 effect_sizes.insert(component.to_string(), effect_size);
1163 }
1164 }
1165
1166 Some(ComparativeBenchmarkAnalysis {
1167 baseline_component: baseline.to_string(),
1168 comparison_components: comparisons.iter().map(|s| (*s).to_string()).collect(),
1169 metrics: vec![], statistical_significance: 0.95, effect_sizes,
1172 confidence_intervals: HashMap::new(), })
1174 } else {
1175 None
1176 }
1177 }
1178
1179 fn fit_model(&self, data: &[(f64, f64)]) -> StatisticalModel {
1182 let n = data.len() as f64;
1184 let sum_x: f64 = data.iter().map(|(x, _)| x).sum();
1185 let sum_y: f64 = data.iter().map(|(_, y)| y).sum();
1186 let sum_xy: f64 = data.iter().map(|(x, y)| x * y).sum();
1187 let sum_x2: f64 = data.iter().map(|(x, _)| x * x).sum();
1188
1189 let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
1190 let intercept = (sum_y - slope * sum_x) / n;
1191
1192 let mean_y = sum_y / n;
1194 let ss_tot: f64 = data.iter().map(|(_, y)| (y - mean_y).powi(2)).sum();
1195 let ss_res: f64 = data
1196 .iter()
1197 .map(|(x, y)| (y - (slope * x + intercept)).powi(2))
1198 .sum();
1199 let r_squared = 1.0 - (ss_res / ss_tot);
1200
1201 StatisticalModel {
1203 model_type: ModelType::Linear,
1204 coefficients: vec![intercept, slope],
1205 r_squared,
1206 confidence_intervals: vec![], prediction_accuracy: r_squared,
1208 }
1209 }
1210
1211 fn apply_model(&self, model: &StatisticalModel, input: f64) -> f64 {
1212 match model.model_type {
1213 ModelType::Linear => model.coefficients[0] + model.coefficients[1] * input,
1214 ModelType::Polynomial => model
1215 .coefficients
1216 .iter()
1217 .enumerate()
1218 .map(|(i, &coef)| coef * input.powi(i as i32))
1219 .sum(),
1220 _ => model.coefficients[0] + model.coefficients[1] * input, }
1222 }
1223
1224 fn calculate_mean_performance(&self, results: &[BenchmarkResult]) -> f64 {
1225 results
1226 .iter()
1227 .map(|r| r.mean_time.as_millis() as f64)
1228 .sum::<f64>()
1229 / results.len() as f64
1230 }
1231
1232 fn calculate_effect_size(
1233 &self,
1234 baseline: &[BenchmarkResult],
1235 comparison: &[BenchmarkResult],
1236 ) -> f64 {
1237 let baseline_mean = self.calculate_mean_performance(baseline);
1238 let comparison_mean = self.calculate_mean_performance(comparison);
1239
1240 let pooled_std = 1.0; (comparison_mean - baseline_mean) / pooled_std
1243 }
1244
1245 fn calculate_efficiency_scores(
1246 &self,
1247 _results: &[BenchmarkResult],
1248 ) -> HashMap<String, f64> {
1249 let mut scores = HashMap::new();
1250 scores.insert("cpu_efficiency".to_string(), 0.85);
1251 scores.insert("memory_efficiency".to_string(), 0.78);
1252 scores.insert("energy_efficiency".to_string(), 0.82);
1253 scores
1254 }
1255
1256 fn generate_component_recommendations(
1257 &self,
1258 _component_name: &str,
1259 _results: &[BenchmarkResult],
1260 ) -> Vec<String> {
1261 vec![
1262 "Consider optimizing memory allocation patterns".to_string(),
1263 "Investigate opportunities for parallel processing".to_string(),
1264 "Profile CPU-intensive operations for bottlenecks".to_string(),
1265 ]
1266 }
1267
1268 fn generate_cross_component_insights(&self) -> Vec<String> {
1269 vec![
1270 "Linear scaling algorithms show better performance on large datasets".to_string(),
1271 "Memory-intensive components benefit from batch processing".to_string(),
1272 "CPU-bound operations should prioritize algorithmic optimizations".to_string(),
1273 ]
1274 }
1275
1276 fn generate_optimization_recommendations(&self) -> Vec<String> {
1277 vec![
1278 "Implement adaptive batch sizing based on available memory".to_string(),
1279 "Consider using SIMD instructions for vectorizable operations".to_string(),
1280 "Implement memory pooling for frequently allocated objects".to_string(),
1281 ]
1282 }
1283 }
1284
1285 impl TrendAnalyzer {
1286 #[must_use]
1287 pub fn new() -> Self {
1288 Self {
1289 historical_data: BTreeMap::new(),
1290 trend_models: HashMap::new(),
1291 }
1292 }
1293
1294 #[must_use]
1295 pub fn analyze_component_trends(&self, _component_name: &str) -> Option<TrendModel> {
1296 Some(TrendModel {
1298 trend_direction: TrendDirection::Stable,
1299 slope: 0.01,
1300 seasonal_component: None,
1301 confidence: 0.8,
1302 forecast_horizon: chrono::Duration::days(30),
1303 })
1304 }
1305 }
1306
1307 #[derive(Debug, Clone)]
1309 pub struct ComponentAnalysis {
1310 pub component_name: String,
1311 pub total_benchmarks: usize,
1312 pub performance_model: Option<StatisticalModel>,
1313 pub trend_analysis: Option<TrendModel>,
1314 pub efficiency_scores: HashMap<String, f64>,
1315 pub recommendations: Vec<String>,
1316 }
1317
1318 #[derive(Debug, Clone)]
1320 pub struct AdvancedAnalysisReport {
1321 pub analysis_timestamp: chrono::DateTime<chrono::Utc>,
1322 pub total_components: usize,
1323 pub component_analyses: HashMap<String, ComponentAnalysis>,
1324 pub cross_component_insights: Vec<String>,
1325 pub optimization_recommendations: Vec<String>,
1326 }
1327
1328 impl Default for TrendAnalyzer {
1329 fn default() -> Self {
1330 Self::new()
1331 }
1332 }
1333}
1334
1335#[allow(non_snake_case)]
1336#[cfg(test)]
1337mod tests {
1338 use super::*;
1339 use crate::mock::MockTransformer;
1340
1341 #[test]
1342 fn test_benchmark_config() {
1343 let config = BenchmarkConfig::new()
1344 .iterations(50)
1345 .sample_sizes(vec![100, 500])
1346 .feature_counts(vec![5, 10]);
1347
1348 assert_eq!(config.iterations, 50);
1349 assert_eq!(config.sample_sizes, vec![100, 500]);
1350 assert_eq!(config.feature_counts, vec![5, 10]);
1351 }
1352
1353 #[test]
1354 fn test_benchmark_result() {
1355 let times = vec![
1356 Duration::from_millis(10),
1357 Duration::from_millis(12),
1358 Duration::from_millis(11),
1359 ];
1360
1361 let result = BenchmarkResult::new("test".to_string(), times, (100, 5));
1362 assert_eq!(result.name, "test");
1363 assert_eq!(result.data_dimensions, (100, 5));
1364 assert!(result.performance_score() > 0.0);
1365 }
1366
1367 #[test]
1368 fn test_benchmark_suite() {
1369 let config = BenchmarkConfig::new()
1370 .iterations(5)
1371 .sample_sizes(vec![10])
1372 .feature_counts(vec![3]);
1373
1374 let mut suite = BenchmarkSuite::new(config);
1375 let transformer = MockTransformer::new();
1376
1377 suite.benchmark_transformer("mock", &transformer).unwrap();
1378 assert!(suite.results().contains_key("mock"));
1379 }
1380
1381 #[test]
1382 fn test_benchmark_report() {
1383 let mut results = HashMap::new();
1384 let times = vec![Duration::from_millis(10); 3];
1385 let result = BenchmarkResult::new("test".to_string(), times, (100, 5));
1386 results.insert("strategy1".to_string(), vec![result]);
1387
1388 let report = BenchmarkReport::new(results);
1389 assert_eq!(report.performance_rankings.len(), 1);
1390 assert!(!report.summary().is_empty());
1391 }
1392
1393 #[test]
1394 fn test_complexity_estimation() {
1395 let data = vec![(10, 0.1), (20, 0.2), (30, 0.3)];
1396 let complexity = BenchmarkReport::estimate_complexity(&data);
1397 assert_eq!(complexity, ComplexityClass::Linear);
1398 }
1399
1400 #[test]
1401 fn test_use_case_recommendations() {
1402 let mut results = HashMap::new();
1403 let times = vec![Duration::from_millis(10); 3];
1404 let result =
1405 BenchmarkResult::new("test".to_string(), times, (100, 5)).with_throughput(1000.0);
1406 results.insert("fast_strategy".to_string(), vec![result]);
1407
1408 let report = BenchmarkReport::new(results);
1409 let recommendations = report.recommendations(UseCase::HighThroughput);
1410 assert!(!recommendations.is_empty());
1411 }
1412}