From 52980f4a9cd5ef252e8b3954837e61772bc4e3c2 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sun, 8 Jan 2023 20:01:24 -0800 Subject: [PATCH 1/3] Use Criterion for benchmarks --- Cargo.toml | 5 ++ benches/benches.rs | 190 ++++++++++++++++++++++++++------------------- 2 files changed, 115 insertions(+), 80 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c2bbce..1317c19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,8 @@ serde = { version = "1.0", features = ["derive"], optional = true } [dev-dependencies] serde_json = "1.0" +criterion = "0.4" + +[[bench]] +name = "benches" +harness = false \ No newline at end of file diff --git a/benches/benches.rs b/benches/benches.rs index ac999b0..b8c4ba6 100644 --- a/benches/benches.rs +++ b/benches/benches.rs @@ -1,133 +1,163 @@ -#![feature(test)] - -extern crate test; +extern crate criterion; extern crate fixedbitset; -use test::Bencher; -use fixedbitset::{FixedBitSet}; -use std::mem::size_of; +use criterion::{criterion_group, criterion_main, Criterion}; +use fixedbitset::FixedBitSet; +use std::hint::black_box; #[inline] fn iter_ones_using_contains(fb: &FixedBitSet, f: &mut F) { - for bit in 0 .. fb.len() { - if fb.contains(bit) { - f(bit); - } - } -} - -#[inline] -fn iter_ones_using_slice_directly(fb: &FixedBitSet, f: &mut F) { - for (block_idx, &block) in fb.as_slice().iter().enumerate() { - let mut bit_pos = block_idx * size_of::() * 8; - let mut block: u32 = block; - - while block != 0 { - if (block & 1) == 1 { - f(bit_pos); - } - block = block >> 1; - bit_pos += 1; + for bit in 0..fb.len() { + if fb.contains(bit) { + f(bit); } } } -#[bench] -fn bench_iter_ones_using_contains_all_zeros(b: &mut Bencher) { +fn iter_ones_using_contains_all_zeros(c: &mut Criterion) { const N: usize = 1_000_000; let fb = FixedBitSet::with_capacity(N); - b.iter(|| { - let mut count = 0; - iter_ones_using_contains(&fb, &mut |_bit| count += 1); - count + c.bench_function("iter_ones/contains_all_zeros", |b| { + b.iter(|| { + let mut count = 0; + iter_ones_using_contains(&fb, &mut |_bit| count += 1); + count + }) }); } -#[bench] -fn bench_iter_ones_using_contains_all_ones(b: &mut Bencher) { +fn iter_ones_using_contains_all_ones(c: &mut Criterion) { const N: usize = 1_000_000; let mut fb = FixedBitSet::with_capacity(N); fb.insert_range(..); - b.iter(|| { - let mut count = 0; - iter_ones_using_contains(&fb, &mut |_bit| count += 1); - count + c.bench_function("iter_ones/contains_all_ones", |b| { + b.iter(|| { + let mut count = 0; + iter_ones_using_contains(&fb, &mut |_bit| count += 1); + count + }) }); } -#[bench] -fn bench_iter_ones_using_slice_directly_all_zero(b: &mut Bencher) { +fn iter_ones_all_zeros(c: &mut Criterion) { const N: usize = 1_000_000; let fb = FixedBitSet::with_capacity(N); - b.iter(|| { - let mut count = 0; - iter_ones_using_slice_directly(&fb, &mut |_bit| count += 1); - count + c.bench_function("iter_ones/all_zeros", |b| { + b.iter(|| { + let mut count = 0; + for _ in fb.ones() { + count += 1; + } + count + }) }); } -#[bench] -fn bench_iter_ones_using_slice_directly_all_ones(b: &mut Bencher) { +fn iter_ones_all_ones(c: &mut Criterion) { const N: usize = 1_000_000; let mut fb = FixedBitSet::with_capacity(N); fb.insert_range(..); - b.iter(|| { - let mut count = 0; - iter_ones_using_slice_directly(&fb, &mut |_bit| count += 1); - count + c.bench_function("iter_ones/all_ones", |b| { + b.iter(|| { + let mut count = 0; + for _ in fb.ones() { + count += 1; + } + count + }) }); } -#[bench] -fn bench_iter_ones_all_zeros(b: &mut Bencher) { +fn insert_range(c: &mut Criterion) { const N: usize = 1_000_000; - let fb = FixedBitSet::with_capacity(N); + let mut fb = FixedBitSet::with_capacity(N); - b.iter(|| { - let mut count = 0; - for _ in fb.ones() { - count += 1; - } - count - }); + c.bench_function("insert_range/1m", |b| b.iter(|| fb.insert_range(..))); } -#[bench] -fn bench_iter_ones_all_ones(b: &mut Bencher) { +fn insert(c: &mut Criterion) { const N: usize = 1_000_000; let mut fb = FixedBitSet::with_capacity(N); - fb.insert_range(..); - b.iter(|| { - let mut count = 0; - for _ in fb.ones() { - count += 1; - } - count + c.bench_function("insert/1m", |b| { + b.iter(|| { + for i in 0..N { + fb.insert(i); + } + }) }); } -#[bench] -fn bench_insert_range(b: &mut Bencher) { +fn union_with(c: &mut Criterion) { const N: usize = 1_000_000; - let mut fb = FixedBitSet::with_capacity(N); + let mut fb_a = FixedBitSet::with_capacity(N); + let fb_b = FixedBitSet::with_capacity(N); + + c.bench_function("union_with/1m", |b| b.iter(|| fb_a.union_with(&fb_b))); +} - b.iter(|| { - fb.insert_range(..) +fn intersect_with(c: &mut Criterion) { + const N: usize = 1_000_000; + let mut fb_a = FixedBitSet::with_capacity(N); + let fb_b = FixedBitSet::with_capacity(N); + + c.bench_function("intersect_with/1m", |b| { + b.iter(|| fb_a.intersect_with(&fb_b)) }); } -#[bench] -fn bench_insert_range_using_loop(b: &mut Bencher) { +fn difference_with(c: &mut Criterion) { const N: usize = 1_000_000; - let mut fb = FixedBitSet::with_capacity(N); + let mut fb_a = FixedBitSet::with_capacity(N); + let fb_b = FixedBitSet::with_capacity(N); - b.iter(|| { - for i in 0..N { - fb.insert(i); - } + c.bench_function("difference_with/1m", |b| { + b.iter(|| fb_a.difference_with(&fb_b)) + }); +} + +fn symmetric_difference_with(c: &mut Criterion) { + const N: usize = 1_000_000; + let mut fb_a = FixedBitSet::with_capacity(N); + let fb_b = FixedBitSet::with_capacity(N); + + c.bench_function("symmetric_difference_with/1m", |b| { + b.iter(|| fb_a.symmetric_difference_with(&fb_b)) + }); +} + +fn clear(c: &mut Criterion) { + const N: usize = 1_000_000; + let mut fb_a = FixedBitSet::with_capacity(N); + + c.bench_function("clear/1m", |b| b.iter(|| fb_a.clear())); +} + +fn count_ones(c: &mut Criterion) { + const N: usize = 1_000_000; + let fb_a = FixedBitSet::with_capacity(N); + + c.bench_function("count_ones/1m", |b| { + b.iter(|| black_box(fb_a.count_ones(..))) }); } + +criterion_group!( + benches, + iter_ones_using_contains_all_zeros, + iter_ones_using_contains_all_ones, + iter_ones_all_zeros, + iter_ones_all_ones, + insert_range, + insert, + intersect_with, + difference_with, + union_with, + symmetric_difference_with, + count_ones, + clear, +); +criterion_main!(benches); From b6026c4c299723adb15f59d64a807ce2e589f3cc Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 9 Jan 2023 17:24:01 -0800 Subject: [PATCH 2/3] Separate benchmarks into it's own crate --- Cargo.toml | 5 ----- benches/Cargo.toml | 15 +++++++++++++++ benches/{ => benches}/benches.rs | 0 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 benches/Cargo.toml rename benches/{ => benches}/benches.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 1317c19..1c2bbce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,3 @@ serde = { version = "1.0", features = ["derive"], optional = true } [dev-dependencies] serde_json = "1.0" -criterion = "0.4" - -[[bench]] -name = "benches" -harness = false \ No newline at end of file diff --git a/benches/Cargo.toml b/benches/Cargo.toml new file mode 100644 index 0000000..76f2fe6 --- /dev/null +++ b/benches/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "benches" +version = "0.1.0" +edition = "2021" +description = "Benchmarks for FixedBitset" +publish = false +license = "MIT OR Apache-2.0" + +[dev-dependencies] +fixedbitset = { path = ".." } +criterion = { version = "0.4", features = ["html_reports"] } + +[[bench]] +name = "benches" +harness = false \ No newline at end of file diff --git a/benches/benches.rs b/benches/benches/benches.rs similarity index 100% rename from benches/benches.rs rename to benches/benches/benches.rs From 172099cc20fa3b62312cb2a6bf0eb0ebcc4bfe05 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 9 Jan 2023 17:28:47 -0800 Subject: [PATCH 3/3] Ensure that the benchmarks compile as a part of CI --- .github/workflows/rust.yml | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0d51309..0d2c683 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,6 +11,7 @@ env: CARGO_INCREMENTAL: 0 jobs: + # Ensure the crate builds build: runs-on: ubuntu-latest @@ -32,6 +33,7 @@ jobs: cargo test --verbose --features "$FEATURES" && cargo test --verbose --release --features "$FEATURES" + # Use clippy to lint for code smells clippy: runs-on: ubuntu-latest @@ -52,6 +54,7 @@ jobs: run: | cargo clippy + # Enforce rustfmt formatting formatting: runs-on: ubuntu-latest @@ -70,4 +73,26 @@ jobs: override: true - name: Run Clippy run: | - cargo fmt --all --check \ No newline at end of file + cargo fmt --all --check + + # Ensure the benchmarks compile + benchmark_compiles: + + runs-on: ubuntu-latest + strategy: + matrix: + # Check builds only on stable + rust: [stable] + + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@stable + with: + profile: minimal + toolchain: ${{ matrix.rust }} + components: clippy + override: true + - name: Run Clippy + run: | + cd benches + cargo bench --bench benches --no-run \ No newline at end of file