proper parallel paths for timing heuristic;
authorFabian Huch <huch@in.tum.de>
Mon, 13 Nov 2023 17:00:13 +0100
changeset 78972 7a39f151e9a7
parent 78971 f930d24f1548
child 78973 d91e131840a0
proper parallel paths for timing heuristic;
src/Pure/Tools/build_schedule.scala
--- a/src/Pure/Tools/build_schedule.scala	Mon Nov 13 16:16:52 2023 +0100
+++ b/src/Pure/Tools/build_schedule.scala	Mon Nov 13 17:00:13 2023 +0100
@@ -359,10 +359,11 @@
     val maximals_preds =
       all_maximals.map(node => node -> build_graph.all_preds(List(node)).toSet).toMap
 
-    val remaining_time = build_graph.node_height(timing_data.best_time(_).ms)
+    val best_times = build_graph.keys.map(node => node -> timing_data.best_time(node)).toMap
+    val remaining_time = build_graph.node_height(best_times(_).ms)
 
     def elapsed_times(node: Node): Map[Node, Long] =
-      build_graph.reachable_length(timing_data.best_time(_).ms, build_graph.imm_succs, List(node))
+      build_graph.reachable_length(best_times(_).ms, build_graph.imm_succs, List(node))
 
     def path_times(node: Node): Map[Node, Long] = {
       val maximals = all_maximals.intersect(build_graph.all_succs(List(node)).toSet)
@@ -379,6 +380,33 @@
       build_graph.keys.map(node =>
         node -> path_times(node).filter((_, time) => is_critical(time)).keySet).toMap
 
+    def parallel_paths(minimals: Set[Node], pred: Node => Boolean = _ => true): Int = {
+      def start(node: Node): (Node, Time) = node -> best_times(node)
+
+      def pass_time(elapsed: Time)(node: Node, time: Time): (Node, Time) =
+        node -> (time - elapsed)
+
+      def parallel_paths(running: Map[Node, Time]): (Int, Map[Node, Time]) =
+        if (running.isEmpty) (0, running)
+        else {
+          def get_next(node: Node): List[Node] =
+            build_graph.imm_succs(node).filter(pred).filter(
+              build_graph.imm_preds(_).intersect(running.keySet).isEmpty).toList
+
+          val (next, elapsed) = running.minBy(_._2.ms)
+          val (remaining, finished) =
+            running.toList.map(pass_time(elapsed)).partition(_._2 > Time.zero)
+
+          val running1 =
+            remaining.map(pass_time(elapsed)).toMap ++
+              finished.map(_._1).flatMap(get_next).map(start)
+          val (res, running2) = parallel_paths(running1)
+          (res max running1.size, running2)
+        }
+
+      parallel_paths(minimals.map(start).toMap)._1
+    }
+
 
     /* scheduling */
 
@@ -403,9 +431,6 @@
         val critical_nodes = state.ready.toSet.flatMap(task => critical_path_nodes(task.name))
         def is_critical(node: Node): Boolean = critical_nodes.contains(node)
 
-        def parallel_paths(node: Node): Int =
-          build_graph.imm_succs(node).filter(is_critical).map(parallel_paths(_) max 1).sum max 1
-
         val (critical, other) =
           state.ready.sortBy(task => remaining_time(task.name)).reverse.partition(task =>
             is_critical(task.name))
@@ -415,7 +440,7 @@
 
         val (critical_hosts, other_hosts) =
           host_infos.hosts.sorted(host_infos.host_speeds).reverse.map(_ -> max_threads).splitAt(
-            critical.map(_.name).map(parallel_paths).sum)
+            parallel_paths(critical.map(_.name).toSet, is_critical))
 
         val (configs1, resources1) = resources.try_allocate_tasks(critical_hosts, critical_tasks)
         val (configs2, _) = resources1.try_allocate_tasks(other_hosts, other_tasks)