Skip to content

Commit 5ae13e3

Browse files
committed
cache the output of build_deps
1 parent c36301c commit 5ae13e3

File tree

5 files changed

+85
-50
lines changed

5 files changed

+85
-50
lines changed

src/cargo/core/resolver/context.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::{BTreeSet, HashMap};
22
use std::num::NonZeroU64;
33
use std::rc::Rc;
44

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

2322
// A `Context` is basically a bunch of local resolution information which is
2423
// kept around for all `BacktrackFrame` instances. As a result, this runs the

src/cargo/core/resolver/dep_cache.rs

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -177,34 +177,63 @@ impl<'a> RegistryQueryer<'a> {
177177
}
178178
}
179179

180-
pub fn build_deps(
181-
registry: &mut RegistryQueryer<'_>,
182-
parent: Option<PackageId>,
183-
candidate: &Summary,
184-
method: &Method,
185-
) -> ActivateResult<(HashSet<InternedString>, Vec<DepInfo>)> {
186-
// First, figure out our set of dependencies based on the requested set
187-
// of features. This also calculates what features we're going to enable
188-
// for our own dependencies.
189-
let (used_features, deps) = resolve_features(parent, candidate, method)?;
190-
191-
// Next, transform all dependencies into a list of possible candidates
192-
// which can satisfy that dependency.
193-
let mut deps = deps
194-
.into_iter()
195-
.map(|(dep, features)| {
196-
let candidates = registry.query(&dep)?;
197-
Ok((dep, candidates, Rc::new(features)))
198-
})
199-
.collect::<CargoResult<Vec<DepInfo>>>()?;
200-
201-
// Attempt to resolve dependencies with fewer candidates before trying
202-
// dependencies with more candidates. This way if the dependency with
203-
// only one candidate can't be resolved we don't have to do a bunch of
204-
// work before we figure that out.
205-
deps.sort_by_key(|&(_, ref a, _)| a.len());
206-
207-
Ok((used_features, deps))
180+
pub struct DepsCache<'a> {
181+
pub registry: RegistryQueryer<'a>,
182+
cache: HashMap<
183+
(Option<PackageId>, Summary, Method),
184+
Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
185+
>,
186+
}
187+
188+
impl<'a> DepsCache<'a> {
189+
pub fn new(registry: RegistryQueryer<'a>) -> Self {
190+
DepsCache {
191+
registry,
192+
cache: HashMap::new(),
193+
}
194+
}
195+
196+
pub fn build_deps(
197+
&mut self,
198+
parent: Option<PackageId>,
199+
candidate: &Summary,
200+
method: &Method,
201+
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
202+
if let Some(out) = self
203+
.cache
204+
.get(&(parent, candidate.clone(), method.clone()))
205+
.cloned()
206+
{
207+
return Ok(out);
208+
}
209+
// First, figure out our set of dependencies based on the requested set
210+
// of features. This also calculates what features we're going to enable
211+
// for our own dependencies.
212+
let (used_features, deps) = resolve_features(parent, candidate, method)?;
213+
214+
// Next, transform all dependencies into a list of possible candidates
215+
// which can satisfy that dependency.
216+
let mut deps = deps
217+
.into_iter()
218+
.map(|(dep, features)| {
219+
let candidates = self.registry.query(&dep)?;
220+
Ok((dep, candidates, Rc::new(features)))
221+
})
222+
.collect::<CargoResult<Vec<DepInfo>>>()?;
223+
224+
// Attempt to resolve dependencies with fewer candidates before trying
225+
// dependencies with more candidates. This way if the dependency with
226+
// only one candidate can't be resolved we don't have to do a bunch of
227+
// work before we figure that out.
228+
deps.sort_by_key(|&(_, ref a, _)| a.len());
229+
230+
let out = Rc::new((used_features, Rc::new(deps)));
231+
232+
self.cache
233+
.insert((parent, candidate.clone(), method.clone()), out.clone());
234+
235+
Ok(out)
236+
}
208237
}
209238

210239
/// Returns all dependencies and the features we want from them.

src/cargo/core/resolver/mod.rs

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use crate::util::errors::CargoResult;
6262
use crate::util::profile;
6363

6464
use self::context::{Activations, Context};
65-
use self::dep_cache::RegistryQueryer;
65+
use self::dep_cache::{DepsCache, RegistryQueryer};
6666
use self::types::{Candidate, ConflictMap, ConflictReason, DepsFrame};
6767
use self::types::{RcVecIter, RemainingDeps, ResolverProgress};
6868

@@ -134,7 +134,8 @@ pub fn resolve(
134134
Some(config) => config.cli_unstable().minimal_versions,
135135
None => false,
136136
};
137-
let mut registry = RegistryQueryer::new(registry, replacements, try_to_use, minimal_versions);
137+
let registry = RegistryQueryer::new(registry, replacements, try_to_use, minimal_versions);
138+
let mut registry = DepsCache::new(registry);
138139
let cx = activate_deps_loop(cx, &mut registry, summaries, config)?;
139140

