Skip to content

Commit

Permalink
Use fold to implement Iterator::for_each
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
cuviper committed Jun 21, 2017
1 parent b403897 commit 4a8ddac
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 4 deletions.
47 changes: 47 additions & 0 deletions src/libcore/benches/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
}
8 changes: 4 additions & 4 deletions src/libcore/iter/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 4a8ddac

Please sign in to comment.