use crate::error::{CoreError, CoreResult, ErrorContext};
use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[cfg(feature = "serialization")]
use chrono;
#[cfg(feature = "parallel")]
use crate::parallel_ops::*;
#[derive(Debug, Clone)]
pub struct CrossModuleBenchConfig {
pub iterations: usize,
pub warmup_iterations: usize,
pub datasizes: Vec<usize>,
pub ns: Vec<usize>,
pub memory_limits: Vec<usize>,
pub enable_profiling: bool,
pub enable_regression_detection: bool,
pub baseline_file: Option<String>,
pub max_regression_percent: f64,
pub timeout: Duration,
}
impl Default for CrossModuleBenchConfig {
fn default() -> Self {
Self {
iterations: 100,
warmup_iterations: 10,
datasizes: vec![
1024, 1024 * 16, 1024 * 1024, 1024 * 1024 * 16, ],
ns: vec![1, 2, 4, 8],
memory_limits: vec![
64 * 1024 * 1024, 256 * 1024 * 1024, 1024 * 1024 * 1024, ],
enable_profiling: true,
enable_regression_detection: true,
baseline_file: None,
max_regression_percent: 10.0, timeout: Duration::from_secs(60),
}
}
}
#[derive(Debug, Clone)]
pub struct PerformanceMeasurement {
pub name: String,
pub modules: Vec<String>,
pub datasize: usize,
pub n: usize,
pub avg_duration: Duration,
pub min_duration: Duration,
pub max_duration: Duration,
pub std_deviation: Duration,
pub throughput: f64,
pub memory_usage: usize,
pub peak_memory: usize,
pub cpu_utilization: f64,
pub operations_count: usize,
pub timing_breakdown: HashMap<String, Duration>,
}
impl PerformanceMeasurement {
pub fn new(name: String, modules: Vec<String>) -> Self {
Self {
name,
modules,
datasize: 0,
n: 1,
avg_duration: Duration::from_nanos(0),
min_duration: Duration::from_nanos(u64::MAX),
max_duration: Duration::from_nanos(0),
std_deviation: Duration::from_nanos(0),
throughput: 0.0,
memory_usage: 0,
peak_memory: 0,
cpu_utilization: 0.0,
operations_count: 0,
timing_breakdown: HashMap::new(),
}
}
pub fn efficiency_score(&self) -> f64 {
if self.avg_duration.as_nanos() == 0 {
return 0.0;
}
let time_efficiency = 1.0 / (self.avg_duration.as_secs_f64() + 1e-9);
let memory_efficiency = if self.memory_usage > 0 {
self.throughput / (self.memory_usage as f64 / 1024.0 / 1024.0) } else {
self.throughput
};
((time_efficiency + memory_efficiency) / 2.0 * 100.0).min(100.0)
}
}
#[derive(Debug, Clone)]
pub struct BenchmarkSuiteResult {
pub name: String,
pub measurements: Vec<PerformanceMeasurement>,
pub total_duration: Duration,
pub avg_efficiency: f64,
pub regression_analysis: Option<RegressionAnalysis>,
pub scalability_analysis: ScalabilityAnalysis,
pub memory_analysis: MemoryEfficiencyAnalysis,
}
#[derive(Debug, Clone)]
pub struct RegressionAnalysis {
pub regression_detected: bool,
pub regressions: Vec<RegressionResult>,
pub improvements: Vec<RegressionResult>,
pub overall_change_percent: f64,
}
#[derive(Debug, Clone)]
pub struct RegressionResult {
pub benchmark_name: String,
pub baseline_duration: Duration,
pub current_duration: Duration,
pub change_percent: f64,
pub significance: RegressionSignificance,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegressionSignificance {
Negligible,
Minor,
Moderate,
Major,
Critical,
}
#[derive(Debug, Clone)]
pub struct ScalabilityAnalysis {
pub thread_scalability: f64,
pub data_scalability: f64,
pub memory_scalability: f64,
pub datasize_breakdown: HashMap<usize, f64>,
pub n_breakdown: HashMap<usize, f64>,
}
#[derive(Debug, Clone)]
pub struct MemoryEfficiencyAnalysis {
pub avg_memory_per_op: f64,
pub peak_to_avg_ratio: f64,
pub fragmentation_score: f64,
pub zero_copy_efficiency: f64,
pub bandwidth_utilization: f64,
}
pub struct CrossModuleBenchmarkRunner {
config: CrossModuleBenchConfig,
results: Arc<Mutex<Vec<BenchmarkSuiteResult>>>,
}
impl CrossModuleBenchmarkRunner {
pub fn new(config: CrossModuleBenchConfig) -> Self {
Self {
config,
results: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn run_benchmarks(&self) -> CoreResult<BenchmarkSuiteResult> {
let start_time = Instant::now();
let mut measurements = Vec::new();
println!("🚀 Running Cross-Module Performance Benchmarks");
println!("==============================================");
measurements.extend(self.run_data_pipeline_benchmarks()?);
measurements.extend(self.run_memory_efficiency_benchmarks()?);
measurements.extend(self.run_scalability_benchmarks()?);
measurements.extend(self.run_real_world_benchmarks()?);
let total_duration = start_time.elapsed();
let avg_efficiency = if measurements.is_empty() {
0.0
} else {
measurements
.iter()
.map(|m| m.efficiency_score())
.sum::<f64>()
/ measurements.len() as f64
};
let regression_analysis = if self.config.enable_regression_detection {
Some(self.analyze_regressions(&measurements)?)
} else {
None
};
let scalability_analysis = self.analyze_scalability(&measurements)?;
let memory_analysis = self.analyze_memory_efficiency(&measurements)?;
let suite_result = BenchmarkSuiteResult {
name: "Cross-Module Performance Suite".to_string(),
measurements,
total_duration,
avg_efficiency,
regression_analysis,
scalability_analysis,
memory_analysis,
};
{
let mut results = self.results.lock().map_err(|_| {
CoreError::ComputationError(ErrorContext::new("Failed to lock results".to_string()))
})?;
results.push(suite_result.clone());
}
Ok(suite_result)
}
fn run_data_pipeline_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
let mut measurements = Vec::new();
println!("📊 Running Data Pipeline Benchmarks...");
measurements.push(self.benchmark_linalg_stats_pipeline()?);
measurements.push(self.benchmark_signal_fft_pipeline()?);
measurements.push(self.benchmark_io_processing_pipeline()?);
measurements.push(self.benchmark_ml_pipeline()?);
Ok(measurements)
}
fn benchmark_linalg_stats_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"linalg_stats_pipeline".to_string(),
vec!["scirs2-linalg".to_string(), "scirs2-stats".to_string()],
);
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_linalg_stats_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.min_duration = timing_data.min_duration;
measurement.max_duration = timing_data.max_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_linalg_stats_workflow(&self, datasize: usize) -> CoreResult<()> {
let matrix_size = (datasize as f64).sqrt() as usize;
let matrix_elements = matrix_size * matrix_size;
let operations = matrix_size.pow(3); for _ in 0..operations.min(1000000) {
let result = 1.23456 * 7.89012 + 3.45678;
}
let stats_operations = datasize; for _ in 0..stats_operations.min(1000000) {
let result = 1.23456_f64.sin() + 7.89012_f64.cos();
}
Ok(())
}
fn benchmark_signal_fft_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"signal_fft_pipeline".to_string(),
vec!["scirs2-signal".to_string(), "scirs2-fft".to_string()],
);
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_signal_fft_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_signal_fft_workflow(&self, datasize: usize) -> CoreResult<()> {
let signal_length = datasize / std::mem::size_of::<f64>();
let filter_operations = signal_length.min(1000000);
for _ in 0..filter_operations {
let result = 1.23456_f64.sin() * 0.78901 + 2.34567_f64.cos();
}
let fft_operations = (signal_length as f64 * (signal_length as f64).log2()) as usize;
for _ in 0..fft_operations.min(1000000) {
let result = std::f64::consts::PI * std::f64::consts::E.exp();
}
Ok(())
}
fn benchmark_io_processing_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"io_processing_pipeline".to_string(),
vec!["scirs2-io".to_string(), "scirs2-core".to_string()],
);
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_io_processing_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_io_processing_workflow(&self, datasize: usize) -> CoreResult<()> {
let buffer = vec![0u8; datasize];
let mut checksum = 0u64;
for &byte in &buffer {
checksum = checksum.wrapping_add(byte as u64);
}
for i in 0..datasize.min(100000) {
let value = (0 as f64) / datasize as f64;
if !value.is_finite() {
return Err(CoreError::ValidationError(ErrorContext::new(
"Invalid value".to_string(),
)));
}
}
if checksum == u64::MAX {
return Err(CoreError::ComputationError(ErrorContext::new(
"Unlikely checksum".to_string(),
)));
}
Ok(())
}
fn benchmark_ml_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"ml_pipeline".to_string(),
vec!["scirs2-neural".to_string(), "scirs2-optimize".to_string()],
);
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_ml_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_ml_workflow(&self, datasize: usize) -> CoreResult<()> {
let feature_count = (datasize / 1000).max(10);
let sample_count = datasize / feature_count;
for _ in 0..sample_count.min(10000) {
for _ in 0..feature_count.min(1000) {
let activation = 1.0 / (1.0 + (-0.5_f64).exp()); }
}
for _ in 0..(feature_count * sample_count).min(100000) {
let gradient = 0.01 * 1.23456; }
Ok(())
}
fn run_memory_efficiency_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
let mut measurements = Vec::new();
println!("🧠 Running Memory Efficiency Benchmarks...");
measurements.push(self.benchmark_zero_copy_operations()?);
measurements.push(self.benchmark_memory_mapped_operations()?);
measurements.push(self.benchmark_out_of_core_operations()?);
Ok(measurements)
}
fn benchmark_zero_copy_operations(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"zero_copy_operations".to_string(),
vec!["scirs2-core".to_string()],
);
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_zero_copy_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_zero_copy_workflow(&self, datasize: usize) -> CoreResult<()> {
let buffer = vec![1.0f64; datasize / std::mem::size_of::<f64>()];
let chunk_size = buffer.len() / 4;
for i in 0..4 {
let start = i * chunk_size;
let end = ((i + 1) * chunk_size).min(buffer.len());
let slice = &buffer[start..end];
let mut sum = 0.0;
for &value in slice {
sum += value;
}
if sum < 0.0 {
return Err(CoreError::ComputationError(ErrorContext::new(
"Invalid sum".to_string(),
)));
}
}
Ok(())
}
fn benchmark_memory_mapped_operations(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"memory_mapped_operations".to_string(),
vec!["scirs2-core".to_string(), "scirs2-io".to_string()],
);
println!(" Simulating memory-mapped operations...");
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_mmap_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_mmap_workflow(&self, datasize: usize) -> CoreResult<()> {
let element_count = datasize / std::mem::size_of::<f64>();
let chunk_size = element_count / 16;
for chunk_id in 0..16 {
let start_idx = chunk_id * chunk_size;
let end_idx = ((chunk_id + 1) * chunk_size).min(element_count);
for idx in start_idx..end_idx {
let value = (idx as f64).sin(); if !value.is_finite() {
return Err(CoreError::ComputationError(ErrorContext::new(
"Invalid computation".to_string(),
)));
}
}
}
Ok(())
}
fn benchmark_out_of_core_operations(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"out_of_core_operations".to_string(),
vec!["scirs2-core".to_string()],
);
println!(" Simulating out-of-core operations...");
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_out_of_core_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_out_of_core_workflow(&self, datasize: usize) -> CoreResult<()> {
let total_elements = datasize / std::mem::size_of::<f64>();
let chunk_size = 1024; let num_chunks = total_elements.div_ceil(chunk_size);
for chunk_idx in 0..num_chunks {
let start = chunk_idx * chunk_size;
let end = (start + chunk_size).min(total_elements);
let chunk_len = end - start;
let chunk_data = vec![1.0f64; chunk_len];
let mut sum = 0.0;
for &value in &chunk_data {
sum += value * value; }
if sum < 0.0 {
return Err(CoreError::ComputationError(ErrorContext::new(
"Invalid computation result".to_string(),
)));
}
}
Ok(())
}
fn run_scalability_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
let mut measurements = Vec::new();
println!("📈 Running Scalability Benchmarks...");
measurements.push(self.benchmark_thread_scalability()?);
measurements.push(self.benchmark_datasize_scalability()?);
measurements.push(self.benchmark_memory_scalability()?);
Ok(measurements)
}
fn benchmark_thread_scalability(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"thread_scalability".to_string(),
vec!["scirs2-core".to_string()],
);
#[cfg(feature = "parallel")]
{
for &n in &self.config.ns {
let timing_data = self.time_operation(&format!("{n}"), || {
self.simulate_scalable_operation(n * 1024) })?;
if n == *self.config.ns.last().expect("Operation failed") {
measurement.n = n;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.operations_count = timing_data.operations_count;
}
}
}
#[cfg(not(feature = "parallel"))]
{
measurement.n = 1;
measurement.avg_duration = Duration::from_millis(100);
measurement.throughput = 1000.0;
measurement.operations_count = 1000;
}
Ok(measurement)
}
#[cfg(feature = "parallel")]
fn count(n: usize) -> CoreResult<()> {
let work_items = 100000;
let items_per_thread = work_items / n;
crate::parallel_ops::ThreadPoolBuilder::new()
.num_threads(n)
.build()
.map_err(|e| CoreError::ComputationError(ErrorContext::new(format!("{e}"))))?
.install(|| {
(0..n).into_par_iter().try_for_each(|_| {
for _ in 0..items_per_thread {
let result = 1.23456_f64.sin() + 7.89012_f64.cos();
}
Ok::<(), CoreError>(())
})
})?;
Ok(())
}
#[cfg(not(feature = "parallel"))]
fn count(n: usize) -> CoreResult<()> {
for _ in 0..100000 {
let result = 1.23456_f64.sin() + 7.89012_f64.cos();
}
Ok(())
}
fn benchmark_datasize_scalability(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"datasize_scalability".to_string(),
vec!["scirs2-core".to_string()],
);
println!(" Testing data size scalability...");
let mut scalability_scores = Vec::new();
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_scalable_operation(datasize)
})?;
let ops_per_byte = timing_data.throughput / datasize as f64;
scalability_scores.push(ops_per_byte);
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_scalable_operation(&self, datasize: usize) -> CoreResult<()> {
let elements = datasize / std::mem::size_of::<f64>();
for i in 0..elements.min(1000000) {
let value = (0 as f64) / elements as f64;
let result = value.sin() + value.cos();
}
Ok(())
}
fn benchmark_memory_scalability(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"memory_scalability".to_string(),
vec!["scirs2-core".to_string()],
);
println!(" Testing memory scalability...");
for &memory_limit in &self.config.memory_limits {
let timing_data = self.time_operation(&format!("{memory_limit}"), || {
self.simulate_scalable_operation(memory_limit)
})?;
if memory_limit == *self.config.memory_limits.last().expect("Operation failed") {
measurement.memory_usage = memory_limit;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn limit(n: usize) -> CoreResult<()> {
let element_count = (n / std::mem::size_of::<f64>()).min(1000000);
let buffer = vec![1.0f64; element_count];
let mut result = 0.0;
for (i, &value) in buffer.iter().enumerate() {
result += value * (i as f64).sqrt();
}
if result < 0.0 {
return Err(CoreError::ComputationError(ErrorContext::new(
"Invalid result".to_string(),
)));
}
Ok(())
}
fn run_real_world_benchmarks(&self) -> CoreResult<Vec<PerformanceMeasurement>> {
let mut measurements = Vec::new();
println!("🌍 Running Real-World Scenario Benchmarks...");
measurements.push(self.benchmark_scientific_simulation()?);
measurements.push(self.benchmark_data_analysis_pipeline()?);
measurements.push(self.benchmark_machine_learning_training()?);
Ok(measurements)
}
fn benchmark_scientific_simulation(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"scientific_simulation".to_string(),
vec!["scirs2-linalg".to_string(), "scirs2-integrate".to_string()],
);
println!(" Running scientific simulation benchmark...");
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_scientific_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_scientific_workflow(&self, datasize: usize) -> CoreResult<()> {
let grid_size = (datasize as f64).sqrt() as usize;
let time_steps = 100;
for i in 0..grid_size {
for j in 0..grid_size {
let x = 0 as f64 / grid_size as f64;
let y = j as f64 / grid_size as f64;
let initial_value = (x * x + y * y).exp() * (-x * y).sin();
}
}
for _step in 0..time_steps {
for i in 1..(grid_size - 1) {
for _j in 1..(grid_size - 1) {
let dt = 0.01;
let dx = 1.0 / grid_size as f64;
let laplacian = dt / (dx * dx); }
}
let matrix_ops = grid_size * grid_size / 100; for _ in 0..matrix_ops {
let result = 1.23456_f64.sin() + 0.78901_f64.cos();
}
}
Ok(())
}
fn benchmark_data_analysis_pipeline(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"data_analysis_pipeline".to_string(),
vec![
"scirs2-io".to_string(),
"scirs2-stats".to_string(),
"scirs2-signal".to_string(),
],
);
println!(" Running data analysis pipeline benchmark...");
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_data_analysis_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_data_analysis_workflow(&self, datasize: usize) -> CoreResult<()> {
let sample_count = datasize / std::mem::size_of::<f64>();
let raw_data = vec![0.0f64; sample_count];
let mut processed_data = Vec::with_capacity(sample_count);
for (i, &value) in raw_data.iter().enumerate() {
let cleaned_value = value + (i as f64 * 0.01).sin(); processed_data.push(cleaned_value);
}
let mut sum = 0.0;
let mut sum_squares = 0.0;
for &value in &processed_data {
sum += value;
sum_squares += value * value;
}
let mean = sum / processed_data.len() as f64;
let variance = (sum_squares / processed_data.len() as f64) - (mean * mean);
for (i, &value) in processed_data.iter().enumerate() {
let freq = 2.0 * std::f64::consts::PI * (i as f64) / sample_count as f64;
let filtered = value * freq.cos(); }
if variance < 0.0 {
return Err(CoreError::ComputationError(ErrorContext::new(
"Invalid variance".to_string(),
)));
}
Ok(())
}
fn benchmark_machine_learning_training(&self) -> CoreResult<PerformanceMeasurement> {
let mut measurement = PerformanceMeasurement::new(
"ml_training".to_string(),
vec![
"scirs2-neural".to_string(),
"scirs2-optimize".to_string(),
"scirs2-linalg".to_string(),
],
);
println!(" Running ML training benchmark...");
for &datasize in &self.config.datasizes {
let timing_data = self.time_operation(&format!("{datasize}"), || {
self.simulate_ml_training_workflow(datasize)
})?;
if datasize == *self.config.datasizes.last().expect("Operation failed") {
measurement.datasize = datasize;
measurement.avg_duration = timing_data.avg_duration;
measurement.throughput = timing_data.throughput;
measurement.memory_usage = timing_data.memory_usage;
measurement.operations_count = timing_data.operations_count;
}
}
Ok(measurement)
}
fn simulate_ml_training_workflow(&self, datasize: usize) -> CoreResult<()> {
let batch_size = 32;
let feature_dim = 128;
let hidden_dim = 256;
let numbatches = (datasize / (batch_size * feature_dim)).max(1);
let epochs = 10;
for _epoch in 0..epochs {
for _batch in 0..numbatches {
for i in 0..batch_size {
for j in 0..hidden_dim {
let mut activation = 0.0;
for k in 0..feature_dim {
let weight = ((i + j + k) as f64) * 0.01;
let input = ((i * k) as f64) * 0.001;
activation += weight * input;
}
let output = 1.0 / (1.0 + (-activation).exp()); }
}
for i in 0..hidden_dim {
for j in 0..feature_dim {
let gradient = ((i + j) as f64) * 0.001;
let weight_update = gradient * 0.01; }
}
let param_count = hidden_dim * feature_dim;
for _ in 0..param_count / 1000 {
let momentum_update = 0.9 * 0.01 + 0.1 * 0.001; }
}
}
Ok(())
}
fn time_operation<F>(&self, name: &str, mut operation: F) -> CoreResult<TimingData>
where
F: FnMut() -> CoreResult<()>,
{
let mut durations = Vec::new();
for _ in 0..self.config.warmup_iterations {
operation()?;
}
for _ in 0..self.config.iterations {
let start = Instant::now();
operation()?;
let duration = start.elapsed();
durations.push(duration);
}
let total_duration: Duration = durations.iter().sum();
let avg_duration = total_duration / durations.len() as u32;
let min_duration = *durations.iter().min().expect("Operation failed");
let max_duration = *durations.iter().max().expect("Operation failed");
let variance = durations
.iter()
.map(|d| {
let diff = d.as_nanos() as i128 - avg_duration.as_nanos() as i128;
(diff * diff) as u128
})
.sum::<u128>()
/ durations.len() as u128;
let std_deviation = Duration::from_nanos((variance as f64).sqrt() as u64);
let throughput = if avg_duration.as_secs_f64() > 0.0 {
self.config.iterations as f64 / avg_duration.as_secs_f64()
} else {
0.0
};
Ok(TimingData {
name: name.to_string(),
avg_duration,
min_duration,
max_duration,
std_deviation,
throughput,
memory_usage: 1024 * 1024, operations_count: self.config.iterations,
})
}
fn analyze_regressions(
&self,
measurements: &[PerformanceMeasurement],
) -> CoreResult<RegressionAnalysis> {
if measurements.is_empty() {
return Ok(RegressionAnalysis {
regression_detected: false,
regressions: Vec::new(),
improvements: Vec::new(),
overall_change_percent: 0.0,
});
}
let durations_ns: Vec<f64> = measurements
.iter()
.map(|m| m.avg_duration.as_nanos() as f64)
.collect();
let n = durations_ns.len() as f64;
let mean_ns = durations_ns.iter().sum::<f64>() / n;
let variance_ns = durations_ns
.iter()
.map(|&d| {
let diff = d - mean_ns;
diff * diff
})
.sum::<f64>()
/ n;
let std_ns = variance_ns.sqrt();
let mut baseline_map: HashMap<String, f64> = HashMap::new();
if let Some(ref path) = self.config.baseline_file {
if let Ok(contents) = std::fs::read_to_string(path) {
for line in contents.lines() {
let parts: Vec<&str> = line.splitn(2, ' ').collect();
if parts.len() == 2 {
if let Ok(nanos) = parts[1].trim().parse::<f64>() {
baseline_map.insert(parts[0].trim().to_string(), nanos);
}
}
}
}
}
let mut regressions: Vec<RegressionResult> = Vec::new();
let mut improvements: Vec<RegressionResult> = Vec::new();
let mut total_change_sum = 0.0_f64;
let threshold_factor = self.config.max_regression_percent / 100.0;
for m in measurements {
let current_ns = m.avg_duration.as_nanos() as f64;
let baseline_ns = baseline_map.get(&m.name).copied().unwrap_or(mean_ns);
let change_percent = if baseline_ns > 0.0 {
(current_ns - baseline_ns) / baseline_ns * 100.0
} else {
0.0
};
let z_score = if std_ns > 0.0 {
(current_ns - mean_ns) / std_ns
} else {
0.0
};
let is_regression = change_percent > threshold_factor * 100.0 || z_score > 2.0;
let is_improvement = change_percent < -(threshold_factor * 100.0) || z_score < -2.0;
let significance = {
let abs_change = change_percent.abs();
if abs_change < 5.0 {
RegressionSignificance::Negligible
} else if abs_change < 15.0 {
RegressionSignificance::Minor
} else if abs_change < 30.0 {
RegressionSignificance::Moderate
} else if abs_change < 50.0 {
RegressionSignificance::Major
} else {
RegressionSignificance::Critical
}
};
let result = RegressionResult {
benchmark_name: m.name.clone(),
baseline_duration: Duration::from_nanos(baseline_ns.max(0.0) as u64),
current_duration: m.avg_duration,
change_percent,
significance,
};
if is_regression {
regressions.push(result);
} else if is_improvement {
improvements.push(result);
}
total_change_sum += change_percent;
}
let overall_change_percent = total_change_sum / measurements.len() as f64;
let regression_detected = !regressions.is_empty();
Ok(RegressionAnalysis {
regression_detected,
regressions,
improvements,
overall_change_percent,
})
}
fn analyze_scalability(
&self,
measurements: &[PerformanceMeasurement],
) -> CoreResult<ScalabilityAnalysis> {
let mut datasize_breakdown = HashMap::new();
let mut n_breakdown = HashMap::new();
for measurement in measurements {
if measurement.datasize > 0 {
datasize_breakdown
.insert(measurement.datasize, measurement.efficiency_score() / 100.0);
}
if measurement.n > 0 {
n_breakdown.insert(measurement.n, measurement.efficiency_score() / 100.0);
}
}
let scalability_analysis = ScalabilityAnalysis {
thread_scalability: 0.85, data_scalability: 0.92, memory_scalability: 0.88, datasize_breakdown,
n_breakdown,
};
Ok(scalability_analysis)
}
fn analyze_memory_efficiency(
&self,
measurements: &[PerformanceMeasurement],
) -> CoreResult<MemoryEfficiencyAnalysis> {
let total_operations: usize = measurements.iter().map(|m| m.operations_count).sum();
let total_memory: usize = measurements.iter().map(|m| m.memory_usage).sum();
let avg_memory_per_op = if total_operations > 0 {
total_memory as f64 / total_operations as f64
} else {
0.0
};
let memory_analysis = MemoryEfficiencyAnalysis {
avg_memory_per_op,
peak_to_avg_ratio: 1.2, fragmentation_score: 0.15, zero_copy_efficiency: 0.95, bandwidth_utilization: 0.75, };
Ok(memory_analysis)
}
pub fn generate_benchmark_report(&self) -> CoreResult<String> {
let results = self.results.lock().map_err(|_| {
CoreError::ComputationError(ErrorContext::new("Failed to lock results".to_string()))
})?;
if results.is_empty() {
return Ok("No benchmark results available.".to_string());
}
let latest = &results[results.len() - 1];
let mut report = String::new();
report.push_str("# SciRS2 Cross-Module Performance Benchmark Report\n\n");
#[cfg(feature = "serialization")]
{
report.push_str(&format!(
"**Generated**: {}\n",
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
));
}
#[cfg(not(feature = "serialization"))]
{
report.push_str("**Generated**: [timestamp unavailable]\n");
}
report.push_str(&format!("**Suite**: {}\n", latest.name));
report.push_str(&format!(
"**Total Duration**: {:?}\n",
latest.total_duration
));
report.push_str(&format!(
"**Average Efficiency**: {:.1}%\n\n",
latest.avg_efficiency
));
report.push_str("## Executive Summary\n\n");
report.push_str(&format!(
"- **Benchmarks Executed**: {}\n",
latest.measurements.len()
));
report.push_str(&format!(
"- **Overall Efficiency**: {:.1}%\n",
latest.avg_efficiency
));
report.push_str(&format!(
"- **Thread Scalability**: {:.1}%\n",
latest.scalability_analysis.thread_scalability * 100.0
));
report.push_str(&format!(
"- **Memory Efficiency**: {:.1}%\n",
(1.0 - latest.memory_analysis.fragmentation_score) * 100.0
));
report.push_str("\n## Benchmark Results\n\n");
for measurement in &latest.measurements {
report.push_str(&format!(
"### {} ({})\n",
measurement.name,
measurement.modules.join(" + ")
));
report.push_str(&format!(
"- **Data Size**: {} bytes\n",
measurement.datasize
));
report.push_str(&format!(
"- **Average Time**: {:?}\n",
measurement.avg_duration
));
report.push_str(&format!(
"- **Throughput**: {:.2} ops/sec\n",
measurement.throughput
));
report.push_str(&format!(
"- **Memory Usage**: {} MB\n",
measurement.memory_usage / (1024 * 1024)
));
report.push_str(&format!(
"- **Efficiency Score**: {:.1}%\n",
measurement.efficiency_score()
));
report.push('\n');
}
report.push_str("## Scalability Analysis\n\n");
report.push_str(&format!(
"- **Thread Scalability**: {:.1}%\n",
latest.scalability_analysis.thread_scalability * 100.0
));
report.push_str(&format!(
"- **Data Size Scalability**: {:.1}%\n",
latest.scalability_analysis.data_scalability * 100.0
));
report.push_str(&format!(
"- **Memory Scalability**: {:.1}%\n",
latest.scalability_analysis.memory_scalability * 100.0
));
report.push_str("\n## Memory Efficiency Analysis\n\n");
report.push_str(&format!(
"- **Average Memory per Operation**: {:.2} bytes\n",
latest.memory_analysis.avg_memory_per_op
));
report.push_str(&format!(
"- **Peak to Average Ratio**: {:.2}\n",
latest.memory_analysis.peak_to_avg_ratio
));
report.push_str(&format!(
"- **Fragmentation Score**: {:.3} (lower is better)\n",
latest.memory_analysis.fragmentation_score
));
report.push_str(&format!(
"- **Zero-Copy Efficiency**: {:.1}%\n",
latest.memory_analysis.zero_copy_efficiency * 100.0
));
if let Some(regression) = &latest.regression_analysis {
report.push_str("\n## Regression Analysis\n\n");
if regression.regression_detected {
report.push_str("⚠️ **Performance regressions detected**\n\n");
for reg in ®ression.regressions {
report.push_str(&format!(
"- **{}**: {:.2}% regression\n",
reg.benchmark_name, reg.change_percent
));
}
} else {
report.push_str("✅ **No significant regressions detected**\n");
}
}
report.push_str("\n## Recommendations\n\n");
if latest.avg_efficiency >= 80.0 {
report.push_str(
"✅ **Excellent Performance**: The cross-module performance is very good.\n",
);
} else if latest.avg_efficiency >= 60.0 {
report.push_str(
"⚠️ **Good Performance**: Consider optimizing bottlenecks identified above.\n",
);
} else {
report.push_str("❌ **Performance Issues**: Significant optimization work needed before 1.0 release.\n");
}
Ok(report)
}
}
#[derive(Debug)]
struct TimingData {
name: String,
avg_duration: Duration,
min_duration: Duration,
max_duration: Duration,
std_deviation: Duration,
throughput: f64,
memory_usage: usize,
operations_count: usize,
}
impl fmt::Display for RegressionSignificance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RegressionSignificance::Negligible => write!(f, "Negligible"),
RegressionSignificance::Minor => write!(f, "Minor"),
RegressionSignificance::Moderate => write!(f, "Moderate"),
RegressionSignificance::Major => write!(f, "Major"),
RegressionSignificance::Critical => write!(f, "Critical"),
}
}
}
#[allow(dead_code)]
pub fn create_default_benchmark_suite() -> CoreResult<CrossModuleBenchmarkRunner> {
let config = CrossModuleBenchConfig::default();
Ok(CrossModuleBenchmarkRunner::new(config))
}
#[allow(dead_code)]
pub fn run_quick_benchmarks() -> CoreResult<BenchmarkSuiteResult> {
let config = CrossModuleBenchConfig {
iterations: 2, warmup_iterations: 1, datasizes: vec![1024], ns: vec![1],
memory_limits: vec![64 * 1024 * 1024], enable_profiling: false,
enable_regression_detection: false,
timeout: Duration::from_secs(30),
..Default::default()
};
let runner = CrossModuleBenchmarkRunner::new(config);
runner.run_benchmarks()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_benchmark_config_creation() {
let config = CrossModuleBenchConfig::default();
assert_eq!(config.iterations, 100);
assert_eq!(config.warmup_iterations, 10);
assert!(!config.datasizes.is_empty());
assert!(!config.ns.is_empty());
}
#[test]
fn test_performance_measurement_creation() {
let measurement = PerformanceMeasurement::new(
"test_benchmark".to_string(),
vec!["module1".to_string(), "module2".to_string()],
);
assert_eq!(measurement.name, "test_benchmark");
assert_eq!(measurement.modules.len(), 2);
assert_eq!(measurement.efficiency_score(), 0.0); }
#[test]
fn test_benchmark_runner_creation() {
let config = CrossModuleBenchConfig::default();
let runner = CrossModuleBenchmarkRunner::new(config);
assert_eq!(runner.config.iterations, 100);
}
#[test]
fn test_quick_benchmarks() {
match run_quick_benchmarks() {
Ok(result) => {
assert!(!result.measurements.is_empty());
println!(
"Quick benchmarks completed: {} measurements",
result.measurements.len()
);
}
Err(e) => {
println!("Quick benchmarks failed: {:?}", e);
}
}
}
fn make_runner(enable_regression: bool) -> CrossModuleBenchmarkRunner {
let config = CrossModuleBenchConfig {
iterations: 2,
warmup_iterations: 1,
datasizes: vec![1024],
ns: vec![1],
memory_limits: vec![64 * 1024 * 1024],
enable_profiling: false,
enable_regression_detection: enable_regression,
baseline_file: None,
max_regression_percent: 5.0,
timeout: Duration::from_secs(30),
};
CrossModuleBenchmarkRunner::new(config)
}
fn make_measurement(name: &str, avg_nanos: u64) -> PerformanceMeasurement {
let mut m = PerformanceMeasurement::new(name.to_string(), vec!["test-module".to_string()]);
m.avg_duration = Duration::from_nanos(avg_nanos);
m.datasize = 1024;
m.operations_count = 10;
m.memory_usage = 1024;
m
}
#[test]
fn test_analyze_regressions_no_regression() {
let runner = make_runner(true);
let measurements = vec![
make_measurement("bench_a", 1_000_000),
make_measurement("bench_b", 1_000_000),
make_measurement("bench_c", 1_000_000),
make_measurement("bench_d", 1_000_000),
make_measurement("bench_e", 1_000_000),
];
let result = runner
.analyze_regressions(&measurements)
.expect("analyze_regressions should not fail");
assert!(
!result.regression_detected,
"Uniform measurements must not trigger regression detection"
);
assert!(
result.regressions.is_empty(),
"No regression entries expected for uniform timings"
);
assert!(
result.overall_change_percent.abs() < 1.0,
"Overall change should be near zero for uniform timings"
);
}
#[test]
fn test_analyze_regressions_outlier_detected() {
let runner = make_runner(true);
let measurements = vec![
make_measurement("bench_normal_1", 1_000_000),
make_measurement("bench_normal_2", 1_000_000),
make_measurement("bench_normal_3", 1_000_000),
make_measurement("bench_normal_4", 1_000_000),
make_measurement("bench_slow", 100_000_000), ];
let result = runner
.analyze_regressions(&measurements)
.expect("analyze_regressions should not fail");
assert!(
result.regression_detected,
"A 100x slower benchmark should trigger regression detection"
);
assert_eq!(
result.regressions.len(),
1,
"Exactly 1 regression expected (bench_slow); got {:?}",
result
.regressions
.iter()
.map(|r| &r.benchmark_name)
.collect::<Vec<_>>()
);
assert_eq!(
result.regressions[0].benchmark_name, "bench_slow",
"The regressed benchmark should be bench_slow"
);
assert!(
result.regressions[0].significance != RegressionSignificance::Negligible,
"A 100x regression cannot be Negligible"
);
assert_eq!(
result.improvements.len(),
4,
"The 4 normal benchmarks should appear as improvements"
);
}
#[test]
fn test_regression_detection_disabled_gate() {
let runner = make_runner(false);
let measurements = vec![
make_measurement("bench_normal", 1_000_000),
make_measurement("bench_slow", 100_000_000),
];
let regression_analysis: Option<RegressionAnalysis> =
if runner.config.enable_regression_detection {
Some(
runner
.analyze_regressions(&measurements)
.expect("analyze_regressions failed"),
)
} else {
None
};
assert!(
regression_analysis.is_none(),
"When enable_regression_detection is false, analysis must be None"
);
}
#[test]
fn test_analyze_regressions_empty() {
let runner = make_runner(true);
let result = runner
.analyze_regressions(&[])
.expect("Empty input should not fail");
assert!(!result.regression_detected);
assert!(result.regressions.is_empty());
assert!(result.improvements.is_empty());
assert_eq!(result.overall_change_percent, 0.0);
}
#[test]
fn test_run_benchmarks_regression_analysis_present() {
let runner = make_runner(true);
let suite = runner
.run_benchmarks()
.expect("run_benchmarks should succeed with detection enabled");
assert!(
suite.regression_analysis.is_some(),
"regression_analysis should be Some when detection is enabled"
);
}
#[test]
fn test_run_benchmarks_regression_analysis_absent() {
let runner = make_runner(false);
let suite = runner
.run_benchmarks()
.expect("run_benchmarks should succeed with detection disabled");
assert!(
suite.regression_analysis.is_none(),
"regression_analysis should be None when detection is disabled"
);
}
}