140141
let mut cksums = HashMap::new();
@@ -144,7 +145,7 @@ pub fn resolve(
144145
}
145146
let resolve = Resolve::new(
146147
cx.graph(),
147-
cx.resolve_replacements(&registry),
148+
cx.resolve_replacements(&registry.registry),
148149
cx.resolve_features
149150
.iter()
150151
.map(|(k, v)| (*k, v.iter().map(|x| x.to_string()).collect()))
@@ -168,7 +169,7 @@ pub fn resolve(
168169
/// dependency graph, cx.resolve is returned.
169170
fn activate_deps_loop(
170171
mut cx: Context,
171-
registry: &mut RegistryQueryer<'_>,
172+
registry: &mut DepsCache<'_>,
172173
summaries: &[(Summary, Method)],
173174
config: Option<&Config>,
174175
) -> CargoResult<Context> {
@@ -186,7 +187,7 @@ fn activate_deps_loop(
186187
summary: summary.clone(),
187188
replace: None,
188189
};
189-
let res = activate(&mut cx, registry, None, candidate, method);
190+
let res = activate(&mut cx, registry, None, candidate, method.clone());
190191
match res {
191192
Ok(Some((frame, _))) => remaining_deps.push(frame),
192193
Ok(None) => (),
@@ -328,7 +329,7 @@ fn activate_deps_loop(
328329
debug!("no candidates found");
329330
Err(errors::activation_error(
330331
&cx,
331-
registry.registry,
332+
registry.registry.registry,
332333
&parent,
333334
&dep,
334335
&conflicting_activations,
@@ -385,7 +386,7 @@ fn activate_deps_loop(
385386
dep.package_name(),
386387
candidate.summary.version()
387388
);
388-
let res = activate(&mut cx, registry, Some((&parent, &dep)), candidate, &method);
389+
let res = activate(&mut cx, registry, Some((&parent, &dep)), candidate, method);
389390

390391
let successfully_activated = match res {
391392
// Success! We've now activated our `candidate` in our context
@@ -594,10 +595,10 @@ fn activate_deps_loop(
594595
/// iterate through next.
595596
fn activate(
596597
cx: &mut Context,
597-
registry: &mut RegistryQueryer<'_>,
598+
registry: &mut DepsCache<'_>,
598599
parent: Option<(&Summary, &Dependency)>,
599600
candidate: Candidate,
600-
method: &Method,
601+
method: Method,
601602
) -> ActivateResult<Option<(DepsFrame, Duration)>> {
602603
let candidate_pid = candidate.summary.package_id();
603604
if let Some((parent, dep)) = parent {
@@ -658,11 +659,11 @@ fn activate(
658659
}
659660
}
660661

661-
let activated = cx.flag_activated(&candidate.summary, method)?;
662+
let activated = cx.flag_activated(&candidate.summary, &method)?;
662663

663664
let candidate = match candidate.replace {
664665
Some(replace) => {
665-
if cx.flag_activated(&replace, method)? && activated {
666+
if cx.flag_activated(&replace, &method)? && activated {
666667
return Ok(None);
667668
}
668669
trace!(
@@ -682,12 +683,8 @@ fn activate(
682683
};
683684

684685
let now = Instant::now();
685-
let (used_features, deps) = dep_cache::build_deps(
686-
registry,
687-
parent.map(|p| p.0.package_id()),
688-
&candidate,
689-
method,
690-
)?;
686+
let (used_features, deps) =
687+
&*registry.build_deps(parent.map(|p| p.0.package_id()), &candidate, &method)?;
691688

692689
// Record what list of features is active for this package.
693690
if !used_features.is_empty() {
@@ -702,7 +699,7 @@ fn activate(
702699
let frame = DepsFrame {
703700
parent: candidate,
704701
just_for_error_messages: false,
705-
remaining_siblings: RcVecIter::new(Rc::new(deps)),
702+
remaining_siblings: RcVecIter::new(Rc::clone(deps)),
706703
};
707704
Ok(Some((frame, now.elapsed())))
708705
}
@@ -862,7 +859,7 @@ impl RemainingCandidates {
862859
/// Panics if the input conflict is not all active in `cx`.
863860
fn generalize_conflicting(
864861
cx: &Context,
865-
registry: &mut RegistryQueryer<'_>,
862+
registry: &mut DepsCache<'_>,
866863
past_conflicting_activations: &mut conflict_cache::ConflictCache,
867864
parent: &Summary,
868865
dep: &Dependency,
@@ -901,6 +898,7 @@ fn generalize_conflicting(
901898
// Thus, if all the things it can resolve to have already ben determined
902899
// to be conflicting, then we can just say that we conflict with the parent.
903900
if registry
901+
.registry
904902
.query(critical_parents_dep)
905903
.expect("an already used dep now error!?")
906904
.iter()

src/cargo/core/resolver/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl ResolverProgress {
8989
}
9090
}
9191

92-
#[derive(Clone)]
92+
#[derive(Clone, Eq, PartialEq, Hash)]
9393
pub enum Method {
9494
Everything, // equivalent to Required { dev_deps: true, all_features: true, .. }
9595
Required {

src/cargo/core/summary.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Borrow;
22
use std::collections::{BTreeMap, HashMap};
33
use std::fmt::Display;
4+
use std::hash::{Hash, Hasher};
45
use std::mem;
56
use std::rc::Rc;
67

@@ -137,6 +138,14 @@ impl PartialEq for Summary {
137138
}
138139
}
139140

141+
impl Eq for Summary {}
142+
143+
impl Hash for Summary {
144+
fn hash<H: Hasher>(&self, state: &mut H) {
145+
self.inner.package_id.hash(state);
146+
}
147+
}
148+
140149
// Checks features for errors, bailing out a CargoResult:Err if invalid,
141150
// and creates FeatureValues for each feature.
142151
fn build_feature_map<K>(

0 commit comments

Comments
 (0)