diff --git a/Cargo.toml b/Cargo.toml index 2a023a1f9..69f653fa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,3 +71,7 @@ harness = false [[bench]] name = "powerset" harness = false + +[[bench]] +name = "specializations" +harness = false diff --git a/benches/bench1.rs b/benches/bench1.rs index 84caf88bc..0946affe5 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -1,8 +1,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::free::cloned; +use itertools::iproduct; use itertools::Itertools; -use itertools::Position; -use itertools::{iproduct, EitherOrBoth}; use std::cmp; use std::iter::repeat; @@ -391,25 +390,6 @@ fn zip_unchecked_counted_loop3(c: &mut Criterion) { }); } -fn ziplongest(c: &mut Criterion) { - let v1 = black_box((0..768).collect_vec()); - let v2 = black_box((0..1024).collect_vec()); - c.bench_function("ziplongest", move |b| { - b.iter(|| { - let zip = v1.iter().zip_longest(v2.iter()); - let sum = zip.fold(0u32, |mut acc, val| { - match val { - EitherOrBoth::Both(x, y) => acc += x * y, - EitherOrBoth::Left(x) => acc += x, - EitherOrBoth::Right(y) => acc += y, - } - acc - }); - sum - }) - }); -} - fn group_by_lazy_1(c: &mut Criterion) { let mut data = vec![0; 1024]; for (index, elt) in data.iter_mut().enumerate() { @@ -448,25 +428,6 @@ fn group_by_lazy_2(c: &mut Criterion) { }); } -fn while_some(c: &mut Criterion) { - c.bench_function("while_some", |b| { - b.iter(|| { - let data = black_box( - (0..) - .fuse() - .map(|i| std::char::from_digit(i, 16)) - .while_some(), - ); - // let result: String = data.fold(String::new(), |acc, ch| acc + &ch.to_string()); - let result = data.fold(String::new(), |mut acc, ch| { - acc.push(ch); - acc - }); - assert_eq!(result.as_str(), "0123456789abcdef"); - }); - }); -} - fn slice_chunks(c: &mut Criterion) { let data = vec![0; 1024]; @@ -703,22 +664,6 @@ fn cartesian_product_iterator(c: &mut Criterion) { }); } -fn cartesian_product_fold(c: &mut Criterion) { - let xs = vec![0; 16]; - - c.bench_function("cartesian product fold", move |b| { - b.iter(|| { - let mut sum = 0; - iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| { - sum += x; - sum += y; - sum += z; - }); - sum - }) - }); -} - fn multi_cartesian_product_iterator(c: &mut Criterion) { let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; @@ -735,22 +680,6 @@ fn multi_cartesian_product_iterator(c: &mut Criterion) { }); } -fn multi_cartesian_product_fold(c: &mut Criterion) { - let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; - - c.bench_function("multi cartesian product fold", move |b| { - b.iter(|| { - let mut sum = 0; - xs.iter().multi_cartesian_product().fold((), |(), x| { - sum += x[0]; - sum += x[1]; - sum += x[2]; - }); - sum - }) - }); -} - fn cartesian_product_nested_for(c: &mut Criterion) { let xs = vec![0; 16]; @@ -839,33 +768,6 @@ fn permutations_slice(c: &mut Criterion) { }); } -fn with_position_fold(c: &mut Criterion) { - let v = black_box((0..10240).collect_vec()); - c.bench_function("with_position fold", move |b| { - b.iter(|| { - v.iter() - .with_position() - .fold(0, |acc, (pos, &item)| match pos { - Position::Middle => acc + item, - Position::First => acc - 2 * item, - Position::Last => acc + 2 * item, - Position::Only => acc + 5 * item, - }) - }) - }); -} - -fn tuple_combinations_fold(c: &mut Criterion) { - let v = black_box((0..64).collect_vec()); - c.bench_function("tuple_combinations fold", move |b| { - b.iter(|| { - v.iter() - .tuple_combinations() - .fold(0, |acc, (a, b, c, d)| acc + *a * *c - *b * *d) - }) - }); -} - criterion_group!( benches, slice_iter, @@ -887,7 +789,6 @@ criterion_group!( zipdot_i32_unchecked_counted_loop, zipdot_f32_unchecked_counted_loop, zip_unchecked_counted_loop3, - ziplongest, group_by_lazy_1, group_by_lazy_2, slice_chunks, @@ -903,9 +804,7 @@ criterion_group!( step_range_2, step_range_10, cartesian_product_iterator, - cartesian_product_fold, multi_cartesian_product_iterator, - multi_cartesian_product_fold, cartesian_product_nested_for, all_equal, all_equal_for, @@ -913,8 +812,5 @@ criterion_group!( permutations_iter, permutations_range, permutations_slice, - with_position_fold, - tuple_combinations_fold, - while_some, ); criterion_main!(benches); diff --git a/benches/specializations.rs b/benches/specializations.rs new file mode 100644 index 000000000..139270897 --- /dev/null +++ b/benches/specializations.rs @@ -0,0 +1,176 @@ +use criterion::black_box; +use itertools::iproduct; +use itertools::Itertools; + +/// Create multiple functions each defining a benchmark group about iterator methods. +/// +/// Each created group has functions with the following ids: +/// +/// - `next`, `size_hint`, `count`, `last`, `nth`, `collect`, `fold` +/// - and when marked as `DoubleEndedIterator`: `next_back`, `rfold` +/// - and when marked as `ExactSizeIterator`: `len` +/// +/// Note that this macro can be called only once. +macro_rules! bench_specializations { + ( + $( + $name:ident { + $($extra:ident)* + {$( + $init:stmt; + )*} + $iterator:expr + } + )* + ) => { + $( + fn $name(c: &mut ::criterion::Criterion) { + let mut bench_group = c.benchmark_group(stringify!($name)); + $( + $init + )* + let bench_first_its = { + let mut bench_idx = 0; + [0; 1000].map(|_| { + let mut it = $iterator; + if bench_idx != 0 { + it.nth(bench_idx - 1); + } + bench_idx += 1; + it + }) + }; + bench_specializations!(@Iterator bench_group bench_first_its: $iterator); + $( + bench_specializations!(@$extra bench_group bench_first_its: $iterator); + )* + bench_group.finish(); + } + )* + + ::criterion::criterion_group!(benches, $($name, )*); + ::criterion::criterion_main!(benches); + }; + + (@Iterator $group:ident $first_its:ident: $iterator:expr) => { + $group.bench_function("next", |bencher| bencher.iter(|| { + let mut it = $iterator; + while let Some(x) = it.next() { + black_box(x); + } + })); + $group.bench_function("size_hint", |bencher| bencher.iter(|| { + $first_its.iter().for_each(|it| { + black_box(it.size_hint()); + }) + })); + $group.bench_function("count", |bencher| bencher.iter(|| { + $iterator.count() + })); + $group.bench_function("last", |bencher| bencher.iter(|| { + $iterator.last() + })); + $group.bench_function("nth", |bencher| bencher.iter(|| { + for start in 0_usize..10 { + for n in 0..10 { + let mut it = $iterator; + if let Some(s) = start.checked_sub(1) { + black_box(it.nth(s)); + } + while let Some(x) = it.nth(n) { + black_box(x); + } + } + } + })); + $group.bench_function("collect", |bencher| bencher.iter(|| { + $iterator.collect::>() + })); + $group.bench_function("fold", |bencher| bencher.iter(|| { + $iterator.fold((), |(), x| { + black_box(x); + }) + })); + }; + + (@DoubleEndedIterator $group:ident $_first_its:ident: $iterator:expr) => { + $group.bench_function("next_back", |bencher| bencher.iter(|| { + let mut it = $iterator; + while let Some(x) = it.next_back() { + black_box(x); + } + })); + $group.bench_function("nth_back", |bencher| bencher.iter(|| { + for start in 0_usize..10 { + for n in 0..10 { + let mut it = $iterator; + if let Some(s) = start.checked_sub(1) { + black_box(it.nth_back(s)); + } + while let Some(x) = it.nth_back(n) { + black_box(x); + } + } + } + })); + $group.bench_function("rfold", |bencher| bencher.iter(|| { + $iterator.rfold((), |(), x| { + black_box(x); + }) + })); + }; + + (@ExactSizeIterator $group:ident $first_its:ident: $_iterator:expr) => { + $group.bench_function("len", |bencher| bencher.iter(|| { + $first_its.iter().for_each(|it| { + black_box(it.len()); + }) + })); + }; +} + +// Example: To bench only `ZipLongest::fold`, you can do +// cargo bench --bench specializations zip_longest/fold +bench_specializations! { + cartesian_product { + { + let v = black_box(vec![0; 16]); + } + iproduct!(&v, &v, &v) + } + multi_cartesian_product { + { + let vs = black_box([0; 3].map(|_| vec![0; 16])); + } + vs.iter().multi_cartesian_product() + } + tuple_combinations { + { + let v = black_box((0..64).collect_vec()); + } + v.iter().tuple_combinations::<(_, _, _, _)>() + } + while_some { + {} + (0..) + .map(black_box) + .map(|i| char::from_digit(i, 16)) + .while_some() + } + with_position { + ExactSizeIterator + { + let v = black_box((0..10240).collect_vec()); + } + v.iter().with_position() + } + zip_longest { + DoubleEndedIterator + ExactSizeIterator + { + let xs = black_box(vec![0; 1024]); + let ys = black_box(vec![0; 768]); + } + xs.iter().zip_longest(ys.iter()) + } +}