//! Benchmark Tests for Hyperparameter Optimization //! //! These benchmarks measure the performance of key operations in the //! hyperparameter optimization framework. use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use ml::hyperopt::{BestHyperparameters, HyperparameterSpace, OptimizationResult, TrialResult}; use ndarray::Array1; // Mock denormalize function for benchmarking (since we can't access private functions) fn denormalize_params_mock( normalized: &Array1, space: &HyperparameterSpace, ) -> (f64, usize, f64, f64) { let lr_norm = normalized[0]; let batch_norm = normalized[1]; let dropout_norm = normalized[2]; let wd_norm = normalized[3]; // Learning rate (log scale) let lr_log = space.learning_rate_log_min + lr_norm * (space.learning_rate_log_max - space.learning_rate_log_min); let learning_rate = 10_f64.powf(lr_log); // Batch size (integer, linear scale) let batch_size = (space.batch_size_min as f64 + batch_norm * (space.batch_size_max - space.batch_size_min) as f64) .round() as usize; // Dropout (linear scale) let dropout = space.dropout_min + dropout_norm * (space.dropout_max - space.dropout_min); // Weight decay (log scale) let wd_log = space.weight_decay_log_min + wd_norm * (space.weight_decay_log_max - space.weight_decay_log_min); let weight_decay = 10_f64.powf(wd_log); (learning_rate, batch_size, dropout, weight_decay) } fn benchmark_param_conversion(c: &mut Criterion) { let space = HyperparameterSpace::default(); let mut group = c.benchmark_group("param_conversion"); // Benchmark single conversion group.bench_function("single_conversion", |b| { let normalized = Array1::from_vec(vec![0.5, 0.5, 0.5, 0.5]); b.iter(|| { let result = denormalize_params_mock(black_box(&normalized), black_box(&space)); black_box(result); }); }); // Benchmark batch conversions (simulating optimization) for batch_size in [10, 50, 100, 500].iter() { group.bench_with_input( BenchmarkId::from_parameter(format!("batch_{}", batch_size)), batch_size, |b, &size| { let normalized_batch: Vec> = (0..size) .map(|i| { let norm = i as f64 / size as f64; Array1::from_vec(vec![norm, norm, norm, norm]) }) .collect(); b.iter(|| { for normalized in &normalized_batch { let result = denormalize_params_mock(black_box(normalized), black_box(&space)); black_box(result); } }); }, ); } group.finish(); } fn benchmark_log_scale_computation(c: &mut Criterion) { let mut group = c.benchmark_group("log_scale"); // Benchmark pow computation (expensive operation) group.bench_function("pow_computation", |b| { let log_value = -3.5; b.iter(|| { let result = 10_f64.powf(black_box(log_value)); black_box(result); }); }); // Benchmark linear interpolation group.bench_function("linear_interpolation", |b| { let min = -5.0; let max = -2.0; let norm = 0.5; b.iter(|| { let result = black_box(min) + black_box(norm) * (black_box(max) - black_box(min)); black_box(result); }); }); group.finish(); } fn benchmark_batch_size_rounding(c: &mut Criterion) { let mut group = c.benchmark_group("batch_rounding"); // Benchmark integer rounding group.bench_function("round_to_integer", |b| { let value = 127.8; b.iter(|| { let result = black_box(value).round() as usize; black_box(result); }); }); // Benchmark floor group.bench_function("floor_to_integer", |b| { let value = 127.8; b.iter(|| { let result = black_box(value).floor() as usize; black_box(result); }); }); // Benchmark ceil group.bench_function("ceil_to_integer", |b| { let value = 127.8; b.iter(|| { let result = black_box(value).ceil() as usize; black_box(result); }); }); group.finish(); } fn benchmark_serialization(c: &mut Criterion) { let mut group = c.benchmark_group("serialization"); let best_params = BestHyperparameters { learning_rate: 0.001, batch_size: 64, dropout: 0.2, weight_decay: 0.0001, best_validation_loss: 12.5, trials_used: 30, }; // Benchmark JSON serialization group.bench_function("json_serialize", |b| { b.iter(|| { let json = serde_json::to_string(black_box(&best_params)).unwrap(); black_box(json); }); }); // Benchmark JSON deserialization let json = serde_json::to_string(&best_params).unwrap(); group.bench_function("json_deserialize", |b| { b.iter(|| { let result: BestHyperparameters = serde_json::from_str(black_box(&json)).unwrap(); black_box(result); }); }); // Benchmark YAML serialization group.bench_function("yaml_serialize", |b| { b.iter(|| { let yaml = serde_yaml::to_string(black_box(&best_params)).unwrap(); black_box(yaml); }); }); // Benchmark YAML deserialization let yaml = serde_yaml::to_string(&best_params).unwrap(); group.bench_function("yaml_deserialize", |b| { b.iter(|| { let result: BestHyperparameters = serde_yaml::from_str(black_box(&yaml)).unwrap(); black_box(result); }); }); group.finish(); } fn benchmark_optimization_result_creation(c: &mut Criterion) { let mut group = c.benchmark_group("result_creation"); // Benchmark creating OptimizationResult for trial_count in [10, 30, 50, 100].iter() { group.bench_with_input( BenchmarkId::from_parameter(format!("trials_{}", trial_count)), trial_count, |b, &count| { b.iter(|| { let best_params = BestHyperparameters { learning_rate: 0.001, batch_size: 64, dropout: 0.2, weight_decay: 0.0001, best_validation_loss: 12.5, trials_used: count, }; let trial_history: Vec = (0..count) .map(|i| TrialResult { trial_number: i + 1, learning_rate: 0.001, batch_size: 64, dropout: 0.2, weight_decay: 0.0001, validation_loss: 15.0 - i as f64 * 0.05, training_time_seconds: 18.0, }) .collect(); let result = OptimizationResult { best_params, trial_history, }; black_box(result); }); }, ); } group.finish(); } fn benchmark_array_creation(c: &mut Criterion) { let mut group = c.benchmark_group("array_ops"); // Benchmark Array1 creation group.bench_function("array1_from_vec", |b| { let values = vec![0.1, 0.2, 0.3, 0.4]; b.iter(|| { let arr = Array1::from_vec(black_box(values.clone())); black_box(arr); }); }); // Benchmark Array1 indexing group.bench_function("array1_indexing", |b| { let arr = Array1::from_vec(vec![0.1, 0.2, 0.3, 0.4]); b.iter(|| { let val = black_box(&arr)[0]; black_box(val); }); }); // Benchmark Array1 to owned group.bench_function("array1_to_owned", |b| { let arr = Array1::from_vec(vec![0.1, 0.2, 0.3, 0.4]); let view = arr.view(); b.iter(|| { let owned = black_box(&view).to_owned(); black_box(owned); }); }); group.finish(); } fn benchmark_hyperparameter_space_creation(c: &mut Criterion) { let mut group = c.benchmark_group("space_creation"); // Benchmark default space creation group.bench_function("default_space", |b| { b.iter(|| { let space = HyperparameterSpace::default(); black_box(space); }); }); // Benchmark custom space creation group.bench_function("custom_space", |b| { b.iter(|| { let space = HyperparameterSpace { learning_rate_log_min: -4.0, learning_rate_log_max: -1.0, batch_size_min: 32, batch_size_max: 128, dropout_min: 0.1, dropout_max: 0.3, weight_decay_log_min: -5.0, weight_decay_log_max: -3.0, }; black_box(space); }); }); // Benchmark space cloning group.bench_function("clone_space", |b| { let space = HyperparameterSpace::default(); b.iter(|| { let cloned = black_box(&space).clone(); black_box(cloned); }); }); group.finish(); } criterion_group!( benches, benchmark_param_conversion, benchmark_log_scale_computation, benchmark_batch_size_rounding, benchmark_serialization, benchmark_optimization_result_creation, benchmark_array_creation, benchmark_hyperparameter_space_creation ); criterion_main!(benches);