Skip to content

Commit

Permalink
cache the output of build_deps
Browse files Browse the repository at this point in the history
  • Loading branch information
Eh2406 committed Apr 25, 2019
1 parent c36301c commit 5ae13e3
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 50 deletions.
3 changes: 1 addition & 2 deletions src/cargo/core/resolver/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{BTreeSet, HashMap};
use std::num::NonZeroU64;
use std::rc::Rc;

Expand All @@ -18,7 +18,6 @@ use super::types::{ConflictMap, Method};
pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
pub use super::encode::{Metadata, WorkspaceResolve};
pub use super::resolve::Resolve;
use std::collections::btree_set::BTreeSet;

// A `Context` is basically a bunch of local resolution information which is
// kept around for all `BacktrackFrame` instances. As a result, this runs the
Expand Down
85 changes: 57 additions & 28 deletions src/cargo/core/resolver/dep_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,34 +177,63 @@ impl<'a> RegistryQueryer<'a> {
}
}

pub fn build_deps(
registry: &mut RegistryQueryer<'_>,
parent: Option<PackageId>,
candidate: &Summary,
method: &Method,
) -> ActivateResult<(HashSet<InternedString>, Vec<DepInfo>)> {
// First, figure out our set of dependencies based on the requested set
// of features. This also calculates what features we're going to enable
// for our own dependencies.
let (used_features, deps) = resolve_features(parent, candidate, method)?;

// Next, transform all dependencies into a list of possible candidates
// which can satisfy that dependency.
let mut deps = deps
.into_iter()
.map(|(dep, features)| {
let candidates = registry.query(&dep)?;
Ok((dep, candidates, Rc::new(features)))
})
.collect::<CargoResult<Vec<DepInfo>>>()?;

// Attempt to resolve dependencies with fewer candidates before trying
// dependencies with more candidates. This way if the dependency with
// only one candidate can't be resolved we don't have to do a bunch of
// work before we figure that out.
deps.sort_by_key(|&(_, ref a, _)| a.len());

Ok((used_features, deps))
pub struct DepsCache<'a> {
pub registry: RegistryQueryer<'a>,
cache: HashMap<
(Option<PackageId>, Summary, Method),
Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
>,
}

impl<'a> DepsCache<'a> {
pub fn new(registry: RegistryQueryer<'a>) -> Self {
DepsCache {
registry,
cache: HashMap::new(),
}
}

pub fn build_deps(
&mut self,
parent: Option<PackageId>,
candidate: &Summary,
method: &Method,
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
if let Some(out) = self
.cache
.get(&(parent, candidate.clone(), method.clone()))
.cloned()
{
return Ok(out);
}
// First, figure out our set of dependencies based on the requested set
// of features. This also calculates what features we're going to enable
// for our own dependencies.
let (used_features, deps) = resolve_features(parent, candidate, method)?;

// Next, transform all dependencies into a list of possible candidates
// which can satisfy that dependency.
let mut deps = deps
.into_iter()
.map(|(dep, features)| {
let candidates = self.registry.query(&dep)?;
Ok((dep, candidates, Rc::new(features)))
})
.collect::<CargoResult<Vec<DepInfo>>>()?;

// Attempt to resolve dependencies with fewer candidates before trying
// dependencies with more candidates. This way if the dependency with
// only one candidate can't be resolved we don't have to do a bunch of
// work before we figure that out.
deps.sort_by_key(|&(_, ref a, _)| a.len());

let out = Rc::new((used_features, Rc::new(deps)));

self.cache
.insert((parent, candidate.clone(), method.clone()), out.clone());

Ok(out)
}
}

/// Returns all dependencies and the features we want from them.
Expand Down
36 changes: 17 additions & 19 deletions src/cargo/core/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ use crate::util::errors::CargoResult;
use crate::util::profile;

use self::context::{Activations, Context};
use self::dep_cache::RegistryQueryer;
use self::dep_cache::{DepsCache, RegistryQueryer};
use self::types::{Candidate, ConflictMap, ConflictReason, DepsFrame};
use self::types::{RcVecIter, RemainingDeps, ResolverProgress};

Expand Down Expand Up @@ -134,7 +134,8 @@ pub fn resolve(
Some(config) => config.cli_unstable().minimal_versions,
None => false,
};
let mut registry = RegistryQueryer::new(registry, replacements, try_to_use, minimal_versions);
let registry = RegistryQueryer::new(registry, replacements, try_to_use, minimal_versions);
let mut registry = DepsCache::new(registry);
let cx = activate_deps_loop(cx, &mut registry, summaries, config)?;

