Skip to content

Commit 8e11a81

Browse files
authored
perf: do not recompute the intersection when backtracking (pubgrub-rs#171)
* perf: do not recompute the accumulated_intersection when backtracking * more correct comment message * make things dry again
1 parent 526775f commit 8e11a81

File tree

2 files changed

+31
-37
lines changed

2 files changed

+31
-37
lines changed

src/internal/core.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
225225
incompat_changed: bool,
226226
decision_level: DecisionLevel,
227227
) {
228-
self.partial_solution
229-
.backtrack(decision_level, &self.incompatibility_store);
228+
self.partial_solution.backtrack(decision_level);
230229
// Remove contradicted incompatibilities that depend on decisions we just backtracked away.
231230
self.contradicted_incompatibilities
232231
.retain(|_, dl| *dl <= decision_level);

src/internal/partial_solution.rs

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ pub struct DatedDerivation<P: Package, VS: VersionSet> {
107107
global_index: u32,
108108
decision_level: DecisionLevel,
109109
cause: IncompId<P, VS>,
110+
accumulated_intersection: Term<VS>,
110111
}
111112

112113
impl<P: Package, VS: VersionSet> Display for DatedDerivation<P, VS> {
@@ -207,11 +208,11 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
207208
store: &Arena<Incompatibility<P, VS>>,
208209
) {
209210
use indexmap::map::Entry;
210-
let term = store[cause].get(&package).unwrap().negate();
211-
let dated_derivation = DatedDerivation {
211+
let mut dated_derivation = DatedDerivation {
212212
global_index: self.next_global_index,
213213
decision_level: self.current_decision_level,
214214
cause,
215+
accumulated_intersection: store[cause].get(&package).unwrap().negate(),
215216
};
216217
self.next_global_index += 1;
217218
let pa_last_index = self.package_assignments.len().saturating_sub(1);
@@ -226,7 +227,8 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
226227
panic!("add_derivation should not be called after a decision")
227228
}
228229
AssignmentsIntersection::Derivations(t) => {
229-
*t = t.intersection(&term);
230+
*t = t.intersection(&dated_derivation.accumulated_intersection);
231+
dated_derivation.accumulated_intersection = t.clone();
230232
if t.is_positive() {
231233
// we can use `swap_indices` to make `changed_this_decision_level` only go down by 1
232234
// but the copying is slower then the larger search
@@ -238,6 +240,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
238240
pa.dated_derivations.push(dated_derivation);
239241
}
240242
Entry::Vacant(v) => {
243+
let term = dated_derivation.accumulated_intersection.clone();
241244
if term.is_positive() {
242245
self.changed_this_decision_level =
243246
std::cmp::min(self.changed_this_decision_level, pa_last_index);
@@ -297,13 +300,9 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
297300
}
298301

299302
/// Backtrack the partial solution to a given decision level.
300-
pub fn backtrack(
301-
&mut self,
302-
decision_level: DecisionLevel,
303-
store: &Arena<Incompatibility<P, VS>>,
304-
) {
303+
pub fn backtrack(&mut self, decision_level: DecisionLevel) {
305304
self.current_decision_level = decision_level;
306-
self.package_assignments.retain(|p, pa| {
305+
self.package_assignments.retain(|_p, pa| {
307306
if pa.smallest_decision_level > decision_level {
308307
// Remove all entries that have a smallest decision level higher than the backtrack target.
309308
false
@@ -325,18 +324,14 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
325324
}
326325
debug_assert!(!pa.dated_derivations.is_empty());
327326

327+
let last = pa.dated_derivations.last().unwrap();
328+
328329
// Update highest_decision_level.
329-
pa.highest_decision_level = pa.dated_derivations.last().unwrap().decision_level;
330-
331-
// Recompute the assignments intersection.
332-
pa.assignments_intersection = AssignmentsIntersection::Derivations(
333-
pa.dated_derivations
334-
.iter()
335-
.fold(Term::any(), |acc, dated_derivation| {
336-
let term = store[dated_derivation.cause].get(p).unwrap().negate();
337-
acc.intersection(&term)
338-
}),
339-
);
330+
pa.highest_decision_level = last.decision_level;
331+
332+
// Reset the assignments intersection.
333+
pa.assignments_intersection =
334+
AssignmentsIntersection::Derivations(last.accumulated_intersection.clone());
340335
true
341336
}
342337
});
@@ -400,7 +395,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
400395
incompat: &Incompatibility<P, VS>,
401396
store: &Arena<Incompatibility<P, VS>>,
402397
) -> (P, SatisfierSearch<P, VS>) {
403-
let satisfied_map = Self::find_satisfier(incompat, &self.package_assignments, store);
398+
let satisfied_map = Self::find_satisfier(incompat, &self.package_assignments);
404399
let (satisfier_package, &(satisfier_index, _, satisfier_decision_level)) = satisfied_map
405400
.iter()
406401
.max_by_key(|(_p, (_, global_index, _))| global_index)
@@ -440,14 +435,13 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
440435
fn find_satisfier(
441436
incompat: &Incompatibility<P, VS>,
442437
package_assignments: &FnvIndexMap<P, PackageAssignments<P, VS>>,
443-
store: &Arena<Incompatibility<P, VS>>,
444438
) -> SmallMap<P, (usize, u32, DecisionLevel)> {
445439
let mut satisfied = SmallMap::Empty;
446440
for (package, incompat_term) in incompat.iter() {
447441
let pa = package_assignments.get(package).expect("Must exist");
448442
satisfied.insert(
449443
package.clone(),
450-
pa.satisfier(package, incompat_term, Term::any(), store),
444+
pa.satisfier(package, incompat_term, &Term::any()),
451445
);
452446
}
453447
satisfied
@@ -483,7 +477,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> PartialSolution<P, VS, P
483477

484478
satisfied_map.insert(
485479
satisfier_package.clone(),
486-
satisfier_pa.satisfier(satisfier_package, incompat_term, accum_term, store),
480+
satisfier_pa.satisfier(satisfier_package, incompat_term, &accum_term),
487481
);
488482

489483
// Finally, let's identify the decision level of that previous satisfier.
@@ -504,16 +498,15 @@ impl<P: Package, VS: VersionSet> PackageAssignments<P, VS> {
504498
&self,
505499
package: &P,
506500
incompat_term: &Term<VS>,
507-
start_term: Term<VS>,
508-
store: &Arena<Incompatibility<P, VS>>,
501+
start_term: &Term<VS>,
509502
) -> (usize, u32, DecisionLevel) {
510-
// Term where we accumulate intersections until incompat_term is satisfied.
511-
let mut accum_term = start_term;
512503
// Indicate if we found a satisfier in the list of derivations, otherwise it will be the decision.
513504
for (idx, dated_derivation) in self.dated_derivations.iter().enumerate() {
514-
let this_term = store[dated_derivation.cause].get(package).unwrap().negate();
515-
accum_term = accum_term.intersection(&this_term);
516-
if accum_term.subset_of(incompat_term) {
505+
if dated_derivation
506+
.accumulated_intersection
507+
.intersection(start_term)
508+
.subset_of(incompat_term)
509+
{
517510
// We found the derivation causing satisfaction.
518511
return (
519512
idx,
@@ -524,13 +517,13 @@ impl<P: Package, VS: VersionSet> PackageAssignments<P, VS> {
524517
}
525518
// If it wasn't found in the derivations,
526519
// it must be the decision which is last (if called in the right context).
527-
match self.assignments_intersection {
520+
match &self.assignments_intersection {
528521
AssignmentsIntersection::Decision((global_index, _, _)) => (
529522
self.dated_derivations.len(),
530-
global_index,
523+
*global_index,
531524
self.highest_decision_level,
532525
),
533-
AssignmentsIntersection::Derivations(_) => {
526+
AssignmentsIntersection::Derivations(accumulated_intersection) => {
534527
unreachable!(
535528
concat!(
536529
"while processing package {}: ",
@@ -539,7 +532,9 @@ impl<P: Package, VS: VersionSet> PackageAssignments<P, VS> {
539532
"but instead it was a derivation. This shouldn't be possible! ",
540533
"(Maybe your Version ordering is broken?)"
541534
),
542-
package, accum_term, incompat_term
535+
package,
536+
accumulated_intersection.intersection(start_term),
537+
incompat_term
543538
)
544539
}
545540
}

0 commit comments

Comments
 (0)