| #![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::<Vec<_>>() |
| })); |
| $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::<u32>() |
| } |
| 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() |
| } |
| } |