From 4a8ddac99e1edfb219e11c3ea2d6c43ccecb29ab Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 21 Jun 2017 13:22:27 -0700 Subject: [PATCH] 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) --- src/libcore/benches/iter.rs | 47 ++++++++++++++++++++++++++++++++++++ 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(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(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(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