@@ -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+ /// Contains all packages that **have** had there assignments changed since
77+ /// the last time `prioritize` has been called on them.
78+ unprioritized_potential_packages : 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+ unprioritized_potential_packages : 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 . unprioritized_potential_packages . 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 . unprioritized_potential_packages . insert ( package) ;
320312 }
321313 v. insert ( PackageAssignments {
322314 smallest_decision_level : self . current_decision_level ,
@@ -330,50 +322,62 @@ 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 ( )
340- . 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
330+ . filter_map ( |( & p, pa) | {
331+ Some ( ( p, pa. assignments_intersection . potential_package_filter ( ) ?) )
346332 } )
347- . filter_map ( |( & p, pa) | pa. assignments_intersection . potential_package_filter ( p) )
348333 }
349334
350335 #[ cold]
351336 pub fn pick_highest_priority_pkg (
352337 & mut self ,
353338 mut prioritizer : impl FnMut ( Id < DP :: P > , & DP :: VS ) -> DP :: Priority ,
354339 ) -> 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 ;
358340 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
341+ while let Some ( p) = self . unprioritized_potential_packages . pop ( ) {
342+ let Some ( pa) = self . package_assignments . get ( & p) else {
343+ continue ;
344+ } ;
345+ let Some ( r) = pa. assignments_intersection . potential_package_filter ( ) else {
346+ continue ;
347+ } ;
348+ let priority = prioritizer ( p, r) ;
349+ prioritized_potential_packages. push ( p, ( priority, Reverse ( p. into_raw ( ) as u32 ) ) ) ;
350+ }
351+ while let Some ( p) = self . prioritized_potential_packages . pop ( ) . map ( |( p, _) | p) {
352+ if self
353+ . package_assignments
354+ . get ( & p)
355+ . and_then ( |pa| pa. assignments_intersection . potential_package_filter ( ) )
356+ . is_some ( )
357+ {
358+ return Some ( p) ;
359+ }
360+ }
361+ None
362+ }
363+
364+ /// Manually update a package priority that changed independent of the range.
365+ pub fn update_priority ( & mut self , package : Id < DP :: P > , priority : DP :: Priority ) {
366+ if self
367+ . package_assignments
368+ . get ( & package)
369+ . and_then ( |assignment| {
370+ assignment
371+ . assignments_intersection
372+ . potential_package_filter ( )
369373 } )
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 )
374+ . is_none ( )
375+ {
376+ // Only prioritize packages up for decision
377+ return ;
378+ }
379+ self . prioritized_potential_packages
380+ . push ( package , ( priority , Reverse ( package . into_raw ( ) as u32 ) ) ) ;
377381 }
378382
379383 /// If a partial solution has, for every positive derivation,
@@ -413,12 +417,20 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
413417 /// Backtrack the partial solution to a given decision level.
414418 pub ( crate ) fn backtrack ( & mut self , decision_level : DecisionLevel ) {
415419 self . current_decision_level = decision_level;
416- self . package_assignments . retain ( |_ , pa| {
420+ self . package_assignments . retain ( |p , pa| {
417421 if pa. smallest_decision_level > decision_level {
418422 // Remove all entries that have a smallest decision level higher than the backtrack target.
419423 false
420424 } else if pa. highest_decision_level <= decision_level {
421425 // Do not change entries older than the backtrack decision level target.
426+ if pa
427+ . assignments_intersection
428+ . potential_package_filter ( )
429+ . is_some ( )
430+ && self . prioritized_potential_packages . get ( p) . is_none ( )
431+ {
432+ self . unprioritized_potential_packages . insert ( * p) ;
433+ }
422434 true
423435 } else {
424436 // smallest_decision_level <= decision_level < highest_decision_level
@@ -443,12 +455,15 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
443455 // Reset the assignments intersection.
444456 pa. assignments_intersection =
445457 AssignmentsIntersection :: Derivations ( last. accumulated_intersection . clone ( ) ) ;
458+
459+ self . prioritized_potential_packages . remove ( p) ;
460+ if pa. assignments_intersection . term ( ) . is_positive ( ) {
461+ self . unprioritized_potential_packages . insert ( * p) ;
462+ }
446463 true
447464 }
448465 } ) ;
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 ;
466+ // Throw away all stored priority levels and mark them for recomputing
452467 self . has_ever_backtracked = true ;
453468 }
454469
@@ -714,12 +729,12 @@ impl<VS: VersionSet> AssignmentsIntersection<VS> {
714729 /// selected version (no "decision")
715730 /// and if it contains at least one positive derivation term
716731 /// in the partial solution.
717- fn potential_package_filter < P : Package > ( & self , package : Id < P > ) -> Option < ( Id < P > , & VS ) > {
732+ fn potential_package_filter ( & self ) -> Option < & VS > {
718733 match self {
719734 Self :: Decision { .. } => None ,
720735 Self :: Derivations ( term_intersection) => {
721736 if term_intersection. is_positive ( ) {
722- Some ( ( package , term_intersection. unwrap_positive ( ) ) )
737+ Some ( term_intersection. unwrap_positive ( ) )
723738 } else {
724739 None
725740 }
0 commit comments