]> git.proxmox.com Git - cargo.git/commitdiff
sort the pending queue according to cost/priority
authorRémy Rakic <remy.rakic+github@gmail.com>
Mon, 29 Aug 2022 14:24:17 +0000 (16:24 +0200)
committerRémy Rakic <remy.rakic+github@gmail.com>
Fri, 2 Sep 2022 15:59:09 +0000 (17:59 +0200)
If multiple pieces of work are waiting in the pending queue, we can sort it according to
their priorities: higher priorities should be scheduled sooner.

They are more often than not wider than pure chains, and this should create more parallelism
opportunities earlier in the pipeline: a high priority piece of work represents more future
pieces of work down the line.

This is a scheduling tradeoff that behaves differently for each project, machine configuration,
amount of available parallelism at a given point in time, etc, but seems to help more often than
hinders, at low-core counts and with enough units of work to be done, so that there is jobserver
token contention where choosing a "better" piece of work to work on next is possible.

src/cargo/core/compiler/job_queue.rs
src/cargo/util/dependency_queue.rs

index 358b6258737fb887b2b240ea64416b9972ca0fb0..c3d6c13928d7f2622331fb2d45562e9003902d27 100644 (file)
@@ -583,11 +583,19 @@ impl<'cfg> DrainState<'cfg> {
             }
         }
 
+        // If multiple pieces of work are waiting in the pending queue, we can
+        // sort it according to their priorities: higher priorities should be
+        // scheduled sooner.
+        self.pending_queue
+            .sort_by_cached_key(|(unit, _)| self.queue.priority(unit));
+
         // Now that we've learned of all possible work that we can execute
         // try to spawn it so long as we've got a jobserver token which says
         // we're able to perform some parallel work.
+        // The `pending_queue` is sorted in ascending priority order, and we're
+        // removing the highest priority items from its end.
         while self.has_extra_tokens() && !self.pending_queue.is_empty() {
-            let (unit, job) = self.pending_queue.remove(0);
+            let (unit, job) = self.pending_queue.pop().unwrap();
             *self.counts.get_mut(&unit.pkg.package_id()).unwrap() -= 1;
             if !cx.bcx.build_config.build_plan {
                 // Print out some nice progress information.
index 5a3289eb7ee7cc91459dc184dd056d8979462210..fae5c22f3b04718948def413472c37e735593ff1 100644 (file)
@@ -170,6 +170,13 @@ impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
         self.dep_map.len()
     }
 
+    /// Returns the relative priority of a node. Higher priorities should be scheduled sooner.
+    /// Currently computed as the transitive cost of the given node: its own, plus the cost of its
+    /// reverse dependencies.
+    pub(crate) fn priority(&self, node: &N) -> usize {
+        self.priority[node]
+    }
+
     /// Indicate that something has finished.
     ///
     /// Calling this function indicates that the `node` has produced `edge`. All