1use crate::error::Result;
12use quote::quote;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::fmt;
16use std::time::{Duration, Instant};
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct AutoBenchmarkConfig {
25 pub benchmark_types: Vec<BenchmarkType>,
26 pub scaling_dimensions: Vec<ScalingDimension>,
27 pub performance_targets: PerformanceTargets,
28 pub comparison_baselines: Vec<Baseline>,
29 pub statistical_config: StatisticalConfig,
30 pub output_formats: Vec<OutputFormat>,
31 pub regression_detection: RegressionDetectionConfig,
32 pub optimization_hints: bool,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37pub enum BenchmarkType {
38 Microbenchmark, IntegrationBenchmark, ScalabilityBenchmark, MemoryBenchmark, LatencyBenchmark, ThroughputBenchmark, AccuracyBenchmark, RegressionBenchmark, ComparativeBenchmark, StressBenchmark, }
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct ScalingDimension {
53 pub name: String,
54 pub parameter_path: String, pub values: ScalingValues,
56 pub expected_complexity: ComplexityClass,
57 pub units: String,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub enum ScalingValues {
63 Linear { start: f64, end: f64, steps: usize },
64 Exponential { start: f64, base: f64, steps: usize },
65 Custom(Vec<f64>),
66 Fibonacci { max_value: f64 },
67 PowersOfTwo { min_power: i32, max_power: i32 },
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub enum ComplexityClass {
73 Constant, Logarithmic, Linear, Linearithmic, Quadratic, Cubic, Exponential, Factorial, Custom(String), }
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct PerformanceTargets {
87 pub max_latency_ms: f64,
88 pub min_throughput_ops_sec: f64,
89 pub max_memory_mb: f64,
90 pub max_accuracy_loss_percent: f64,
91 pub regression_threshold_percent: f64,
92 pub stability_coefficient_of_variation: f64, }
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct Baseline {
98 pub name: String,
99 pub implementation: BaselineType,
100 pub expected_performance_ratio: f64, pub accuracy_expectation: AccuracyExpectation,
102 pub availability: BaselineAvailability,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
107pub enum BaselineType {
108 ScikitLearn,
109 NumPy,
110 Scipy,
111 NativeRust,
112 BLAS,
113 LAPACK,
114 Custom(String),
115 Theoretical, }
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub enum AccuracyExpectation {
121 Identical,
122 WithinTolerance(f64),
123 Approximate(f64), Different, }
126
127#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
129pub enum BaselineAvailability {
130 Always,
131 ConditionalOnFeature(String),
132 Manual, Unavailable,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct StatisticalConfig {
139 pub min_iterations: usize,
140 pub max_iterations: usize,
141 pub warmup_iterations: usize,
142 pub confidence_level: f64, pub outlier_detection: OutlierDetectionMethod,
144 pub measurement_precision: MeasurementPrecision,
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
149pub enum OutlierDetectionMethod {
150 None,
151 IQR, ZScore, ModifiedZScore, Isolation, Custom(String),
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct MeasurementPrecision {
161 pub timing_precision_ns: u64,
162 pub memory_precision_bytes: u64,
163 pub accuracy_precision_digits: u8,
164 pub min_relative_precision: f64, }
166
167#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
169pub enum OutputFormat {
170 Json,
171 Csv,
172 Html,
173 Markdown,
174 PlotlyJson,
175 CriterionCompatible,
176 Custom(String),
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct RegressionDetectionConfig {
182 pub enabled: bool,
183 pub historical_data_path: String,
184 pub regression_threshold_percent: f64,
185 pub minimum_effect_size: f64,
186 pub statistical_test: StatisticalTest,
187 pub alert_on_regression: bool,
188}
189
190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
192pub enum StatisticalTest {
193 TTest,
194 MannWhitneyU,
195 WelchTTest,
196 Bootstrap,
197 PermutationTest,
198}
199
200pub struct BenchmarkGenerator {
206 config: AutoBenchmarkConfig,
207 generated_benchmarks: Vec<GeneratedBenchmark>,
208 #[allow(dead_code)]
209 performance_models: HashMap<String, PerformanceModel>,
210}
211
212impl BenchmarkGenerator {
213 pub fn new(config: AutoBenchmarkConfig) -> Self {
215 Self {
216 config,
217 generated_benchmarks: Vec::new(),
218 performance_models: HashMap::new(),
219 }
220 }
221
222 pub fn generate_for_type<T>(&mut self, type_name: &str) -> Result<Vec<GeneratedBenchmark>> {
224 let mut benchmarks = Vec::new();
225
226 for benchmark_type in &self.config.benchmark_types {
227 let benchmark = match benchmark_type {
228 BenchmarkType::Microbenchmark => self.generate_microbenchmark(type_name)?,
229 BenchmarkType::IntegrationBenchmark => {
230 self.generate_integration_benchmark(type_name)?
231 }
232 BenchmarkType::ScalabilityBenchmark => {
233 self.generate_scalability_benchmark(type_name)?
234 }
235 BenchmarkType::MemoryBenchmark => self.generate_memory_benchmark(type_name)?,
236 BenchmarkType::LatencyBenchmark => self.generate_latency_benchmark(type_name)?,
237 BenchmarkType::ThroughputBenchmark => {
238 self.generate_throughput_benchmark(type_name)?
239 }
240 BenchmarkType::AccuracyBenchmark => self.generate_accuracy_benchmark(type_name)?,
241 BenchmarkType::RegressionBenchmark => {
242 self.generate_regression_benchmark(type_name)?
243 }
244 BenchmarkType::ComparativeBenchmark => {
245 self.generate_comparative_benchmark(type_name)?
246 }
247 BenchmarkType::StressBenchmark => self.generate_stress_benchmark(type_name)?,
248 };
249
250 benchmarks.push(benchmark);
251 }
252
253 self.generated_benchmarks.extend(benchmarks.clone());
254 Ok(benchmarks)
255 }
256
257 fn generate_microbenchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
259 let benchmark_name = format!("microbench_{}", type_name.to_lowercase());
260
261 let code = quote! {
262 use criterion::{criterion_group, criterion_main, Criterion, black_box};
263
264 fn #benchmark_name(c: &mut Criterion) {
265 let mut group = c.benchmark_group(stringify!(#type_name));
266
267 let test_data = generate_test_data();
269
270 group.bench_function("fit", |b| {
271 let mut model = #type_name::default();
272 b.iter(|| {
273 black_box(model.fit(&test_data.x, &test_data.y).unwrap())
274 })
275 });
276
277 group.bench_function("predict", |b| {
278 let model = #type_name::default().fit(&test_data.x, &test_data.y).unwrap();
279 b.iter(|| {
280 black_box(model.predict(&test_data.x_test).unwrap())
281 })
282 });
283
284 group.finish();
285 }
286
287 criterion_group!(benches, #benchmark_name);
288 criterion_main!(benches);
289 }
290 .to_string();
291
292 Ok(GeneratedBenchmark {
293 name: benchmark_name,
294 benchmark_type: BenchmarkType::Microbenchmark,
295 code,
296 setup_code: self.generate_setup_code(type_name),
297 dependencies: self.get_benchmark_dependencies(),
298 expected_performance: self
299 .estimate_performance(type_name, BenchmarkType::Microbenchmark),
300 scaling_analysis: None,
301 })
302 }
303
304 fn generate_integration_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
306 let benchmark_name = format!("integration_bench_{}", type_name.to_lowercase());
307
308 let code = quote! {
309 use criterion::{criterion_group, criterion_main, Criterion, black_box};
310
311 fn #benchmark_name(c: &mut Criterion) {
312 let mut group = c.benchmark_group("integration");
313
314 group.bench_function("full_pipeline", |b| {
316 b.iter(|| {
317 let (x_train, y_train, x_test, y_test) = load_and_preprocess_data();
319
320 let model = #type_name::default()
322 .fit(&x_train, &y_train)
323 .unwrap();
324
325 let predictions = model.predict(&x_test).unwrap();
327 let score = evaluate_predictions(&predictions, &y_test);
328
329 black_box(score)
330 })
331 });
332
333 group.finish();
334 }
335
336 criterion_group!(benches, #benchmark_name);
337 criterion_main!(benches);
338 }
339 .to_string();
340
341 Ok(GeneratedBenchmark {
342 name: benchmark_name,
343 benchmark_type: BenchmarkType::IntegrationBenchmark,
344 code,
345 setup_code: self.generate_setup_code(type_name),
346 dependencies: self.get_benchmark_dependencies(),
347 expected_performance: self
348 .estimate_performance(type_name, BenchmarkType::IntegrationBenchmark),
349 scaling_analysis: None,
350 })
351 }
352
353 fn generate_scalability_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
355 let benchmark_name = format!("scalability_bench_{}", type_name.to_lowercase());
356
357 let scaling_tests = self.config.scaling_dimensions.iter().map(|dim| {
358 let values = self.generate_scaling_values(&dim.values);
359 let param_name = &dim.name;
360
361 quote! {
362 for &value in &[#(#values),*] {
364 group.bench_with_input(
365 criterion::BenchmarkId::new(#param_name, value),
366 &value,
367 |b, &size| {
368 let test_data = generate_test_data_with_size(size as usize);
369 let mut model = #type_name::default();
370
371 b.iter(|| {
372 black_box(model.fit(&test_data.x, &test_data.y).unwrap())
373 })
374 }
375 );
376 }
377 }
378 });
379
380 let code = quote! {
381 use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId, black_box};
382
383 fn #benchmark_name(c: &mut Criterion) {
384 let mut group = c.benchmark_group("scalability");
385
386 #(#scaling_tests)*
387
388 group.finish();
389 }
390
391 criterion_group!(benches, #benchmark_name);
392 criterion_main!(benches);
393 }
394 .to_string();
395
396 Ok(GeneratedBenchmark {
397 name: benchmark_name,
398 benchmark_type: BenchmarkType::ScalabilityBenchmark,
399 code,
400 setup_code: self.generate_setup_code(type_name),
401 dependencies: self.get_benchmark_dependencies(),
402 expected_performance: self
403 .estimate_performance(type_name, BenchmarkType::ScalabilityBenchmark),
404 scaling_analysis: Some(self.generate_scaling_analysis()),
405 })
406 }
407
408 fn generate_memory_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
410 let benchmark_name = format!("memory_bench_{}", type_name.to_lowercase());
411
412 let code = quote! {
413 use criterion::{criterion_group, criterion_main, Criterion, black_box};
414 use std::alloc::{GlobalAlloc, Layout, System};
415 use std::sync::atomic::{AtomicUsize, Ordering};
416
417 struct MemoryTracker;
418
419 static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
420
421 unsafe impl GlobalAlloc for MemoryTracker {
422 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
423 let ret = System.alloc(layout);
424 if !ret.is_null() {
425 ALLOCATED.fetch_add(layout.size(), Ordering::SeqCst);
426 }
427 ret
428 }
429
430 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
431 System.dealloc(ptr, layout);
432 ALLOCATED.fetch_sub(layout.size(), Ordering::SeqCst);
433 }
434 }
435
436 #[global_allocator]
437 static GLOBAL: MemoryTracker = MemoryTracker;
438
439 fn #benchmark_name(c: &mut Criterion) {
440 let mut group = c.benchmark_group("memory");
441
442 group.bench_function("memory_usage", |b| {
443 b.iter_custom(|iters| {
444 let start_memory = ALLOCATED.load(Ordering::SeqCst);
445 let start_time = std::time::Instant::now();
446
447 for _ in 0..iters {
448 let test_data = generate_test_data();
449 let model = #type_name::default()
450 .fit(&test_data.x, &test_data.y)
451 .unwrap();
452 black_box(model);
453 }
454
455 let duration = start_time.elapsed();
456 let end_memory = ALLOCATED.load(Ordering::SeqCst);
457 let memory_used = end_memory.saturating_sub(start_memory);
458
459 println!("Memory used: {} bytes", memory_used);
460 duration
461 })
462 });
463
464 group.finish();
465 }
466
467 criterion_group!(benches, #benchmark_name);
468 criterion_main!(benches);
469 }
470 .to_string();
471
472 Ok(GeneratedBenchmark {
473 name: benchmark_name,
474 benchmark_type: BenchmarkType::MemoryBenchmark,
475 code,
476 setup_code: self.generate_setup_code(type_name),
477 dependencies: self.get_benchmark_dependencies(),
478 expected_performance: self
479 .estimate_performance(type_name, BenchmarkType::MemoryBenchmark),
480 scaling_analysis: None,
481 })
482 }
483
484 fn generate_latency_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
486 self.generate_simple_benchmark(type_name, BenchmarkType::LatencyBenchmark, "latency")
487 }
488
489 fn generate_throughput_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
490 self.generate_simple_benchmark(type_name, BenchmarkType::ThroughputBenchmark, "throughput")
491 }
492
493 fn generate_accuracy_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
494 self.generate_simple_benchmark(type_name, BenchmarkType::AccuracyBenchmark, "accuracy")
495 }
496
497 fn generate_regression_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
498 self.generate_simple_benchmark(type_name, BenchmarkType::RegressionBenchmark, "regression")
499 }
500
501 fn generate_comparative_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
502 self.generate_simple_benchmark(
503 type_name,
504 BenchmarkType::ComparativeBenchmark,
505 "comparative",
506 )
507 }
508
509 fn generate_stress_benchmark(&self, type_name: &str) -> Result<GeneratedBenchmark> {
510 self.generate_simple_benchmark(type_name, BenchmarkType::StressBenchmark, "stress")
511 }
512
513 fn generate_simple_benchmark(
515 &self,
516 type_name: &str,
517 benchmark_type: BenchmarkType,
518 prefix: &str,
519 ) -> Result<GeneratedBenchmark> {
520 let benchmark_name = format!("{}_{}", prefix, type_name.to_lowercase());
521
522 let code = format!(
523 r#"
524 use criterion::{{criterion_group, criterion_main, Criterion, black_box}};
525
526 fn {}(c: &mut Criterion) {{
527 let mut group = c.benchmark_group("{}");
528
529 group.bench_function("operation", |b| {{
530 let test_data = generate_test_data();
531 let mut model = {}::default();
532
533 b.iter(|| {{
534 black_box(model.fit(&test_data.x, &test_data.y).unwrap())
535 }})
536 }});
537
538 group.finish();
539 }}
540
541 criterion_group!(benches, {});
542 criterion_main!(benches);
543 "#,
544 benchmark_name, prefix, type_name, benchmark_name
545 );
546
547 Ok(GeneratedBenchmark {
548 name: benchmark_name,
549 benchmark_type: benchmark_type.clone(),
550 code,
551 setup_code: self.generate_setup_code(type_name),
552 dependencies: self.get_benchmark_dependencies(),
553 expected_performance: self.estimate_performance(type_name, benchmark_type.clone()),
554 scaling_analysis: None,
555 })
556 }
557
558 fn generate_setup_code(&self, type_name: &str) -> String {
560 format!(
561 r#"
562 use {}::*;
563
564 struct TestData {{
565 x: Array2<f64>,
566 y: Array1<f64>,
567 x_test: Array2<f64>,
568 y_test: Array1<f64>,
569 }}
570
571 fn generate_test_data() -> TestData {{
572 let n_samples = 1000;
573 let n_features = 20;
574
575 let x = Array2::random((n_samples, n_features), Normal::new(0.0, 1.0).unwrap());
576 let y = Array1::random(n_samples, Normal::new(0.0, 1.0).unwrap());
577 let x_test = Array2::random((100, n_features), Normal::new(0.0, 1.0).unwrap());
578 let y_test = Array1::random(100, Normal::new(0.0, 1.0).unwrap());
579
580 TestData {{ x, y, x_test, y_test }}
581 }}
582
583 fn generate_test_data_with_size(size: usize) -> TestData {{
584 let n_features = 20;
585
586 let x = Array2::random((size, n_features), Normal::new(0.0, 1.0).unwrap());
587 let y = Array1::random(size, Normal::new(0.0, 1.0).unwrap());
588 let x_test = Array2::random((size / 10, n_features), Normal::new(0.0, 1.0).unwrap());
589 let y_test = Array1::random(size / 10, Normal::new(0.0, 1.0).unwrap());
590
591 TestData {{ x, y, x_test, y_test }}
592 }}
593
594 fn load_and_preprocess_data() -> (Array2<f64>, Array1<f64>, Array2<f64>, Array1<f64>) {{
595 let test_data = generate_test_data();
596 (test_data.x, test_data.y, test_data.x_test, test_data.y_test)
597 }}
598
599 fn evaluate_predictions(predictions: &Array1<f64>, y_true: &Array1<f64>) -> f64 {{
600 // Mean squared error
601 let diff = predictions - y_true;
602 diff.mapv(|x| x * x).mean().unwrap()
603 }}
604 "#,
605 type_name
606 )
607 }
608
609 fn get_benchmark_dependencies(&self) -> Vec<String> {
611 vec![
612 "criterion".to_string(),
613 "ndarray".to_string(),
614 "ndarray-rand".to_string(),
615 "rand_distr".to_string(),
616 ]
617 }
618
619 fn estimate_performance(
621 &self,
622 _type_name: &str,
623 benchmark_type: BenchmarkType,
624 ) -> PerformanceEstimate {
625 PerformanceEstimate {
627 expected_latency_ms: match benchmark_type {
628 BenchmarkType::Microbenchmark => 1.0,
629 BenchmarkType::IntegrationBenchmark => 100.0,
630 BenchmarkType::ScalabilityBenchmark => 50.0,
631 _ => 10.0,
632 },
633 expected_throughput_ops_sec: 1000.0,
634 expected_memory_mb: 10.0,
635 confidence_interval: 0.95,
636 }
637 }
638
639 fn generate_scaling_values(&self, scaling_values: &ScalingValues) -> Vec<f64> {
641 match scaling_values {
642 ScalingValues::Linear { start, end, steps } => {
643 let step_size = (end - start) / (*steps as f64 - 1.0);
644 (0..*steps)
645 .map(|i| start + (i as f64) * step_size)
646 .collect()
647 }
648 ScalingValues::Exponential { start, base, steps } => {
649 (0..*steps).map(|i| start * base.powi(i as i32)).collect()
650 }
651 ScalingValues::Custom(values) => values.clone(),
652 ScalingValues::Fibonacci { max_value } => {
653 let mut fib = vec![1.0, 1.0];
654 while fib[fib.len() - 1] < *max_value {
655 let next = fib[fib.len() - 1] + fib[fib.len() - 2];
656 fib.push(next);
657 }
658 fib
659 }
660 ScalingValues::PowersOfTwo {
661 min_power,
662 max_power,
663 } => (*min_power..=*max_power).map(|p| 2.0_f64.powi(p)).collect(),
664 }
665 }
666
667 fn generate_scaling_analysis(&self) -> ScalingAnalysis {
669 ScalingAnalysis {
670 complexity_models: self
671 .config
672 .scaling_dimensions
673 .iter()
674 .map(|dim| {
675 ComplexityModel {
676 dimension: dim.name.clone(),
677 expected_complexity: dim.expected_complexity.clone(),
678 coefficients: vec![1.0, 0.1, 0.01], r_squared: 0.95,
680 }
681 })
682 .collect(),
683 performance_predictions: HashMap::new(),
684 optimization_recommendations: vec![],
685 }
686 }
687}
688
689#[derive(Debug, Clone, Serialize, Deserialize)]
695pub struct GeneratedBenchmark {
696 pub name: String,
697 pub benchmark_type: BenchmarkType,
698 pub code: String,
699 pub setup_code: String,
700 pub dependencies: Vec<String>,
701 pub expected_performance: PerformanceEstimate,
702 pub scaling_analysis: Option<ScalingAnalysis>,
703}
704
705#[derive(Debug, Clone, Serialize, Deserialize)]
707pub struct PerformanceEstimate {
708 pub expected_latency_ms: f64,
709 pub expected_throughput_ops_sec: f64,
710 pub expected_memory_mb: f64,
711 pub confidence_interval: f64,
712}
713
714#[derive(Debug, Clone, Serialize, Deserialize)]
716pub struct ScalingAnalysis {
717 pub complexity_models: Vec<ComplexityModel>,
718 pub performance_predictions: HashMap<String, f64>,
719 pub optimization_recommendations: Vec<String>,
720}
721
722#[derive(Debug, Clone, Serialize, Deserialize)]
724pub struct ComplexityModel {
725 pub dimension: String,
726 pub expected_complexity: ComplexityClass,
727 pub coefficients: Vec<f64>, pub r_squared: f64, }
730
731#[derive(Debug, Clone, Serialize, Deserialize)]
733pub struct PerformanceModel {
734 pub algorithm_name: String,
735 pub complexity_class: ComplexityClass,
736 pub base_performance: f64,
737 pub scaling_factors: HashMap<String, f64>,
738 pub confidence: f64,
739}
740
741pub struct BenchmarkExecutor {
747 results: Vec<BenchmarkResult>,
748 regression_detector: RegressionDetector,
749}
750
751impl Default for BenchmarkExecutor {
752 fn default() -> Self {
753 Self::new()
754 }
755}
756
757impl BenchmarkExecutor {
758 pub fn new() -> Self {
760 Self {
761 results: Vec::new(),
762 regression_detector: RegressionDetector::new(),
763 }
764 }
765
766 pub fn execute_benchmark(&mut self, benchmark: &GeneratedBenchmark) -> Result<BenchmarkResult> {
768 let _start_time = Instant::now();
769
770 let execution_time = Duration::from_millis(100);
773
774 let result = BenchmarkResult {
775 benchmark_name: benchmark.name.clone(),
776 benchmark_type: benchmark.benchmark_type.clone(),
777 execution_time,
778 memory_usage_bytes: 1024 * 1024, throughput_ops_sec: 1000.0,
780 accuracy_score: Some(0.95),
781 regression_detected: false,
782 performance_vs_baseline: 1.2, statistical_significance: 0.99,
784 };
785
786 self.results.push(result.clone());
787 Ok(result)
788 }
789
790 pub fn analyze_results(&mut self) -> AnalysisReport {
792 let regressions = self.regression_detector.detect_regressions(&self.results);
793 let recommendations = self.generate_optimization_recommendations();
794
795 AnalysisReport {
796 total_benchmarks: self.results.len(),
797 regressions_detected: regressions.len(),
798 average_performance_change: self.calculate_average_performance_change(),
799 recommendations,
800 detailed_results: self.results.clone(),
801 }
802 }
803
804 fn generate_optimization_recommendations(&self) -> Vec<OptimizationRecommendation> {
806 let mut recommendations = Vec::new();
807
808 for result in &self.results {
809 if result.performance_vs_baseline < 1.0 {
810 recommendations.push(OptimizationRecommendation {
811 benchmark_name: result.benchmark_name.clone(),
812 issue: "Performance below baseline".to_string(),
813 suggestion: "Consider algorithm optimization or SIMD vectorization".to_string(),
814 expected_improvement: 1.5,
815 implementation_effort: ImplementationEffort::Medium,
816 });
817 }
818 }
819
820 recommendations
821 }
822
823 fn calculate_average_performance_change(&self) -> f64 {
825 if self.results.is_empty() {
826 return 0.0;
827 }
828
829 let sum: f64 = self.results.iter().map(|r| r.performance_vs_baseline).sum();
830 sum / self.results.len() as f64
831 }
832}
833
834#[derive(Debug, Clone, Serialize, Deserialize)]
836pub struct BenchmarkResult {
837 pub benchmark_name: String,
838 pub benchmark_type: BenchmarkType,
839 pub execution_time: Duration,
840 pub memory_usage_bytes: u64,
841 pub throughput_ops_sec: f64,
842 pub accuracy_score: Option<f64>,
843 pub regression_detected: bool,
844 pub performance_vs_baseline: f64, pub statistical_significance: f64,
846}
847
848#[derive(Debug, Clone, Serialize, Deserialize)]
850pub struct AnalysisReport {
851 pub total_benchmarks: usize,
852 pub regressions_detected: usize,
853 pub average_performance_change: f64,
854 pub recommendations: Vec<OptimizationRecommendation>,
855 pub detailed_results: Vec<BenchmarkResult>,
856}
857
858#[derive(Debug, Clone, Serialize, Deserialize)]
860pub struct OptimizationRecommendation {
861 pub benchmark_name: String,
862 pub issue: String,
863 pub suggestion: String,
864 pub expected_improvement: f64,
865 pub implementation_effort: ImplementationEffort,
866}
867
868#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
870pub enum ImplementationEffort {
871 Low,
872 Medium,
873 High,
874 Expert,
875}
876
877pub struct RegressionDetector {
879 #[allow(dead_code)]
880 historical_data: HashMap<String, Vec<f64>>,
881 threshold: f64,
882}
883
884impl Default for RegressionDetector {
885 fn default() -> Self {
886 Self::new()
887 }
888}
889
890impl RegressionDetector {
891 pub fn new() -> Self {
892 Self {
893 historical_data: HashMap::new(),
894 threshold: 0.05, }
896 }
897
898 pub fn detect_regressions(&self, results: &[BenchmarkResult]) -> Vec<RegressionAlert> {
899 let mut alerts = Vec::new();
900
901 for result in results {
902 if result.performance_vs_baseline < (1.0 - self.threshold) {
903 alerts.push(RegressionAlert {
904 benchmark_name: result.benchmark_name.clone(),
905 performance_drop_percent: (1.0 - result.performance_vs_baseline) * 100.0,
906 severity: if result.performance_vs_baseline < 0.8 {
907 RegressionSeverity::High
908 } else {
909 RegressionSeverity::Medium
910 },
911 recommendation: "Investigate performance regression".to_string(),
912 });
913 }
914 }
915
916 alerts
917 }
918}
919
920#[derive(Debug, Clone, Serialize, Deserialize)]
922pub struct RegressionAlert {
923 pub benchmark_name: String,
924 pub performance_drop_percent: f64,
925 pub severity: RegressionSeverity,
926 pub recommendation: String,
927}
928
929#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
931pub enum RegressionSeverity {
932 Low,
933 Medium,
934 High,
935 Critical,
936}
937
938#[macro_export]
944macro_rules! auto_benchmark {
945 ($type:ty) => {
946 $crate::auto_benchmark_generation::generate_benchmarks_for_type::<$type>()
947 };
948 ($type:ty, $config:expr) => {
949 $crate::auto_benchmark_generation::generate_benchmarks_with_config::<$type>($config)
950 };
951}
952
953pub fn generate_benchmarks_for_type<T>() -> Result<Vec<GeneratedBenchmark>> {
955 let config = AutoBenchmarkConfig::default();
956 let mut generator = BenchmarkGenerator::new(config);
957 generator.generate_for_type::<T>(std::any::type_name::<T>())
958}
959
960pub fn generate_benchmarks_with_config<T>(
962 config: AutoBenchmarkConfig,
963) -> Result<Vec<GeneratedBenchmark>> {
964 let mut generator = BenchmarkGenerator::new(config);
965 generator.generate_for_type::<T>(std::any::type_name::<T>())
966}
967
968impl Default for AutoBenchmarkConfig {
973 fn default() -> Self {
974 Self {
975 benchmark_types: vec![
976 BenchmarkType::Microbenchmark,
977 BenchmarkType::IntegrationBenchmark,
978 BenchmarkType::ScalabilityBenchmark,
979 ],
980 scaling_dimensions: vec![
981 ScalingDimension {
982 name: "n_samples".to_string(),
983 parameter_path: "n_samples".to_string(),
984 values: ScalingValues::PowersOfTwo {
985 min_power: 6,
986 max_power: 16,
987 }, expected_complexity: ComplexityClass::Linear,
989 units: "samples".to_string(),
990 },
991 ScalingDimension {
992 name: "n_features".to_string(),
993 parameter_path: "n_features".to_string(),
994 values: ScalingValues::Linear {
995 start: 10.0,
996 end: 1000.0,
997 steps: 10,
998 },
999 expected_complexity: ComplexityClass::Linear,
1000 units: "features".to_string(),
1001 },
1002 ],
1003 performance_targets: PerformanceTargets::default(),
1004 comparison_baselines: vec![Baseline {
1005 name: "scikit-learn".to_string(),
1006 implementation: BaselineType::ScikitLearn,
1007 expected_performance_ratio: 3.0, accuracy_expectation: AccuracyExpectation::WithinTolerance(0.01),
1009 availability: BaselineAvailability::ConditionalOnFeature(
1010 "python-comparison".to_string(),
1011 ),
1012 }],
1013 statistical_config: StatisticalConfig::default(),
1014 output_formats: vec![OutputFormat::Json, OutputFormat::Html],
1015 regression_detection: RegressionDetectionConfig::default(),
1016 optimization_hints: true,
1017 }
1018 }
1019}
1020
1021impl Default for PerformanceTargets {
1022 fn default() -> Self {
1023 Self {
1024 max_latency_ms: 100.0,
1025 min_throughput_ops_sec: 1000.0,
1026 max_memory_mb: 1024.0,
1027 max_accuracy_loss_percent: 1.0,
1028 regression_threshold_percent: 5.0,
1029 stability_coefficient_of_variation: 0.1, }
1031 }
1032}
1033
1034impl Default for StatisticalConfig {
1035 fn default() -> Self {
1036 Self {
1037 min_iterations: 10,
1038 max_iterations: 1000,
1039 warmup_iterations: 3,
1040 confidence_level: 0.95,
1041 outlier_detection: OutlierDetectionMethod::IQR,
1042 measurement_precision: MeasurementPrecision {
1043 timing_precision_ns: 1000, memory_precision_bytes: 1024, accuracy_precision_digits: 6,
1046 min_relative_precision: 0.01, },
1048 }
1049 }
1050}
1051
1052impl Default for RegressionDetectionConfig {
1053 fn default() -> Self {
1054 Self {
1055 enabled: true,
1056 historical_data_path: "benchmark_history.json".to_string(),
1057 regression_threshold_percent: 5.0,
1058 minimum_effect_size: 0.1,
1059 statistical_test: StatisticalTest::TTest,
1060 alert_on_regression: true,
1061 }
1062 }
1063}
1064
1065impl fmt::Display for ComplexityClass {
1066 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1067 match self {
1068 ComplexityClass::Constant => write!(f, "O(1)"),
1069 ComplexityClass::Logarithmic => write!(f, "O(log n)"),
1070 ComplexityClass::Linear => write!(f, "O(n)"),
1071 ComplexityClass::Linearithmic => write!(f, "O(n log n)"),
1072 ComplexityClass::Quadratic => write!(f, "O(n²)"),
1073 ComplexityClass::Cubic => write!(f, "O(n³)"),
1074 ComplexityClass::Exponential => write!(f, "O(2^n)"),
1075 ComplexityClass::Factorial => write!(f, "O(n!)"),
1076 ComplexityClass::Custom(s) => write!(f, "O({})", s),
1077 }
1078 }
1079}
1080
1081impl fmt::Display for BenchmarkType {
1082 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1083 match self {
1084 BenchmarkType::Microbenchmark => write!(f, "Microbenchmark"),
1085 BenchmarkType::IntegrationBenchmark => write!(f, "Integration Benchmark"),
1086 BenchmarkType::ScalabilityBenchmark => write!(f, "Scalability Benchmark"),
1087 BenchmarkType::MemoryBenchmark => write!(f, "Memory Benchmark"),
1088 BenchmarkType::LatencyBenchmark => write!(f, "Latency Benchmark"),
1089 BenchmarkType::ThroughputBenchmark => write!(f, "Throughput Benchmark"),
1090 BenchmarkType::AccuracyBenchmark => write!(f, "Accuracy Benchmark"),
1091 BenchmarkType::RegressionBenchmark => write!(f, "Regression Benchmark"),
1092 BenchmarkType::ComparativeBenchmark => write!(f, "Comparative Benchmark"),
1093 BenchmarkType::StressBenchmark => write!(f, "Stress Benchmark"),
1094 }
1095 }
1096}
1097
1098#[allow(non_snake_case)]
1099#[cfg(test)]
1100mod tests {
1101 use super::*;
1102
1103 #[test]
1104 fn test_auto_benchmark_config_default() {
1105 let config = AutoBenchmarkConfig::default();
1106 assert!(!config.benchmark_types.is_empty());
1107 assert!(!config.scaling_dimensions.is_empty());
1108 assert!(config.optimization_hints);
1109 }
1110
1111 #[test]
1112 fn test_scaling_values_generation() {
1113 let generator = BenchmarkGenerator::new(AutoBenchmarkConfig::default());
1114
1115 let linear_values = generator.generate_scaling_values(&ScalingValues::Linear {
1116 start: 0.0,
1117 end: 10.0,
1118 steps: 5,
1119 });
1120 assert_eq!(linear_values.len(), 5);
1121 assert_eq!(linear_values[0], 0.0);
1122 assert_eq!(linear_values[4], 10.0);
1123
1124 let powers_of_two = generator.generate_scaling_values(&ScalingValues::PowersOfTwo {
1125 min_power: 2,
1126 max_power: 4,
1127 });
1128 assert_eq!(powers_of_two, vec![4.0, 8.0, 16.0]);
1129 }
1130
1131 #[test]
1132 fn test_benchmark_generation() {
1133 let config = AutoBenchmarkConfig::default();
1134 let mut generator = BenchmarkGenerator::new(config);
1135
1136 let benchmarks = generator.generate_for_type::<String>("TestType").unwrap();
1137 assert!(!benchmarks.is_empty());
1138
1139 for benchmark in &benchmarks {
1140 assert!(!benchmark.name.is_empty());
1141 assert!(!benchmark.code.is_empty());
1142 assert!(!benchmark.setup_code.is_empty());
1143 }
1144 }
1145
1146 #[test]
1147 fn test_benchmark_executor() {
1148 let mut executor = BenchmarkExecutor::new();
1149
1150 let benchmark = GeneratedBenchmark {
1151 name: "test_benchmark".to_string(),
1152 benchmark_type: BenchmarkType::Microbenchmark,
1153 code: "mock code".to_string(),
1154 setup_code: "mock setup".to_string(),
1155 dependencies: vec![],
1156 expected_performance: PerformanceEstimate {
1157 expected_latency_ms: 1.0,
1158 expected_throughput_ops_sec: 1000.0,
1159 expected_memory_mb: 1.0,
1160 confidence_interval: 0.95,
1161 },
1162 scaling_analysis: None,
1163 };
1164
1165 let result = executor.execute_benchmark(&benchmark).unwrap();
1166 assert_eq!(result.benchmark_name, "test_benchmark");
1167 assert_eq!(result.benchmark_type, BenchmarkType::Microbenchmark);
1168 }
1169
1170 #[test]
1171 fn test_regression_detection() {
1172 let detector = RegressionDetector::new();
1173
1174 let results = vec![
1175 BenchmarkResult {
1176 benchmark_name: "test1".to_string(),
1177 benchmark_type: BenchmarkType::Microbenchmark,
1178 execution_time: Duration::from_millis(100),
1179 memory_usage_bytes: 1024,
1180 throughput_ops_sec: 1000.0,
1181 accuracy_score: Some(0.95),
1182 regression_detected: false,
1183 performance_vs_baseline: 0.8, statistical_significance: 0.99,
1185 },
1186 BenchmarkResult {
1187 benchmark_name: "test2".to_string(),
1188 benchmark_type: BenchmarkType::Microbenchmark,
1189 execution_time: Duration::from_millis(50),
1190 memory_usage_bytes: 512,
1191 throughput_ops_sec: 2000.0,
1192 accuracy_score: Some(0.97),
1193 regression_detected: false,
1194 performance_vs_baseline: 1.2, statistical_significance: 0.99,
1196 },
1197 ];
1198
1199 let alerts = detector.detect_regressions(&results);
1200 assert_eq!(alerts.len(), 1); assert_eq!(alerts[0].benchmark_name, "test1");
1202 }
1203
1204 #[test]
1205 fn test_complexity_class_display() {
1206 assert_eq!(format!("{}", ComplexityClass::Linear), "O(n)");
1207 assert_eq!(format!("{}", ComplexityClass::Quadratic), "O(n²)");
1208 assert_eq!(
1209 format!("{}", ComplexityClass::Custom("n log n".to_string())),
1210 "O(n log n)"
1211 );
1212 }
1213}