Skip to content

Commit

Permalink
Initial working example of lending iterator for combinations.
Browse files Browse the repository at this point in the history
Added benchmarks comparing `combinations` and `combinations_lending`.
They show the lending implementation is ~1x-7x speed.

Feature works either on or off for conditional compilation.
  • Loading branch information
Easyoakland committed Mar 10, 2023
1 parent 5c21a87 commit 0eefe16
Show file tree
Hide file tree
Showing 6 changed files with 625 additions and 24 deletions.
7 changes: 7 additions & 0 deletions Cargo.toml
Expand Up @@ -27,6 +27,7 @@ test = false

[dependencies]
either = { version = "1.0", default-features = false }
lending-iterator = { version = "0.1.6", optional = true}

[dev-dependencies]
rand = "0.7"
Expand All @@ -39,6 +40,7 @@ quickcheck = { version = "0.9", default_features = false }
default = ["use_std"]
use_std = ["use_alloc", "either/use_std"]
use_alloc = []
lending_iters = ["dep:lending-iterator"]

[profile]
bench = { debug = true }
Expand Down Expand Up @@ -71,6 +73,11 @@ harness = false
name = "combinations"
harness = false

[[bench]]
name = "combinations_lending"
harness = false
required-features = ["default", "lending_iters"]

[[bench]]
name = "powerset"
harness = false
94 changes: 94 additions & 0 deletions benches/combinations.rs
@@ -1,3 +1,5 @@
use std::collections::{HashSet, VecDeque};

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use itertools::Itertools;

Expand Down Expand Up @@ -110,6 +112,91 @@ fn comb_c14(c: &mut Criterion) {
});
}

fn comb_single_use(c: &mut Criterion) {
c.bench_function("comb single use", move |b| {
b.iter(|| {
let mut combination_bitmask = 0usize;
(0..N14).combinations(14).for_each(|combo| {
let compared_bitmask = 0b101010101010101011110000usize;
combo.into_iter().for_each(|bit_pos| {
combination_bitmask |= 1 << bit_pos;
});
black_box((combination_bitmask & compared_bitmask).count_ones());
});
})
});
}

fn comb_into_hash_set(c: &mut Criterion) {
c.bench_function("comb into hash set", move |b| {
b.iter(|| {
(0..N14).combinations(14).for_each(|combo| {
black_box({
let mut out = HashSet::with_capacity(14);
out.extend(combo);
out
});
});
})
});
}

fn comb_into_vec_deque(c: &mut Criterion) {
c.bench_function("comb into vec deque", move |b| {
b.iter(|| {
(0..N14).combinations(14).for_each(|combo| {
black_box(VecDeque::from(combo));
});
})
});
}

fn comb_into_slice(c: &mut Criterion) {
c.bench_function("comb into slice", move |b| {
b.iter(|| {
(0..N14).combinations(14).for_each(|combo| {
black_box({
let mut out = [0; 14];
let mut combo_iter = combo.into_iter();
out.fill_with(|| combo_iter.next().unwrap_or_default());
out
});
});
})
});
}

fn comb_into_slice_unchecked(c: &mut Criterion) {
c.bench_function("comb into slice unchecked", move |b| {
b.iter(|| {
(0..N14).combinations(14).for_each(|combo| {
black_box({
let mut out = [0; 14];
let mut combo_iter = combo.into_iter();
out.fill_with(|| combo_iter.next().unwrap());
out
});
});
})
});
}

fn comb_into_slice_for_loop(c: &mut Criterion) {
c.bench_function("comb into slice for loop", move |b| {
b.iter(|| {
(0..N14).combinations(14).for_each(|combo| {
black_box({
let mut out = [0; 14];
for (i, elem) in combo.into_iter().enumerate() {
out[i] = elem;
}
out
});
});
})
});
}

