Skip to content

Commit f8b22e7

Browse files
author
Anon
committed
Initial working example of lending iterator for combinations.
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.
1 parent 5c21a87 commit f8b22e7

File tree

6 files changed

+625
-24
lines changed

6 files changed

+625
-24
lines changed

Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ test = false
2727

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

3132
[dev-dependencies]
3233
rand = "0.7"
@@ -39,6 +40,7 @@ quickcheck = { version = "0.9", default_features = false }
3940
default = ["use_std"]
4041
use_std = ["use_alloc", "either/use_std"]
4142
use_alloc = []
43+
lending_iters = ["dep:lending-iterator"]
4244

4345
[profile]
4446
bench = { debug = true }
@@ -71,6 +73,11 @@ harness = false
7173
name = "combinations"
7274
harness = false
7375

76+
[[bench]]
77+
name = "combinations_lending"
78+
harness = false
79+
required-features = ["default", "lending_iters"]
80+
7481
[[bench]]
7582
name = "powerset"
7683
harness = false

benches/combinations.rs

+94
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::{HashSet, VecDeque};
2+
13
use criterion::{black_box, criterion_group, criterion_main, Criterion};
24
use itertools::Itertools;
35

@@ -110,6 +112,91 @@ fn comb_c14(c: &mut Criterion) {
110112
});
111113
}
112114

