423 lines
14 KiB
Rust
423 lines
14 KiB
Rust
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
|
|
use std::time::Duration;
|
|
use sublinear_time_solver::core::{SparseMatrix, Vector};
|
|
use sublinear_time_solver::solver::hybrid::{HybridSolver, HybridConfig};
|
|
use sublinear_time_solver::solver::random_walk::{RandomWalkEngine, RandomWalkConfig, BidirectionalWalk, VarianceReduction};
|
|
use sublinear_time_solver::solver::sampling::{AdaptiveSampler, SamplingConfig, SamplingStrategy, MultiLevelSampler};
|
|
use sublinear_time_solver::algorithms::Algorithm;
|
|
|
|
/// Create a test sparse matrix with given size and sparsity
|
|
fn create_benchmark_matrix(n: usize, sparsity: f64) -> SparseMatrix {
|
|
let mut matrix = SparseMatrix::new(n, n);
|
|
let num_nonzeros = (n * n) as f64 * sparsity;
|
|
|
|
// Create a symmetric positive definite matrix
|
|
for i in 0..n {
|
|
matrix.insert(i, i, 5.0 + i as f64 * 0.01); // Strong diagonal dominance
|
|
}
|
|
|
|
let mut rng = 12345u64; // Simple LCG for reproducible results
|
|
let mut added_elements = 0;
|
|
|
|
while added_elements < num_nonzeros as usize - n {
|
|
// Simple LCG
|
|
rng = rng.wrapping_mul(1664525).wrapping_add(1013904223);
|
|
let i = (rng as usize) % n;
|
|
|
|
rng = rng.wrapping_mul(1664525).wrapping_add(1013904223);
|
|
let j = (rng as usize) % n;
|
|
|
|
if i != j && !matrix.get_row(i).contains_key(&j) {
|
|
let value = (rng as f64 / u64::MAX as f64) * 0.5 - 0.25; // [-0.25, 0.25]
|
|
matrix.insert(i, j, value);
|
|
matrix.insert(j, i, value); // Symmetric
|
|
added_elements += 2;
|
|
}
|
|
}
|
|
|
|
matrix
|
|
}
|
|
|
|
/// Create a test vector
|
|
fn create_benchmark_vector(n: usize) -> Vector {
|
|
(0..n).map(|i| 1.0 + (i as f64) * 0.1).collect()
|
|
}
|
|
|
|
/// Benchmark random walk solver with different configurations
|
|
fn benchmark_random_walk_solver(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("random_walk_solver");
|
|
group.measurement_time(Duration::from_secs(20));
|
|
group.sample_size(10);
|
|
|
|
let sizes = vec![50, 100, 200];
|
|
let variance_methods = vec![
|
|
VarianceReduction::None,
|
|
VarianceReduction::Antithetic,
|
|
];
|
|
|
|
for size in sizes {
|
|
let matrix = create_benchmark_matrix(size, 0.1);
|
|
let b = create_benchmark_vector(size);
|
|
|
|
for method in &variance_methods {
|
|
let config = RandomWalkConfig {
|
|
max_steps: 5000,
|
|
variance_reduction: method.clone(),
|
|
convergence_tolerance: 1e-6,
|
|
seed: Some(42),
|
|
..Default::default()
|
|
};
|
|
|
|
let benchmark_id = BenchmarkId::from_parameter(format!("size_{}_method_{:?}", size, method));
|
|
|
|
group.bench_with_input(benchmark_id, &size, |b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut engine = RandomWalkEngine::new(config.clone());
|
|
black_box(engine.solve_linear_system(&matrix, &b).unwrap())
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark bidirectional random walk
|
|
fn benchmark_bidirectional_walk(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("bidirectional_walk");
|
|
group.measurement_time(Duration::from_secs(15));
|
|
group.sample_size(10);
|
|
|
|
let sizes = vec![30, 60, 120];
|
|
|
|
for size in sizes {
|
|
let matrix = create_benchmark_matrix(size, 0.15);
|
|
let b = create_benchmark_vector(size);
|
|
|
|
let config = RandomWalkConfig {
|
|
max_steps: 3000,
|
|
variance_reduction: VarianceReduction::Antithetic,
|
|
seed: Some(42),
|
|
..Default::default()
|
|
};
|
|
|
|
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut solver = BidirectionalWalk::new(config.clone());
|
|
black_box(solver.solve_linear_system(&matrix, &b).unwrap())
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark adaptive sampling strategies
|
|
fn benchmark_adaptive_sampling(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("adaptive_sampling");
|
|
group.measurement_time(Duration::from_secs(10));
|
|
group.sample_size(15);
|
|
|
|
let strategies = vec![
|
|
SamplingStrategy::Uniform,
|
|
SamplingStrategy::ImportanceSampling,
|
|
SamplingStrategy::AdaptiveSampling,
|
|
SamplingStrategy::QuasiMonteCarlo,
|
|
];
|
|
|
|
let domain_size = 1000;
|
|
let target_function = |x: usize| (x as f64 / 100.0).sin().abs() + 0.5;
|
|
|
|
for strategy in strategies {
|
|
let config = SamplingConfig {
|
|
strategy: strategy.clone(),
|
|
sample_size: 500,
|
|
seed: Some(42),
|
|
..Default::default()
|
|
};
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(format!("{:?}", strategy)),
|
|
&strategy,
|
|
|b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut sampler = AdaptiveSampler::new(config.clone());
|
|
black_box(sampler.generate_samples(domain_size, &target_function).unwrap())
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark multi-level sampling
|
|
fn benchmark_multilevel_sampling(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("multilevel_sampling");
|
|
group.measurement_time(Duration::from_secs(15));
|
|
group.sample_size(10);
|
|
|
|
let num_levels_vec = vec![2, 3, 4];
|
|
|
|
for num_levels in num_levels_vec {
|
|
let base_config = SamplingConfig {
|
|
sample_size: 1000,
|
|
seed: Some(42),
|
|
..Default::default()
|
|
};
|
|
|
|
let domain_sizes: Vec<usize> = (0..num_levels).map(|i| 1000 / (2_usize.pow(i as u32))).collect();
|
|
let target_functions: Vec<Box<dyn Fn(usize) -> f64>> = (0..num_levels)
|
|
.map(|level| {
|
|
Box::new(move |x: usize| {
|
|
(x as f64 / (10.0 * (level + 1) as f64)).sin().abs() + 0.1
|
|
}) as Box<dyn Fn(usize) -> f64>
|
|
})
|
|
.collect();
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(num_levels),
|
|
&num_levels,
|
|
|b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut ml_sampler = MultiLevelSampler::new(num_levels, base_config.clone());
|
|
let fn_refs: Vec<&dyn Fn(usize) -> f64> = target_functions.iter().map(|f| f.as_ref()).collect();
|
|
black_box(ml_sampler.generate_multilevel_samples(&domain_sizes, &fn_refs).unwrap())
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark hybrid solver with different configurations
|
|
fn benchmark_hybrid_solver(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("hybrid_solver");
|
|
group.measurement_time(Duration::from_secs(30));
|
|
group.sample_size(8);
|
|
|
|
let sizes = vec![40, 80, 160];
|
|
let configs = vec![
|
|
("deterministic_only", HybridConfig {
|
|
use_deterministic: true,
|
|
use_random_walk: false,
|
|
use_bidirectional: false,
|
|
use_multilevel: false,
|
|
max_iterations: 1000,
|
|
convergence_tolerance: 1e-6,
|
|
parallel_execution: false,
|
|
..Default::default()
|
|
}),
|
|
("random_walk_only", HybridConfig {
|
|
use_deterministic: false,
|
|
use_random_walk: true,
|
|
use_bidirectional: false,
|
|
use_multilevel: false,
|
|
max_iterations: 500,
|
|
convergence_tolerance: 1e-5,
|
|
parallel_execution: false,
|
|
random_walk_config: RandomWalkConfig {
|
|
max_steps: 3000,
|
|
variance_reduction: VarianceReduction::Antithetic,
|
|
seed: Some(42),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
}),
|
|
("hybrid_approach", HybridConfig {
|
|
use_deterministic: true,
|
|
use_random_walk: true,
|
|
use_bidirectional: true,
|
|
use_multilevel: false,
|
|
deterministic_weight: 0.6,
|
|
max_iterations: 300,
|
|
convergence_tolerance: 1e-6,
|
|
parallel_execution: false,
|
|
adaptation_interval: 50,
|
|
..Default::default()
|
|
}),
|
|
];
|
|
|
|
for size in sizes {
|
|
let matrix = create_benchmark_matrix(size, 0.08);
|
|
let b = create_benchmark_vector(size);
|
|
|
|
for (config_name, config) in &configs {
|
|
let benchmark_id = BenchmarkId::from_parameter(format!("size_{}_config_{}", size, config_name));
|
|
|
|
group.bench_with_input(benchmark_id, &size, |b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut solver = HybridSolver::new(config.clone());
|
|
black_box(solver.solve_linear_system(&matrix, &b).unwrap())
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark parallel vs sequential execution
|
|
fn benchmark_parallel_vs_sequential(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("parallel_vs_sequential");
|
|
group.measurement_time(Duration::from_secs(25));
|
|
group.sample_size(5);
|
|
|
|
let size = 100;
|
|
let matrix = create_benchmark_matrix(size, 0.1);
|
|
let b = create_benchmark_vector(size);
|
|
|
|
let configs = vec![
|
|
("sequential", HybridConfig {
|
|
parallel_execution: false,
|
|
max_iterations: 200,
|
|
convergence_tolerance: 1e-5,
|
|
..Default::default()
|
|
}),
|
|
("parallel", HybridConfig {
|
|
parallel_execution: true,
|
|
max_iterations: 200,
|
|
convergence_tolerance: 1e-5,
|
|
..Default::default()
|
|
}),
|
|
];
|
|
|
|
for (mode, config) in configs {
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(mode),
|
|
&mode,
|
|
|b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut solver = HybridSolver::new(config.clone());
|
|
black_box(solver.solve_linear_system(&matrix, &b).unwrap())
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark convergence speed with different tolerances
|
|
fn benchmark_convergence_tolerance(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("convergence_tolerance");
|
|
group.measurement_time(Duration::from_secs(20));
|
|
group.sample_size(10);
|
|
|
|
let size = 60;
|
|
let matrix = create_benchmark_matrix(size, 0.12);
|
|
let b = create_benchmark_vector(size);
|
|
|
|
let tolerances = vec![1e-4, 1e-6, 1e-8];
|
|
|
|
for tolerance in tolerances {
|
|
let config = HybridConfig {
|
|
convergence_tolerance: tolerance,
|
|
max_iterations: 1000,
|
|
parallel_execution: false,
|
|
..Default::default()
|
|
};
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(format!("{:.0e}", tolerance)),
|
|
&tolerance,
|
|
|b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut solver = HybridSolver::new(config.clone());
|
|
black_box(solver.solve_linear_system(&matrix, &b).unwrap())
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark memory usage and performance correlation
|
|
fn benchmark_memory_performance(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("memory_performance");
|
|
group.measurement_time(Duration::from_secs(15));
|
|
group.sample_size(8);
|
|
|
|
let size = 80;
|
|
let matrix = create_benchmark_matrix(size, 0.1);
|
|
let b = create_benchmark_vector(size);
|
|
|
|
let memory_limits = vec![512, 1024, 2048]; // MB
|
|
|
|
for memory_limit in memory_limits {
|
|
let config = HybridConfig {
|
|
memory_limit,
|
|
max_iterations: 500,
|
|
convergence_tolerance: 1e-6,
|
|
parallel_execution: false,
|
|
..Default::default()
|
|
};
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(format!("{}MB", memory_limit)),
|
|
&memory_limit,
|
|
|b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut solver = HybridSolver::new(config.clone());
|
|
let solution = solver.solve_linear_system(&matrix, &b).unwrap();
|
|
let metrics = solver.get_metrics();
|
|
black_box((solution, metrics.memory_usage))
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Benchmark sparsity effects on performance
|
|
fn benchmark_sparsity_effects(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("sparsity_effects");
|
|
group.measurement_time(Duration::from_secs(20));
|
|
group.sample_size(10);
|
|
|
|
let size = 100;
|
|
let sparsity_levels = vec![0.05, 0.1, 0.2, 0.3];
|
|
|
|
for sparsity in sparsity_levels {
|
|
let matrix = create_benchmark_matrix(size, sparsity);
|
|
let b = create_benchmark_vector(size);
|
|
|
|
let config = HybridConfig {
|
|
max_iterations: 300,
|
|
convergence_tolerance: 1e-6,
|
|
parallel_execution: false,
|
|
..Default::default()
|
|
};
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(format!("sparsity_{:.2}", sparsity)),
|
|
&sparsity,
|
|
|b_bench, _| {
|
|
b_bench.iter(|| {
|
|
let mut solver = HybridSolver::new(config.clone());
|
|
black_box(solver.solve_linear_system(&matrix, &b).unwrap())
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(
|
|
solver_benches,
|
|
benchmark_random_walk_solver,
|
|
benchmark_bidirectional_walk,
|
|
benchmark_adaptive_sampling,
|
|
benchmark_multilevel_sampling,
|
|
benchmark_hybrid_solver,
|
|
benchmark_parallel_vs_sequential,
|
|
benchmark_convergence_tolerance,
|
|
benchmark_memory_performance,
|
|
benchmark_sparsity_effects
|
|
);
|
|
|
|
criterion_main!(solver_benches); |