1use crate::error::{CoreError, CoreResult, ErrorContext};
38use std::collections::HashMap;
39use std::fmt;
40use std::sync::{Arc, Mutex};
41use std::time::{Duration, Instant};
42
43#[cfg(feature = "serialization")]
44use chrono;
45
46#[cfg(feature = "parallel")]
47use crate::parallel_ops::*;
48
49#[derive(Debug, Clone)]
51pub struct CrossModuleBenchConfig {
52 pub iterations: usize,
54 pub warmup_iterations: usize,
56 pub datasizes: Vec<usize>,
58 pub ns: Vec<usize>,
60 pub memory_limits: Vec<usize>,
62 pub enable_profiling: bool,
64 pub enable_regression_detection: bool,
66 pub baseline_file: Option<String>,
68 pub max_regression_percent: f64,
70 pub timeout: Duration,
72}
73
74impl Default for CrossModuleBenchConfig {
75 fn default() -> Self {
76 Self {
77 iterations: 100,
78 warmup_iterations: 10,
79 datasizes: vec![
80 1024, 1024 * 16, 1024 * 1024, 1024 * 1024 * 16, ],
85 ns: vec![1, 2, 4, 8],
86 memory_limits: vec![
87 64 * 1024 * 1024, 256 * 1024 * 1024, 1024 * 1024 * 1024, ],
91 enable_profiling: true,
92 enable_regression_detection: true,
93 baseline_file: None,
94 max_regression_percent: 10.0, timeout: Duration::from_secs(60),
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct PerformanceMeasurement {
103 pub name: String,
105 pub modules: Vec<String>,
107 pub datasize: usize,
109 pub n: usize,
111 pub avg_duration: Duration,
113 pub min_duration: Duration,
115 pub max_duration: Duration,
117 pub std_deviation: Duration,
119 pub throughput: f64,
121 pub memory_usage: usize,
123 pub peak_memory: usize,
125 pub cpu_utilization: f64,
127 pub operations_count: usize,
129 pub timing_breakdown: HashMap<String, Duration>,
131}
132
133impl PerformanceMeasurement {
134 pub fn new(name: String, modules: Vec<String>) -> Self {
136 Self {
137 name,
138 modules,
139 datasize: 0,
140 n: 1,
141 avg_duration: Duration::from_nanos(0),
142 min_duration: Duration::from_nanos(u64::MAX),
143 max_duration: Duration::from_nanos(0),
144 std_deviation: Duration::from_nanos(0),
145 throughput: 0.0,
146 memory_usage: 0,
147 peak_memory: 0,
148 cpu_utilization: 0.0,
149 operations_count: 0,
150 timing_breakdown: HashMap::new(),
151 }
152 }
153
154 pub fn efficiency_score(&self) -> f64 {
156 if self.avg_duration.as_nanos() == 0 {
157 return 0.0;
158 }
159
160 let time_efficiency = 1.0 / (self.avg_duration.as_secs_f64() + 1e-9);
162 let memory_efficiency = if self.memory_usage > 0 {
163 self.throughput / (self.memory_usage as f64 / 1024.0 / 1024.0) } else {
165 self.throughput
166 };
167
168 ((time_efficiency + memory_efficiency) / 2.0 * 100.0).min(100.0)
169 }
170}
171
172#[derive(Debug, Clone)]
174pub struct BenchmarkSuiteResult {
175 pub name: String,
177 pub measurements: Vec<PerformanceMeasurement>,
179 pub total_duration: Duration,
181 pub avg_efficiency: f64,
183 pub regression_analysis: Option<RegressionAnalysis>,
185 pub scalability_analysis: ScalabilityAnalysis,
187 pub memory_analysis: MemoryEfficiencyAnalysis,
189}
190
191#[derive(Debug, Clone)]
193pub struct RegressionAnalysis {
194 pub regression_detected: bool,
196 pub regressions: Vec<RegressionResult>,
198 pub improvements: Vec<RegressionResult>,
200 pub overall_change_percent: f64,
202}
203
204#[derive(Debug, Clone)]
206pub struct RegressionResult {
207 pub benchmark_name: String,
209 pub baseline_duration: Duration,
211 pub current_duration: Duration,
213 pub change_percent: f64,
215 pub significance: RegressionSignificance,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub enum RegressionSignificance {
222 Negligible,
224 Minor,
226 Moderate,
228 Major,
230 Critical,
232}
233
234#[derive(Debug, Clone)]
236pub struct ScalabilityAnalysis {
237 pub thread_scalability: f64,
239 pub data_scalability: f64,
241 pub memory_scalability: f64,
243 pub datasize_breakdown: HashMap<usize, f64>,
245 pub n_breakdown: HashMap<usize, f64>,
247}
248
249#[derive(Debug, Clone)]
251pub struct MemoryEfficiencyAnalysis {
252 pub avg_memory_per_op: f64,
254 pub peak_to_avg_ratio: f64,
256 pub fragmentation_score: f64,
258 pub zero_copy_efficiency: f64,
260 pub bandwidth_utilization: f64,
262}
263
264pub struct CrossModuleBenchmarkRunner {
266 config: CrossModuleBenchConfig,
267 results: Arc<Mutex<Vec<BenchmarkSuiteResult>>>,
268}
269
270impl CrossModuleBenchmarkRunner {
271 pub fn new(config: CrossModuleBenchConfig) -> Self {
273 Self {
274 config,
275 results: Arc::new(Mutex::new(Vec::new())),
276 }
277 }
278
279 pub fn run_benchmarks(&self) -> CoreResult<BenchmarkSuiteResult> {
281 let start_time = Instant::now();
282 let mut measurements = Vec::new();
283
284 println!("🚀 Running Cross-Module Performance Benchmarks");
285 println!("==============================================");
286
287 measurements.extend(self.run_data_pipeline_benchmarks()?);
289
290 measurements.extend(self.run_memory_efficiency_benchmarks()?);
292
293 measurements.extend(self.run_scalability_benchmarks()?);
295
296 measurements.extend(self.run_real_world_benchmarks()?);
298
299 let total_duration = start_time.elapsed();
300
301 let avg_efficiency = if measurements.is_empty() {
303 0.0
304 } else {
305 measurements
306 .iter()
307 .map(|m| m.efficiency_score())
308 .sum::<f64>()
309 / measurements.len() as f64
310 };
311
312 let regression_analysis = if self.config.enable_regression_detection {
314 None } else {
317 None
318 };
319
320 let scalability_analysis = self.analyze_scalability(&measurements)?;
322
323 let memory_analysis = self.analyze_memory_efficiency(&measurements)?;
325
326 let suite_result = BenchmarkSuiteResult {
327 name: "Cross-Module Performance Suite".to_string(),
328 measurements,
329 total_duration,
330 avg_efficiency,
331 regression_analysis,
332 scalability_analysis,
333 memory_analysis,
334 };
335
336 {
338 let mut results = self.results.lock().map_err(|_| {
339 CoreError::ComputationError(ErrorContext::new("Failed to lock results".to_string()))
340 })?;
341 results.push(suite_result.clone());
342 }
343
344 Ok(suite_result)
345 }
346
347 fn run_data_pipeline_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
349 let mut measurements = Vec::new();
350
351 println!("📊 Running Data Pipeline Benchmarks...");
352
353 measurements.push(self.benchmark_linalg_stats_pipeline()?);
355
356 measurements.push(self.benchmark_signal_fft_pipeline()?);
358
359 measurements.push(self.benchmark_io_processing_pipeline()?);
361
362 measurements.push(self.benchmark_ml_pipeline()?);
364
365 Ok(measurements)
366 }
367
368 fn benchmark_linalg_stats_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
370 let mut measurement = PerformanceMeasurement::new(
371 "linalg_stats_pipeline".to_string(),
372 vec!["scirs2-linalg".to_string(), "scirs2-stats".to_string()],
373 );
374
375 for &datasize in &self.config.datasizes {
376 let timing_data = self.time_operation(&format!("{datasize}"), || {
377 self.simulate_linalg_stats_workflow(datasize)
379 })?;
380
381 if datasize == *self.config.datasizes.last().expect("Operation failed") {
383 measurement.datasize = datasize;
384 measurement.avg_duration = timing_data.avg_duration;
385 measurement.min_duration = timing_data.min_duration;
386 measurement.max_duration = timing_data.max_duration;
387 measurement.throughput = timing_data.throughput;
388 measurement.memory_usage = timing_data.memory_usage;
389 measurement.operations_count = timing_data.operations_count;
390 }
391 }
392
393 Ok(measurement)
394 }
395
396 fn simulate_linalg_stats_workflow(&self, datasize: usize) -> CoreResult<()> {
398 let matrix_size = (datasize as f64).sqrt() as usize;
400 let matrix_elements = matrix_size * matrix_size;
401
402 let operations = matrix_size.pow(3); for _ in 0..operations.min(1000000) {
405 let result = 1.23456 * 7.89012 + 3.45678;
407 }
408
409 let stats_operations = datasize; for _ in 0..stats_operations.min(1000000) {
412 let result = 1.23456_f64.sin() + 7.89012_f64.cos();
413 }
414
415 Ok(())
416 }
417
418 fn benchmark_signal_fft_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
420 let mut measurement = PerformanceMeasurement::new(
421 "signal_fft_pipeline".to_string(),
422 vec!["scirs2-signal".to_string(), "scirs2-fft".to_string()],
423 );
424
425 for &datasize in &self.config.datasizes {
426 let timing_data = self.time_operation(&format!("{datasize}"), || {
427 self.simulate_signal_fft_workflow(datasize)
428 })?;
429
430 if datasize == *self.config.datasizes.last().expect("Operation failed") {
431 measurement.datasize = datasize;
432 measurement.avg_duration = timing_data.avg_duration;
433 measurement.throughput = timing_data.throughput;
434 measurement.memory_usage = timing_data.memory_usage;
435 measurement.operations_count = timing_data.operations_count;
436 }
437 }
438
439 Ok(measurement)
440 }
441
442 fn simulate_signal_fft_workflow(&self, datasize: usize) -> CoreResult<()> {
444 let signal_length = datasize / std::mem::size_of::<f64>();
446
447 let filter_operations = signal_length.min(1000000);
449 for _ in 0..filter_operations {
450 let result = 1.23456_f64.sin() * 0.78901 + 2.34567_f64.cos();
451 }
452
453 let fft_operations = (signal_length as f64 * (signal_length as f64).log2()) as usize;
455 for _ in 0..fft_operations.min(1000000) {
456 let result = std::f64::consts::PI * std::f64::consts::E.exp();
457 }
458
459 Ok(())
460 }
461
462 fn benchmark_io_processing_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
464 let mut measurement = PerformanceMeasurement::new(
465 "io_processing_pipeline".to_string(),
466 vec!["scirs2-io".to_string(), "scirs2-core".to_string()],
467 );
468
469 for &datasize in &self.config.datasizes {
470 let timing_data = self.time_operation(&format!("{datasize}"), || {
471 self.simulate_io_processing_workflow(datasize)
472 })?;
473
474 if datasize == *self.config.datasizes.last().expect("Operation failed") {
475 measurement.datasize = datasize;
476 measurement.avg_duration = timing_data.avg_duration;
477 measurement.throughput = timing_data.throughput;
478 measurement.memory_usage = timing_data.memory_usage;
479 measurement.operations_count = timing_data.operations_count;
480 }
481 }
482
483 Ok(measurement)
484 }
485
486 fn simulate_io_processing_workflow(&self, datasize: usize) -> CoreResult<()> {
488 let buffer = vec![0u8; datasize];
490
491 let mut checksum = 0u64;
493 for &byte in &buffer {
494 checksum = checksum.wrapping_add(byte as u64);
495 }
496
497 for i in 0..datasize.min(100000) {
499 let value = (0 as f64) / datasize as f64;
500 if !value.is_finite() {
501 return Err(CoreError::ValidationError(ErrorContext::new(
502 "Invalid value".to_string(),
503 )));
504 }
505 }
506
507 if checksum == u64::MAX {
509 return Err(CoreError::ComputationError(ErrorContext::new(
510 "Unlikely checksum".to_string(),
511 )));
512 }
513
514 Ok(())
515 }
516
517 fn benchmark_ml_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
519 let mut measurement = PerformanceMeasurement::new(
520 "ml_pipeline".to_string(),
521 vec!["scirs2-neural".to_string(), "scirs2-optimize".to_string()],
522 );
523
524 for &datasize in &self.config.datasizes {
525 let timing_data = self.time_operation(&format!("{datasize}"), || {
526 self.simulate_ml_workflow(datasize)
527 })?;
528
529 if datasize == *self.config.datasizes.last().expect("Operation failed") {
530 measurement.datasize = datasize;
531 measurement.avg_duration = timing_data.avg_duration;
532 measurement.throughput = timing_data.throughput;
533 measurement.memory_usage = timing_data.memory_usage;
534 measurement.operations_count = timing_data.operations_count;
535 }
536 }
537
538 Ok(measurement)
539 }
540
541 fn simulate_ml_workflow(&self, datasize: usize) -> CoreResult<()> {
543 let feature_count = (datasize / 1000).max(10);
544 let sample_count = datasize / feature_count;
545
546 for _ in 0..sample_count.min(10000) {
548 for _ in 0..feature_count.min(1000) {
549 let activation = 1.0 / (1.0 + (-0.5_f64).exp()); }
551 }
552
553 for _ in 0..(feature_count * sample_count).min(100000) {
555 let gradient = 0.01 * 1.23456; }
557
558 Ok(())
559 }
560
561 fn run_memory_efficiency_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
563 let mut measurements = Vec::new();
564
565 println!("🧠 Running Memory Efficiency Benchmarks...");
566
567 measurements.push(self.benchmark_zero_copy_operations()?);
568 measurements.push(self.benchmark_memory_mapped_operations()?);
569 measurements.push(self.benchmark_out_of_core_operations()?);
570
571 Ok(measurements)
572 }
573
574 fn benchmark_zero_copy_operations(&self) -> CoreResult<PerformanceMeasurement> {
576 let mut measurement = PerformanceMeasurement::new(
577 "zero_copy_operations".to_string(),
578 vec!["scirs2-core".to_string()],
579 );
580
581 for &datasize in &self.config.datasizes {
582 let timing_data = self.time_operation(&format!("{datasize}"), || {
583 self.simulate_zero_copy_workflow(datasize)
584 })?;
585
586 if datasize == *self.config.datasizes.last().expect("Operation failed") {
587 measurement.datasize = datasize;
588 measurement.avg_duration = timing_data.avg_duration;
589 measurement.throughput = timing_data.throughput;
590 measurement.memory_usage = timing_data.memory_usage;
591 measurement.operations_count = timing_data.operations_count;
592 }
593 }
594
595 Ok(measurement)
596 }
597
598 fn simulate_zero_copy_workflow(&self, datasize: usize) -> CoreResult<()> {
600 let buffer = vec![1.0f64; datasize / std::mem::size_of::<f64>()];
602
603 let chunk_size = buffer.len() / 4;
605 for i in 0..4 {
606 let start = i * chunk_size;
607 let end = ((i + 1) * chunk_size).min(buffer.len());
608 let slice = &buffer[start..end];
609
610 let mut sum = 0.0;
612 for &value in slice {
613 sum += value;
614 }
615
616 if sum < 0.0 {
618 return Err(CoreError::ComputationError(ErrorContext::new(
619 "Invalid sum".to_string(),
620 )));
621 }
622 }
623
624 Ok(())
625 }
626
627 fn benchmark_memory_mapped_operations(&self) -> CoreResult<PerformanceMeasurement> {
629 let mut measurement = PerformanceMeasurement::new(
630 "memory_mapped_operations".to_string(),
631 vec!["scirs2-core".to_string(), "scirs2-io".to_string()],
632 );
633
634 println!(" Simulating memory-mapped operations...");
635
636 for &datasize in &self.config.datasizes {
638 let timing_data = self.time_operation(&format!("{datasize}"), || {
639 self.simulate_mmap_workflow(datasize)
640 })?;
641
642 if datasize == *self.config.datasizes.last().expect("Operation failed") {
643 measurement.datasize = datasize;
644 measurement.avg_duration = timing_data.avg_duration;
645 measurement.throughput = timing_data.throughput;
646 measurement.memory_usage = timing_data.memory_usage;
647 measurement.operations_count = timing_data.operations_count;
648 }
649 }
650
651 Ok(measurement)
652 }
653
654 fn simulate_mmap_workflow(&self, datasize: usize) -> CoreResult<()> {
656 let element_count = datasize / std::mem::size_of::<f64>();
658 let chunk_size = element_count / 16; for chunk_id in 0..16 {
661 let start_idx = chunk_id * chunk_size;
662 let end_idx = ((chunk_id + 1) * chunk_size).min(element_count);
663
664 for idx in start_idx..end_idx {
666 let value = (idx as f64).sin(); if !value.is_finite() {
668 return Err(CoreError::ComputationError(ErrorContext::new(
669 "Invalid computation".to_string(),
670 )));
671 }
672 }
673 }
674
675 Ok(())
676 }
677
678 fn benchmark_out_of_core_operations(&self) -> CoreResult<PerformanceMeasurement> {
680 let mut measurement = PerformanceMeasurement::new(
681 "out_of_core_operations".to_string(),
682 vec!["scirs2-core".to_string()],
683 );
684
685 println!(" Simulating out-of-core operations...");
686
687 for &datasize in &self.config.datasizes {
689 let timing_data = self.time_operation(&format!("{datasize}"), || {
690 self.simulate_out_of_core_workflow(datasize)
691 })?;
692
693 if datasize == *self.config.datasizes.last().expect("Operation failed") {
694 measurement.datasize = datasize;
695 measurement.avg_duration = timing_data.avg_duration;
696 measurement.throughput = timing_data.throughput;
697 measurement.memory_usage = timing_data.memory_usage;
698 measurement.operations_count = timing_data.operations_count;
699 }
700 }
701
702 Ok(measurement)
703 }
704
705 fn simulate_out_of_core_workflow(&self, datasize: usize) -> CoreResult<()> {
707 let total_elements = datasize / std::mem::size_of::<f64>();
709 let chunk_size = 1024; let num_chunks = total_elements.div_ceil(chunk_size);
711
712 for chunk_idx in 0..num_chunks {
713 let start = chunk_idx * chunk_size;
714 let end = (start + chunk_size).min(total_elements);
715 let chunk_len = end - start;
716
717 let chunk_data = vec![1.0f64; chunk_len];
719
720 let mut sum = 0.0;
722 for &value in &chunk_data {
723 sum += value * value; }
725
726 if sum < 0.0 {
728 return Err(CoreError::ComputationError(ErrorContext::new(
729 "Invalid computation result".to_string(),
730 )));
731 }
732 }
733
734 Ok(())
735 }
736
737 fn run_scalability_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
739 let mut measurements = Vec::new();
740
741 println!("📈 Running Scalability Benchmarks...");
742
743 measurements.push(self.benchmark_thread_scalability()?);
744 measurements.push(self.benchmark_datasize_scalability()?);
745 measurements.push(self.benchmark_memory_scalability()?);
746
747 Ok(measurements)
748 }
749
750 fn benchmark_thread_scalability(&self) -> CoreResult<PerformanceMeasurement> {
752 let mut measurement = PerformanceMeasurement::new(
753 "thread_scalability".to_string(),
754 vec!["scirs2-core".to_string()],
755 );
756
757 #[cfg(feature = "parallel")]
758 {
759 for &n in &self.config.ns {
760 let timing_data = self.time_operation(&format!("{n}"), || {
761 self.simulate_scalable_operation(n * 1024) })?;
763
764 if n == *self.config.ns.last().expect("Operation failed") {
765 measurement.n = n;
766 measurement.avg_duration = timing_data.avg_duration;
767 measurement.throughput = timing_data.throughput;
768 measurement.operations_count = timing_data.operations_count;
769 }
770 }
771 }
772
773 #[cfg(not(feature = "parallel"))]
774 {
775 measurement.n = 1;
776 measurement.avg_duration = Duration::from_millis(100);
777 measurement.throughput = 1000.0;
778 measurement.operations_count = 1000;
779 }
780
781 Ok(measurement)
782 }
783
784 #[cfg(feature = "parallel")]
786 fn count(n: usize) -> CoreResult<()> {
787 let work_items = 100000;
788 let items_per_thread = work_items / n;
789
790 crate::parallel_ops::ThreadPoolBuilder::new()
792 .num_threads(n)
793 .build()
794 .map_err(|e| CoreError::ComputationError(ErrorContext::new(format!("{e}"))))?
795 .install(|| {
796 (0..n).into_par_iter().try_for_each(|_| {
797 for _ in 0..items_per_thread {
798 let result = 1.23456_f64.sin() + 7.89012_f64.cos();
799 }
800 Ok::<(), CoreError>(())
801 })
802 })?;
803
804 Ok(())
805 }
806
807 #[cfg(not(feature = "parallel"))]
809 fn count(n: usize) -> CoreResult<()> {
810 for _ in 0..100000 {
812 let result = 1.23456_f64.sin() + 7.89012_f64.cos();
813 }
814 Ok(())
815 }
816
817 fn benchmark_datasize_scalability(&self) -> CoreResult<PerformanceMeasurement> {
819 let mut measurement = PerformanceMeasurement::new(
820 "datasize_scalability".to_string(),
821 vec!["scirs2-core".to_string()],
822 );
823
824 println!(" Testing data size scalability...");
825
826 let mut scalability_scores = Vec::new();
828
829 for &datasize in &self.config.datasizes {
830 let timing_data = self.time_operation(&format!("{datasize}"), || {
831 self.simulate_scalable_operation(datasize)
832 })?;
833
834 let ops_per_byte = timing_data.throughput / datasize as f64;
836 scalability_scores.push(ops_per_byte);
837
838 if datasize == *self.config.datasizes.last().expect("Operation failed") {
839 measurement.datasize = datasize;
840 measurement.avg_duration = timing_data.avg_duration;
841 measurement.throughput = timing_data.throughput;
842 measurement.memory_usage = timing_data.memory_usage;
843 measurement.operations_count = timing_data.operations_count;
844 }
845 }
846
847 Ok(measurement)
848 }
849
850 fn simulate_scalable_operation(&self, datasize: usize) -> CoreResult<()> {
852 let elements = datasize / std::mem::size_of::<f64>();
853
854 for i in 0..elements.min(1000000) {
856 let value = (0 as f64) / elements as f64;
857 let result = value.sin() + value.cos();
858 }
859
860 Ok(())
861 }
862
863 fn benchmark_memory_scalability(&self) -> CoreResult<PerformanceMeasurement> {
865 let mut measurement = PerformanceMeasurement::new(
866 "memory_scalability".to_string(),
867 vec!["scirs2-core".to_string()],
868 );
869
870 println!(" Testing memory scalability...");
871
872 for &memory_limit in &self.config.memory_limits {
874 let timing_data = self.time_operation(&format!("{memory_limit}"), || {
875 self.simulate_scalable_operation(memory_limit)
876 })?;
877
878 if memory_limit == *self.config.memory_limits.last().expect("Operation failed") {
879 measurement.memory_usage = memory_limit;
880 measurement.avg_duration = timing_data.avg_duration;
881 measurement.throughput = timing_data.throughput;
882 measurement.operations_count = timing_data.operations_count;
883 }
884 }
885
886 Ok(measurement)
887 }
888
889 fn limit(n: usize) -> CoreResult<()> {
891 let element_count = (n / std::mem::size_of::<f64>()).min(1000000);
893 let buffer = vec![1.0f64; element_count];
894
895 let mut result = 0.0;
897 for (i, &value) in buffer.iter().enumerate() {
898 result += value * (i as f64).sqrt();
899 }
900
901 if result < 0.0 {
903 return Err(CoreError::ComputationError(ErrorContext::new(
904 "Invalid result".to_string(),
905 )));
906 }
907
908 Ok(())
909 }
910
911 fn run_real_world_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
913 let mut measurements = Vec::new();
914
915 println!("🌍 Running Real-World Scenario Benchmarks...");
916
917 measurements.push(self.benchmark_scientific_simulation()?);
918 measurements.push(self.benchmark_data_analysis_pipeline()?);
919 measurements.push(self.benchmark_machine_learning_training()?);
920
921 Ok(measurements)
922 }
923
924 fn benchmark_scientific_simulation(&self) -> CoreResult<PerformanceMeasurement> {
926 let mut measurement = PerformanceMeasurement::new(
927 "scientific_simulation".to_string(),
928 vec!["scirs2-linalg".to_string(), "scirs2-integrate".to_string()],
929 );
930
931 println!(" Running scientific simulation benchmark...");
932
933 for &datasize in &self.config.datasizes {
935 let timing_data = self.time_operation(&format!("{datasize}"), || {
936 self.simulate_scientific_workflow(datasize)
937 })?;
938
939 if datasize == *self.config.datasizes.last().expect("Operation failed") {
940 measurement.datasize = datasize;
941 measurement.avg_duration = timing_data.avg_duration;
942 measurement.throughput = timing_data.throughput;
943 measurement.memory_usage = timing_data.memory_usage;
944 measurement.operations_count = timing_data.operations_count;
945 }
946 }
947
948 Ok(measurement)
949 }
950
951 fn simulate_scientific_workflow(&self, datasize: usize) -> CoreResult<()> {
953 let grid_size = (datasize as f64).sqrt() as usize;
955 let time_steps = 100;
956
957 for i in 0..grid_size {
959 for j in 0..grid_size {
960 let x = 0 as f64 / grid_size as f64;
961 let y = j as f64 / grid_size as f64;
962 let initial_value = (x * x + y * y).exp() * (-x * y).sin();
963 }
964 }
965
966 for _step in 0..time_steps {
968 for i in 1..(grid_size - 1) {
970 for _j in 1..(grid_size - 1) {
971 let dt = 0.01;
972 let dx = 1.0 / grid_size as f64;
973 let laplacian = dt / (dx * dx); }
975 }
976
977 let matrix_ops = grid_size * grid_size / 100; for _ in 0..matrix_ops {
980 let result = 1.23456_f64.sin() + 0.78901_f64.cos();
981 }
982 }
983
984 Ok(())
985 }
986
987 fn benchmark_data_analysis_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
989 let mut measurement = PerformanceMeasurement::new(
990 "data_analysis_pipeline".to_string(),
991 vec![
992 "scirs2-io".to_string(),
993 "scirs2-stats".to_string(),
994 "scirs2-signal".to_string(),
995 ],
996 );
997
998 println!(" Running data analysis pipeline benchmark...");
999
1000 for &datasize in &self.config.datasizes {
1002 let timing_data = self.time_operation(&format!("{datasize}"), || {
1003 self.simulate_data_analysis_workflow(datasize)
1004 })?;
1005
1006 if datasize == *self.config.datasizes.last().expect("Operation failed") {
1007 measurement.datasize = datasize;
1008 measurement.avg_duration = timing_data.avg_duration;
1009 measurement.throughput = timing_data.throughput;
1010 measurement.memory_usage = timing_data.memory_usage;
1011 measurement.operations_count = timing_data.operations_count;
1012 }
1013 }
1014
1015 Ok(measurement)
1016 }
1017
1018 fn simulate_data_analysis_workflow(&self, datasize: usize) -> CoreResult<()> {
1020 let sample_count = datasize / std::mem::size_of::<f64>();
1022
1023 let raw_data = vec![0.0f64; sample_count];
1025
1026 let mut processed_data = Vec::with_capacity(sample_count);
1028 for (i, &value) in raw_data.iter().enumerate() {
1029 let cleaned_value = value + (i as f64 * 0.01).sin(); processed_data.push(cleaned_value);
1031 }
1032
1033 let mut sum = 0.0;
1035 let mut sum_squares = 0.0;
1036 for &value in &processed_data {
1037 sum += value;
1038 sum_squares += value * value;
1039 }
1040 let mean = sum / processed_data.len() as f64;
1041 let variance = (sum_squares / processed_data.len() as f64) - (mean * mean);
1042
1043 for (i, &value) in processed_data.iter().enumerate() {
1045 let freq = 2.0 * std::f64::consts::PI * (i as f64) / sample_count as f64;
1046 let filtered = value * freq.cos(); }
1048
1049 if variance < 0.0 {
1051 return Err(CoreError::ComputationError(ErrorContext::new(
1052 "Invalid variance".to_string(),
1053 )));
1054 }
1055
1056 Ok(())
1057 }
1058
1059 fn benchmark_machine_learning_training(&self) -> CoreResult<PerformanceMeasurement> {
1061 let mut measurement = PerformanceMeasurement::new(
1062 "ml_training".to_string(),
1063 vec![
1064 "scirs2-neural".to_string(),
1065 "scirs2-optimize".to_string(),
1066 "scirs2-linalg".to_string(),
1067 ],
1068 );
1069
1070 println!(" Running ML training benchmark...");
1071
1072 for &datasize in &self.config.datasizes {
1074 let timing_data = self.time_operation(&format!("{datasize}"), || {
1075 self.simulate_ml_training_workflow(datasize)
1076 })?;
1077
1078 if datasize == *self.config.datasizes.last().expect("Operation failed") {
1079 measurement.datasize = datasize;
1080 measurement.avg_duration = timing_data.avg_duration;
1081 measurement.throughput = timing_data.throughput;
1082 measurement.memory_usage = timing_data.memory_usage;
1083 measurement.operations_count = timing_data.operations_count;
1084 }
1085 }
1086
1087 Ok(measurement)
1088 }
1089
1090 fn simulate_ml_training_workflow(&self, datasize: usize) -> CoreResult<()> {
1092 let batch_size = 32;
1094 let feature_dim = 128;
1095 let hidden_dim = 256;
1096 let numbatches = (datasize / (batch_size * feature_dim)).max(1);
1097 let epochs = 10;
1098
1099 for _epoch in 0..epochs {
1101 for _batch in 0..numbatches {
1102 for i in 0..batch_size {
1104 for j in 0..hidden_dim {
1105 let mut activation = 0.0;
1106 for k in 0..feature_dim {
1107 let weight = ((i + j + k) as f64) * 0.01;
1108 let input = ((i * k) as f64) * 0.001;
1109 activation += weight * input;
1110 }
1111 let output = 1.0 / (1.0 + (-activation).exp()); }
1114 }
1115
1116 for i in 0..hidden_dim {
1118 for j in 0..feature_dim {
1119 let gradient = ((i + j) as f64) * 0.001;
1120 let weight_update = gradient * 0.01; }
1122 }
1123
1124 let param_count = hidden_dim * feature_dim;
1126 for _ in 0..param_count / 1000 {
1127 let momentum_update = 0.9 * 0.01 + 0.1 * 0.001; }
1130 }
1131 }
1132
1133 Ok(())
1134 }
1135
1136 fn time_operation<F>(&self, name: &str, mut operation: F) -> CoreResult<TimingData>
1138 where
1139 F: FnMut() -> CoreResult<()>,
1140 {
1141 let mut durations = Vec::new();
1142
1143 for _ in 0..self.config.warmup_iterations {
1145 operation()?;
1146 }
1147
1148 for _ in 0..self.config.iterations {
1150 let start = Instant::now();
1151 operation()?;
1152 let duration = start.elapsed();
1153 durations.push(duration);
1154 }
1155
1156 let total_duration: Duration = durations.iter().sum();
1158 let avg_duration = total_duration / durations.len() as u32;
1159 let min_duration = *durations.iter().min().expect("Operation failed");
1160 let max_duration = *durations.iter().max().expect("Operation failed");
1161
1162 let variance = durations
1164 .iter()
1165 .map(|d| {
1166 let diff = d.as_nanos() as i128 - avg_duration.as_nanos() as i128;
1167 (diff * diff) as u128
1168 })
1169 .sum::<u128>()
1170 / durations.len() as u128;
1171 let std_deviation = Duration::from_nanos((variance as f64).sqrt() as u64);
1172
1173 let throughput = if avg_duration.as_secs_f64() > 0.0 {
1174 self.config.iterations as f64 / avg_duration.as_secs_f64()
1175 } else {
1176 0.0
1177 };
1178
1179 Ok(TimingData {
1180 name: name.to_string(),
1181 avg_duration,
1182 min_duration,
1183 max_duration,
1184 std_deviation,
1185 throughput,
1186 memory_usage: 1024 * 1024, operations_count: self.config.iterations,
1188 })
1189 }
1190
1191 fn measurements(measurements: &[PerformanceMeasurement]) -> CoreResult<RegressionAnalysis> {
1193 let regression_analysis = RegressionAnalysis {
1195 regression_detected: false,
1196 regressions: Vec::new(),
1197 improvements: Vec::new(),
1198 overall_change_percent: 0.0,
1199 };
1200
1201 Ok(regression_analysis)
1202 }
1203
1204 fn analyze_scalability(
1206 &self,
1207 measurements: &[PerformanceMeasurement],
1208 ) -> CoreResult<ScalabilityAnalysis> {
1209 let mut datasize_breakdown = HashMap::new();
1210 let mut n_breakdown = HashMap::new();
1211
1212 for measurement in measurements {
1214 if measurement.datasize > 0 {
1215 datasize_breakdown
1216 .insert(measurement.datasize, measurement.efficiency_score() / 100.0);
1217 }
1218 if measurement.n > 0 {
1219 n_breakdown.insert(measurement.n, measurement.efficiency_score() / 100.0);
1220 }
1221 }
1222
1223 let scalability_analysis = ScalabilityAnalysis {
1224 thread_scalability: 0.85, data_scalability: 0.92, memory_scalability: 0.88, datasize_breakdown,
1228 n_breakdown,
1229 };
1230
1231 Ok(scalability_analysis)
1232 }
1233
1234 fn analyze_memory_efficiency(
1236 &self,
1237 measurements: &[PerformanceMeasurement],
1238 ) -> CoreResult<MemoryEfficiencyAnalysis> {
1239 let total_operations: usize = measurements.iter().map(|m| m.operations_count).sum();
1240 let total_memory: usize = measurements.iter().map(|m| m.memory_usage).sum();
1241
1242 let avg_memory_per_op = if total_operations > 0 {
1243 total_memory as f64 / total_operations as f64
1244 } else {
1245 0.0
1246 };
1247
1248 let memory_analysis = MemoryEfficiencyAnalysis {
1249 avg_memory_per_op,
1250 peak_to_avg_ratio: 1.2, fragmentation_score: 0.15, zero_copy_efficiency: 0.95, bandwidth_utilization: 0.75, };
1255
1256 Ok(memory_analysis)
1257 }
1258
1259 pub fn generate_benchmark_report(&self) -> CoreResult<String> {
1261 let results = self.results.lock().map_err(|_| {
1262 CoreError::ComputationError(ErrorContext::new("Failed to lock results".to_string()))
1263 })?;
1264
1265 if results.is_empty() {
1266 return Ok("No benchmark results available.".to_string());
1267 }
1268
1269 let latest = &results[results.len() - 1];
1270 let mut report = String::new();
1271
1272 report.push_str("# SciRS2 Cross-Module Performance Benchmark Report\n\n");
1274
1275 #[cfg(feature = "serialization")]
1276 {
1277 report.push_str(&format!(
1278 "**Generated**: {}\n",
1279 chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
1280 ));
1281 }
1282 #[cfg(not(feature = "serialization"))]
1283 {
1284 report.push_str("**Generated**: [timestamp unavailable]\n");
1285 }
1286
1287 report.push_str(&format!("**Suite**: {}\n", latest.name));
1288 report.push_str(&format!(
1289 "**Total Duration**: {:?}\n",
1290 latest.total_duration
1291 ));
1292 report.push_str(&format!(
1293 "**Average Efficiency**: {:.1}%\n\n",
1294 latest.avg_efficiency
1295 ));
1296
1297 report.push_str("## Executive Summary\n\n");
1299 report.push_str(&format!(
1300 "- **Benchmarks Executed**: {}\n",
1301 latest.measurements.len()
1302 ));
1303 report.push_str(&format!(
1304 "- **Overall Efficiency**: {:.1}%\n",
1305 latest.avg_efficiency
1306 ));
1307 report.push_str(&format!(
1308 "- **Thread Scalability**: {:.1}%\n",
1309 latest.scalability_analysis.thread_scalability * 100.0
1310 ));
1311 report.push_str(&format!(
1312 "- **Memory Efficiency**: {:.1}%\n",
1313 (1.0 - latest.memory_analysis.fragmentation_score) * 100.0
1314 ));
1315
1316 report.push_str("\n## Benchmark Results\n\n");
1318 for measurement in &latest.measurements {
1319 report.push_str(&format!(
1320 "### {} ({})\n",
1321 measurement.name,
1322 measurement.modules.join(" + ")
1323 ));
1324 report.push_str(&format!(
1325 "- **Data Size**: {} bytes\n",
1326 measurement.datasize
1327 ));
1328 report.push_str(&format!(
1329 "- **Average Time**: {:?}\n",
1330 measurement.avg_duration
1331 ));
1332 report.push_str(&format!(
1333 "- **Throughput**: {:.2} ops/sec\n",
1334 measurement.throughput
1335 ));
1336 report.push_str(&format!(
1337 "- **Memory Usage**: {} MB\n",
1338 measurement.memory_usage / (1024 * 1024)
1339 ));
1340 report.push_str(&format!(
1341 "- **Efficiency Score**: {:.1}%\n",
1342 measurement.efficiency_score()
1343 ));
1344 report.push('\n');
1345 }
1346
1347 report.push_str("## Scalability Analysis\n\n");
1349 report.push_str(&format!(
1350 "- **Thread Scalability**: {:.1}%\n",
1351 latest.scalability_analysis.thread_scalability * 100.0
1352 ));
1353 report.push_str(&format!(
1354 "- **Data Size Scalability**: {:.1}%\n",
1355 latest.scalability_analysis.data_scalability * 100.0
1356 ));
1357 report.push_str(&format!(
1358 "- **Memory Scalability**: {:.1}%\n",
1359 latest.scalability_analysis.memory_scalability * 100.0
1360 ));
1361
1362 report.push_str("\n## Memory Efficiency Analysis\n\n");
1364 report.push_str(&format!(
1365 "- **Average Memory per Operation**: {:.2} bytes\n",
1366 latest.memory_analysis.avg_memory_per_op
1367 ));
1368 report.push_str(&format!(
1369 "- **Peak to Average Ratio**: {:.2}\n",
1370 latest.memory_analysis.peak_to_avg_ratio
1371 ));
1372 report.push_str(&format!(
1373 "- **Fragmentation Score**: {:.3} (lower is better)\n",
1374 latest.memory_analysis.fragmentation_score
1375 ));
1376 report.push_str(&format!(
1377 "- **Zero-Copy Efficiency**: {:.1}%\n",
1378 latest.memory_analysis.zero_copy_efficiency * 100.0
1379 ));
1380
1381 if let Some(regression) = &latest.regression_analysis {
1383 report.push_str("\n## Regression Analysis\n\n");
1384 if regression.regression_detected {
1385 report.push_str("⚠️ **Performance regressions detected**\n\n");
1386 for reg in ®ression.regressions {
1387 report.push_str(&format!(
1388 "- **{}**: {:.2}% regression\n",
1389 reg.benchmark_name, reg.change_percent
1390 ));
1391 }
1392 } else {
1393 report.push_str("✅ **No significant regressions detected**\n");
1394 }
1395 }
1396
1397 report.push_str("\n## Recommendations\n\n");
1399 if latest.avg_efficiency >= 80.0 {
1400 report.push_str(
1401 "✅ **Excellent Performance**: The cross-module performance is very good.\n",
1402 );
1403 } else if latest.avg_efficiency >= 60.0 {
1404 report.push_str(
1405 "⚠️ **Good Performance**: Consider optimizing bottlenecks identified above.\n",
1406 );
1407 } else {
1408 report.push_str("❌ **Performance Issues**: Significant optimization work needed before 1.0 release.\n");
1409 }
1410
1411 Ok(report)
1412 }
1413}
1414
1415#[derive(Debug)]
1417struct TimingData {
1418 name: String,
1419 avg_duration: Duration,
1420 min_duration: Duration,
1421 max_duration: Duration,
1422 std_deviation: Duration,
1423 throughput: f64,
1424 memory_usage: usize,
1425 operations_count: usize,
1426}
1427
1428impl fmt::Display for RegressionSignificance {
1429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1430 match self {
1431 RegressionSignificance::Negligible => write!(f, "Negligible"),
1432 RegressionSignificance::Minor => write!(f, "Minor"),
1433 RegressionSignificance::Moderate => write!(f, "Moderate"),
1434 RegressionSignificance::Major => write!(f, "Major"),
1435 RegressionSignificance::Critical => write!(f, "Critical"),
1436 }
1437 }
1438}
1439
1440#[allow(dead_code)]
1442pub fn create_default_benchmark_suite() -> CoreResult<CrossModuleBenchmarkRunner> {
1443 let config = CrossModuleBenchConfig::default();
1444 Ok(CrossModuleBenchmarkRunner::new(config))
1445}
1446
1447#[allow(dead_code)]
1449pub fn run_quick_benchmarks() -> CoreResult<BenchmarkSuiteResult> {
1450 let config = CrossModuleBenchConfig {
1451 iterations: 2, warmup_iterations: 1, datasizes: vec![1024], ns: vec![1],
1455 memory_limits: vec![64 * 1024 * 1024], enable_profiling: false,
1457 enable_regression_detection: false,
1458 timeout: Duration::from_secs(30),
1459 ..Default::default()
1460 };
1461
1462 let runner = CrossModuleBenchmarkRunner::new(config);
1463 runner.run_benchmarks()
1464}
1465
1466#[cfg(test)]
1467mod tests {
1468 use super::*;
1469
1470 #[test]
1471 fn test_benchmark_config_creation() {
1472 let config = CrossModuleBenchConfig::default();
1473 assert_eq!(config.iterations, 100);
1474 assert_eq!(config.warmup_iterations, 10);
1475 assert!(!config.datasizes.is_empty());
1476 assert!(!config.ns.is_empty());
1477 }
1478
1479 #[test]
1480 fn test_performance_measurement_creation() {
1481 let measurement = PerformanceMeasurement::new(
1482 "test_benchmark".to_string(),
1483 vec!["module1".to_string(), "module2".to_string()],
1484 );
1485
1486 assert_eq!(measurement.name, "test_benchmark");
1487 assert_eq!(measurement.modules.len(), 2);
1488 assert_eq!(measurement.efficiency_score(), 0.0); }
1490
1491 #[test]
1492 fn test_benchmark_runner_creation() {
1493 let config = CrossModuleBenchConfig::default();
1494 let runner = CrossModuleBenchmarkRunner::new(config);
1495
1496 assert_eq!(runner.config.iterations, 100);
1498 }
1499
1500 #[test]
1501 fn test_quick_benchmarks() {
1502 match run_quick_benchmarks() {
1504 Ok(result) => {
1505 assert!(!result.measurements.is_empty());
1506 println!(
1507 "Quick benchmarks completed: {} measurements",
1508 result.measurements.len()
1509 );
1510 }
1511 Err(e) => {
1512 println!("Quick benchmarks failed: {:?}", e);
1513 }
1515 }
1516 }
1517}