Files
rocm-systems/src/utils/specs.py
T

305 строки
12 KiB
Python
Исходник Обычный вид История

2022-11-04 14:49:36 -05:00
"""Get host/gpu specs."""
2023-02-13 09:26:12 -06:00
##############################################################################bl
# MIT License
2023-02-13 14:50:24 -06:00
#
2024-01-24 17:11:39 -06:00
# Copyright (c) 2021 - 2024 Advanced Micro Devices, Inc. All Rights Reserved.
2023-02-13 14:50:24 -06:00
#
2022-11-04 14:49:36 -05:00
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
2023-02-13 14:50:24 -06:00
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
2023-02-13 14:50:24 -06:00
#
2022-11-04 14:49:36 -05:00
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2022-11-04 14:49:36 -05:00
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
2023-02-13 09:26:12 -06:00
##############################################################################el
2022-11-04 14:49:36 -05:00
import os
import re
2023-01-23 15:28:35 -06:00
import sys
2022-11-04 14:49:36 -05:00
import socket
import subprocess
2024-01-24 12:44:42 -06:00
import importlib
import logging
import pandas as pd
2022-11-04 14:49:36 -05:00
from datetime import datetime
2024-02-21 17:00:39 -06:00
from math import ceil
2022-11-04 14:49:36 -05:00
from dataclasses import dataclass
from pathlib import Path as path
from textwrap import dedent
2024-02-13 21:19:56 -05:00
from utils.utils import error, get_hbm_stack_num
2022-11-04 14:49:36 -05:00
VERSION_LOC = [
"version",
"version-dev",
"version-hip-libraries",
"version-hiprt",
"version-hiprt-devel",
"version-hip-sdk",
"version-libs",
"version-utils",
]
2024-02-16 15:34:28 -06:00
2022-11-04 14:49:36 -05:00
@dataclass
class MachineSpecs:
def __init__(self, args, sysinfo=None):
if not sysinfo is None:
self.gpu_arch = sysinfo.iloc[0]["gpu_arch"]
return
# read timestamp info
now = datetime.now()
local_now = now.astimezone()
local_tz = local_now.tzinfo
local_tzname = local_tz.tzname(local_now)
self.timestamp = now.strftime("%c") + " (" + local_tzname + ")"
# read rocminfo
rocminfo_full = run(["rocminfo"])
self._rocminfo = rocminfo_full.split("\n")
##########################################
## A. Machine Specs
##########################################
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()
self.hostname: str = socket.gethostname()
self.cpu_model: str = search(r"^model name\s*: (.*?)$", cpuinfo)
self.sbios: str = (
path("/sys/class/dmi/id/bios_vendor").read_text().strip()
+ path("/sys/class/dmi/id/bios_version").read_text().strip()
)
self.linux_kernel_version: str = search(r"version (\S*)", version)
self.amd_gpu_kernel_version: str = None
if self.amd_gpu_kernel_version is None: #TODO: Extract amdgpu kernel version
self.amd_gpu_kernel_version = ""
self.cpu_memory: str = search(r"MemTotal:\s*(\S*)", meminfo)
self.gpu_memory: str = None #TODO: Extract total gpu memory
if self.gpu_memory is None:
self.gpu_memory = ""
self.linux_distro: str = search(r'PRETTY_NAME="(.*?)"', os_release)
if self.linux_distro is None:
self.linux_distro = ""
self.rocm_version: str = get_rocm_ver().strip()
#FIXME: use device
self.vbios: str = search(
r"VBIOS version: (.*?)$", run(["rocm-smi", "-v"], exit_on_error=True)
)
self.compute_partition: str = search(
r"Compute Partition:\s*(\w+)", run(["rocm-smi", "--showcomputepartition"])
)
if self.compute_partition is None:
self.compute_partition = "NA"
self.memory_partition: str = search(
r"Memory Partition:\s*(\w+)", run(["rocm-smi", "--showmemorypartition"])
)
if self.memory_partition is None:
self.memory_partition = "NA"
##########################################
## B. SoC Specs
##########################################
self.gpu_arch: str = self.detect_arch()[0]
self.gpu_l1: str = None
self.gpu_l2: str = None
self.cu_per_gpu: str = None
self.simd_per_cu: str = None
self.se_per_gpu: str = None
self.wave_size: str = None
self.workgroup_max_size: str = None
self.max_sclk: str = None
self.max_mclk: str = None
self.cur_sclk: str = None
self.cur_mclk: str = None
self.max_waves_per_cu: str = None
self.gpu_model: str = None
self.L2Banks: str = None #NB: This only used in flatten_tcc_info_across_hbm_stacks()
self.lds_banks_per_cu: str = None
self.sqc_per_gpu: str = None
self.pipes_per_gpu: str = None
self.total_l2_chan: str = None
self.hbm_bw: str = None
# Load above SoC specs via module import
try:
soc_module = importlib.import_module('omniperf_soc.soc_'+ self.gpu_arch)
except ModuleNotFoundError as e:
error("Arch %s marked as supported, but couldn't find class implementation %s." % (self.gpu_arch, e))
soc_class = getattr(soc_module, self.gpu_arch+'_soc')
self._rocminfo = self._rocminfo[self.detect_arch()[1] + 1 :] # update rocminfo for target section
soc_obj = soc_class(args, self)
# Update arch specific specs
self.total_l2_chan: str = total_l2_banks(
self.gpu_model, int(self.L2Banks), self.memory_partition
)
self.hbm_bw: str = str(int(self.max_mclk) / 1000 * 32 * self.get_hbm_channels())
2022-11-04 14:49:36 -05:00
def detect_arch(self):
from omniperf_base import SUPPORTED_ARCHS
for idx1, linetext in enumerate(self._rocminfo):
gpu_arch = search(r"^\s*Name\s*:\s+ ([a-zA-Z0-9]+)\s*$", linetext)
if gpu_arch in SUPPORTED_ARCHS.keys():
break
if str(gpu_arch) in SUPPORTED_ARCHS.keys():
gpu_arch = str(gpu_arch)
break
if not gpu_arch in SUPPORTED_ARCHS.keys():
error("[profiling] Cannot find a supported arch in rocminfo")
else:
return (gpu_arch, idx1)
def get_hbm_channels(self):
hbmchannels = int(self.total_l2_chan)
if (
self.gpu_model.lower() == "mi300a_a0"
or self.gpu_model.lower() == "mi300a_a1"
) and self.memory_partition.lower() == "nps1":
# we have an extra 32 channels for the CCD
hbmchannels += 32
return hbmchannels
def get_class_members(self):
all_populated = True
data = {}
# dataclass uses an OrderedDict for member variables, ensuring order consistency
for attr_name in self.__dict__.keys():
if not attr_name.startswith("_"):
attr_value = getattr(self, attr_name)
if attr_value is None:
#TODO: use proper logging function when that's merged
logging.warning(f"WARNING: Incomplete class definition for {self.gpu_arch}. Expecting populated {attr_name} but detected None.")
all_populated = False
data[attr_name] = attr_value
if not all_populated:
error("Missing specs fields for %s" % self.gpu_arch)
return pd.DataFrame(data, index=[0])
2022-11-04 14:49:36 -05:00
def __str__(self):
return dedent(
f"""\
Host info:
Hostname: {self.hostname}
CPU Model: {self.cpu_model}
sbios: {self.sbios}
CPU Memory: {self.cpu_memory}
GPU Memory: {self.gpu_memory}
Linux Distro: {self.linux_distro}
Linux Kernel Version: {self.linux_kernel_version}
AMD GPU Kernel Version: {self.amd_gpu_kernel_version}
ROCm Version: {self.rocm_version}
2022-11-04 14:49:36 -05:00
Device info:
GPU Model: {self.gpu_model}
GPU Arch: {self.gpu_arch}
vbios: {self.vbios}
GPU L1: {self.gpu_l1} KB
GPU L2: {self.gpu_l2} KB
Max SCLK: {self.max_sclk} MHz
Max MCLK: {self.max_mclk} MHz
Cur SCLK: {self.cur_sclk} MHz
Cur MCLK: {self.cur_mclk} MHz
CU per GPU: {self.cu_per_gpu}
SIMD per CU: {self.simd_per_cu}
SE per GPU: {self.se_per_gpu}
Wave Size: {self.wave_size}
Workgroup Max Size: {self.workgroup_max_size}
Max Waves per CU: {self.max_waves_per_cu}
Total L2 Channels: {self.total_l2_chan}
LDS Banks per CU: {self.lds_banks_per_cu}
SQC per GPU: {self.sqc_per_gpu}
Pipes per GPU: {self.pipes_per_gpu}
HBM BW: {self.hbm_bw} MB/s
Compute Partition: {self.compute_partition}
Memory Partition: {self.memory_partition}
2022-11-04 14:49:36 -05:00
"""
)
def get_rocm_ver():
rocm_found = False
for itr in VERSION_LOC:
_path = os.path.join(os.getenv("ROCM_PATH", "/opt/rocm"), ".info", itr)
if os.path.exists(_path):
rocm_ver = path(_path).read_text()
rocm_found = True
break
if not rocm_found:
# check if ROCM_VER is supplied externally
ROCM_VER_USER = os.getenv("ROCM_VER")
if ROCM_VER_USER is not None:
logging.info(
"Overriding missing ROCm version detection with ROCM_VER = %s"
% ROCM_VER_USER
2024-02-26 11:41:39 -06:00
)
rocm_ver = ROCM_VER_USER
else:
_rocm_path = os.getenv("ROCM_PATH", "/opt/rocm")
error("Unable to detect a complete local ROCm installation.\nThe expected %s/.info/ versioning directory is missing. Please ensure you have valid ROCm installation." % _rocm_path)
return rocm_ver
2022-11-04 14:49:36 -05:00
2024-02-16 15:34:28 -06:00
def run(cmd, exit_on_error=False):
2022-11-04 14:49:36 -05:00
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if exit_on_error:
if cmd[0] == "rocm-smi":
if p.returncode != 2 and p.returncode != 0:
logging.error("ERROR: No GPU detected. Unable to load rocm-smi")
sys.exit(1)
elif p.returncode != 0:
logging.error("ERROR: command [%s] failed with non-zero exit code" % cmd)
sys.exit(1)
return p.stdout.decode("utf-8")
2022-11-04 14:49:36 -05:00
def search(pattern, string):
m = re.search(pattern, string, re.MULTILINE)
if m is not None:
return m.group(1)
return None
2024-02-16 15:34:28 -06:00
2024-02-13 21:19:56 -05:00
def total_l2_banks(archname, L2Banks, memory_partition):
# Fixme: support all supported partitioning mode
# Fixme: "name" is a bad name!
totalL2Banks = L2Banks
if (
archname.lower() == "mi300a_a0"
or archname.lower() == "mi300a_a1"
):
totalL2Banks = L2Banks * get_hbm_stack_num(
archname, memory_partition)
elif (
archname.lower() == "mi300x_a0"
or archname.lower() == "mi300x_a1"
):
totalL2Banks = L2Banks * get_hbm_stack_num(
archname, memory_partition)
return str(totalL2Banks)
2022-11-04 14:49:36 -05:00
2024-02-21 17:00:39 -06:00
def total_sqc(archname, numCUs, numSEs):
cu_per_se = float(numCUs) / float(numSEs)
sq_per_se = cu_per_se / 2
if archname.lower() in ['mi50', 'mi100']:
sq_per_se = cu_per_se / 3
sq_per_se = ceil(sq_per_se)
return int(sq_per_se) * int(numSEs)
2022-11-04 14:49:36 -05:00
if __name__ == "__main__":
print(MachineSpecs())