Skip to content

Commit

Permalink
Merge #348
Browse files Browse the repository at this point in the history
348: Specialize Intersperse::fold r=bluss a=jswrenn

Overriding `Intersperse::fold`'s default implementation makes iteration with `for_each` approximately 10× faster than external iteration, and approximately 20× faster than `for_each` with an un-specialized `fold`:
```
$ cargo bench -- specialization
specialization::intersperse::internal_specialized          64 ns/iter (+/- 1)
specialization::intersperse::external                     638 ns/iter (+/- 45)
specialization::intersperse::internal_unspecialized     1,274 ns/iter (+/- 131)
```

Co-authored-by: Jack Wrenn <me@jswrenn.com>
  • Loading branch information
bors[bot] and jswrenn committed Jul 10, 2019
2 parents 70988ff + 91c421c commit e983dda
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 0 deletions.
66 changes: 66 additions & 0 deletions benches/fold_specialization.rs
@@ -0,0 +1,66 @@
#![feature(test)]

extern crate test;
extern crate itertools;

use itertools::Itertools;

struct Unspecialized<I>(I);

impl<I> Iterator for Unspecialized<I>
where I: Iterator
{
type Item = I::Item;

#[inline(always)]
fn next(&mut self) -> Option<I::Item> {
self.0.next()
}

#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}

mod specialization {
use super::*;

mod intersperse {
use super::*;

#[bench]
fn external(b: &mut test::Bencher)
{
let arr = [1; 1024];

b.iter(|| {
let mut sum = 0;
for &x in arr.into_iter().intersperse(&0) {
sum += x;
}
sum
})
}

#[bench]
fn internal_specialized(b: &mut test::Bencher)
{
let arr = [1; 1024];

b.iter(|| {
arr.into_iter().intersperse(&0).fold(0, |acc, x| acc + x)
})
}

#[bench]
fn internal_unspecialized(b: &mut test::Bencher)
{
let arr = [1; 1024];

b.iter(|| {
Unspecialized(arr.into_iter().intersperse(&0)).fold(0, |acc, x| acc + x)
})
}
}
}
19 changes: 19 additions & 0 deletions src/intersperse.rs
Expand Up @@ -57,4 +57,23 @@ impl<I> Iterator for Intersperse<I>
let sh = self.iter.size_hint();
size_hint::add_scalar(size_hint::add(sh, sh), has_peek)
}

fn fold<B, F>(mut self, init: B, mut f: F) -> B where
Self: Sized, F: FnMut(B, Self::Item) -> B,
{
let mut accum = init;

if let Some(x) = self.peek.take() {
accum = f(accum, x);
}

let element = &self.element;

self.iter.fold(accum,
|accum, x| {
let accum = f(accum, element.clone());
let accum = f(accum, x);
accum
})
}
}
15 changes: 15 additions & 0 deletions tests/fold_specialization.rs
@@ -0,0 +1,15 @@
extern crate itertools;

use itertools::Itertools;

#[test]
fn specialization_intersperse() {
let mut iter = (1..2).intersperse(0);
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));

let mut iter = (1..3).intersperse(0);
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));

let mut iter = (1..4).intersperse(0);
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
}

0 comments on commit e983dda

Please sign in to comment.