add information about static host resources such as available NUMA nodes, cpus, etc. (e.g., for offline build planning);
authorFabian Huch <huch@in.tum.de>
Wed, 18 Oct 2023 19:05:06 +0200
changeset 78838 4b014e6c1dfe
parent 78837 f97e23eaa628
child 78839 7799ec03b8bd
add information about static host resources such as available NUMA nodes, cpus, etc. (e.g., for offline build planning);
src/Pure/Concurrent/isabelle_thread.scala
src/Pure/System/host.scala
--- a/src/Pure/Concurrent/isabelle_thread.scala	Wed Oct 18 18:53:26 2023 +0200
+++ b/src/Pure/Concurrent/isabelle_thread.scala	Wed Oct 18 19:05:06 2023 +0200
@@ -74,7 +74,7 @@
 
   def max_threads(): Int = {
     val m = Value.Int.unapply(System.getProperty("isabelle.threads", "0")) getOrElse 0
-    if (m > 0) m else (Runtime.getRuntime.availableProcessors max 1) min 8
+    if (m > 0) m else (Host.num_cpus() max 1) min 8
   }
 
   lazy val pool: ThreadPoolExecutor = {
--- a/src/Pure/System/host.scala	Wed Oct 18 18:53:26 2023 +0200
+++ b/src/Pure/System/host.scala	Wed Oct 18 19:05:06 2023 +0200
@@ -71,7 +71,7 @@
   }
 
 
-  /* available NUMA nodes */
+  /* statically available resources */
 
   private val numa_info_linux: Path = Path.explode("/sys/devices/system/node/online")
 
@@ -99,6 +99,17 @@
     }
     catch { case ERROR(_) => None }
 
+  def num_cpus(): Int = Runtime.getRuntime.availableProcessors
+
+  object Info {
+    def gather(hostname: String, ssh: SSH.System = SSH.Local): Info =
+      Info(hostname, numa_nodes(ssh = ssh), num_cpus())
+  }
+
+  sealed case class Info(
+    hostname: String,
+    numa_nodes: List[Int],
+    num_cpus: Int)
 
 
   /* shuffling of NUMA nodes */
@@ -126,7 +137,7 @@
   object private_data extends SQL.Data("isabelle_host") {
     val database: Path = Path.explode("$ISABELLE_HOME_USER/host.db")
 
-    override lazy val tables = SQL.Tables(Node_Info.table)
+    override lazy val tables = SQL.Tables(Node_Info.table, Info.table)
 
     object Node_Info {
       val hostname = SQL.Column.string("hostname").make_primary_key
@@ -150,6 +161,35 @@
           stmt.int(2) = numa_next
         })
     }
+
+    object Info {
+      val hostname = SQL.Column.string("hostname").make_primary_key
+      val numa_info = SQL.Column.string("numa_info")
+      val num_cpus = SQL.Column.int("num_cpus")
+
+      val table =
+        make_table(List(hostname, numa_info, num_cpus), name = "info")
+    }
+
+    def write_info(db: SQL.Database, info: Info): Unit = {
+      db.execute_statement(Info.table.delete(sql = Info.hostname.where_equal(info.hostname)))
+      db.execute_statement(Info.table.insert(), body =
+        { stmt =>
+          stmt.string(1) = info.hostname
+          stmt.string(2) = info.numa_nodes.mkString(",")
+          stmt.int(3) = info.num_cpus
+        })
+    }
+
+    def read_info(db: SQL.Database, hostname: String): Option[Host.Info] =
+      db.execute_query_statementO[Host.Info](
+        Info.table.select(Info.table.columns.tail, sql = Info.hostname.where_equal(hostname)),
+        { res =>
+          val numa_info = res.string(Info.numa_info)
+          val num_cpus = res.int(Info.num_cpus)
+
+          Host.Info(hostname, parse_numa_info(numa_info), num_cpus)
+        })
   }
 
   def next_numa_node(
@@ -176,4 +216,13 @@
         Some(n)
       }
     }
+
+  def write_info(db: SQL.Database, info: Info): Unit =
+    private_data.transaction_lock(db, create = true, label = "Host.write_info") {
+      private_data.write_info(db, info)
+    }
+  def read_info(db: SQL.Database, hostname: String): Option[Host.Info] =
+    private_data.transaction_lock(db, create = true, label = "Host.read_info") {
+      private_data.read_info(db, hostname)
+    }
 }