Skip to content

Commit 0771fdc

Browse files
authored
Merge pull request #68 from acgetchell/perf/62-stack-backed-exact-kernels
perf: stack-backed storage for exact arithmetic kernels
2 parents c1495b0 + d4b1452 commit 0771fdc

7 files changed

Lines changed: 429 additions & 103 deletions

File tree

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ name = "vs_linalg"
4848
harness = false
4949
required-features = [ "bench" ]
5050

51+
[[bench]]
52+
name = "exact"
53+
harness = false
54+
required-features = [ "bench", "exact" ]
55+
5156
[profile.release]
5257
lto = "fat"
5358
codegen-units = 1

benches/exact.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//! Benchmarks for exact arithmetic operations.
2+
//!
3+
//! These benchmarks measure the performance of the `exact` feature's
4+
//! arbitrary-precision methods across dimensions D=2..5 (the primary
5+
//! target for geometric predicates).
6+
7+
use criterion::Criterion;
8+
use la_stack::{Matrix, Vector};
9+
use pastey::paste;
10+
use std::hint::black_box;
11+
12+
#[inline]
13+
#[allow(clippy::cast_precision_loss)]
14+
const fn matrix_entry<const D: usize>(r: usize, c: usize) -> f64 {
15+
if r == c {
16+
(r as f64).mul_add(1.0e-3, (D as f64) + 1.0)
17+
} else {
18+
0.1 / ((r + c + 1) as f64)
19+
}
20+
}
21+
22+
#[inline]
23+
const fn make_matrix_rows<const D: usize>() -> [[f64; D]; D] {
24+
let mut rows = [[0.0; D]; D];
25+
let mut r = 0;
26+
while r < D {
27+
let mut c = 0;
28+
while c < D {
29+
rows[r][c] = matrix_entry::<D>(r, c);
30+
c += 1;
31+
}
32+
r += 1;
33+
}
34+
rows
35+
}
36+
37+
#[inline]
38+
#[allow(clippy::cast_precision_loss)]
39+
fn make_vector_array<const D: usize>() -> [f64; D] {
40+
let mut data = [0.0; D];
41+
let mut i = 0;
42+
while i < D {
43+
data[i] = (i as f64) + 1.0;
44+
i += 1;
45+
}
46+
data
47+
}
48+
49+
/// Near-singular matrix: base singular matrix + tiny perturbation.
50+
/// This forces the exact Bareiss fallback in `det_sign_exact` (the fast
51+
/// f64 filter cannot resolve the sign).
52+
#[inline]
53+
fn near_singular_3x3() -> Matrix<3> {
54+
let perturbation = f64::from_bits(0x3CD0_0000_0000_0000); // 2^-50
55+
Matrix::<3>::from_rows([
56+
[1.0 + perturbation, 2.0, 3.0],
57+
[4.0, 5.0, 6.0],
58+
[7.0, 8.0, 9.0],
59+
])
60+
}
61+
62+
macro_rules! gen_exact_benches_for_dim {
63+
($c:expr, $d:literal) => {
64+
paste! {{
65+
let a = Matrix::<$d>::from_rows(make_matrix_rows::<$d>());
66+
let rhs = Vector::<$d>::new(make_vector_array::<$d>());
67+
68+
let mut [<group_d $d>] = ($c).benchmark_group(concat!("exact_d", stringify!($d)));
69+
70+
// === f64 baselines ===
71+
[<group_d $d>].bench_function("det", |bencher| {
72+
bencher.iter(|| {
73+
let det = black_box(a)
74+
.det(la_stack::DEFAULT_PIVOT_TOL)
75+
.expect("should not fail");
76+
black_box(det);
77+
});
78+
});
79+
80+
[<group_d $d>].bench_function("det_direct", |bencher| {
81+
bencher.iter(|| {
82+
let det = black_box(a).det_direct();
83+
black_box(det);
84+
});
85+
});
86+
87+
// === det_exact (BigRational result) ===
88+
[<group_d $d>].bench_function("det_exact", |bencher| {
89+
bencher.iter(|| {
90+
let det = black_box(a).det_exact().expect("should not fail");
91+
black_box(det);
92+
});
93+
});
94+
95+
// === det_exact_f64 (exact → f64) ===
96+
[<group_d $d>].bench_function("det_exact_f64", |bencher| {
97+
bencher.iter(|| {
98+
let det = black_box(a).det_exact_f64().expect("should not fail");
99+
black_box(det);
100+
});
101+
});
102+
103+
// === det_sign_exact (adaptive: fast filter + exact fallback) ===
104+
[<group_d $d>].bench_function("det_sign_exact", |bencher| {
105+
bencher.iter(|| {
106+
let sign = black_box(a).det_sign_exact().expect("should not fail");
107+
black_box(sign);
108+
});
109+
});
110+
111+
// === solve_exact (BigRational result) ===
112+
[<group_d $d>].bench_function("solve_exact", |bencher| {
113+
bencher.iter(|| {
114+
let x = black_box(a).solve_exact(black_box(rhs)).expect("should not fail");
115+
black_box(x);
116+
});
117+
});
118+
119+
// === solve_exact_f64 (exact → f64) ===
120+
[<group_d $d>].bench_function("solve_exact_f64", |bencher| {
121+
bencher.iter(|| {
122+
let x = black_box(a).solve_exact_f64(black_box(rhs)).expect("should not fail");
123+
black_box(x);
124+
});
125+
});
126+
127+
[<group_d $d>].finish();
128+
}};
129+
};
130+
}
131+
132+
fn main() {
133+
let mut c = Criterion::default().configure_from_args();
134+
135+
#[allow(unused_must_use)]
136+
{
137+
gen_exact_benches_for_dim!(&mut c, 2);
138+
gen_exact_benches_for_dim!(&mut c, 3);
139+
gen_exact_benches_for_dim!(&mut c, 4);
140+
gen_exact_benches_for_dim!(&mut c, 5);
141+
}
142+
143+
// Near-singular 3×3: forces Bareiss fallback in det_sign_exact.
144+
{
145+
let m = near_singular_3x3();
146+
let mut group = c.benchmark_group("exact_near_singular_3x3");
147+
148+
group.bench_function("det_sign_exact", |bencher| {
149+
bencher.iter(|| {
150+
let sign = black_box(m).det_sign_exact().expect("should not fail");
151+
black_box(sign);
152+
});
153+
});
154+
155+
group.bench_function("det_exact", |bencher| {
156+
bencher.iter(|| {
157+
let det = black_box(m).det_exact().expect("should not fail");
158+
black_box(det);
159+
});
160+
});
161+
162+
group.finish();
163+
}
164+
165+
c.final_summary();
166+
}

0 commit comments

Comments
 (0)