src/Pure/System/host.scala
author wenzelm
Mon, 11 Sep 2023 19:30:48 +0200
changeset 78659 b5f3d1051b13
parent 78435 a623cb346b4a
child 78837 f97e23eaa628
permissions -rw-r--r--
tuned;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
77475
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
     1
/*  Title:      Pure/System/host.scala
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
     3
77477
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
     4
Information about compute hosts, including NUMA: Non-Uniform Memory Access of
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
     5
separate CPU nodes.
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
     6
77478
e50cad69cbe7 tuned comments;
wenzelm
parents: 77477
diff changeset
     7
See also https://www.open-mpi.org/projects/hwloc --- notably "lstopo" or
e50cad69cbe7 tuned comments;
wenzelm
parents: 77477
diff changeset
     8
"hwloc-ls" (e.g. via Ubuntu package "hwloc").
77475
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
     9
*/
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    10
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    11
package isabelle
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    12
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    13
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    14
object Host {
77549
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    15
  /* process policy via numactl tool */
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    16
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    17
  def numactl(node: Int): String = "numactl -m" + node + " -N" + node
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    18
  def numactl_ok(node: Int): Boolean = Isabelle_System.bash(numactl(node) + " true").ok
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    19
78435
a623cb346b4a more flexible Build.Engine.process_options: e.g. to manipulate "process_policy" for ML process;
wenzelm
parents: 78396
diff changeset
    20
  def process_policy_options(options: Options, numa_node: Option[Int]): Options =
77549
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    21
    numa_node match {
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    22
      case None => options
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    23
      case Some(node) =>
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    24
        options.string("process_policy") = if (numactl_ok(node)) numactl(node) else ""
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    25
    }
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    26
6339c3a3720b tuned structure;
wenzelm
parents: 77548
diff changeset
    27
77475
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    28
  /* allocated resources */
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    29
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    30
  object Node_Info { def none: Node_Info = Node_Info("", None) }
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
    31
77526
e3ed231fa214 tuned output;
wenzelm
parents: 77525
diff changeset
    32
  sealed case class Node_Info(hostname: String, numa_node: Option[Int]) {
e3ed231fa214 tuned output;
wenzelm
parents: 77525
diff changeset
    33
    override def toString: String =
e3ed231fa214 tuned output;
wenzelm
parents: 77525
diff changeset
    34
      hostname + if_proper(numa_node, "/" + numa_node.get.toString)
e3ed231fa214 tuned output;
wenzelm
parents: 77525
diff changeset
    35
  }
77476
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
    36
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
    37
77477
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    38
  /* available NUMA nodes */
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    39
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    40
  private val numa_info_linux: Path = Path.explode("/sys/devices/system/node/online")
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    41
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    42
  def numa_nodes(enabled: Boolean = true, ssh: SSH.System = SSH.Local): List[Int] = {
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    43
    val Single = """^(\d+)$""".r
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    44
    val Multiple = """^(\d+)-(\d+)$""".r
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    45
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    46
    def parse(s: String): List[Int] =
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    47
      s match {
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    48
        case Single(Value.Int(i)) => List(i)
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    49
        case Multiple(Value.Int(i), Value.Int(j)) => (i to j).toList
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    50
        case _ => error("Cannot parse CPU NUMA node specification: " + quote(s))
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    51
      }
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    52
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    53
    val numa_info = if (ssh.isabelle_platform.is_linux) Some(numa_info_linux) else None
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    54
    for {
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    55
      path <- numa_info.toList
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    56
      if enabled && ssh.is_file(path)
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    57
      s <- space_explode(',', ssh.read(path).trim)
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    58
      n <- parse(s)
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    59
    } yield n
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    60
  }
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    61
77548
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    62
  def numa_node0(): Option[Int] =
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    63
    try {
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    64
      numa_nodes() match {
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    65
        case ns if ns.length >= 2 && numactl_ok(ns.head) => Some(ns.head)
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    66
        case _ => None
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    67
      }
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    68
    }
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    69
    catch { case ERROR(_) => None }
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    70
28b94fe1c00f clarified signature;
wenzelm
parents: 77547
diff changeset
    71
77477
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    72
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    73
  /* shuffling of NUMA nodes */
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    74
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    75
  def numa_check(progress: Progress, enabled: Boolean): Boolean = {
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    76
    def warning =
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    77
      numa_nodes() match {
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    78
        case ns if ns.length < 2 => Some("no NUMA nodes available")
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    79
        case ns if !numactl_ok(ns.head) => Some("bad numactl tool")
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    80
        case _ => None
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    81
      }
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    82
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    83
    enabled &&
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    84
      (warning match {
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    85
        case Some(s) =>
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    86
          progress.echo_warning("Shuffling of NUMA CPU nodes is disabled: " + s)
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    87
          false
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    88
        case _ => true
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    89
      })
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    90
  }
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    91
f376aebca9c1 clarified modules;
wenzelm
parents: 77476
diff changeset
    92
77476
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
    93
  /* SQL data model */
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
    94
78396
7853d9072d1b renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents: 78356
diff changeset
    95
  object private_data extends SQL.Data("isabelle_host") {
78174
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
    96
    val database: Path = Path.explode("$ISABELLE_HOME_USER/host.db")
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
    97
78187
2df0f3604a67 clarified signature: more explicit class SQL.Data;
wenzelm
parents: 78174
diff changeset
    98
    override lazy val tables = SQL.Tables(Node_Info.table)
77476
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
    99
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
   100
    object Node_Info {
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
   101
      val hostname = SQL.Column.string("hostname").make_primary_key
77525
de6fb423fd4b clarified database content: store actual value instead of index;
wenzelm
parents: 77483
diff changeset
   102
      val numa_next = SQL.Column.int("numa_next")
77476
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
   103
78266
d8c99a497502 clarified signature;
wenzelm
parents: 78187
diff changeset
   104
      val table = make_table(List(hostname, numa_next), name = "node_info")
77476
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
   105
    }
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
   106
77525
de6fb423fd4b clarified database content: store actual value instead of index;
wenzelm
parents: 77483
diff changeset
   107
    def read_numa_next(db: SQL.Database, hostname: String): Int =
77552
080422b3d914 clarified signature: reduce boilerplate;
wenzelm
parents: 77550
diff changeset
   108
      db.execute_query_statementO[Int](
77525
de6fb423fd4b clarified database content: store actual value instead of index;
wenzelm
parents: 77483
diff changeset
   109
        Node_Info.table.select(List(Node_Info.numa_next),
77552
080422b3d914 clarified signature: reduce boilerplate;
wenzelm
parents: 77550
diff changeset
   110
          sql = Node_Info.hostname.where_equal(hostname)),
080422b3d914 clarified signature: reduce boilerplate;
wenzelm
parents: 77550
diff changeset
   111
        res => res.int(Node_Info.numa_next)
080422b3d914 clarified signature: reduce boilerplate;
wenzelm
parents: 77550
diff changeset
   112
      ).getOrElse(0)
77476
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
   113
78174
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   114
    def write_numa_next(db: SQL.Database, hostname: String, numa_next: Int): Unit = {
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   115
      db.execute_statement(Node_Info.table.delete(sql = Node_Info.hostname.where_equal(hostname)))
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   116
      db.execute_statement(Node_Info.table.insert(), body =
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   117
        { stmt =>
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   118
          stmt.string(1) = hostname
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   119
          stmt.int(2) = numa_next
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   120
        })
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   121
    }
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   122
  }
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   123
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   124
  def next_numa_node(
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   125
    db: SQL.Database,
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   126
    hostname: String,
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   127
    available_nodes: List[Int],
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   128
    used_nodes: => Set[Int]
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   129
  ): Option[Int] =
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   130
    if (available_nodes.isEmpty) None
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   131
    else {
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   132
      val available = available_nodes.zipWithIndex
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   133
      val used = used_nodes
78396
7853d9072d1b renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents: 78356
diff changeset
   134
      private_data.transaction_lock(db, create = true, label = "Host.next_numa_node") {
7853d9072d1b renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents: 78356
diff changeset
   135
        val numa_next = private_data.read_numa_next(db, hostname)
78174
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   136
        val numa_index = available.collectFirst({ case (n, i) if n == numa_next => i }).getOrElse(0)
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   137
        val candidates = available.drop(numa_index) ::: available.take(numa_index)
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   138
        val (n, i) =
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   139
          candidates.find({ case (n, i) => i == numa_index && !used(n) }) orElse
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   140
          candidates.find({ case (n, _) => !used(n) }) getOrElse candidates.head
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   141
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   142
        val numa_next1 = available_nodes((i + 1) % available_nodes.length)
78396
7853d9072d1b renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents: 78356
diff changeset
   143
        if (numa_next != numa_next1) private_data.write_numa_next(db, hostname, numa_next1)
78174
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   144
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   145
        Some(n)
77476
5f6f567a2661 clarified modules;
wenzelm
parents: 77475
diff changeset
   146
      }
78174
cc0bd66eb498 separate host.db for independent db.transaction_lock;
wenzelm
parents: 77552
diff changeset
   147
    }
77475
3bc611c80346 clarified modules;
wenzelm
parents:
diff changeset
   148
}