2022-11-04 14:49:36 -05:00
"""Get host/gpu specs."""
2023-02-13 09:26:12 -06:00
##############################################################################bl
2023-02-13 13:47:11 -06:00
# 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
#
2023-02-13 13:47:11 -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,
2023-02-13 13:47:11 -06:00
# 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,
2023-02-13 13:47:11 -06:00
# 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
2024-02-20 13:34:56 -06:00
import pandas as pd
2022-11-04 14:49:36 -05:00
2024-02-20 13:34:56 -06:00
from datetime import datetime
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
2024-02-20 13:34:56 -06: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 :
2024-02-20 13:34:56 -06:00
def __init__ ( self , args , sysinfo = None ):
if not sysinfo is None :
2024-02-21 15:58:07 -06:00
self . gpu_arch = sysinfo . iloc [ 0 ][ "gpu_arch" ]
2024-02-20 13:34:56 -06:00
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 ()
2024-02-21 15:58:07 -06:00
self . cpu_model : str = search ( r "^model name\s*: (.*?)$" , cpuinfo )
2024-02-20 13:34:56 -06:00
self . sbios : str = (
path ( "/sys/class/dmi/id/bios_vendor" ) . read_text () . strip ()
+ path ( "/sys/class/dmi/id/bios_version" ) . read_text () . strip ()
)
2024-02-21 15:58:07 -06:00
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 = ""
2024-02-20 13:34:56 -06:00
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
##########################################
2024-02-21 15:58:07 -06:00
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
2024-02-20 13:34:56 -06:00
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
2024-02-21 15:58:07 -06:00
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
2024-02-20 13:34:56 -06:00
# Load above SoC specs via module import
try :
2024-02-21 15:58:07 -06:00
soc_module = importlib . import_module ( 'omniperf_soc.soc_' + self . gpu_arch )
2024-02-20 13:34:56 -06:00
except ModuleNotFoundError as e :
2024-02-21 15:58:07 -06:00
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' )
2024-02-20 13:34:56 -06:00
self . _rocminfo = self . _rocminfo [ self . detect_arch ()[ 1 ] + 1 :] # update rocminfo for target section
soc_obj = soc_class ( args , self )
# Update arch specific specs
2024-02-21 15:58:07 -06:00
self . total_l2_chan : str = total_l2_banks (
self . gpu_model , int ( self . L2Banks ), self . memory_partition
2024-02-20 13:34:56 -06:00
)
2024-02-21 15:58:07 -06:00
self . hbm_bw : str = str ( int ( self . max_mclk ) / 1000 * 32 * self . get_hbm_channels ())
2022-11-04 14:49:36 -05:00
2024-02-20 13:34:56 -06: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 ):
2024-02-21 15:58:07 -06:00
hbmchannels = int ( self . total_l2_chan )
2024-02-20 13:34:56 -06:00
if (
2024-02-21 15:58:07 -06:00
self . gpu_model . lower () == "mi300a_a0"
or self . gpu_model . lower () == "mi300a_a1"
2024-02-20 13:34:56 -06:00
) 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
2024-02-21 15:58:07 -06:00
logging . warning ( f "WARNING: Incomplete class definition for { self . gpu_arch } . Expecting populated { attr_name } but detected None." )
2024-02-20 13:34:56 -06:00
all_populated = False
data [ attr_name ] = attr_value
if not all_populated :
2024-02-21 15:58:07 -06:00
error ( "Missing specs fields for %s " % self . gpu_arch )
2024-02-20 13:34:56 -06:00
return pd . DataFrame ( data , index = [ 0 ])
2022-11-04 14:49:36 -05:00
def __str__ ( self ):
return dedent (
f """ \
Host info:
2024-02-21 15:58:07 -06:00
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:
2024-02-21 15:58:07 -06:00
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
"""
)
2024-02-20 13:34:56 -06: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
)
2024-02-20 13:34:56 -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. \n The 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 )
2024-02-05 16:15:33 -06:00
if exit_on_error :
2024-02-13 19:56:46 -06:00
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 )
2024-02-05 16:15:33 -06:00
elif p . returncode != 0 :
logging . error ( "ERROR: command [ %s ] failed with non-zero exit code" % cmd )
sys . exit ( 1 )
2024-01-17 15:36:51 -06:00
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 (
2024-02-20 13:34:56 -06:00
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
if __name__ == "__main__" :
2024-02-20 13:34:56 -06:00
print ( MachineSpecs ())