Skip to content

Commit

Permalink
Rollup merge of #33695 - nikomatsakis:spezializes-cache, r=aturon
Browse files Browse the repository at this point in the history
introduce a specializes cache

This query is frequently used during trait selection and caching the
result can be a reasonable performance win.

The one case I examined thus far was the mrusty package (v0.5.1), where I saw an improvement in "typeck item bodies" from ~8.3s to ~1.9s.

r? @aturon
  • Loading branch information
Manishearth committed May 18, 2016
2 parents 9246cc4 + 29dad1a commit 07194a0
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
pub use self::specialize::{OverlapError, specialization_graph, specializes, translate_substs};
pub use self::specialize::{SpecializesCache};
pub use self::util::elaborate_predicates;
pub use self::util::supertraits;
pub use self::util::Supertraits;
Expand Down
32 changes: 30 additions & 2 deletions src/librustc/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use super::{SelectionContext, FulfillmentContext};
use super::util::{fresh_type_vars_for_impl, impl_trait_ref_and_oblig};

use rustc_data_structures::fnv::FnvHashMap;
use hir::def_id::DefId;
use infer::{InferCtxt, TypeOrigin};
use middle::region;
Expand Down Expand Up @@ -111,6 +112,10 @@ pub fn translate_substs<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
impl1_def_id: DefId,
impl2_def_id: DefId) -> bool {
if let Some(r) = tcx.specializes_cache.borrow().check(impl1_def_id, impl2_def_id) {
return r;
}

// The feature gate should prevent introducing new specializations, but not
// taking advantage of upstream ones.
if !tcx.sess.features.borrow().specialization &&
Expand Down Expand Up @@ -146,7 +151,7 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
.unwrap()
.subst(tcx, &penv.free_substs);

tcx.normalizing_infer_ctxt(ProjectionMode::Topmost).enter(|mut infcx| {
let result = tcx.normalizing_infer_ctxt(ProjectionMode::Topmost).enter(|mut infcx| {
// Normalize the trait reference, adding any obligations
// that arise into the impl1 assumptions.
let Normalized { value: impl1_trait_ref, obligations: normalization_obligations } = {
Expand All @@ -167,7 +172,10 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

// Attempt to prove that impl2 applies, given all of the above.
fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok()
})
});

tcx.specializes_cache.borrow_mut().insert(impl1_def_id, impl2_def_id, result);
result
}

/// Attempt to fulfill all obligations of `target_impl` after unification with
Expand Down Expand Up @@ -225,3 +233,23 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
}
})
}

pub struct SpecializesCache {
map: FnvHashMap<(DefId, DefId), bool>
}

impl SpecializesCache {
pub fn new() -> Self {
SpecializesCache {
map: FnvHashMap()
}
}

pub fn check(&self, a: DefId, b: DefId) -> Option<bool> {
self.map.get(&(a, b)).cloned()
}

pub fn insert(&mut self, a: DefId, b: DefId, result: bool) {
self.map.insert((a, b), result);
}
}
3 changes: 3 additions & 0 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ impl<'a, 'gcx, 'tcx> Deref for TyCtxt<'a, 'gcx, 'tcx> {
pub struct GlobalCtxt<'tcx> {
global_interners: CtxtInterners<'tcx>,

pub specializes_cache: RefCell<traits::SpecializesCache>,

pub dep_graph: DepGraph,

/// Common types, pre-interned for your convenience.
Expand Down Expand Up @@ -637,6 +639,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
let dep_graph = map.dep_graph.clone();
let fulfilled_predicates = traits::GlobalFulfilledPredicates::new(dep_graph.clone());
tls::enter_global(GlobalCtxt {
specializes_cache: RefCell::new(traits::SpecializesCache::new()),
global_interners: interners,
dep_graph: dep_graph.clone(),
types: common_types,
Expand Down

0 comments on commit 07194a0

Please sign in to comment.