Skip to content

Commit

Permalink
Auto merge of #44856 - cuviper:more-fold, r=dtolnay
Browse files Browse the repository at this point in the history
Add more custom folding to `core::iter` adaptors

Many of the iterator adaptors will perform faster folds if they forward
to their inner iterator's folds, especially for inner types like `Chain`
which are optimized too.  The following types are newly specialized:

| Type        | `fold` | `rfold` |
| ----------- | ------ | ------- |
| `Enumerate` | ✓      | ✓       |
| `Filter`    | ✓      | ✓       |
| `FilterMap` | ✓      | ✓       |
| `FlatMap`   | exists | ✓       |
| `Fuse`      | ✓      | ✓       |
| `Inspect`   | ✓      | ✓       |
| `Peekable`  | ✓      | N/A¹    |
| `Skip`      | ✓      | N/A²    |
| `SkipWhile` | ✓      | N/A¹    |

¹ not a `DoubleEndedIterator`

² `Skip::next_back` doesn't pull skipped items at all, but this couldn't
be avoided if `Skip::rfold` were to call its inner iterator's `rfold`.

Benchmarks
----------

In the following results, plain `_sum` computes the sum of a million
integers -- note that `sum()` is implemented with `fold()`.  The
`_ref_sum` variants do the same on a `by_ref()` iterator, which is
limited to calling `next()` one by one, without specialized `fold`.

The `chain` variants perform the same tests on two iterators chained
together, to show a greater benefit of forwarding `fold` internally.

    test iter::bench_enumerate_chain_ref_sum  ... bench:   2,216,264 ns/iter (+/- 29,228)
    test iter::bench_enumerate_chain_sum      ... bench:     922,380 ns/iter (+/- 2,676)
    test iter::bench_enumerate_ref_sum        ... bench:     476,094 ns/iter (+/- 7,110)
    test iter::bench_enumerate_sum            ... bench:     476,438 ns/iter (+/- 3,334)

    test iter::bench_filter_chain_ref_sum     ... bench:   2,266,095 ns/iter (+/- 6,051)
    test iter::bench_filter_chain_sum         ... bench:     745,594 ns/iter (+/- 2,013)
    test iter::bench_filter_ref_sum           ... bench:     889,696 ns/iter (+/- 1,188)
    test iter::bench_filter_sum               ... bench:     667,325 ns/iter (+/- 1,894)

    test iter::bench_filter_map_chain_ref_sum ... bench:   2,259,195 ns/iter (+/- 353,440)
    test iter::bench_filter_map_chain_sum     ... bench:   1,223,280 ns/iter (+/- 1,972)
    test iter::bench_filter_map_ref_sum       ... bench:     611,607 ns/iter (+/- 2,507)
    test iter::bench_filter_map_sum           ... bench:     611,610 ns/iter (+/- 472)

    test iter::bench_fuse_chain_ref_sum       ... bench:   2,246,106 ns/iter (+/- 22,395)
    test iter::bench_fuse_chain_sum           ... bench:     634,887 ns/iter (+/- 1,341)
    test iter::bench_fuse_ref_sum             ... bench:     444,816 ns/iter (+/- 1,748)
    test iter::bench_fuse_sum                 ... bench:     316,954 ns/iter (+/- 2,616)

    test iter::bench_inspect_chain_ref_sum    ... bench:   2,245,431 ns/iter (+/- 21,371)
    test iter::bench_inspect_chain_sum        ... bench:     631,645 ns/iter (+/- 4,928)
    test iter::bench_inspect_ref_sum          ... bench:     317,437 ns/iter (+/- 702)
    test iter::bench_inspect_sum              ... bench:     315,942 ns/iter (+/- 4,320)

    test iter::bench_peekable_chain_ref_sum   ... bench:   2,243,585 ns/iter (+/- 12,186)
    test iter::bench_peekable_chain_sum       ... bench:     634,848 ns/iter (+/- 1,712)
    test iter::bench_peekable_ref_sum         ... bench:     444,808 ns/iter (+/- 480)
    test iter::bench_peekable_sum             ... bench:     317,133 ns/iter (+/- 3,309)

    test iter::bench_skip_chain_ref_sum       ... bench:   1,778,734 ns/iter (+/- 2,198)
    test iter::bench_skip_chain_sum           ... bench:     761,850 ns/iter (+/- 1,645)
    test iter::bench_skip_ref_sum             ... bench:     478,207 ns/iter (+/- 119,252)
    test iter::bench_skip_sum                 ... bench:     315,614 ns/iter (+/- 3,054)

    test iter::bench_skip_while_chain_ref_sum ... bench:   2,486,370 ns/iter (+/- 4,845)
    test iter::bench_skip_while_chain_sum     ... bench:     633,915 ns/iter (+/- 5,892)
    test iter::bench_skip_while_ref_sum       ... bench:     666,926 ns/iter (+/- 804)
    test iter::bench_skip_while_sum           ... bench:     444,405 ns/iter (+/- 571)
  • Loading branch information
