use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use std::hint::black_box;
use sparse_ldlt::SparseLdlt;
fn banded(n: usize) -> (Vec<usize>, Vec<usize>, Vec<f64>) {
let mut cp = vec![0usize];
let mut ri = Vec::new();
let mut v = Vec::new();
for j in 0..n {
for i in j.saturating_sub(1)..=j {
ri.push(i);
v.push(if i == j { 4.0 } else { 1.0 });
}
cp.push(ri.len());
}
(cp, ri, v)
}
struct Rng(u64);
impl Rng {
fn next_f64(&mut self) -> f64 {
self.0 = self.0.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
((self.0 >> 11) as f64 / (1u64 << 53) as f64) * 2.0 - 1.0
}
}
fn random_spd(n: usize, density: f64, seed: u64) -> (Vec<usize>, Vec<usize>, Vec<f64>) {
let mut rng = Rng(seed);
let mut dense = vec![0.0f64; n * n];
for j in 0..n {
for i in 0..j {
if rng.next_f64() < density * 2.0 - 1.0 {
let v = rng.next_f64();
dense[i * n + j] = v;
dense[j * n + i] = v;
}
}
dense[j * n + j] = n as f64 + rng.next_f64();
}
let mut cp = vec![0usize];
let mut ri = Vec::new();
let mut v = Vec::new();
for j in 0..n {
for i in 0..=j {
if dense[i * n + j] != 0.0 {
ri.push(i);
v.push(dense[i * n + j]);
}
}
cp.push(ri.len());
}
(cp, ri, v)
}
fn bench_factor(c: &mut Criterion) {
let mut group = c.benchmark_group("factor");
for n in [64usize, 256, 1024, 4096] {
let (cp, ri, v) = banded(n);
group.bench_function(format!("banded/{n}"), |b| {
b.iter(|| {
SparseLdlt::factor(black_box(n), black_box(&cp), black_box(&ri), black_box(&v))
.unwrap()
})
});
if n <= 1024 {
let (cp, ri, v) = random_spd(n, 0.02, 42 + n as u64);
group.bench_function(format!("random2pct/{n}"), |b| {
b.iter(|| {
SparseLdlt::factor(black_box(n), black_box(&cp), black_box(&ri), black_box(&v))
.unwrap()
})
});
let order = sparse_ldlt::amd(n, &cp, &ri);
group.bench_function(format!("random2pct-amd/{n}"), |b| {
b.iter(|| {
SparseLdlt::factor_perm(
black_box(n),
black_box(&cp),
black_box(&ri),
black_box(&v),
black_box(&order),
)
.unwrap()
})
});
} else {
let (cp, ri, v) = banded(n);
let order = sparse_ldlt::amd(n, &cp, &ri);
group.bench_function(format!("banded-amd/{n}"), |b| {
b.iter(|| {
SparseLdlt::factor_perm(
black_box(n),
black_box(&cp),
black_box(&ri),
black_box(&v),
black_box(&order),
)
.unwrap()
})
});
}
}
group.finish();
}
fn bench_solve(c: &mut Criterion) {
let mut group = c.benchmark_group("solve");
for n in [64usize, 256, 1024, 4096] {
let (cp, ri, v) = banded(n);
let f = SparseLdlt::factor(n, &cp, &ri, &v).unwrap();
let b: Vec<f64> = (0..n).map(|i| (i % 7) as f64 - 3.0).collect();
group.bench_with_input(BenchmarkId::new("banded", n), &b, |b, b_rhs| {
b.iter(|| f.solve(black_box(b_rhs)).unwrap())
});
}
group.finish();
}
criterion_group!(benches, bench_factor, bench_solve);
criterion_main!(benches);