104 lines
3.2 KiB
Rust
104 lines
3.2 KiB
Rust
//! Performance benchmarks to validate optimizations against Python baselines
|
|
|
|
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
|
|
use sublinear_time_solver::fast_solver::{FastCSRMatrix, FastConjugateGradient};
|
|
use sublinear_time_solver::types::Precision;
|
|
|
|
fn generate_sparse_matrix(size: usize, sparsity: f64) -> (FastCSRMatrix, Vec<Precision>) {
|
|
let mut triplets = Vec::new();
|
|
let mut rng_state = 1u64;
|
|
|
|
// Generate diagonally dominant sparse matrix
|
|
for i in 0..size {
|
|
// Diagonal element (make it dominant)
|
|
let diag_val = 5.0 + (i as f64) * 0.01;
|
|
triplets.push((i, i, diag_val));
|
|
|
|
// Off-diagonal elements
|
|
let nnz_per_row = ((size as f64) * sparsity).max(1.0) as usize;
|
|
for _ in 0..nnz_per_row.min(5) {
|
|
// Simple LCG for reproducible random numbers
|
|
rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
|
|
let j = (rng_state as usize) % size;
|
|
|
|
if i != j {
|
|
let val = (rng_state as f64 / u64::MAX as f64) * 0.5; // Keep small for diagonal dominance
|
|
triplets.push((i, j, val));
|
|
}
|
|
}
|
|
}
|
|
|
|
let matrix = FastCSRMatrix::from_triplets(triplets, size, size);
|
|
let b = vec![1.0; size]; // Simple right-hand side
|
|
|
|
(matrix, b)
|
|
}
|
|
|
|
fn bench_matrix_vector_multiply(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("matrix_vector_multiply");
|
|
|
|
for size in [100, 1000, 5000].iter() {
|
|
let (matrix, _) = generate_sparse_matrix(*size, 0.01);
|
|
let x = vec![1.0; *size];
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::new("fast_csr", size),
|
|
size,
|
|
|b, _| {
|
|
let mut y = vec![0.0; *size];
|
|
b.iter(|| {
|
|
matrix.multiply_vector_fast(black_box(&x), black_box(&mut y));
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_conjugate_gradient_solve(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("conjugate_gradient_solve");
|
|
|
|
for size in [100, 1000].iter() {
|
|
let (matrix, b) = generate_sparse_matrix(*size, 0.01);
|
|
let solver = FastConjugateGradient::new(1000, 1e-8);
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::new("fast_cg", size),
|
|
size,
|
|
|bench, _| {
|
|
bench.iter(|| {
|
|
let _solution = solver.solve(black_box(&matrix), black_box(&b));
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_sparse_dense_comparison(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("sparse_vs_dense");
|
|
|
|
// Test the critical 1000x1000 case that shows MCP Dense is 190x slower
|
|
let size = 1000;
|
|
let (sparse_matrix, b) = generate_sparse_matrix(size, 0.001); // Very sparse
|
|
|
|
let solver = FastConjugateGradient::new(100, 1e-6);
|
|
|
|
group.bench_function("optimized_sparse_1000x1000", |bench| {
|
|
bench.iter(|| {
|
|
let _solution = solver.solve(black_box(&sparse_matrix), black_box(&b));
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
bench_matrix_vector_multiply,
|
|
bench_conjugate_gradient_solve,
|
|
bench_sparse_dense_comparison
|
|
);
|
|
criterion_main!(benches); |