Skip to content

Commit

Permalink
feat: Add basic benchmarking harness (#18)
Browse files Browse the repository at this point in the history
Adds some micro benchmarks for the permutation functions so we can
easily run them with `cargo bench`
  • Loading branch information
afnanenayet authored Jan 31, 2025
1 parent 2ebb5c6 commit 9939906
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 8 deletions.
16 changes: 15 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,29 @@ authors = ["Afnan Enayet <[email protected]>"]
edition = "2018"
license = "MIT"
readme = "docs/README.md"
resolver = "2"

[badges]
azure-devops = { project = "afnanenayet/hashed-permutation", pipeline = "afnanenayet.hashed-permutation" }
maintenance = { status = "actively-developed" }

[dependencies]
rand = { version = "0.8", optional = true }
rand = { version = "0.9", optional = true }
thiserror = "1.0"

[features]
default = []
use-rand = ["rand"]

[dev-dependencies]
divan = "0.1.17"
hashed-permutation = { path = ".", features = ["use-rand"] }

[[bench]]
name = "kensler"
harness = false

[profile.bench]
debug = 2
lto = "thin"
codegen-units = 1
57 changes: 57 additions & 0 deletions benches/kensler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use divan::counter::ItemsCount;
use divan::{black_box_drop, Bencher};
use hashed_permutation::{HashedIter, HashedPermutation};
use rand::seq::SliceRandom;
use rand::{rng, Rng};
use std::num::NonZeroU32;

fn main() {
divan::main();
}

fn lens() -> impl IntoIterator<Item = u32> {
vec![1u32, 2, 4, 8, 16, 20, 21, 22]
.iter()
.map(|x| (1 << x) as u32)
.collect::<Vec<u32>>()
}

/// Benchmarks by setting the size of the permutation vector using len_exp as the shift factor for
/// the length.
#[divan::bench(args = lens())]
fn permutation(bencher: Bencher, length: u32) {
let mut rng = rng();
let seed: u32 = rng.random();
let perm = HashedPermutation::new_with_seed(NonZeroU32::new(length).unwrap(), seed);
let l: u32 = length.into();
bencher.counter(ItemsCount::new(l)).bench(|| {
for i in 0..l {
black_box_drop(perm.shuffle(i).unwrap());
}
l
});
}

#[divan::bench(args = lens())]
fn iterator(bencher: Bencher, length: u32) {
let mut rng = rng();
let seed: u32 = rng.random();
let l: u32 = length.into();
bencher
.counter(ItemsCount::new(l))
.with_inputs(|| HashedIter::new_with_seed(NonZeroU32::new(length).unwrap(), seed))
.bench_refs(|perm| {
perm.for_each(black_box_drop);
});
}

#[divan::bench(args = lens())]
fn naive_shuffle(bencher: Bencher, length: u32) {
bencher
.counter(ItemsCount::new(length))
.with_inputs(|| -> Vec<u32> { (0..length).collect::<Vec<u32>>() })
.bench_local_refs(|v| {
let mut rng = rng();
v.shuffle(&mut rng);
})
}
15 changes: 9 additions & 6 deletions src/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,16 @@ impl Iterator for HashedIter {
type Item = u32;

fn next(&mut self) -> Option<Self::Item> {
match self.permutation_engine.shuffle(self.current_idx) {
Ok(elem) => {
self.current_idx += 1;
Some(elem)
}
Err(_) => None,
if self.current_idx >= self.permutation_engine.length.into() {
return None;
}
let res = unsafe {
self.permutation_engine
.shuffle(self.current_idx)
.unwrap_unchecked()
};
self.current_idx += 1;
Some(res)
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/kensler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
//! on correlated multi-jittered sampling.
use crate::error::{PermutationError, PermutationResult};
use std::num::{NonZeroU32, Wrapping};

#[cfg(feature = "use-rand")]
use rand::prelude::*;
use std::num::{NonZeroU32, Wrapping};

/// The `HashedPermutation` struct stores the initial `seed` and `length` of the permutation
/// vector. In other words, if you want to shuffle the numbers from `0..n`, then `length = n`.
Expand Down

0 comments on commit 9939906

Please sign in to comment.