[v1,2/2] usertools/cpu_layout: print out NUMA nodes

Message ID de03ee482c3623fd6d730c46a35d2a36ce43be49.1723634354.git.anatoly.burakov@intel.com (mailing list archive)
State Superseded
Delegated to: Thomas Monjalon
Headers
Series [v1,1/2] usertools/cpu_layout: update coding style |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS
ci/intel-Functional success Functional PASS
ci/github-robot: build success github build: passed
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-marvell-Functional success Functional Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-sample-apps-testing success Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS

Commit Message

Burakov, Anatoly Aug. 14, 2024, 11:19 a.m. UTC
In traditional NUMA case, NUMA nodes and physical sockets were used
interchangeably, but there are cases where there can be multiple NUMA
nodes per socket, as well as all CPU's being assigned NUMA node 0 even in
cases of multiple sockets. Use sysfs to print out NUMA information.

Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 usertools/cpu_layout.py | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)
  

Patch

diff --git a/usertools/cpu_layout.py b/usertools/cpu_layout.py
index 843b29a134..be89909464 100755
--- a/usertools/cpu_layout.py
+++ b/usertools/cpu_layout.py
@@ -4,6 +4,7 @@ 
 # Copyright(c) 2017 Cavium, Inc. All rights reserved.
 
 from typing import List, Set, Dict, Tuple
+import glob
 
 
 def _range_expand(rstr: str) -> List[int]:
@@ -26,11 +27,19 @@  def _read_sysfs(path: str) -> str:
         return fd.read().strip()
 
 
+def _read_numa_node(base: str) -> int:
+    node_glob = f"{base}/node*"
+    node_dirs = glob.glob(node_glob)
+    if not node_dirs:
+        return 0  # default to node 0
+    return int(node_dirs[0].split("node")[1])
+
+
 def _print_row(row: Tuple[str, ...], col_widths: List[int]) -> None:
     first, *rest = row
     w_first, *w_rest = col_widths
     first_end = " " * 4
-    rest_end = " " * 10
+    rest_end = " " * 4
 
     print(first.ljust(w_first), end=first_end)
     for cell, width in zip(rest, w_rest):
@@ -50,6 +59,7 @@  def _main() -> None:
     sockets_s: Set[int] = set()
     cores_s: Set[int] = set()
     core_map: Dict[Tuple[int, int], List[int]] = {}
+    numa_map: Dict[int, int] = {}
     base_path = "/sys/devices/system/cpu"
 
     cpus = _range_expand(_read_sysfs(f"{base_path}/online"))
@@ -58,12 +68,14 @@  def _main() -> None:
         lcore_base = f"{base_path}/cpu{cpu}"
         core = int(_read_sysfs(f"{lcore_base}/topology/core_id"))
         socket = int(_read_sysfs(f"{lcore_base}/topology/physical_package_id"))
+        node = _read_numa_node(lcore_base)
 
         cores_s.add(core)
         sockets_s.add(socket)
         key = (socket, core)
         core_map.setdefault(key, [])
         core_map[key].append(cpu)
+        numa_map[cpu] = node
 
     cores = sorted(cores_s)
     sockets = sorted(sockets_s)
@@ -73,24 +85,37 @@  def _main() -> None:
 
     print("cores = ", cores)
     print("sockets = ", sockets)
+    print("numa = ", sorted(set(numa_map.values())))
     print()
 
-    # Core, [Socket, Socket, ...]
-    heading_strs = "", *[f"Socket {s}" for s in sockets]
+    # Core, [NUMA, Socket, NUMA, Socket, ...]
+    heading_strs = "", *[v for s in sockets for v in ("", f"Socket {s}")]
     sep_strs = tuple("-" * len(hstr) for hstr in heading_strs)
     rows: List[Tuple[str, ...]] = []
 
+    prev_numa = None
     for c in cores:
         # Core,
         row: Tuple[str, ...] = (f"Core {c}",)
 
-        # [lcores, lcores, ...]
+        # assume NUMA changes symmetrically
+        first_lcore = core_map[(0, c)][0]
+        cur_numa = numa_map[first_lcore]
+        numa_changed = prev_numa != cur_numa
+        prev_numa = cur_numa
+
+        # [NUMA, lcores, NUMA, lcores, ...]
         for s in sockets:
             try:
                 lcores = core_map[(s, c)]
+                numa = numa_map[lcores[0]]
+                if numa_changed:
+                    row += (f"NUMA {numa}",)
+                else:
+                    row += ("",)
                 row += (f"{lcores}",)
             except KeyError:
-                row += ("",)
+                row += ("", "")
         rows += [row]
 
     # find max widths for each column, including header and rows