clarified build heuristics parameters;
authorFabian Huch <huch@in.tum.de>
Wed, 06 Dec 2023 17:55:30 +0100
changeset 79180 229f49204603
parent 79179 7ed43417770f
child 79181 9d6d559c9fde
clarified build heuristics parameters;
src/Pure/Tools/build_schedule.scala
--- a/src/Pure/Tools/build_schedule.scala	Wed Dec 06 17:44:51 2023 +0100
+++ b/src/Pure/Tools/build_schedule.scala	Wed Dec 06 17:55:30 2023 +0100
@@ -611,34 +611,52 @@
 
 
   object Path_Time_Heuristic {
-    sealed trait Criterion
-    case class Absolute_Time(time: Time) extends Criterion {
+    sealed trait Critical_Criterion
+    case class Absolute_Time(time: Time) extends Critical_Criterion {
       override def toString: String = "absolute time (" + time.message_hms + ")"
     }
-    case class Relative_Time(factor: Double) extends Criterion {
+    case class Relative_Time(factor: Double) extends Critical_Criterion {
       override def toString: String = "relative time (" + factor + ")"
     }
 
-    sealed trait Strategy
-    case class Fixed_Thread(threads: Int) extends Strategy {
+    sealed trait Parallel_Strategy
+    case class Fixed_Thread(threads: Int) extends Parallel_Strategy {
       override def toString: String = "fixed threads (" + threads + ")"
     }
-    case class Time_Based_Threads(f: Time => Int) extends Strategy {
+    case class Time_Based_Threads(f: Time => Int) extends Parallel_Strategy {
       override def toString: String = "time based threads"
     }
+
+    sealed trait Host_Criterion
+    case object Critical_Nodes extends Host_Criterion {
+      override def toString: String = "per critical node"
+    }
+    case class Fixed_Fraction(fraction: Double) extends Host_Criterion {
+      override def toString: String = "fixed fraction (" + fraction + ")"
+    }
+    case class Host_Speed(min_factor: Double) extends Host_Criterion {
+      override def toString: String = "host speed (" + min_factor + ")"
+    }
   }
 
   class Path_Time_Heuristic(
-    is_critical: Path_Time_Heuristic.Criterion,
-    parallel_threads: Path_Time_Heuristic.Strategy,
+    is_critical: Path_Time_Heuristic.Critical_Criterion,
+    parallel_threads: Path_Time_Heuristic.Parallel_Strategy,
+    host_criterion: Path_Time_Heuristic.Host_Criterion,
     timing_data: Timing_Data,
     sessions_structure: Sessions.Structure,
     max_threads_limit: Int = 8
   ) extends Path_Heuristic(timing_data, sessions_structure, max_threads_limit) {
     import Path_Time_Heuristic.*
 
-    override def toString: Node =
-      "path time heuristic (critical: " + is_critical + ", parallel: " + parallel_threads + ")"
+    override def toString: Node = {
+      val params =
+        List(
+          "critical: " + is_critical,
+          "parallel: " + parallel_threads,
+          "fast hosts: " + host_criterion)
+      "path time heuristic (" + params.mkString(", ") + ")"
+    }
 
     def next(state: Build_Process.State): List[Config] = {
       val resources = host_infos.available(state)
@@ -648,7 +666,10 @@
 
       val rev_ordered_hosts = ordered_hosts.reverse.map(_ -> max_threads)
 
-      val resources0 = host_infos.available(state.copy(running = Map.empty))
+      val available_nodes =
+        host_infos.available(state.copy(running = Map.empty))
+          .unused_nodes(max_threads)
+          .sortBy(node => host_infos.the_host(node))(host_infos.host_speeds).reverse
 
       def remaining_time(node: Node): (Node, Time) =
         state.running.get(node) match {
@@ -661,11 +682,9 @@
         }
 
       val max_parallel = parallel_paths(state.ready.map(_.name).map(remaining_time))
-      val fully_parallelizable = max_parallel <= resources0.unused_nodes(max_threads).length
-
       val next_sorted = state.next_ready.sortBy(max_time(_).ms).reverse
 
-      if (fully_parallelizable) {
+      if (max_parallel <= available_nodes.length) {
         val all_tasks = next_sorted.map(task => (task, best_threads(task), best_threads(task)))
         resources.try_allocate_tasks(rev_ordered_hosts, all_tasks)._1
       }
@@ -694,7 +713,22 @@
 
         val max_critical_parallel =
           parallel_paths(critical_minimals.map(remaining_time), critical_nodes.contains)
-        val (critical_hosts, other_hosts) = rev_ordered_hosts.splitAt(max_critical_parallel)
+        val max_critical_hosts =
+          available_nodes.take(max_critical_parallel).map(_.hostname).distinct.length
+
+        val split =
+          this.host_criterion match {
+            case Critical_Nodes => max_critical_hosts
+            case Fixed_Fraction(fraction) =>
+              ((rev_ordered_hosts.length * fraction).ceil.toInt max 1) min max_critical_hosts
+            case Host_Speed(min_factor) =>
+              val best = rev_ordered_hosts.head._1.info.benchmark_score.get
+              val num_fast =
+                rev_ordered_hosts.count(_._1.info.benchmark_score.exists(_ >= best * min_factor))
+              num_fast min max_critical_hosts
+          }
+
+        val (critical_hosts, other_hosts) = rev_ordered_hosts.splitAt(split)
 
         val (configs1, resources1) = resources.try_allocate_tasks(critical_hosts, critical_tasks)
         val (configs2, _) = resources1.try_allocate_tasks(other_hosts, other_tasks)
@@ -903,12 +937,19 @@
             case time if time < Time.minutes(5) => 4
             case _ => 8
           }))
+      val machine_splits =
+        List(
+          Path_Time_Heuristic.Critical_Nodes,
+          Path_Time_Heuristic.Fixed_Fraction(0.3),
+          Path_Time_Heuristic.Host_Speed(0.9))
 
       val path_time_heuristics =
         for {
           is_critical <- is_criticals
           parallel <- parallel_threads
-        } yield Path_Time_Heuristic(is_critical, parallel, timing_data, sessions_structure)
+          machine_split <- machine_splits
+        } yield
+          Path_Time_Heuristic(is_critical, parallel, machine_split, timing_data, sessions_structure)
       val heuristics = Default_Heuristic(timing_data, context.build_options) :: path_time_heuristics
 
       new Meta_Heuristic(heuristics)