115+
fn comb_single_use(c: &mut Criterion) {
116+
c.bench_function("comb single use", move |b| {
117+
b.iter(|| {
118+
let mut combination_bitmask = 0usize;
119+
(0..N14).combinations(14).for_each(|combo| {
120+
let compared_bitmask = 0b101010101010101011110000usize;
121+
combo.into_iter().for_each(|bit_pos| {
122+
combination_bitmask |= 1 << bit_pos;
123+
});
124+
black_box((combination_bitmask & compared_bitmask).count_ones());
125+
});
126+
})
127+
});
128+
}
129+
130+
fn comb_into_hash_set(c: &mut Criterion) {
131+
c.bench_function("comb into hash set", move |b| {
132+
b.iter(|| {
133+
(0..N14).combinations(14).for_each(|combo| {
134+
black_box({
135+
let mut out = HashSet::with_capacity(14);
136+
out.extend(combo);
137+
out
138+
});
139+
});
140+
})
141+
});
142+
}
143+
144+
fn comb_into_vec_deque(c: &mut Criterion) {
145+
c.bench_function("comb into vec deque", move |b| {
146+
b.iter(|| {
147+
(0..N14).combinations(14).for_each(|combo| {
148+
black_box(VecDeque::from(combo));
149+
});
150+
})
151+
});
152+
}
153+
154+
fn comb_into_slice(c: &mut Criterion) {
155+
c.bench_function("comb into slice", move |b| {
156+
b.iter(|| {
157+
(0..N14).combinations(14).for_each(|combo| {
158+
black_box({
159+
let mut out = [0; 14];
160+
let mut combo_iter = combo.into_iter();
161+
out.fill_with(|| combo_iter.next().unwrap_or_default());
162+
out
163+
});
164+
});
165+
})
166+
});
167+
}
168+
169+
fn comb_into_slice_unchecked(c: &mut Criterion) {
170+
c.bench_function("comb into slice unchecked", move |b| {
171+
b.iter(|| {
172+
(0..N14).combinations(14).for_each(|combo| {
173+
black_box({
174+
let mut out = [0; 14];
175+
let mut combo_iter = combo.into_iter();
176+
out.fill_with(|| combo_iter.next().unwrap());
177+
out
178+
});
179+
});
180+
})
181+
});
182+
}
183+
184+
fn comb_into_slice_for_loop(c: &mut Criterion) {
185+
c.bench_function("comb into slice for loop", move |b| {
186+
b.iter(|| {
187+
(0..N14).combinations(14).for_each(|combo| {
188+
black_box({
189+
let mut out = [0; 14];
190+
for (i, elem) in combo.into_iter().enumerate() {
191+
out[i] = elem;
192+
}
193+
out
194+
});
195+
});
196+
})
197+
});
198+
}
199+
113200
criterion_group!(
114201
benches,
115202
comb_for1,
@@ -121,5 +208,12 @@ criterion_group!(
121208
comb_c3,
122209
comb_c4,
123210
comb_c14,
211+
comb_single_use,
212+
comb_into_hash_set,
213+
comb_into_vec_deque,
214+
comb_into_slice,
215+
comb_into_slice_unchecked,
216+
comb_into_slice_for_loop,
124217
);
218+
125219
criterion_main!(benches);

benches/combinations_lending.rs

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#![cfg(feature = "lending_iters")]
2+
3+
use std::collections::{HashSet, VecDeque};
4+
5+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
6+
use itertools::Itertools;
7+
use itertools::LendingIterator;
8+
9+
// approximate 100_000 iterations for each combination
10+
const N1: usize = 100_000;
11+
const N2: usize = 448;
12+
const N3: usize = 86;
13+
const N4: usize = 41;
14+
const N14: usize = 21;
15+
16+
fn comb_lending_c1(c: &mut Criterion) {
17+
c.bench_function("comb lending c1", move |b| {
18+
b.iter(|| {
19+
(0..N1).combinations_lending(1).for_each(|combo| {
20+
black_box({
21+
let mut out = Vec::with_capacity(1);
22+
out.extend(combo);
23+
out
24+
});
25+
});
26+
})
27+
});
28+
}
29+
30+
fn comb_lending_c2(c: &mut Criterion) {
31+
c.bench_function("comb lending c2", move |b| {
32+
b.iter(|| {
33+
(0..N2).combinations_lending(2).for_each(|combo| {
34+
black_box({
35+
let mut out = Vec::with_capacity(2);
36+
out.extend(combo);
37+
out
38+
});
39+
});
40+
})
41+
});
42+
}
43+
44+
fn comb_lending_c3(c: &mut Criterion) {
45+
c.bench_function("comb lending c3", move |b| {
46+
b.iter(|| {
47+
(0..N3).combinations_lending(3).for_each(|combo| {
48+
black_box({
49+
let mut out = Vec::with_capacity(3);
50+
out.extend(combo);
51+
out
52+
});
53+
});
54+
})
55+
});
56+
}
57+
58+
fn comb_lending_c4(c: &mut Criterion) {
59+
c.bench_function("comb lending c4", move |b| {
60+
b.iter(|| {
61+
(0..N4).combinations_lending(4).for_each(|combo| {
62+
black_box({
63+
let mut out = Vec::with_capacity(4);
64+
out.extend(combo);
65+
out
66+
});
67+
});
68+
})
69+
});
70+
}
71+
72+
fn comb_lending_c14(c: &mut Criterion) {
73+
c.bench_function("comb lending c14", move |b| {
74+
b.iter(|| {
75+
(0..N14).combinations_lending(14).for_each(|combo| {
76+
black_box({
77+
let mut out = Vec::with_capacity(14);
78+
out.extend(combo);
79+
out
80+
});
81+
});
82+
})
83+
});
84+
}
85+
86+
fn comb_lending_single_use(c: &mut Criterion) {
87+
c.bench_function("comb lending single use", move |b| {
88+
b.iter(|| {
89+
let mut combination_bitmask = 0usize;
90+
(0..N14).combinations_lending(14).for_each(|combo| {
91+
let compared_bitmask = 0b101010101010101011110000usize;
92+
combo.for_each(|bit_pos| {
93+
combination_bitmask |= 1 << bit_pos;
94+
});
95+
black_box((combination_bitmask & compared_bitmask).count_ones());
96+
});
97+
})
98+
});
99+
}
100+
101+
fn comb_lending_into_hash_set_from_collect(c: &mut Criterion) {
102+
c.bench_function("comb lending into hash set from collect", move |b| {
103+
b.iter(|| {
104+
(0..N14).combinations_lending(14).for_each(|combo| {
105+
black_box(combo.collect::<HashSet<_>>());
106+
});
107+
})
108+
});
109+
}
110+
111+
fn comb_lending_into_hash_set_from_extend(c: &mut Criterion) {
112+
c.bench_function("comb lending into hash set from extend", move |b| {
113+
b.iter(|| {
114+
(0..N14).combinations_lending(14).for_each(|combo| {
115+
black_box({
116+
let mut out = HashSet::with_capacity(14);
117+
out.extend(combo);
118+
out
119+
});
120+
});
121+
})
122+
});
123+
}
124+
125+
fn comb_lending_into_vec_deque_from_collect(c: &mut Criterion) {
126+
c.bench_function("comb lending into vec deque from collect", move |b| {
127+
b.iter(|| {
128+
(0..N14).combinations_lending(14).for_each(|combo| {
129+
black_box(combo.collect::<VecDeque<_>>());
130+
});
131+
})
132+
});
133+
}
134+
135+
fn comb_lending_into_vec_deque_from_extend(c: &mut Criterion) {
136+
c.bench_function("comb lending into vec deque from extend", move |b| {
137+
b.iter(|| {
138+
(0..N14).combinations_lending(14).for_each(|combo| {
139+
black_box({
140+
let mut out = VecDeque::with_capacity(14);
141+
out.extend(combo);
142+
out
143+
});
144+
});
145+
})
146+
});
147+
}
148+
149+
fn comb_lending_into_slice(c: &mut Criterion) {
150+
c.bench_function("comb lending into slice", move |b| {
151+
b.iter(|| {
152+
(0..N14).combinations_lending(14).for_each(|mut combo| {
153+
black_box({
154+
let mut out = [0; 14];
155+
out.fill_with(|| combo.next().unwrap_or_default());
156+
out
157+
});
158+
});
159+
})
160+
});
161+
}
162+
163+
fn comb_lending_into_slice_unchecked(c: &mut Criterion) {
164+
c.bench_function("comb lending into slice unchecked", move |b| {
165+
b.iter(|| {
166+
(0..N14).combinations_lending(14).for_each(|mut combo| {
167+
black_box({
168+
let mut out = [0; 14];
169+
out.fill_with(|| combo.next().unwrap());
170+
out
171+
});
172+
});
173+
})
174+
});
175+
}
176+
177+
criterion_group!(
178+
benches,
179+
comb_lending_c1,
180+
comb_lending_c2,
181+
comb_lending_c3,
182+
comb_lending_c4,
183+
comb_lending_c14,
184+
comb_lending_single_use,
185+
comb_lending_into_hash_set_from_collect,
186+
comb_lending_into_hash_set_from_extend,
187+
comb_lending_into_vec_deque_from_collect,
188+
comb_lending_into_vec_deque_from_extend,
189+
comb_lending_into_slice,
190+
comb_lending_into_slice_unchecked,
191+
);
192+
193+
criterion_main!(benches);

0 commit comments

Comments
 (0)