Avoid allocations in has_any_child_of.

`has_any_child_of` is hot. It allocates a `Vec` that almost always
doesn't exceed a length of 1.

This patch peels off the first iteration of the loop, avoiding the need
for the `Vec` creation in ~99% of cases.
This commit is contained in:
Nicholas Nethercote 2018-06-18 14:52:59 +10:00
parent 2b973e6532
commit 8c7433a3cc

View File

@ -204,10 +204,22 @@ where
T: HasMoveData<'tcx> + BitDenotation<Idx = MovePathIndex>,
{
pub fn has_any_child_of(&self, mpi: T::Idx) -> Option<T::Idx> {
// We process `mpi` before the loop below, for two reasons:
// - it's a little different from the loop case (we don't traverse its
// siblings);
// - ~99% of the time the loop isn't reached, and this code is hot, so
// we don't want to allocate `todo` unnecessarily.
if self.contains(&mpi) {
return Some(mpi);
}
let move_data = self.operator().move_data();
let move_path = &move_data.move_paths[mpi];
let mut todo = if let Some(child) = move_path.first_child {
vec![child]
} else {
return None;
};
let mut todo = vec![mpi];
let mut push_siblings = false; // don't look at siblings of original `mpi`.
while let Some(mpi) = todo.pop() {
if self.contains(&mpi) {
return Some(mpi);
@ -216,15 +228,10 @@ where
if let Some(child) = move_path.first_child {
todo.push(child);
}
if push_siblings {
if let Some(sibling) = move_path.next_sibling {
todo.push(sibling);
}
} else {
// after we've processed the original `mpi`, we should
// always traverse the siblings of any of its
// children.
push_siblings = true;
// After we've processed the original `mpi`, we should always
// traverse the siblings of any of its children.
if let Some(sibling) = move_path.next_sibling {
todo.push(sibling);
}
}
return None;