From 08bdc03ce3239e2cd06a167bdbb1c53f92fc436f Mon Sep 17 00:00:00 2001 From: Emil Koutanov Date: Fri, 24 Nov 2023 18:37:26 +1100 Subject: [PATCH] Benchmarking combinatorics --- Cargo.toml | 4 ++++ benches/cri_comb.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/comb.rs | 6 +++++- 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 benches/cri_comb.rs diff --git a/Cargo.toml b/Cargo.toml index 16b2480..e312e15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,10 @@ assert_float_eq = "1.1.3" criterion = { version = "0.5.1", features = ["html_reports"] } tinyrand-alloc = "0.5.0" +[[bench]] +name = "cri_comb" +harness = false + [[bench]] name = "cri_mc" harness = false diff --git a/benches/cri_comb.rs b/benches/cri_comb.rs new file mode 100644 index 0000000..044ead4 --- /dev/null +++ b/benches/cri_comb.rs @@ -0,0 +1,45 @@ +use criterion::{criterion_group, criterion_main, Criterion}; + +use brumby::comb::{count_combinations, is_unique_linear, pick}; + +fn criterion_benchmark(c: &mut Criterion) { + fn fixtures(items: usize, times: usize) -> (Vec, Vec, Vec) { + let mut bitmap = Vec::with_capacity(items); + bitmap.resize(bitmap.capacity(), true); + let mut cardinalities = Vec::with_capacity(times); + cardinalities.resize(cardinalities.capacity(), items); + let mut ordinals = Vec::with_capacity(times); + ordinals.resize(ordinals.capacity(), 0usize); + (bitmap, cardinalities, ordinals) + } + + let (mut bitmap, cardinalities, mut ordinals) = fixtures(10, 5); + + // sanity check + let unique_combinations = (0..count_combinations(&cardinalities)) + .into_iter() + .map(|combination| { + pick(&cardinalities, combination, &mut ordinals); + is_unique_linear(&ordinals, &mut bitmap) + }) + .filter(|&unique| unique) + .count(); + assert_eq!(10 * 9 * 8 * 7 * 6, unique_combinations); + + fn bench(c: &mut Criterion, items: usize, times: usize) { + let (mut bitmap, cardinalities, mut ordinals) = fixtures(items, times); + c.bench_function(&format!("cri_comb_{items}c{times}"), |b| { + b.iter(|| { + for combination in 0..count_combinations(&cardinalities) { + pick(&cardinalities, combination, &mut ordinals); + is_unique_linear(&ordinals, &mut bitmap); + } + }); + }); + } + bench(c, 10, 3); + bench(c, 10, 4); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/comb.rs b/src/comb.rs index 78daac0..43d6d76 100644 --- a/src/comb.rs +++ b/src/comb.rs @@ -1,5 +1,6 @@ //! Combinatorics. +#[inline] pub fn pick(cardinalities: &[usize], combination: u64, ordinals: &mut [usize]) { let mut residual = combination; for (index, &cardinality) in cardinalities.iter().enumerate() { @@ -10,8 +11,9 @@ pub fn pick(cardinalities: &[usize], combination: u64, ordinals: &mut [usize]) { } } +#[inline] pub fn count_combinations(cardinalities: &[usize]) -> u64 { - cardinalities.iter().product::() as u64 + cardinalities.iter().fold(1u64, |acc, &num| acc * num as u64) } pub struct Combinator<'a> { @@ -63,6 +65,7 @@ impl<'a> Iterator for Iter<'a> { } } +#[inline] pub fn is_unique_quadratic(elements: &[usize]) -> bool { for (index, element) in elements.iter().enumerate() { for other in &elements[index + 1..] { @@ -74,6 +77,7 @@ pub fn is_unique_quadratic(elements: &[usize]) -> bool { true } +#[inline] pub fn is_unique_linear(elements: &[usize], bitmap: &mut [bool]) -> bool { bitmap.fill(false); for &element in elements {