#![allow(unstable_name_collisions, clippy::incompatible_msrv)] use criterion::black_box; use criterion::BenchmarkId; use itertools::Itertools; const NTH_INPUTS: &[usize] = &[0, 1, 2, 4, 8]; /// 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`, `nth_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 } )* ) => { $( #[allow(unused_must_use)] 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() })); for n in NTH_INPUTS { $group.bench_with_input(BenchmarkId::new("nth", n), n, |bencher, n| bencher.iter(|| { for start in 0_usize..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); } })); for n in NTH_INPUTS { $group.bench_with_input(BenchmarkId::new("nth_back", n), n, |bencher, n| bencher.iter(|| { for start in 0_usize..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()); }) })); }; } // Usage examples: // - For `ZipLongest::fold` only: // cargo bench --bench specializations zip_longest/fold // - For `.combinations(k).nth(8)`: // cargo bench --bench specializations combinations./nth/8 bench_specializations! { interleave { { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); } v1.iter().interleave(&v2) } interleave_shortest { { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); } v1.iter().interleave_shortest(&v2) } batching { { let v = black_box(vec![0; 1024]); } v.iter().batching(Iterator::next) } tuple_windows1 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_,)>() } tuple_windows2 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_, _)>() } tuple_windows3 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_, _, _)>() } tuple_windows4 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_, _, _, _)>() } circular_tuple_windows1 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_,)>() } circular_tuple_windows2 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_, _)>() } circular_tuple_windows3 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_, _, _)>() } circular_tuple_windows4 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_, _, _, _)>() } tuples1 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_,)>() } tuples2 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_, _)>() } tuples3 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_, _, _)>() } tuples4 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_, _, _, _)>() } tuple_buffer { ExactSizeIterator { let v = black_box(vec![0; 11]); // Short but the buffer can't have 12 or more elements. } { let mut it = v.iter().tuples::<(_, _, _, _, _, _, _, _, _, _, _, _)>(); it.next(); // No element but it fills the buffer. it.into_buffer() } } cartesian_product { { let v = black_box(vec![0; 16]); } itertools::iproduct!(&v, &v, &v) } multi_cartesian_product { { let vs = black_box([0; 3].map(|_| vec![0; 16])); } vs.iter().multi_cartesian_product() } coalesce { { let v = black_box(vec![0; 1024]); } v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }) } dedup { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup() } dedup_by { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup_by(PartialOrd::ge) } dedup_with_count { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup_with_count() } dedup_by_with_count { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup_by_with_count(PartialOrd::ge) } duplicates { DoubleEndedIterator { let v = black_box((0..32).cycle().take(1024).collect_vec()); } v.iter().duplicates() } duplicates_by { DoubleEndedIterator { let v = black_box((0..1024).collect_vec()); } v.iter().duplicates_by(|x| *x % 10) } unique { DoubleEndedIterator { let v = black_box((0..32).cycle().take(1024).collect_vec()); } v.iter().unique() } unique_by { DoubleEndedIterator { let v = black_box((0..1024).collect_vec()); } v.iter().unique_by(|x| *x % 50) } take_while_inclusive { { let v = black_box((0..1024).collect_vec()); } v.iter().take_while_inclusive(|x| **x < 1000) } pad_using { DoubleEndedIterator ExactSizeIterator { let v = black_box((0..1024).collect_vec()); } v.iter().copied().pad_using(2048, |i| 5 * i) } positions { DoubleEndedIterator { let v = black_box((0..1024).collect_vec()); } v.iter().positions(|x| x % 5 == 0) } update { DoubleEndedIterator ExactSizeIterator { let v = black_box((0_i32..1024).collect_vec()); } v.iter().copied().update(|x| *x *= 7) } tuple_combinations1 { { let v = black_box(vec![0; 1024]); } v.iter().tuple_combinations::<(_,)>() } tuple_combinations2 { { let v = black_box(vec![0; 64]); } v.iter().tuple_combinations::<(_, _)>() } tuple_combinations3 { { let v = black_box(vec![0; 64]); } v.iter().tuple_combinations::<(_, _, _)>() } tuple_combinations4 { { let v = black_box(vec![0; 64]); } v.iter().tuple_combinations::<(_, _, _, _)>() } intersperse { { let v = black_box(vec![0; 1024]); let n = black_box(0); } v.iter().intersperse(&n) } intersperse_with { { let v = black_box(vec![0; 1024]); let n = black_box(0); } v.iter().intersperse_with(|| &n) } combinations1 { { let v = black_box(vec![0; 1792]); } v.iter().combinations(1) } combinations2 { { let v = black_box(vec![0; 60]); } v.iter().combinations(2) } combinations3 { { let v = black_box(vec![0; 23]); } v.iter().combinations(3) } combinations4 { { let v = black_box(vec![0; 16]); } v.iter().combinations(4) } combinations_with_replacement1 { { let v = black_box(vec![0; 4096]); } v.iter().combinations_with_replacement(1) } combinations_with_replacement2 { { let v = black_box(vec![0; 90]); } v.iter().combinations_with_replacement(2) } combinations_with_replacement3 { { let v = black_box(vec![0; 28]); } v.iter().combinations_with_replacement(3) } combinations_with_replacement4 { { let v = black_box(vec![0; 16]); } v.iter().combinations_with_replacement(4) } permutations1 { { let v = black_box(vec![0; 1024]); } v.iter().permutations(1) } permutations2 { { let v = black_box(vec![0; 36]); } v.iter().permutations(2) } permutations3 { { let v = black_box(vec![0; 12]); } v.iter().permutations(3) } permutations4 { { let v = black_box(vec![0; 8]); } v.iter().permutations(4) } powerset { { let v = black_box(vec![0; 10]); } v.iter().powerset() } 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()) } zip_eq { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().zip_eq(v.iter().rev()) } multizip { DoubleEndedIterator ExactSizeIterator { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); let v3 = black_box(vec![0; 2048]); } itertools::multizip((&v1, &v2, &v3)) } izip { DoubleEndedIterator ExactSizeIterator { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); let v3 = black_box(vec![0; 2048]); } itertools::izip!(&v1, &v2, &v3) } put_back { { let v = black_box(vec![0; 1024]); } itertools::put_back(&v).with_value(black_box(&0)) } put_back_n { { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 16]); } { let mut it = itertools::put_back_n(&v1); for n in &v2 { it.put_back(n); } it } } exactly_one_error { ExactSizeIterator { let v = black_box(vec![0; 1024]); } // Use `at_most_one` would be similar. v.iter().exactly_one().unwrap_err() } multipeek { ExactSizeIterator { let v = black_box(vec![0; 1024]); let n = black_box(16); } { let mut it = v.iter().multipeek(); for _ in 0..n { it.peek(); } it } } peek_nth { ExactSizeIterator { let v = black_box(vec![0; 1024]); let n = black_box(16); } { let mut it = itertools::peek_nth(&v); it.peek_nth(n); it } } repeat_n { DoubleEndedIterator ExactSizeIterator {} itertools::repeat_n(black_box(0), black_box(1024)) } merge { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge(&v2) } merge_by { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge_by(&v2, PartialOrd::ge) } merge_join_by_ordering { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge_join_by(&v2, Ord::cmp) } merge_join_by_bool { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge_join_by(&v2, PartialOrd::ge) } kmerge { { let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]); } vs.iter().kmerge() } kmerge_by { { let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]); } vs.iter().kmerge_by(PartialOrd::ge) } map_into { DoubleEndedIterator ExactSizeIterator { let v = black_box(vec![0_u8; 1024]); } v.iter().copied().map_into::() } map_ok { DoubleEndedIterator ExactSizeIterator { let v = black_box((0_u32..1024) .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) .collect_vec()); } v.iter().copied().map_ok(|x| x + 1) } filter_ok { { let v = black_box((0_u32..1024) .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) .collect_vec()); } v.iter().copied().filter_ok(|x| x % 3 == 0) } filter_map_ok { { let v = black_box((0_u32..1024) .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) .collect_vec()); } v.iter().copied().filter_map_ok(|x| if x % 3 == 0 { Some(x + 1) } else { None }) } flatten_ok { DoubleEndedIterator { let d = black_box(vec![0; 8]); let v = black_box((0..512) .map(|x| if x % 2 == 0 { Ok(&d) } else { Err(x) }) .collect_vec()); } v.iter().copied().flatten_ok() } }