@@ -31,8 +31,11 @@ pub struct DependencyQueue<N: Hash + Eq, E: Hash + Eq, V> {
31
31
/// easily indexable with just an `N`
32
32
reverse_dep_map : HashMap < N , HashMap < E , HashSet < N > > > ,
33
33
34
- /// The total number of packages that are transitively waiting on this package
34
+ /// The relative priority of this package. Higher values should be scheduled sooner.
35
35
priority : HashMap < N , usize > ,
36
+
37
+ /// An expected cost for building this package. Used to determine priority.
38
+ cost : HashMap < N , usize > ,
36
39
}
37
40
38
41
impl < N : Hash + Eq , E : Hash + Eq , V > Default for DependencyQueue < N , E , V > {
@@ -48,6 +51,7 @@ impl<N: Hash + Eq, E: Hash + Eq, V> DependencyQueue<N, E, V> {
48
51
dep_map : HashMap :: new ( ) ,
49
52
reverse_dep_map : HashMap :: new ( ) ,
50
53
priority : HashMap :: new ( ) ,
54
+ cost : HashMap :: new ( ) ,
51
55
}
52
56
}
53
57
}
@@ -63,7 +67,18 @@ impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
63
67
///
64
68
/// An optional `value` can also be associated with `key` which is reclaimed
65
69
/// when the node is ready to go.
66
- pub fn queue ( & mut self , key : N , value : V , dependencies : impl IntoIterator < Item = ( N , E ) > ) {
70
+ ///
71
+ /// The cost parameter can be used to hint at the relative cost of building
72
+ /// this node. This implementation does not care about the units of this value, so
73
+ /// the calling code is free to use whatever they'd like. In general, higher cost
74
+ /// nodes are expected to take longer to build.
75
+ pub fn queue (
76
+ & mut self ,
77
+ key : N ,
78
+ value : V ,
79
+ dependencies : impl IntoIterator < Item = ( N , E ) > ,
80
+ cost : usize ,
81
+ ) {
67
82
assert ! ( !self . dep_map. contains_key( & key) ) ;
68
83
69
84
let mut my_dependencies = HashSet :: new ( ) ;
@@ -76,7 +91,8 @@ impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
76
91
. or_insert_with ( HashSet :: new)
77
92
. insert ( key. clone ( ) ) ;
78
93
}
79
- self . dep_map . insert ( key, ( my_dependencies, value) ) ;
94
+ self . dep_map . insert ( key. clone ( ) , ( my_dependencies, value) ) ;
95
+ self . cost . insert ( key, cost) ;
80
96
}
81
97
82
98
/// All nodes have been added, calculate some internal metadata and prepare
@@ -86,8 +102,19 @@ impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
86
102
for key in self . dep_map . keys ( ) {
87
103
depth ( key, & self . reverse_dep_map , & mut out) ;
88
104
}
89
- self . priority = out. into_iter ( ) . map ( |( n, set) | ( n, set. len ( ) ) ) . collect ( ) ;
105
+ self . priority = out
106
+ . into_iter ( )
107
+ . map ( |( n, set) | {
108
+ let total_cost =
109
+ self . cost [ & n] + set. iter ( ) . map ( |key| self . cost [ key] ) . sum :: < usize > ( ) ;
110
+ ( n, total_cost)
111
+ } )
112
+ . collect ( ) ;
90
113
114
+ /// Creates a flattened reverse dependency list. For a given key, finds the
115
+ /// set of nodes which depend on it, including transitively. This is different
116
+ /// from self.reverse_dep_map because self.reverse_dep_map only maps one level
117
+ /// of reverse dependencies.
91
118
fn depth < ' a , N : Hash + Eq + Clone , E : Hash + Eq + Clone > (
92
119
key : & N ,
93
120
map : & HashMap < N , HashMap < E , HashSet < N > > > ,
@@ -123,16 +150,6 @@ impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
123
150
/// A package is ready to be built when it has 0 un-built dependencies. If
124
151
/// `None` is returned then no packages are ready to be built.
125
152
pub fn dequeue ( & mut self ) -> Option < ( N , V ) > {
126
- // Look at all our crates and find everything that's ready to build (no
127
- // deps). After we've got that candidate set select the one which has
128
- // the maximum priority in the dependency graph. This way we should
129
- // hopefully keep CPUs hottest the longest by ensuring that long
130
- // dependency chains are scheduled early on in the build process and the
131
- // leafs higher in the tree can fill in the cracks later.
132
- //
133
- // TODO: it'd be best here to throw in a heuristic of crate size as
134
- // well. For example how long did this crate historically take to
135
- // compile? How large is its source code? etc.
136
153
let next = self
137
154
. dep_map
138
155
. iter ( )
@@ -190,14 +207,14 @@ mod test {
190
207
use super :: DependencyQueue ;
191
208
192
209
#[ test]
193
- fn deep_first ( ) {
210
+ fn deep_first_equal_cost ( ) {
194
211
let mut q = DependencyQueue :: new ( ) ;
195
212
196
- q. queue ( 1 , ( ) , vec ! [ ] ) ;
197
- q. queue ( 2 , ( ) , vec ! [ ( 1 , ( ) ) ] ) ;
198
- q. queue ( 3 , ( ) , vec ! [ ] ) ;
199
- q. queue ( 4 , ( ) , vec ! [ ( 2 , ( ) ) , ( 3 , ( ) ) ] ) ;
200
- q. queue ( 5 , ( ) , vec ! [ ( 4 , ( ) ) , ( 3 , ( ) ) ] ) ;
213
+ q. queue ( 1 , ( ) , vec ! [ ] , 1 ) ;
214
+ q. queue ( 2 , ( ) , vec ! [ ( 1 , ( ) ) ] , 1 ) ;
215
+ q. queue ( 3 , ( ) , vec ! [ ] , 1 ) ;
216
+ q. queue ( 4 , ( ) , vec ! [ ( 2 , ( ) ) , ( 3 , ( ) ) ] , 1 ) ;
217
+ q. queue ( 5 , ( ) , vec ! [ ( 4 , ( ) ) , ( 3 , ( ) ) ] , 1 ) ;
201
218
q. queue_finished ( ) ;
202
219
203
220
assert_eq ! ( q. dequeue( ) , Some ( ( 1 , ( ) ) ) ) ;
@@ -214,4 +231,29 @@ mod test {
214
231
q. finish ( & 4 , & ( ) ) ;
215
232
assert_eq ! ( q. dequeue( ) , Some ( ( 5 , ( ) ) ) ) ;
216
233
}
234
+
235
+ #[ test]
236
+ fn sort_by_highest_cost ( ) {
237
+ let mut q = DependencyQueue :: new ( ) ;
238
+
239
+ q. queue ( 1 , ( ) , vec ! [ ] , 1 ) ;
240
+ q. queue ( 2 , ( ) , vec ! [ ( 1 , ( ) ) ] , 1 ) ;
241
+ q. queue ( 3 , ( ) , vec ! [ ] , 4 ) ;
242
+ q. queue ( 4 , ( ) , vec ! [ ( 2 , ( ) ) , ( 3 , ( ) ) ] , 1 ) ;
243
+ q. queue_finished ( ) ;
244
+
245
+ assert_eq ! ( q. dequeue( ) , Some ( ( 3 , ( ) ) ) ) ;
246
+ assert_eq ! ( q. dequeue( ) , Some ( ( 1 , ( ) ) ) ) ;
247
+ assert_eq ! ( q. dequeue( ) , None ) ;
248
+ q. finish ( & 3 , & ( ) ) ;
249
+ assert_eq ! ( q. dequeue( ) , None ) ;
250
+ q. finish ( & 1 , & ( ) ) ;
251
+ assert_eq ! ( q. dequeue( ) , Some ( ( 2 , ( ) ) ) ) ;
252
+ assert_eq ! ( q. dequeue( ) , None ) ;
253
+ q. finish ( & 2 , & ( ) ) ;
254
+ assert_eq ! ( q. dequeue( ) , Some ( ( 4 , ( ) ) ) ) ;
255
+ assert_eq ! ( q. dequeue( ) , None ) ;
256
+ q. finish ( & 4 , & ( ) ) ;
257
+ assert_eq ! ( q. dequeue( ) , None ) ;
258
+ }
217
259
}
0 commit comments