Skip to content

Commit adadfab

Browse files
committed
Auto merge of #5112 - Eh2406:cache_queries, r=alexcrichton
Cache the query result. Small performance gain. In a test on #4810 (comment) Before we got to 1700000 ticks in ~97 sec After we got to 1700000 ticks in ~92 sec. I just reran and got ~82, so it seems to be unstable. And query disappears from the flame graph
2 parents 4429f4e + cb0df8e commit adadfab

File tree

3 files changed

+125
-103
lines changed

3 files changed

+125
-103
lines changed

src/cargo/core/dependency.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ use util::errors::{CargoResult, CargoResultExt, CargoError};
1212

1313
/// Information about a dependency requested by a Cargo manifest.
1414
/// Cheap to copy.
15-
#[derive(PartialEq, Clone, Debug)]
15+
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug)]
1616
pub struct Dependency {
1717
inner: Rc<Inner>,
1818
}
1919

2020
/// The data underlying a Dependency.
21-
#[derive(PartialEq, Clone, Debug)]
21+
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug)]
2222
struct Inner {
2323
name: String,
2424
source_id: SourceId,
@@ -38,7 +38,7 @@ struct Inner {
3838
platform: Option<Platform>,
3939
}
4040

41-
#[derive(Clone, Debug, PartialEq)]
41+
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
4242
pub enum Platform {
4343
Name(String),
4444
Cfg(CfgExpr),
@@ -76,7 +76,7 @@ impl ser::Serialize for Dependency {
7676
}
7777
}
7878

79-
#[derive(PartialEq, Clone, Debug, Copy)]
79+
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
8080
pub enum Kind {
8181
Normal,
8282
Development,

src/cargo/core/resolver/mod.rs

+119-97
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ enum GraphNode {
326326
// risk of being cloned *a lot* so we want to make this as cheap to clone as
327327
// possible.
328328
#[derive(Clone)]
329-
struct Context<'a> {
329+
struct Context {
330330
// TODO: Both this and the two maps below are super expensive to clone. We should
331331
// switch to persistent hash maps if we can at some point or otherwise
332332
// make these much cheaper to clone in general.
@@ -340,8 +340,6 @@ struct Context<'a> {
340340
resolve_graph: RcList<GraphNode>,
341341
resolve_replacements: RcList<(PackageId, PackageId)>,
342342

343-
replacements: &'a [(PackageIdSpec, Dependency)],
344-
345343
// These warnings are printed after resolution.
346344
warnings: RcList<String>,
347345
}
@@ -360,11 +358,10 @@ pub fn resolve(summaries: &[(Summary, Method)],
360358
links: HashMap::new(),
361359
resolve_replacements: RcList::new(),
362360
activations: HashMap::new(),
363-
replacements,
364361
warnings: RcList::new(),
365362
};
366363
let _p = profile::start("resolving");
367-
let cx = activate_deps_loop(cx, registry, summaries, config)?;
364+
let cx = activate_deps_loop(cx, &mut RegistryQueryer::new(registry, replacements), summaries, config)?;
368365

369366
let mut resolve = Resolve {
370367
graph: cx.graph(),
@@ -410,7 +407,7 @@ pub fn resolve(summaries: &[(Summary, Method)],
410407
/// If `candidate` was activated, this function returns the dependency frame to
411408
/// iterate through next.
412409
fn activate(cx: &mut Context,
413-
registry: &mut Registry,
410+
registry: &mut RegistryQueryer,
414411
parent: Option<&Summary>,
415412
candidate: Candidate,
416413
method: &Method)
@@ -573,10 +570,112 @@ impl ConflictReason {
573570
}
574571
}
575572

573+
struct RegistryQueryer<'a> {
574+
registry: &'a mut (Registry + 'a),
575+
replacements: &'a [(PackageIdSpec, Dependency)],
576+
// TODO: with nll the Rc can be removed
577+
cache: HashMap<Dependency, Rc<Vec<Candidate>>>,
578+
}
579+
580+
impl<'a> RegistryQueryer<'a> {
581+
fn new(registry: &'a mut Registry, replacements: &'a [(PackageIdSpec, Dependency)],) -> Self {
582+
RegistryQueryer {
583+
registry,
584+
replacements,
585+
cache: HashMap::new(),
586+
}
587+
}
588+
589+
/// Queries the `registry` to return a list of candidates for `dep`.
590+
///
591+
/// This method is the location where overrides are taken into account. If
592+
/// any candidates are returned which match an override then the override is
593+
/// applied by performing a second query for what the override should
594+
/// return.
595+
fn query(&mut self, dep: &Dependency) -> CargoResult<Rc<Vec<Candidate>>> {
596+
if let Some(out) = self.cache.get(dep).cloned() {
597+
return Ok(out);
598+
}
599+
600+
let mut ret = Vec::new();
601+
self.registry.query(dep, &mut |s| {
602+
ret.push(Candidate { summary: s, replace: None });
603+
})?;
604+
for candidate in ret.iter_mut() {
605+
let summary = &candidate.summary;
606+
607+
let mut potential_matches = self.replacements.iter()
608+
.filter(|&&(ref spec, _)| spec.matches(summary.package_id()));
609+
610+
let &(ref spec, ref dep) = match potential_matches.next() {
611+
None => continue,
612+
Some(replacement) => replacement,
613+
};
614+
debug!("found an override for {} {}", dep.name(), dep.version_req());
615+
616+
let mut summaries = self.registry.query_vec(dep)?.into_iter();
617+
let s = summaries.next().ok_or_else(|| {
618+
format_err!("no matching package for override `{}` found\n\
619+
location searched: {}\n\
620+
version required: {}",
621+
spec, dep.source_id(), dep.version_req())
622+
})?;
623+
let summaries = summaries.collect::<Vec<_>>();
624+
if !summaries.is_empty() {
625+
let bullets = summaries.iter().map(|s| {
626+
format!(" * {}", s.package_id())
627+
}).collect::<Vec<_>>();
628+
bail!("the replacement specification `{}` matched \
629+
multiple packages:\n * {}\n{}", spec, s.package_id(),
630+
bullets.join("\n"));
631+
}
632+
633+
// The dependency should be hard-coded to have the same name and an
634+
// exact version requirement, so both of these assertions should
635+
// never fail.
636+
assert_eq!(s.version(), summary.version());
637+
assert_eq!(s.name(), summary.name());
638+
639+
let replace = if s.source_id() == summary.source_id() {
640+
debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
641+
None
642+
} else {
643+
Some(s)
644+
};
645+
let matched_spec = spec.clone();
646+
647+
// Make sure no duplicates
648+
if let Some(&(ref spec, _)) = potential_matches.next() {
649+
bail!("overlapping replacement specifications found:\n\n \
650+
* {}\n * {}\n\nboth specifications match: {}",
651+
matched_spec, spec, summary.package_id());
652+
}
653+
654+
for dep in summary.dependencies() {
655+
debug!("\t{} => {}", dep.name(), dep.version_req());
656+
}
657+
658+
candidate.replace = replace;
659+
}
660+
661+
// When we attempt versions for a package, we'll want to start at
662+
// the maximum version and work our way down.
663+
ret.sort_unstable_by(|a, b| {
664+
b.summary.version().cmp(a.summary.version())
665+
});
666+
667+
let out = Rc::new(ret);
668+
669+
self.cache.insert(dep.clone(), out.clone());
670+
671+
Ok(out)
672+
}
673+
}
674+
576675
#[derive(Clone)]
577-
struct BacktrackFrame<'a> {
676+
struct BacktrackFrame {
578677
cur: usize,
579-
context_backup: Context<'a>,
678+
context_backup: Context,
580679
deps_backup: BinaryHeap<DepsFrame>,
581680
remaining_candidates: RemainingCandidates,
582681
parent: Summary,
@@ -658,12 +757,12 @@ impl RemainingCandidates {
658757
///
659758
/// If all dependencies can be activated and resolved to a version in the
660759
/// dependency graph, cx.resolve is returned.
661-
fn activate_deps_loop<'a>(
662-
mut cx: Context<'a>,
663-
registry: &mut Registry,
760+
fn activate_deps_loop(
761+
mut cx: Context,
762+
registry: &mut RegistryQueryer,
664763
summaries: &[(Summary, Method)],
665764
config: Option<&Config>,
666-
) -> CargoResult<Context<'a>> {
765+
) -> CargoResult<Context> {
667766
// Note that a `BinaryHeap` is used for the remaining dependencies that need
668767
// activation. This heap is sorted such that the "largest value" is the most
669768
// constrained dependency, or the one with the least candidates.
@@ -780,7 +879,7 @@ fn activate_deps_loop<'a>(
780879
).ok_or_else(|| {
781880
activation_error(
782881
&cx,
783-
registry,
882+
registry.registry,
784883
&parent,
785884
&dep,
786885
&conflicting_activations,
@@ -850,9 +949,9 @@ fn activate_deps_loop<'a>(
850949
/// If the outcome could differ, resets `cx` and `remaining_deps` to that
851950
/// level and returns the next candidate.
852951
/// If all candidates have been exhausted, returns None.
853-
fn find_candidate<'a>(
854-
backtrack_stack: &mut Vec<BacktrackFrame<'a>>,
855-
cx: &mut Context<'a>,
952+
fn find_candidate(
953+
backtrack_stack: &mut Vec<BacktrackFrame>,
954+
cx: &mut Context,
856955
remaining_deps: &mut BinaryHeap<DepsFrame>,
857956
parent: &mut Summary,
858957
cur: &mut usize,
@@ -1176,7 +1275,7 @@ fn build_requirements<'a, 'b: 'a>(s: &'a Summary, method: &'b Method)
11761275
Ok(reqs)
11771276
}
11781277

1179-
impl<'a> Context<'a> {
1278+
impl Context {
11801279
/// Activate this summary by inserting it into our list of known activations.
11811280
///
11821281
/// Returns true if this summary with the given method is already activated.
@@ -1219,7 +1318,7 @@ impl<'a> Context<'a> {
12191318
}
12201319

12211320
fn build_deps(&mut self,
1222-
registry: &mut Registry,
1321+
registry: &mut RegistryQueryer,
12231322
parent: Option<&Summary>,
12241323
candidate: &Summary,
12251324
method: &Method) -> ActivateResult<Vec<DepInfo>> {
@@ -1231,13 +1330,8 @@ impl<'a> Context<'a> {
12311330
// Next, transform all dependencies into a list of possible candidates
12321331
// which can satisfy that dependency.
12331332
let mut deps = deps.into_iter().map(|(dep, features)| {
1234-
let mut candidates = self.query(registry, &dep)?;
1235-
// When we attempt versions for a package, we'll want to start at
1236-
// the maximum version and work our way down.
1237-
candidates.sort_by(|a, b| {
1238-
b.summary.version().cmp(a.summary.version())
1239-
});
1240-
Ok((dep, Rc::new(candidates), Rc::new(features)))
1333+
let candidates = registry.query(&dep)?;
1334+
Ok((dep, candidates, Rc::new(features)))
12411335
}).collect::<CargoResult<Vec<DepInfo>>>()?;
12421336

12431337
// Attempt to resolve dependencies with fewer candidates before trying
@@ -1249,78 +1343,6 @@ impl<'a> Context<'a> {
12491343
Ok(deps)
12501344
}
12511345

1252-
/// Queries the `registry` to return a list of candidates for `dep`.
1253-
///
1254-
/// This method is the location where overrides are taken into account. If
1255-
/// any candidates are returned which match an override then the override is
1256-
/// applied by performing a second query for what the override should
1257-
/// return.
1258-
fn query(&self,
1259-
registry: &mut Registry,
1260-
dep: &Dependency) -> CargoResult<Vec<Candidate>> {
1261-
let mut ret = Vec::new();
1262-
registry.query(dep, &mut |s| {
1263-
ret.push(Candidate { summary: s, replace: None });
1264-
})?;
1265-
for candidate in ret.iter_mut() {
1266-
let summary = &candidate.summary;
1267-
1268-
let mut potential_matches = self.replacements.iter()
1269-
.filter(|&&(ref spec, _)| spec.matches(summary.package_id()));
1270-
1271-
let &(ref spec, ref dep) = match potential_matches.next() {
1272-
None => continue,
1273-
Some(replacement) => replacement,
1274-
};
1275-
debug!("found an override for {} {}", dep.name(), dep.version_req());
1276-
1277-
let mut summaries = registry.query_vec(dep)?.into_iter();
1278-
let s = summaries.next().ok_or_else(|| {
1279-
format_err!("no matching package for override `{}` found\n\
1280-
location searched: {}\n\
1281-
version required: {}",
1282-
spec, dep.source_id(), dep.version_req())
1283-
})?;
1284-
let summaries = summaries.collect::<Vec<_>>();
1285-
if !summaries.is_empty() {
1286-
let bullets = summaries.iter().map(|s| {
1287-
format!(" * {}", s.package_id())
1288-
}).collect::<Vec<_>>();
1289-
bail!("the replacement specification `{}` matched \
1290-
multiple packages:\n * {}\n{}", spec, s.package_id(),
1291-
bullets.join("\n"));
1292-
}
1293-
1294-
// The dependency should be hard-coded to have the same name and an
1295-
// exact version requirement, so both of these assertions should
1296-
// never fail.
1297-
assert_eq!(s.version(), summary.version());
1298-
assert_eq!(s.name(), summary.name());
1299-
1300-
let replace = if s.source_id() == summary.source_id() {
1301-
debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
1302-
None
1303-
} else {
1304-
Some(s)
1305-
};
1306-
let matched_spec = spec.clone();
1307-
1308-
// Make sure no duplicates
1309-
if let Some(&(ref spec, _)) = potential_matches.next() {
1310-
bail!("overlapping replacement specifications found:\n\n \
1311-
* {}\n * {}\n\nboth specifications match: {}",
1312-
matched_spec, spec, summary.package_id());
1313-
}
1314-
1315-
for dep in summary.dependencies() {
1316-
debug!("\t{} => {}", dep.name(), dep.version_req());
1317-
}
1318-
1319-
candidate.replace = replace;
1320-
}
1321-
Ok(ret)
1322-
}
1323-
13241346
fn prev_active(&self, dep: &Dependency) -> &[Summary] {
13251347
self.activations.get(dep.name())
13261348
.and_then(|v| v.get(dep.source_id()))

src/cargo/util/cfg.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ use std::fmt;
44

55
use util::{CargoError, CargoResult};
66

7-
#[derive(Clone, PartialEq, Debug)]
7+
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
88
pub enum Cfg {
99
Name(String),
1010
KeyPair(String, String),
1111
}
1212

13-
#[derive(Clone, PartialEq, Debug)]
13+
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
1414
pub enum CfgExpr {
1515
Not(Box<CfgExpr>),
1616
All(Vec<CfgExpr>),

0 commit comments

Comments
 (0)