better build time interpolation: model with Amdahl's law where applicable;
authorFabian Huch <huch@in.tum.de>
Thu, 23 Nov 2023 11:30:43 +0100
changeset 79023 abc27a824419
parent 79022 e4fc535d4d2f
child 79024 d989e00c3ff5
better build time interpolation: model with Amdahl's law where applicable;
src/Pure/Tools/build_schedule.scala
--- a/src/Pure/Tools/build_schedule.scala	Wed Nov 22 18:10:21 2023 +0100
+++ b/src/Pure/Tools/build_schedule.scala	Thu Nov 23 11:30:43 2023 +0100
@@ -67,18 +67,36 @@
             data.by_threads.toList.map((threads, data) =>
               threads -> Timing_Data.median_time(unify_hosts(data))).sortBy(_._1)
 
-          val ((threads0, time0), (threads1, time1)) =
-            if (entries.head._1 <= threads && threads <= entries.last._1) {
-              val split = entries.partition(_._1 <= threads)
-              (split._1.last, split._2.head)
-            } else {
-              val piece = if (threads < entries.head._1) entries.take(2) else entries.takeRight(2)
-              (piece.head, piece.last)
-            }
+          val monotone_prefix =
+            entries.zip(entries.sortBy(_._2.ms).reverse).takeWhile((e1, e2) => e1 == e2).map(_._1)
+
+          if (monotone_prefix.length > 1 && threads <= monotone_prefix.last._1) {
+            // Model with Amdahl's law
+            val t_p =
+              Timing_Data.median_time(for {
+                (n, t0) <- monotone_prefix
+                (m, t1) <- monotone_prefix
+                if m != n
+              } yield (t0 - t1).scale(n.toDouble * m / (m - n)))
+            val t_c =
+              Timing_Data.median_time(for ((n, t) <- monotone_prefix) yield t - t_p.scale(1.0 / n))
 
-          val a = (time1 - time0).scale(1.0 / (threads1 - threads0))
-          val b = time0 - a.scale(threads0)
-          Time.ms((a.scale(threads) + b).ms max 0)
+            t_c + t_p.scale(1.0 / threads)
+          } else {
+            // Piecewise linear
+            val ((threads0, time0), (threads1, time1)) =
+              if (entries.head._1 <= threads && threads <= entries.last._1) {
+                val split = entries.partition(_._1 <= threads)
+                (split._1.last, split._2.head)
+              } else {
+                val piece = if (threads < entries.head._1) entries.take(2) else entries.takeRight(2)
+                (piece.head, piece.last)
+              }
+
+            val a = (time1 - time0).scale(1.0 / (threads1 - threads0))
+            val b = time0 - a.scale(threads0)
+            Time.ms((a.scale(threads) + b).ms max 0)
+          }
         }
       if (approximations.isEmpty) None else Some(Timing_Data.mean_time(approximations))
     }