Skip to content

Commit

Permalink
core: Unroll the loop in the slice iterator search methods
Browse files Browse the repository at this point in the history
Introduce a helper method .search_while() that generalizes internal
iteration (Iterator's all, find, position, fold and so on).

The compiler does not unroll loops with conditional exits; we can do
this manually instead to improve the performance of for example
Iterator::find and Iterator::position when used on the slice iterators.

The unrolling is patterned on libstdc++'s implementation of std::find_if.
  • Loading branch information
bluss committed Nov 24, 2016
1 parent 7611e42 commit a54ddfb
Showing 1 changed file with 118 additions and 0 deletions.
118 changes: 118 additions & 0 deletions src/libcore/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,64 @@ macro_rules! iterator {
fn last(mut self) -> Option<$elem> {
self.next_back()
}

fn all<F>(&mut self, mut predicate: F) -> bool
where F: FnMut(Self::Item) -> bool,
{
self.search_while(true, move |elt| {
if predicate(elt) {
SearchWhile::Continue
} else {
SearchWhile::Done(false)
}
})
}

fn any<F>(&mut self, mut predicate: F) -> bool
where F: FnMut(Self::Item) -> bool,
{
!self.all(move |elt| !predicate(elt))
}

fn find<F>(&mut self, mut predicate: F) -> Option<Self::Item>
where F: FnMut(&Self::Item) -> bool,
{
self.search_while(None, move |elt| {
if predicate(&elt) {
SearchWhile::Done(Some(elt))
} else {
SearchWhile::Continue
}
})
}

fn position<F>(&mut self, mut predicate: F) -> Option<usize>
where F: FnMut(Self::Item) -> bool,
{
let mut index = 0;
self.search_while(None, move |elt| {
if predicate(elt) {
SearchWhile::Done(Some(index))
} else {
index += 1;
SearchWhile::Continue
}
})
}

fn rposition<F>(&mut self, mut predicate: F) -> Option<usize>
where F: FnMut(Self::Item) -> bool,
{
let mut index = self.len();
self.rsearch_while(None, move |elt| {
index -= 1;
if predicate(elt) {
SearchWhile::Done(Some(index))
} else {
SearchWhile::Continue
}
})
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand All @@ -872,6 +930,48 @@ macro_rules! iterator {
}
}
}

// search_while is a generalization of the internal iteration methods.
impl<'a, T> $name<'a, T> {
// search through the iterator's element using the closure `g`.
// if no element was found, return `default`.
fn search_while<Acc, G>(&mut self, default: Acc, mut g: G) -> Acc
where Self: Sized,
G: FnMut($elem) -> SearchWhile<Acc>
{
// manual unrolling is needed when there are conditional exits from the loop
unsafe {
while ptrdistance(self.ptr, self.end) >= 4 {
search_while!(g($mkref!(self.ptr.post_inc())));
search_while!(g($mkref!(self.ptr.post_inc())));
search_while!(g($mkref!(self.ptr.post_inc())));
search_while!(g($mkref!(self.ptr.post_inc())));
}
while self.ptr != self.end {
search_while!(g($mkref!(self.ptr.post_inc())));
}
}
default
}

fn rsearch_while<Acc, G>(&mut self, default: Acc, mut g: G) -> Acc
where Self: Sized,
G: FnMut($elem) -> SearchWhile<Acc>
{
unsafe {
while ptrdistance(self.ptr, self.end) >= 4 {
search_while!(g($mkref!(self.end.pre_dec())));
search_while!(g($mkref!(self.end.pre_dec())));
search_while!(g($mkref!(self.end.pre_dec())));
search_while!(g($mkref!(self.end.pre_dec())));
}
while self.ptr != self.end {
search_while!(g($mkref!(self.end.pre_dec())));
}
}
default
}
}
}
}

Expand Down Expand Up @@ -903,6 +1003,24 @@ macro_rules! make_mut_slice {
}}
}

// An enum used for controlling the execution of `.search_while()`.
enum SearchWhile<T> {
// Continue searching
Continue,
// Fold is complete and will return this value
Done(T),
}

// helper macro for search while's control flow
macro_rules! search_while {
($e:expr) => {
match $e {
SearchWhile::Continue => { }
SearchWhile::Done(done) => return done,
}
}
}

/// Immutable slice iterator
///
/// This struct is created by the [`iter`] method on [slices].
Expand Down

0 comments on commit a54ddfb

Please sign in to comment.