CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
290 lines
8.2 KiB
Python
290 lines
8.2 KiB
Python
# Copyright Advanced Micro Devices, Inc., or its affiliates.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Get host/gpu specs."""
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import shutil
|
|
import socket
|
|
import subprocess
|
|
from dataclasses import dataclass
|
|
from pathlib import Path as path
|
|
from textwrap import dedent
|
|
|
|
import yaml
|
|
|
|
|
|
@dataclass
|
|
class MachineSpecs(yaml.YAMLObject):
|
|
yaml_tag = "!rrperfMachineSpecs"
|
|
|
|
hostname: str
|
|
cpu: str
|
|
kernel: str
|
|
ram: str
|
|
distro: str
|
|
rocmversion: str
|
|
vbios: str
|
|
gpuid: str
|
|
deviceinfo: str
|
|
vram: str
|
|
perflevel: str
|
|
mclk: str
|
|
sclk: str
|
|
|
|
def __init__(
|
|
self,
|
|
hostname="",
|
|
cpu="",
|
|
kernel="",
|
|
ram="",
|
|
distro="",
|
|
rocmversion="",
|
|
vbios="",
|
|
gpuid="",
|
|
deviceinfo="",
|
|
vram="",
|
|
perflevel="",
|
|
mclk="",
|
|
sclk="",
|
|
):
|
|
self.hostname = hostname
|
|
self.cpu = cpu
|
|
self.kernel = kernel
|
|
self.ram = ram
|
|
self.distro = distro
|
|
self.rocmversion = rocmversion
|
|
self.vbios = vbios
|
|
self.gpuid = gpuid
|
|
self.deviceinfo = deviceinfo
|
|
self.vram = vram
|
|
self.perflevel = perflevel
|
|
self.mclk = mclk
|
|
self.sclk = sclk
|
|
|
|
def __str__(self):
|
|
return yaml.dump(self)
|
|
|
|
def __hash__(self):
|
|
return hash(str(self))
|
|
|
|
def __lt__(self, other):
|
|
return str(self) < str(other)
|
|
|
|
@classmethod
|
|
def from_yaml(cls, loader, node):
|
|
values = loader.construct_mapping(node, deep=True)
|
|
return cls(
|
|
values.get("hostname", ""),
|
|
values.get("cpu", ""),
|
|
values.get("kernel", ""),
|
|
values.get("ram", ""),
|
|
values.get("distro", ""),
|
|
values.get("rocmversion", ""),
|
|
values.get("vbios", ""),
|
|
values.get("gpuid", ""),
|
|
values.get("deviceinfo", ""),
|
|
values.get("vram", ""),
|
|
values.get("perflevel", ""),
|
|
values.get("mclk", ""),
|
|
values.get("sclk", ""),
|
|
)
|
|
|
|
def pretty_string(self):
|
|
return dedent(f"""\
|
|
Host info:
|
|
hostname: {self.hostname}
|
|
cpu info: {self.cpu}
|
|
ram: {self.ram}
|
|
distro: {self.distro}
|
|
kernel version: {self.kernel}
|
|
rocm version: {self.rocmversion}
|
|
Device info:
|
|
device: {self.deviceinfo}
|
|
vbios version: {self.vbios}
|
|
vram: {self.vram}
|
|
performance level: {self.perflevel}
|
|
system clock: {self.sclk}
|
|
memory clock: {self.mclk}
|
|
""")
|
|
|
|
|
|
def search(pattern, string):
|
|
m = re.search(pattern, string, re.MULTILINE)
|
|
if m is not None:
|
|
return m.group(1)
|
|
return None
|
|
|
|
|
|
def _run_amdsmi_json(cmd: list):
|
|
"""Run an amd-smi command that emits JSON and return the parsed object."""
|
|
try:
|
|
completed = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if completed.returncode != 0:
|
|
return None
|
|
return json.loads(completed.stdout.decode("utf-8", errors="replace"))
|
|
except (FileNotFoundError, json.JSONDecodeError, ValueError):
|
|
return None
|
|
|
|
|
|
def _gpu_entry(payload, devicenum: int):
|
|
"""Return the gpu_data entry for devicenum, if present."""
|
|
if not payload:
|
|
return None
|
|
gpu_data = payload.get("gpu_data")
|
|
if not gpu_data:
|
|
return None
|
|
for entry in gpu_data:
|
|
if entry.get("gpu") == devicenum:
|
|
return entry
|
|
return gpu_data[0]
|
|
|
|
|
|
def _clock_mhz(clock_entry):
|
|
"""Extract a clock string from an amd-smi clock block."""
|
|
if not isinstance(clock_entry, dict):
|
|
return None
|
|
clk = clock_entry.get("clk")
|
|
if isinstance(clk, dict):
|
|
value = clk.get("value")
|
|
if value is not None:
|
|
return f"{value}Mhz"
|
|
elif isinstance(clk, (int, float)):
|
|
return f"{clk}Mhz"
|
|
elif isinstance(clk, str) and clk not in ("", "N/A"):
|
|
return clk
|
|
return None
|
|
|
|
|
|
def get_amdsmi_specs(devicenum: int = 0, amd_smi_path: str = "amd-smi") -> dict:
|
|
"""
|
|
Collect per-device GPU specs using amd-smi structured JSON output.
|
|
|
|
Any field that cannot be resolved is returned as None.
|
|
"""
|
|
g = str(devicenum)
|
|
static = _run_amdsmi_json(
|
|
[amd_smi_path, "static", "-g", g, "--asic", "--vbios", "--json"]
|
|
)
|
|
metric = _run_amdsmi_json(
|
|
[
|
|
amd_smi_path,
|
|
"metric",
|
|
"-g",
|
|
g,
|
|
"--mem-usage",
|
|
"--clock",
|
|
"--perf-level",
|
|
"--json",
|
|
]
|
|
)
|
|
|
|
result = {
|
|
"vbios_version": None,
|
|
"gpuid": None,
|
|
"market_name": None,
|
|
"vram": None,
|
|
"performance_level": None,
|
|
"memory_clk": None,
|
|
"system_clk": None,
|
|
}
|
|
|
|
if static:
|
|
d = _gpu_entry(static, devicenum)
|
|
if d:
|
|
result["gpuid"] = d.get("asic", {}).get("device_id")
|
|
result["market_name"] = d.get("asic", {}).get("market_name")
|
|
vbios = d.get("ifwi") or d.get("vbios") or {}
|
|
result["vbios_version"] = vbios.get("part_number")
|
|
|
|
if metric:
|
|
d = _gpu_entry(metric, devicenum)
|
|
if d:
|
|
total = d.get("mem_usage", {}).get("total_vram", {}).get("value")
|
|
if total is not None:
|
|
result["vram"] = int(total) * 1024 * 1024
|
|
|
|
perf = d.get("perf_level")
|
|
if isinstance(perf, str):
|
|
result["performance_level"] = perf.split("_")[-1].lower()
|
|
|
|
clock = d.get("clock", {})
|
|
result["system_clk"] = _clock_mhz(clock.get("gfx_0"))
|
|
result["memory_clk"] = _clock_mhz(clock.get("mem_0"))
|
|
|
|
return result
|
|
|
|
|
|
def load_machine_specs(path):
|
|
if path.exists():
|
|
contents = path.read_text()
|
|
if contents.startswith(MachineSpecs.yaml_tag):
|
|
return yaml.load(contents, Loader=yaml.Loader)
|
|
return MachineSpecs()
|
|
|
|
|
|
def _gpu_spec_text(value, missing="no amd-smi"):
|
|
return missing if value is None else value
|
|
|
|
|
|
def get_machine_specs(devicenum, amd_smi_path="amd-smi"):
|
|
cpuinfo = path("/proc/cpuinfo").read_text()
|
|
meminfo = path("/proc/meminfo").read_text()
|
|
version = path("/proc/version").read_text()
|
|
os_release = path("/etc/os-release").read_text()
|
|
if os.path.isfile("/opt/rocm/.info/version-utils"):
|
|
rocm_info = path("/opt/rocm/.info/version-utils").read_text()
|
|
elif os.path.isfile("/opt/rocm/.info/version"):
|
|
rocm_info = path("/opt/rocm/.info/version").read_text()
|
|
else:
|
|
rocm_info = "rocm info not available"
|
|
|
|
amd_smi_available = shutil.which(amd_smi_path) is not None
|
|
amdsmi = get_amdsmi_specs(devicenum, amd_smi_path) if amd_smi_available else {}
|
|
|
|
missing = "no amd-smi"
|
|
|
|
# Use the NODE_NAME env var in CI.
|
|
hostname = os.environ.get("NODE_NAME")
|
|
if not hostname:
|
|
hostname = socket.gethostname()
|
|
cpu = search(r"^model name\s*: (.*?)$", cpuinfo)
|
|
kernel = search(r"version (\S*)", version)
|
|
ram = search(r"MemTotal:\s*(\S*)", meminfo)
|
|
distro = search(r'PRETTY_NAME="(.*?)"', os_release)
|
|
rocmversion = rocm_info.strip()
|
|
vbios = _gpu_spec_text(amdsmi.get("vbios_version"), missing) if amd_smi_available else missing
|
|
gpuid = _gpu_spec_text(amdsmi.get("gpuid"), missing) if amd_smi_available else missing
|
|
deviceinfo = _gpu_spec_text(amdsmi.get("market_name"), missing) if amd_smi_available else missing
|
|
vram = amdsmi.get("vram") if amd_smi_available else 0
|
|
perflevel = _gpu_spec_text(amdsmi.get("performance_level"), missing) if amd_smi_available else missing
|
|
mclk = _gpu_spec_text(amdsmi.get("memory_clk"), "") if amd_smi_available else ""
|
|
sclk = _gpu_spec_text(amdsmi.get("system_clk"), "") if amd_smi_available else ""
|
|
|
|
if ram is not None:
|
|
ram = "{:.2f} GiB".format(float(ram) / 1024**2)
|
|
if vram is not None:
|
|
vram = "{:.2f} GiB".format(float(vram) / 1024**3)
|
|
|
|
return MachineSpecs(
|
|
hostname,
|
|
cpu,
|
|
kernel,
|
|
ram,
|
|
distro,
|
|
rocmversion,
|
|
vbios,
|
|
gpuid,
|
|
deviceinfo,
|
|
vram,
|
|
perflevel,
|
|
mclk,
|
|
sclk,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(get_machine_specs(0))
|