From acf7d4dcdba4046917c61aab141c1dec25669ce9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 18 Sep 2019 14:50:48 +1000 Subject: [PATCH] Specialize the `stalled_on` handling in `process_obligation()`. Optimizing for the common numbers of entries in `stalled_on` wins about 4% on `keccak` and `inflate`. --- src/librustc/infer/mod.rs | 4 +-- src/librustc/traits/fulfill.rs | 55 ++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index c5712cc9941a..eaef2198e3bc 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1600,8 +1600,8 @@ impl<'a, 'tcx> ShallowResolver<'a, 'tcx> { // `resolver.shallow_resolve_changed(ty)` is equivalent to // `resolver.shallow_resolve(ty) != ty`, but more efficient. It's always - // inlined, despite being large, because it has a single call site that is - // extremely hot. + // inlined, despite being large, because it has only two call sites that + // are extremely hot. #[inline(always)] pub fn shallow_resolve_changed(&mut self, typ: Ty<'tcx>) -> bool { match typ.sty { diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 805727b6ce0d..6c421e9df680 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -256,29 +256,46 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { &mut self, pending_obligation: &mut Self::Obligation, ) -> ProcessResult { - // If we were stalled on some unresolved variables, first check - // whether any of them have been resolved; if not, don't bother - // doing more work yet - if !pending_obligation.stalled_on.is_empty() { - let mut changed = false; - // This `for` loop was once a call to `all()`, but this lower-level - // form was a perf win. See #64545 for details. - for &ty in &pending_obligation.stalled_on { - if ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(ty) { - changed = true; - break; - } + // If we were stalled on some unresolved variables, first check whether + // any of them have been resolved; if not, don't bother doing more work + // yet. + let change = match pending_obligation.stalled_on.len() { + // Match arms are in order of frequency, which matters because this + // code is so hot. 1 and 0 dominate; 2+ is fairly rare. + 1 => { + let ty = pending_obligation.stalled_on[0]; + ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(ty) } - if !changed { - debug!("process_predicate: pending obligation {:?} still stalled on {:?}", - self.selcx.infcx() - .resolve_vars_if_possible(&pending_obligation.obligation), - pending_obligation.stalled_on); - return ProcessResult::Unchanged; + 0 => { + // In this case we haven't changed, but wish to make a change. + true } - pending_obligation.stalled_on = vec![]; + _ => { + // This `for` loop was once a call to `all()`, but this lower-level + // form was a perf win. See #64545 for details. + (|| { + for &ty in &pending_obligation.stalled_on { + if ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(ty) { + return true; + } + } + false + })() + } + }; + + if !change { + debug!("process_predicate: pending obligation {:?} still stalled on {:?}", + self.selcx.infcx() + .resolve_vars_if_possible(&pending_obligation.obligation), + pending_obligation.stalled_on); + return ProcessResult::Unchanged; } + // This part of the code is much colder. + + pending_obligation.stalled_on.truncate(0); + let obligation = &mut pending_obligation.obligation; if obligation.predicate.has_infer_types() {