criterion_group!(
benches,
comb_for1,
Expand All @@ -121,5 +208,12 @@ criterion_group!(
comb_c3,
comb_c4,
comb_c14,
comb_single_use,
comb_into_hash_set,
comb_into_vec_deque,
comb_into_slice,
comb_into_slice_unchecked,
comb_into_slice_for_loop,
);

criterion_main!(benches);
193 changes: 193 additions & 0 deletions benches/combinations_lending.rs
@@ -0,0 +1,193 @@
#![cfg(feature = "lending_iters")]

use std::collections::{HashSet, VecDeque};

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use itertools::Itertools;
use itertools::LendingIterator;

// approximate 100_000 iterations for each combination
const N1: usize = 100_000;
const N2: usize = 448;
const N3: usize = 86;
const N4: usize = 41;
const N14: usize = 21;

fn comb_lending_c1(c: &mut Criterion) {
c.bench_function("comb lending c1", move |b| {
b.iter(|| {
(0..N1).combinations_lending(1).for_each(|combo| {
black_box({
let mut out = Vec::with_capacity(1);
out.extend(combo);
out
});
});
})
});
}

fn comb_lending_c2(c: &mut Criterion) {
c.bench_function("comb lending c2", move |b| {
b.iter(|| {
(0..N2).combinations_lending(2).for_each(|combo| {
black_box({
let mut out = Vec::with_capacity(2);
out.extend(combo);
out
});
});
})
});
}

fn comb_lending_c3(c: &mut Criterion) {
c.bench_function("comb lending c3", move |b| {
b.iter(|| {
(0..N3).combinations_lending(3).for_each(|combo| {
black_box({
let mut out = Vec::with_capacity(3);
out.extend(combo);
out
});
});
})
});
}

fn comb_lending_c4(c: &mut Criterion) {
c.bench_function("comb lending c4", move |b| {
b.iter(|| {
(0..N4).combinations_lending(4).for_each(|combo| {
black_box({
let mut out = Vec::with_capacity(4);
out.extend(combo);
out
});
});
})
});
}

fn comb_lending_c14(c: &mut Criterion) {
c.bench_function("comb lending c14", move |b| {
b.iter(|| {
(0..N14).combinations_lending(14).for_each(|combo| {
black_box({
let mut out = Vec::with_capacity(14);
out.extend(combo);
out
});
});
})
});
}

fn comb_lending_single_use(c: &mut Criterion) {
c.bench_function("comb lending single use", move |b| {
b.iter(|| {
let mut combination_bitmask = 0usize;
(0..N14).combinations_lending(14).for_each(|combo| {
let compared_bitmask = 0b101010101010101011110000usize;
combo.for_each(|bit_pos| {
combination_bitmask |= 1 << bit_pos;
});
black_box((combination_bitmask & compared_bitmask).count_ones());
});
})
});
}

fn comb_lending_into_hash_set_from_collect(c: &mut Criterion) {
c.bench_function("comb lending into hash set from collect", move |b| {
b.iter(|| {
(0..N14).combinations_lending(14).for_each(|combo| {
black_box(combo.collect::<HashSet<_>>());
});
})
});
}

fn comb_lending_into_hash_set_from_extend(c: &mut Criterion) {
c.bench_function("comb lending into hash set from extend", move |b| {
b.iter(|| {
(0..N14).combinations_lending(14).for_each(|combo| {
black_box({
let mut out = HashSet::with_capacity(14);
out.extend(combo);
out
});
});
})
});
}

fn comb_lending_into_vec_deque_from_collect(c: &mut Criterion) {
c.bench_function("comb lending into vec deque from collect", move |b| {
b.iter(|| {
(0..N14).combinations_lending(14).for_each(|combo| {
black_box(combo.collect::<VecDeque<_>>());
});
})
});
}

fn comb_lending_into_vec_deque_from_extend(c: &mut Criterion) {
c.bench_function("comb lending into vec deque from extend", move |b| {
b.iter(|| {
(0..N14).combinations_lending(14).for_each(|combo| {
black_box({
let mut out = VecDeque::with_capacity(14);
out.extend(combo);
out
});
});
})
});
}

fn comb_lending_into_slice(c: &mut Criterion) {
c.bench_function("comb lending into slice", move |b| {
b.iter(|| {
(0..N14).combinations_lending(14).for_each(|mut combo| {
black_box({
let mut out = [0; 14];
out.fill_with(|| combo.next().unwrap_or_default());
out
});
});
})
});
}

fn comb_lending_into_slice_unchecked(c: &mut Criterion) {
c.bench_function("comb lending into slice unchecked", move |b| {
b.iter(|| {
(0..N14).combinations_lending(14).for_each(|mut combo| {
black_box({
let mut out = [0; 14];
out.fill_with(|| combo.next().unwrap());
out
});
});
})
});
}

criterion_group!(
benches,
comb_lending_c1,
comb_lending_c2,
comb_lending_c3,
comb_lending_c4,
comb_lending_c14,
comb_lending_single_use,
comb_lending_into_hash_set_from_collect,
comb_lending_into_hash_set_from_extend,
comb_lending_into_vec_deque_from_collect,
comb_lending_into_vec_deque_from_extend,
comb_lending_into_slice,
comb_lending_into_slice_unchecked,
);

criterion_main!(benches);

0 comments on commit 0eefe16

Please sign in to comment.