bors committed Sep 29, 2017
2 parents d514263 + 13724fa commit 09ee9b7
Show file tree
Hide file tree
Showing 4 changed files with 471 additions and 33 deletions.
151 changes: 121 additions & 30 deletions src/libcore/benches/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,40 +147,131 @@ fn bench_for_each_chain_ref_fold(b: &mut Bencher) {
});
}

#[bench]
fn bench_flat_map_sum(b: &mut Bencher) {
b.iter(|| -> i64 {
(0i64..1000).flat_map(|x| x..x+1000)
.map(black_box)
.sum()
});

/// Helper to benchmark `sum` for iterators taken by value which
/// can optimize `fold`, and by reference which cannot.
macro_rules! bench_sums {
($bench_sum:ident, $bench_ref_sum:ident, $iter:expr) => {
#[bench]
fn $bench_sum(b: &mut Bencher) {
b.iter(|| -> i64 {
$iter.map(black_box).sum()
});
}

#[bench]
fn $bench_ref_sum(b: &mut Bencher) {
b.iter(|| -> i64 {
$iter.map(black_box).by_ref().sum()
});
}
}
}

#[bench]
fn bench_flat_map_ref_sum(b: &mut Bencher) {
b.iter(|| -> i64 {
(0i64..1000).flat_map(|x| x..x+1000)
.map(black_box)
.by_ref()
.sum()
});
bench_sums! {
bench_flat_map_sum,
bench_flat_map_ref_sum,
(0i64..1000).flat_map(|x| x..x+1000)
}

#[bench]
fn bench_flat_map_chain_sum(b: &mut Bencher) {
b.iter(|| -> i64 {
(0i64..1000000).flat_map(|x| once(x).chain(once(x)))
.map(black_box)
.sum()
});
bench_sums! {
bench_flat_map_chain_sum,
bench_flat_map_chain_ref_sum,
(0i64..1000000).flat_map(|x| once(x).chain(once(x)))
}

#[bench]
fn bench_flat_map_chain_ref_sum(b: &mut Bencher) {
b.iter(|| -> i64 {
(0i64..1000000).flat_map(|x| once(x).chain(once(x)))
.map(black_box)
.by_ref()
.sum()
});
bench_sums! {
bench_enumerate_sum,
bench_enumerate_ref_sum,
(0i64..1000000).enumerate().map(|(i, x)| x * i as i64)
}

bench_sums! {
bench_enumerate_chain_sum,
bench_enumerate_chain_ref_sum,
(0i64..1000000).chain(0..1000000).enumerate().map(|(i, x)| x * i as i64)
}

bench_sums! {
bench_filter_sum,
bench_filter_ref_sum,
(0i64..1000000).filter(|x| x % 2 == 0)
}

bench_sums! {
bench_filter_chain_sum,
bench_filter_chain_ref_sum,
(0i64..1000000).chain(0..1000000).filter(|x| x % 2 == 0)
}

bench_sums! {
bench_filter_map_sum,
bench_filter_map_ref_sum,
(0i64..1000000).filter_map(|x| x.checked_mul(x))
}

bench_sums! {
bench_filter_map_chain_sum,
bench_filter_map_chain_ref_sum,
(0i64..1000000).chain(0..1000000).filter_map(|x| x.checked_mul(x))
}

bench_sums! {
bench_fuse_sum,
bench_fuse_ref_sum,
(0i64..1000000).fuse()
}

bench_sums! {
bench_fuse_chain_sum,
bench_fuse_chain_ref_sum,
(0i64..1000000).chain(0..1000000).fuse()
}

bench_sums! {
bench_inspect_sum,
bench_inspect_ref_sum,
(0i64..1000000).inspect(|_| {})
}

bench_sums! {
bench_inspect_chain_sum,
bench_inspect_chain_ref_sum,
(0i64..1000000).chain(0..1000000).inspect(|_| {})
}

bench_sums! {
bench_peekable_sum,
bench_peekable_ref_sum,
(0i64..1000000).peekable()
}

bench_sums! {
bench_peekable_chain_sum,
bench_peekable_chain_ref_sum,
(0i64..1000000).chain(0..1000000).peekable()
}

bench_sums! {
bench_skip_sum,
bench_skip_ref_sum,
(0i64..1000000).skip(1000)
}

bench_sums! {
bench_skip_chain_sum,
bench_skip_chain_ref_sum,
(0i64..1000000).chain(0..1000000).skip(1000)
}

bench_sums! {
bench_skip_while_sum,
bench_skip_while_ref_sum,
(0i64..1000000).skip_while(|&x| x < 1000)
}

bench_sums! {
bench_skip_while_chain_sum,
bench_skip_while_chain_ref_sum,
(0i64..1000000).chain(0..1000000).skip_while(|&x| x < 1000)
}
Loading

0 comments on commit 09ee9b7

Please sign in to comment.