diff options
| author | Josh Stone <jistone@redhat.com> | 2017-06-21 13:22:27 -0700 |
|---|---|---|
| committer | Josh Stone <jistone@redhat.com> | 2017-06-21 13:22:27 -0700 |
| commit | 4a8ddac99e1edfb219e11c3ea2d6c43ccecb29ab (patch) | |
| tree | 0b8806c71633a73dc6b369b61001ec9942cebceb /src/libcore | |
| parent | b4038977a39f7c5bfa76cccf586930ec57befbad (diff) | |
| download | rust-4a8ddac99e1edfb219e11c3ea2d6c43ccecb29ab.tar.gz rust-4a8ddac99e1edfb219e11c3ea2d6c43ccecb29ab.zip | |
Use `fold` to implement `Iterator::for_each`
The benefit of using internal iteration is shown in new benchmarks:
test iter::bench_for_each_chain_fold ... bench: 635,110 ns/iter (+/- 5,135)
test iter::bench_for_each_chain_loop ... bench: 2,249,983 ns/iter (+/- 42,001)
test iter::bench_for_each_chain_ref_fold ... bench: 2,248,061 ns/iter (+/- 51,940)
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/benches/iter.rs | 47 | ||||
| -rw-r--r-- | src/libcore/iter/iterator.rs | 8 |
2 files changed, 51 insertions, 4 deletions
diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index 93d38a5bc83..5b06229c21f 100644 --- a/src/libcore/benches/iter.rs +++ b/src/libcore/benches/iter.rs @@ -99,3 +99,50 @@ fn bench_zip_add(b: &mut Bencher) { add_zip(&source, &mut dst) }); } + +/// `Iterator::for_each` implemented as a plain loop. +fn for_each_loop<I, F>(iter: I, mut f: F) where + I: Iterator, F: FnMut(I::Item) +{ + for item in iter { + f(item); + } +} + +/// `Iterator::for_each` implemented with `fold` for internal iteration. +/// (except when `by_ref()` effectively disables that optimization.) +fn for_each_fold<I, F>(iter: I, mut f: F) where + I: Iterator, F: FnMut(I::Item) +{ + iter.fold((), move |(), item| f(item)); +} + +#[bench] +fn bench_for_each_chain_loop(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let iter = (0i64..1000000).chain(0..1000000).map(black_box); + for_each_loop(iter, |x| acc += x); + acc + }); +} + +#[bench] +fn bench_for_each_chain_fold(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let iter = (0i64..1000000).chain(0..1000000).map(black_box); + for_each_fold(iter, |x| acc += x); + acc + }); +} + +#[bench] +fn bench_for_each_chain_ref_fold(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let mut iter = (0i64..1000000).chain(0..1000000).map(black_box); + for_each_fold(iter.by_ref(), |x| acc += x); + acc + }); +} diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 49c43d133e5..d38864f3edd 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -487,7 +487,9 @@ pub trait Iterator { /// This is equivalent to using a [`for`] loop on the iterator, although /// `break` and `continue` are not possible from a closure. It's generally /// more idiomatic to use a `for` loop, but `for_each` may be more legible - /// when processing items at the end of longer iterator chains. + /// when processing items at the end of longer iterator chains. In some + /// cases `for_each` may also be faster than a loop, because it will use + /// internal iteration on adaptors like `Chain`. /// /// [`for`]: ../../book/first-edition/loops.html#for /// @@ -523,9 +525,7 @@ pub trait Iterator { fn for_each<F>(self, mut f: F) where Self: Sized, F: FnMut(Self::Item), { - for item in self { - f(item); - } + self.fold((), move |(), item| f(item)); } /// Creates an iterator which uses a closure to determine if an element |
