@@ -17,6 +17,7 @@ use crate::internal::{
1717use crate :: { DependencyProvider , Package , Term , VersionSet } ;
1818
1919type FnvIndexMap < K , V > = indexmap:: IndexMap < K , V , BuildHasherDefault < FxHasher > > ;
20+ type FnvIndexSet < T > = indexmap:: IndexSet < T , BuildHasherDefault < FxHasher > > ;
2021
2122#[ derive( Debug , Copy , Clone , Ord , PartialOrd , Eq , PartialEq ) ]
2223pub ( crate ) struct DecisionLevel ( pub ( crate ) u32 ) ;
@@ -63,8 +64,6 @@ pub struct PartialSolution<DP: DependencyProvider> {
6364 /// range.
6465 #[ allow( clippy:: type_complexity) ]
6566 package_assignments : FnvIndexMap < Id < DP :: P > , PackageAssignments < DP :: P , DP :: VS , DP :: M > > ,
66- /// Index into `package_assignments` to decide which packages need to be re-prioritized.
67- prioritize_decision_level : usize ,
6867 /// The undecided packages order by their `Priority`.
6968 ///
7069 /// The max heap allows quickly `pop`ing the highest priority package.
@@ -74,6 +73,9 @@ pub struct PartialSolution<DP: DependencyProvider> {
7473 #[ allow( clippy:: type_complexity) ]
7574 prioritized_potential_packages :
7675 PriorityQueue < Id < DP :: P > , ( DP :: Priority , Reverse < u32 > ) , BuildHasherDefault < FxHasher > > ,
76+ /// Packages whose derivations changed since the last time `prioritize` was called and need
77+ /// their priorities to be updated.
78+ outdated_priorities : FnvIndexSet < Id < DP :: P > > ,
7779 /// Whether we have never backtracked, to enable fast path optimizations.
7880 has_ever_backtracked : bool ,
7981}
@@ -181,7 +183,7 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
181183 current_decision_level : DecisionLevel ( 0 ) ,
182184 package_assignments : FnvIndexMap :: default ( ) ,
183185 prioritized_potential_packages : PriorityQueue :: default ( ) ,
184- prioritize_decision_level : 0 ,
186+ outdated_priorities : FnvIndexSet :: default ( ) ,
185187 has_ever_backtracked : false ,
186188 }
187189 }
@@ -234,10 +236,6 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
234236 }
235237 } ,
236238 }
237- assert_eq ! (
238- self . prioritize_decision_level,
239- self . package_assignments. len( )
240- ) ;
241239 }
242240 let new_idx = self . current_decision_level . 0 as usize ;
243241 self . current_decision_level = self . current_decision_level . increment ( ) ;
@@ -288,10 +286,8 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
288286 accumulated_intersection : store[ cause] . get ( package) . unwrap ( ) . negate ( ) ,
289287 } ;
290288 self . next_global_index += 1 ;
291- let pa_last_index = self . package_assignments . len ( ) . saturating_sub ( 1 ) ;
292289 match self . package_assignments . entry ( package) {
293290 Entry :: Occupied ( mut occupied) => {
294- let idx = occupied. index ( ) ;
295291 let pa = occupied. get_mut ( ) ;
296292 pa. highest_decision_level = self . current_decision_level ;
297293 match & mut pa. assignments_intersection {
@@ -303,10 +299,7 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
303299 * t = t. intersection ( & dated_derivation. accumulated_intersection ) ;
304300 dated_derivation. accumulated_intersection = t. clone ( ) ;
305301 if t. is_positive ( ) {
306- // we can use `swap_indices` to make `prioritize_decision_level` only go down by 1
307- // but the copying is slower then the larger search
308- self . prioritize_decision_level =
309- std:: cmp:: min ( self . prioritize_decision_level , idx) ;
302+ self . outdated_priorities . insert ( package) ;
310303 }
311304 }
312305 }
@@ -315,8 +308,7 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
315308 Entry :: Vacant ( v) => {
316309 let term = dated_derivation. accumulated_intersection . clone ( ) ;
317310 if term. is_positive ( ) {
318- self . prioritize_decision_level =
319- std:: cmp:: min ( self . prioritize_decision_level , pa_last_index) ;
311+ self . outdated_priorities . insert ( package) ;
320312 }
321313 v. insert ( PackageAssignments {
322314 smallest_decision_level : self . current_decision_level ,
@@ -330,50 +322,49 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
330322
331323 #[ cold]
332324 pub fn prioritized_packages ( & self ) -> impl Iterator < Item = ( Id < DP :: P > , & DP :: VS ) > {
333- let check_all = self . prioritize_decision_level
334- == self . current_decision_level . 0 . saturating_sub ( 1 ) as usize ;
335325 let current_decision_level = self . current_decision_level ;
336326 self . package_assignments
337- . get_range ( self . prioritize_decision_level ..)
327+ . get_range ( current_decision_level . 0 as usize ..)
338328 . unwrap ( )
339329 . iter ( )
340330 . filter ( move |( _, pa) | {
341- // We only actually need to update the package if its Been changed
342- // since the last time we called prioritize.
343- // Which means it's highest decision level is the current decision level,
344- // or if we backtracked in the mean time.
345- check_all || pa. highest_decision_level == current_decision_level
331+ // We only actually need to update the package if it was changed
332+ // since the last time we called prioritize, which means it's highest decision level
333+ // is the current decision level, or if we backtracked in the meantime.
334+ pa. highest_decision_level == current_decision_level
335+ } )
336+ . filter_map ( |( & p, pa) | {
337+ Some ( ( p, pa. assignments_intersection . potential_package_filter ( ) ?) )
346338 } )
347- . filter_map ( |( & p, pa) | pa. assignments_intersection . potential_package_filter ( p) )
348339 }
349340
350341 #[ cold]
351342 pub fn pick_highest_priority_pkg (
352343 & mut self ,
353344 mut prioritizer : impl FnMut ( Id < DP :: P > , & DP :: VS ) -> DP :: Priority ,
354345 ) -> Option < Id < DP :: P > > {
355- let check_all = self . prioritize_decision_level
356- == self . current_decision_level . 0 . saturating_sub ( 1 ) as usize ;
357- let current_decision_level = self . current_decision_level ;
358346 let prioritized_potential_packages = & mut self . prioritized_potential_packages ;
359- self . package_assignments
360- . get_range ( self . prioritize_decision_level ..)
361- . unwrap ( )
362- . iter ( )
363- . filter ( |( _, pa) | {
364- // We only actually need to update the package if it has been changed
365- // since the last time we called prioritize.
366- // Which means it's highest decision level is the current decision level,
367- // or if we backtracked in the meantime.
368- check_all || pa. highest_decision_level == current_decision_level
369- } )
370- . filter_map ( |( & p, pa) | pa. assignments_intersection . potential_package_filter ( p) )
371- . for_each ( |( p, r) | {
372- let priority = prioritizer ( p, r) ;
373- prioritized_potential_packages. push ( p, ( priority, Reverse ( p. into_raw ( ) as u32 ) ) ) ;
374- } ) ;
375- self . prioritize_decision_level = self . package_assignments . len ( ) ;
376- prioritized_potential_packages. pop ( ) . map ( |( p, _) | p)
347+ while let Some ( p) = self . outdated_priorities . pop ( ) {
348+ let Some ( pa) = self . package_assignments . get ( & p) else {
349+ continue ;
350+ } ;
351+ let Some ( r) = pa. assignments_intersection . potential_package_filter ( ) else {
352+ continue ;
353+ } ;
354+ let priority = prioritizer ( p, r) ;
355+ prioritized_potential_packages. push ( p, ( priority, Reverse ( p. into_raw ( ) as u32 ) ) ) ;
356+ }
357+ while let Some ( p) = self . prioritized_potential_packages . pop ( ) . map ( |( p, _) | p) {
358+ if self
359+ . package_assignments
360+ . get ( & p)
361+ . and_then ( |pa| pa. assignments_intersection . potential_package_filter ( ) )
362+ . is_some ( )
363+ {
364+ return Some ( p) ;
365+ }
366+ }
367+ None
377368 }
378369
379370 /// If a partial solution has, for every positive derivation,
@@ -413,12 +404,20 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
413404 /// Backtrack the partial solution to a given decision level.
414405 pub ( crate ) fn backtrack ( & mut self , decision_level : DecisionLevel ) {
415406 self . current_decision_level = decision_level;
416- self . package_assignments . retain ( |_ , pa| {
407+ self . package_assignments . retain ( |p , pa| {
417408 if pa. smallest_decision_level > decision_level {
418409 // Remove all entries that have a smallest decision level higher than the backtrack target.
419410 false
420411 } else if pa. highest_decision_level <= decision_level {
421412 // Do not change entries older than the backtrack decision level target.
413+ if pa
414+ . assignments_intersection
415+ . potential_package_filter ( )
416+ . is_some ( )
417+ && self . prioritized_potential_packages . get ( p) . is_none ( )
418+ {
419+ self . outdated_priorities . insert ( * p) ;
420+ }
422421 true
423422 } else {
424423 // smallest_decision_level <= decision_level < highest_decision_level
@@ -443,12 +442,15 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
443442 // Reset the assignments intersection.
444443 pa. assignments_intersection =
445444 AssignmentsIntersection :: Derivations ( last. accumulated_intersection . clone ( ) ) ;
445+
446+ self . prioritized_potential_packages . remove ( p) ;
447+ if pa. assignments_intersection . term ( ) . is_positive ( ) {
448+ self . outdated_priorities . insert ( * p) ;
449+ }
446450 true
447451 }
448452 } ) ;
449- // Throw away all stored priority levels, And mark that they all need to be recomputed.
450- self . prioritized_potential_packages . clear ( ) ;
451- self . prioritize_decision_level = self . current_decision_level . 0 . saturating_sub ( 1 ) as usize ;
453+ // Throw away all stored priority levels and mark them for recomputing
452454 self . has_ever_backtracked = true ;
453455 }
454456
@@ -714,12 +716,12 @@ impl<VS: VersionSet> AssignmentsIntersection<VS> {
714716 /// selected version (no "decision")
715717 /// and if it contains at least one positive derivation term
716718 /// in the partial solution.
717- fn potential_package_filter < P : Package > ( & self , package : Id < P > ) -> Option < ( Id < P > , & VS ) > {
719+ fn potential_package_filter ( & self ) -> Option < & VS > {
718720 match self {
719721 Self :: Decision { .. } => None ,
720722 Self :: Derivations ( term_intersection) => {
721723 if term_intersection. is_positive ( ) {
722- Some ( ( package , term_intersection. unwrap_positive ( ) ) )
724+ Some ( term_intersection. unwrap_positive ( ) )
723725 } else {
724726 None
725727 }
0 commit comments