@@ -34,6 +34,9 @@ pub struct DependencyQueue<K: Eq + Hash, V> {
34
34
/// The packages which are currently being built, waiting for a call to
35
35
/// `finish`.
36
36
pending : HashSet < K > ,
37
+
38
+ /// Topological depth of each key
39
+ depth : HashMap < K , usize > ,
37
40
}
38
41
39
42
/// Indication of the freshness of a package.
@@ -66,6 +69,7 @@ impl<K: Hash + Eq + Clone, V> DependencyQueue<K, V> {
66
69
reverse_dep_map : HashMap :: new ( ) ,
67
70
dirty : HashSet :: new ( ) ,
68
71
pending : HashSet :: new ( ) ,
72
+ depth : HashMap :: new ( ) ,
69
73
}
70
74
}
71
75
@@ -97,14 +101,60 @@ impl<K: Hash + Eq + Clone, V> DependencyQueue<K, V> {
97
101
& mut slot. insert ( ( my_dependencies, value) ) . 1
98
102
}
99
103
104
+ /// All nodes have been added, calculate some internal metadata and prepare
105
+ /// for `dequeue`.
106
+ pub fn queue_finished ( & mut self ) {
107
+ for key in self . dep_map . keys ( ) {
108
+ depth ( key, & self . reverse_dep_map , & mut self . depth ) ;
109
+ }
110
+
111
+ fn depth < K : Hash + Eq + Clone > (
112
+ key : & K ,
113
+ map : & HashMap < K , HashSet < K > > ,
114
+ results : & mut HashMap < K , usize > ,
115
+ ) -> usize {
116
+ const IN_PROGRESS : usize = !0 ;
117
+
118
+ if let Some ( & depth) = results. get ( key) {
119
+ assert_ne ! ( depth, IN_PROGRESS , "cycle in DependencyQueue" ) ;
120
+ return depth;
121
+ }
122
+
123
+ results. insert ( key. clone ( ) , IN_PROGRESS ) ;
124
+
125
+ let depth = 1 + map. get ( & key)
126
+ . into_iter ( )
127
+ . flat_map ( |it| it)
128
+ . map ( |dep| depth ( dep, map, results) )
129
+ . max ( )
130
+ . unwrap_or ( 0 ) ;
131
+
132
+ * results. get_mut ( key) . unwrap ( ) = depth;
133
+
134
+ depth
135
+ }
136
+ }
137
+
100
138
/// Dequeues a package that is ready to be built.
101
139
///
102
140
/// A package is ready to be built when it has 0 un-built dependencies. If
103
141
/// `None` is returned then no packages are ready to be built.
104
142
pub fn dequeue ( & mut self ) -> Option < ( Freshness , K , V ) > {
105
- let key = match self . dep_map . iter ( )
106
- . find ( |& ( _, & ( ref deps, _) ) | deps. is_empty ( ) )
107
- . map ( |( key, _) | key. clone ( ) ) {
143
+ // Look at all our crates and find everything that's ready to build (no
144
+ // deps). After we've got that candidate set select the one which has
145
+ // the maximum depth in the dependency graph. This way we should
146
+ // hopefully keep CPUs hottest the longest by ensuring that long
147
+ // dependency chains are scheduled early on in the build process and the
148
+ // leafs higher in the tree can fill in the cracks later.
149
+ //
150
+ // TODO: it'd be best here to throw in a heuristic of crate size as
151
+ // well. For example how long did this crate historically take to
152
+ // compile? How large is its source code? etc.
153
+ let next = self . dep_map . iter ( )
154
+ . filter ( |& ( _, & ( ref deps, _) ) | deps. is_empty ( ) )
155
+ . map ( |( key, _) | key. clone ( ) )
156
+ . max_by_key ( |k| self . depth [ k] ) ;
157
+ let key = match next {
108
158
Some ( key) => key,
109
159
None => return None
110
160
} ;
@@ -142,3 +192,34 @@ impl<K: Hash + Eq + Clone, V> DependencyQueue<K, V> {
142
192
}
143
193
}
144
194
}
195
+
196
+ #[ cfg( test) ]
197
+ mod test {
198
+ use super :: { DependencyQueue , Freshness } ;
199
+
200
+ #[ test]
201
+ fn deep_first ( ) {
202
+ let mut q = DependencyQueue :: new ( ) ;
203
+
204
+ q. queue ( Freshness :: Fresh , 1 , ( ) , & [ ] ) ;
205
+ q. queue ( Freshness :: Fresh , 2 , ( ) , & [ 1 ] ) ;
206
+ q. queue ( Freshness :: Fresh , 3 , ( ) , & [ ] ) ;
207
+ q. queue ( Freshness :: Fresh , 4 , ( ) , & [ 2 , 3 ] ) ;
208
+ q. queue ( Freshness :: Fresh , 5 , ( ) , & [ 4 , 3 ] ) ;
209
+ q. queue_finished ( ) ;
210
+
211
+ assert_eq ! ( q. dequeue( ) , Some ( ( Freshness :: Fresh , 1 , ( ) ) ) ) ;
212
+ assert_eq ! ( q. dequeue( ) , Some ( ( Freshness :: Fresh , 3 , ( ) ) ) ) ;
213
+ assert_eq ! ( q. dequeue( ) , None ) ;
214
+ q. finish ( & 3 , Freshness :: Fresh ) ;
215
+ assert_eq ! ( q. dequeue( ) , None ) ;
216
+ q. finish ( & 1 , Freshness :: Fresh ) ;
217
+ assert_eq ! ( q. dequeue( ) , Some ( ( Freshness :: Fresh , 2 , ( ) ) ) ) ;
218
+ assert_eq ! ( q. dequeue( ) , None ) ;
219
+ q. finish ( & 2 , Freshness :: Fresh ) ;
220
+ assert_eq ! ( q. dequeue( ) , Some ( ( Freshness :: Fresh , 4 , ( ) ) ) ) ;
221
+ assert_eq ! ( q. dequeue( ) , None ) ;
222
+ q. finish ( & 4 , Freshness :: Fresh ) ;
223
+ assert_eq ! ( q. dequeue( ) , Some ( ( Freshness :: Fresh , 5 , ( ) ) ) ) ;
224
+ }
225
+ }
0 commit comments