let mut cksums = HashMap::new();
Expand All @@ -144,7 +145,7 @@ pub fn resolve(
}
let resolve = Resolve::new(
cx.graph(),
cx.resolve_replacements(&registry),
cx.resolve_replacements(&registry.registry),
cx.resolve_features
.iter()
.map(|(k, v)| (*k, v.iter().map(|x| x.to_string()).collect()))
Expand All @@ -168,7 +169,7 @@ pub fn resolve(
/// dependency graph, cx.resolve is returned.
fn activate_deps_loop(
mut cx: Context,
registry: &mut RegistryQueryer<'_>,
registry: &mut DepsCache<'_>,
summaries: &[(Summary, Method)],
config: Option<&Config>,
) -> CargoResult<Context> {
Expand All @@ -186,7 +187,7 @@ fn activate_deps_loop(
summary: summary.clone(),
replace: None,
};
let res = activate(&mut cx, registry, None, candidate, method);
let res = activate(&mut cx, registry, None, candidate, method.clone());
match res {
Ok(Some((frame, _))) => remaining_deps.push(frame),
Ok(None) => (),
Expand Down Expand Up @@ -328,7 +329,7 @@ fn activate_deps_loop(
debug!("no candidates found");
Err(errors::activation_error(
&cx,
registry.registry,
registry.registry.registry,
&parent,
&dep,
&conflicting_activations,
Expand Down Expand Up @@ -385,7 +386,7 @@ fn activate_deps_loop(
dep.package_name(),
candidate.summary.version()
);
let res = activate(&mut cx, registry, Some((&parent, &dep)), candidate, &method);
let res = activate(&mut cx, registry, Some((&parent, &dep)), candidate, method);

let successfully_activated = match res {
// Success! We've now activated our `candidate` in our context
Expand Down Expand Up @@ -594,10 +595,10 @@ fn activate_deps_loop(
/// iterate through next.
fn activate(
cx: &mut Context,
registry: &mut RegistryQueryer<'_>,
registry: &mut DepsCache<'_>,
parent: Option<(&Summary, &Dependency)>,
candidate: Candidate,
method: &Method,
method: Method,
) -> ActivateResult<Option<(DepsFrame, Duration)>> {
let candidate_pid = candidate.summary.package_id();
if let Some((parent, dep)) = parent {
Expand Down Expand Up @@ -658,11 +659,11 @@ fn activate(
}
}

let activated = cx.flag_activated(&candidate.summary, method)?;
let activated = cx.flag_activated(&candidate.summary, &method)?;

let candidate = match candidate.replace {
Some(replace) => {
if cx.flag_activated(&replace, method)? && activated {
if cx.flag_activated(&replace, &method)? && activated {
return Ok(None);
}
trace!(
Expand All @@ -682,12 +683,8 @@ fn activate(
};

let now = Instant::now();
let (used_features, deps) = dep_cache::build_deps(
registry,
parent.map(|p| p.0.package_id()),
&candidate,
method,
)?;
let (used_features, deps) =
&*registry.build_deps(parent.map(|p| p.0.package_id()), &candidate, &method)?;

// Record what list of features is active for this package.
if !used_features.is_empty() {
Expand All @@ -702,7 +699,7 @@ fn activate(
let frame = DepsFrame {
parent: candidate,
just_for_error_messages: false,
remaining_siblings: RcVecIter::new(Rc::new(deps)),
remaining_siblings: RcVecIter::new(Rc::clone(deps)),
};
Ok(Some((frame, now.elapsed())))
}
Expand Down Expand Up @@ -862,7 +859,7 @@ impl RemainingCandidates {
/// Panics if the input conflict is not all active in `cx`.
fn generalize_conflicting(
cx: &Context,
registry: &mut RegistryQueryer<'_>,
registry: &mut DepsCache<'_>,
past_conflicting_activations: &mut conflict_cache::ConflictCache,
parent: &Summary,
dep: &Dependency,
Expand Down Expand Up @@ -901,6 +898,7 @@ fn generalize_conflicting(
// Thus, if all the things it can resolve to have already ben determined
// to be conflicting, then we can just say that we conflict with the parent.
if registry
.registry
.query(critical_parents_dep)
.expect("an already used dep now error!?")
.iter()
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/core/resolver/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl ResolverProgress {
}
}

#[derive(Clone)]
#[derive(Clone, Eq, PartialEq, Hash)]
pub enum Method {
Everything, // equivalent to Required { dev_deps: true, all_features: true, .. }
Required {
Expand Down
9 changes: 9 additions & 0 deletions src/cargo/core/summary.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::borrow::Borrow;
use std::collections::{BTreeMap, HashMap};
use std::fmt::Display;
use std::hash::{Hash, Hasher};
use std::mem;
use std::rc::Rc;

Expand Down Expand Up @@ -137,6 +138,14 @@ impl PartialEq for Summary {
}
}

impl Eq for Summary {}

impl Hash for Summary {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.package_id.hash(state);
}
}

// Checks features for errors, bailing out a CargoResult:Err if invalid,
// and creates FeatureValues for each feature.
fn build_feature_map<K>(
Expand Down

0 comments on commit 5ae13e3

Please sign in to comment.