Python formatting

Signed-off-by: colramos-amd <colramos@amd.com>
This commit is contained in:
colramos-amd
2024-03-01 11:52:31 -06:00
committed by Cole Ramos
parent 817774be5e
commit d6f45411eb
19 changed files with 688 additions and 380 deletions
+3 -1
View File
@@ -90,7 +90,9 @@ def omniarg_parser(parser, omniperf_home, supported_archs, omniperf_version):
general_group.add_argument(
"-V", "--verbose", help="Increase output verbosity", action="count", default=0
)
general_group.add_argument("-s", "--specs", action="store_true", help="Print system specs.")
general_group.add_argument(
"-s", "--specs", action="store_true", help="Print system specs."
)
profile_group.add_argument(
"-n",
+2 -2
View File
@@ -41,8 +41,8 @@ class OmniAnalyze_Base:
self._runs = OrderedDict()
self._arch_configs = {}
self.__supported_archs = supported_archs
self._output = None
self.__socs:dict = None # available OmniSoC objs
self._output = None
self.__socs: dict = None # available OmniSoC objs
def get_args(self):
return self.__args
+8 -4
View File
@@ -70,13 +70,17 @@ class cli_analysis(OmniAnalyze_Base):
tty.show_kernel_stats(
self.get_args(),
self._runs,
self._arch_configs[self._runs[self.get_args().path[0][0]].sys_info.iloc[0]["gpu_arch"]],
self._output
self._arch_configs[
self._runs[self.get_args().path[0][0]].sys_info.iloc[0]["gpu_arch"]
],
self._output,
)
else:
tty.show_all(
self.get_args(),
self._runs,
self._arch_configs[self._runs[self.get_args().path[0][0]].sys_info.iloc[0]["gpu_arch"]],
self._output
self._arch_configs[
self._runs[self.get_args().path[0][0]].sys_info.iloc[0]["gpu_arch"]
],
self._output,
)
+11 -9
View File
@@ -163,12 +163,12 @@ class webui_analysis(OmniAnalyze_Base):
# update roofline for visualization in GUI
self.get_socs()[self.arch].analysis_setup(
roofline_parameters={
'workload_dir': self.dest_dir,
'device_id': 0,
'sort_type': 'kernels',
'mem_level': 'ALL',
'include_kernel_names': False,
'is_standalone': False
"workload_dir": self.dest_dir,
"device_id": 0,
"sort_type": "kernels",
"mem_level": "ALL",
"include_kernel_names": False,
"is_standalone": False,
}
)
roof_obj = self.get_socs()[self.arch].roofline_obj
@@ -284,10 +284,12 @@ class webui_analysis(OmniAnalyze_Base):
# create the loaded kernel stats
parser.load_kernel_top(self._runs[self.dest_dir], self.dest_dir)
# set architecture
self.arch = self._runs[self.dest_dir].sys_info.iloc[0]['gpu_arch']
self.arch = self._runs[self.dest_dir].sys_info.iloc[0]["gpu_arch"]
else:
error("Multiple runs not yet supported in GUI. Retry without --gui flag.")
self.error(
"Multiple runs not yet supported in GUI. Retry without --gui flag."
)
@demarcate
def run_analysis(self):
+32 -15
View File
@@ -29,7 +29,15 @@ import os
from pathlib import Path
import shutil
from utils.specs import generate_machine_specs
from utils.utils import demarcate, trace_logger, get_version, get_version_display, detect_rocprof, error, get_submodules
from utils.utils import (
demarcate,
trace_logger,
get_version,
get_version_display,
detect_rocprof,
error,
get_submodules,
)
from argparser import omniarg_parser
import config
import pandas as pd
@@ -60,7 +68,7 @@ class Omniperf:
}
self.__options = {}
self.__supported_archs = SUPPORTED_ARCHS
self.__mspec: MachineSpecs = None # to be initalized in load_soc_specs()
self.__mspec: MachineSpecs = None # to be initalized in load_soc_specs()
self.setup_logging()
self.set_version()
@@ -76,8 +84,7 @@ class Omniperf:
logging.info("Execution mode = %s" % self.__mode)
def print_graphic(self):
"""Log program name as ascii art to terminal.
"""
"""Log program name as ascii art to terminal."""
ascii_art = r"""
___ _ __
/ _ \ _ __ ___ _ __ (_)_ __ ___ _ __ / _|
@@ -157,9 +164,8 @@ class Omniperf:
return
@demarcate
def load_soc_specs(self, sysinfo:dict=None):
"""Load OmniSoC instance for Omniperf run
"""
def load_soc_specs(self, sysinfo: dict = None):
"""Load OmniSoC instance for Omniperf run"""
self.__mspec = generate_machine_specs(self.__args, sysinfo)
if self.__args.specs:
print(self.__mspec)
@@ -171,8 +177,8 @@ class Omniperf:
if arch not in self.__supported_archs.keys():
error("%s is an unsupported SoC" % arch)
soc_module = importlib.import_module('omniperf_soc.soc_'+arch)
soc_class = getattr(soc_module, arch+'_soc')
soc_module = importlib.import_module("omniperf_soc.soc_" + arch)
soc_class = getattr(soc_module, arch + "_soc")
self.__soc[arch] = soc_class(self.__args, self.__mspec)
return
@@ -207,27 +213,38 @@ class Omniperf:
# Update default path
if self.__args.path == os.path.join(os.getcwd(), "workloads"):
self.__args.path = os.path.join(self.__args.path, self.__args.name, self.__mspec.gpu_model)
self.__args.path = os.path.join(
self.__args.path, self.__args.name, self.__mspec.gpu_model
)
logging.info("Profiler choice = %s" % self.__profiler_mode)
# instantiate desired profiler
if self.__profiler_mode == "rocprofv1":
from omniperf_profile.profiler_rocprof_v1 import rocprof_v1_profiler
profiler = rocprof_v1_profiler(self.__args, self.__profiler_mode, self.__soc[self.__mspec.gpu_arch])
profiler = rocprof_v1_profiler(
self.__args, self.__profiler_mode, self.__soc[self.__mspec.gpu_arch]
)
elif self.__profiler_mode == "rocprofv2":
from omniperf_profile.profiler_rocprof_v2 import rocprof_v2_profiler
profiler = rocprof_v2_profiler(self.__args, self.__profiler_mode, self.__soc[self.__mspec.gpu_arch])
profiler = rocprof_v2_profiler(
self.__args, self.__profiler_mode, self.__soc[self.__mspec.gpu_arch]
)
elif self.__profiler_mode == "rocscope":
from omniperf_profile.profiler_rocscope import rocscope_profiler
profiler = rocscope_profiler(self.__args, self.__profiler_mode, self.__soc[self.__mspec.gpu_arch])
profiler = rocscope_profiler(
self.__args, self.__profiler_mode, self.__soc[self.__mspec.gpu_arch]
)
else:
logging.error("Unsupported profiler")
sys.exit(1)
# -----------------------
# run profiling workflow
#-----------------------
# -----------------------
self.__soc[self.__mspec.gpu_arch].profiling_setup()
profiler.pre_processing()
profiler.run_profiling(self.__version["ver"], config.prog)
@@ -279,7 +296,7 @@ class Omniperf:
# Load required SoC(s) from input
for d in analyzer.get_args().path:
sys_info = pd.read_csv(Path(d[0], "sysinfo.csv"))
sys_info = sys_info.to_dict('list')
sys_info = sys_info.to_dict("list")
sys_info = {key: value[0] for key, value in sys_info.items()}
self.load_soc_specs(sys_info)
+9 -2
View File
@@ -270,7 +270,14 @@ class OmniProfiler_Base:
# verify soc compatibility
if self.__profiler not in self._soc.get_compatible_profilers():
error("%s is not enabled in %s. Available profilers include: %s" % (self._soc.get_arch(), self.__profiler, self._soc.get_compatible_profilers()))
error(
"%s is not enabled in %s. Available profilers include: %s"
% (
self._soc.get_arch(),
self.__profiler,
self._soc.get_compatible_profilers(),
)
)
# verify not accessing parent directories
if ".." in str(self.__args.path):
error("Access denied. Cannot access parent directories in path (i.e. ../)")
@@ -367,7 +374,7 @@ class OmniProfiler_Base:
if self.__profiler == "rocprofv1" or self.__profiler == "rocprofv2":
run_prof(
fname=fname,
fname=fname,
profiler_options=options,
workload_dir=self.get_args().path,
mspec=self._soc._mspec,
+46 -26
View File
@@ -35,25 +35,33 @@ from pathlib import Path
from omniperf_base import SUPPORTED_ARCHS
class OmniSoC_Base():
def __init__(self,args,mspec): # new info field will contain rocminfo or sysinfo to populate properties
class OmniSoC_Base:
def __init__(
self, args, mspec
): # new info field will contain rocminfo or sysinfo to populate properties
self.__args = args
self.__arch = None
self._mspec = mspec
self.__perfmon_dir = None
self.__perfmon_config = {} # Per IP block max number of simulutaneous counters. GFX IP Blocks
self.__soc_params = {} # SoC specifications
self.__compatible_profilers = [] # Store profilers compatible with SoC
self.__perfmon_config = (
{}
) # Per IP block max number of simulutaneous counters. GFX IP Blocks
self.__soc_params = {} # SoC specifications
self.__compatible_profilers = [] # Store profilers compatible with SoC
self.populate_mspec()
# In some cases (i.e. --specs) path will not be given
if hasattr(self.__args, "path"):
if self.__args.path == os.path.join(os.getcwd(), "workloads"):
self.__workload_dir = os.path.join(self.__args.path, self.__args.name, self._mspec.gpu_model)
self.__workload_dir = os.path.join(
self.__args.path, self.__args.name, self._mspec.gpu_model
)
else:
self.__workload_dir = self.__args.path
def __hash__(self):
return hash(self.__arch)
def __eq__(self, other):
if not isinstance(other, type(self)):
return NotImplemented
@@ -64,15 +72,19 @@ class OmniSoC_Base():
def set_perfmon_config(self, config: dict):
self.__perfmon_config = config
def get_workload_perfmon_dir(self):
return str(Path(self.__perfmon_dir).parent.absolute())
def get_soc_param(self):
return self.__soc_params
def set_arch(self, arch: str):
self.__arch = arch
def get_arch(self):
return self.__arch
def get_args(self):
return self.__args
@@ -143,17 +155,16 @@ class OmniSoC_Base():
if key != None:
self._mspec.max_waves_per_cu = key
break
self._mspec.sqc_per_gpu = str(
total_sqc(
self._mspec.gpu_arch,
self._mspec.cu_per_gpu,
self._mspec.se_per_gpu
))
self._mspec.gpu_arch, self._mspec.cu_per_gpu, self._mspec.se_per_gpu
)
)
# we get the max mclk from rocm-smi --showmclkrange
rocm_smi_mclk = run(["rocm-smi", "--showmclkrange"], exit_on_error=True)
self._mspec.max_mclk = search(r'(\d+)Mhz\s*$', rocm_smi_mclk)
self._mspec.max_mclk = search(r"(\d+)Mhz\s*$", rocm_smi_mclk)
# these are just max's now, because the parsing was broken and this was inconsistent
# with how we use the clocks elsewhere (all max, all the time)
@@ -161,15 +172,25 @@ class OmniSoC_Base():
self._mspec.cur_mclk = self._mspec.max_mclk
# specify gpu name for gfx942 hardware
self._mspec.gpu_model = list(SUPPORTED_ARCHS[self._mspec.gpu_arch].keys())[0].upper()
self._mspec.gpu_model = list(SUPPORTED_ARCHS[self._mspec.gpu_arch].keys())[
0
].upper()
if self._mspec.gpu_model == "MI300":
self._mspec.gpu_model = list(SUPPORTED_ARCHS[self._mspec.gpu_arch].values())[0][0]
if (self._mspec.gpu_arch == "gfx942") and ("MI300A" in '\n'.join(self._mspec._rocminfo)):
self._mspec.gpu_model = list(SUPPORTED_ARCHS[self._mspec.gpu_arch].values())[
0
][0]
if (self._mspec.gpu_arch == "gfx942") and (
"MI300A" in "\n".join(self._mspec._rocminfo)
):
self._mspec.gpu_model = "MI300A_A1"
if (self._mspec.gpu_arch == "gfx942") and ("MI300A" not in '\n'.join(self._mspec._rocminfo)):
if (self._mspec.gpu_arch == "gfx942") and (
"MI300A" not in "\n".join(self._mspec._rocminfo)
):
self._mspec.gpu_model = "MI300X_A1"
self._mspec.num_xcd = str(total_xcds(self._mspec.gpu_model, self._mspec.compute_partition))
self._mspec.num_xcd = str(
total_xcds(self._mspec.gpu_model, self._mspec.compute_partition)
)
@demarcate
def perfmon_filter(self, roofline_perfmon_only: bool):
@@ -190,7 +211,9 @@ class OmniSoC_Base():
if not roofline_perfmon_only:
ref_pmc_files_list = glob.glob(self.__perfmon_dir + "/" + "pmc_*perf*.txt")
ref_pmc_files_list += glob.glob(self.__perfmon_dir + "/" + self.__arch + "/pmc_*_perf*.txt")
ref_pmc_files_list += glob.glob(
self.__perfmon_dir + "/" + self.__arch + "/pmc_*_perf*.txt"
)
# Perfmon list filtering
if self.__args.ipblocks != None:
@@ -226,22 +249,19 @@ class OmniSoC_Base():
# ----------------------------------------------------
@abstractmethod
def profiling_setup(self):
"""Perform any SoC-specific setup prior to profiling.
"""
"""Perform any SoC-specific setup prior to profiling."""
logging.debug("[profiling] perform SoC profiling setup for %s" % self.__arch)
@abstractmethod
def post_profiling(self):
"""Perform any SoC-specific post profiling activities.
"""
"""Perform any SoC-specific post profiling activities."""
logging.debug("[profiling] perform SoC post processing for %s" % self.__arch)
@abstractmethod
def analysis_setup(self):
"""Perform any SoC-specific setup prior to analysis.
"""
"""Perform any SoC-specific setup prior to analysis."""
logging.debug("[analysis] perform SoC analysis setup for %s" % self.__arch)
@demarcate
def perfmon_coalesce(pmc_files_list, perfmon_config, workload_dir):
+13 -5
View File
@@ -27,11 +27,19 @@ import config
from omniperf_soc.soc_base import OmniSoC_Base
from utils.utils import demarcate, error
class gfx906_soc (OmniSoC_Base):
def __init__(self,args,mspec):
super().__init__(args,mspec)
class gfx906_soc(OmniSoC_Base):
def __init__(self, args, mspec):
super().__init__(args, mspec)
self.set_arch("gfx906")
self.set_perfmon_dir(os.path.join(str(config.omniperf_home), "omniperf_soc", "profile_configs", self.get_arch()))
self.set_perfmon_dir(
os.path.join(
str(config.omniperf_home),
"omniperf_soc",
"profile_configs",
self.get_arch(),
)
)
self.set_compatible_profilers(["rocprofv1", "rocscope"])
# Per IP block max number of simultaneous counters. GFX IP Blocks
self.set_perfmon_config(
@@ -49,7 +57,7 @@ class gfx906_soc (OmniSoC_Base):
"TCC_channels": 16,
}
)
# Set arch specific specs
self._mspec._l2_banks = 16
self._mspec.lds_banks_per_cu = 32
+13 -6
View File
@@ -27,11 +27,19 @@ import config
from omniperf_soc.soc_base import OmniSoC_Base
from utils.utils import demarcate, error
class gfx908_soc (OmniSoC_Base):
def __init__(self,args,mspec):
super().__init__(args,mspec)
class gfx908_soc(OmniSoC_Base):
def __init__(self, args, mspec):
super().__init__(args, mspec)
self.set_arch("gfx908")
self.set_perfmon_dir(os.path.join(str(config.omniperf_home), "omniperf_soc", "profile_configs", self.get_arch()))
self.set_perfmon_dir(
os.path.join(
str(config.omniperf_home),
"omniperf_soc",
"profile_configs",
self.get_arch(),
)
)
self.set_compatible_profilers(["rocprofv1", "rocscope"])
# Per IP block max number of simultaneous counters. GFX IP Blocks
self.set_perfmon_config(
@@ -82,6 +90,5 @@ class gfx908_soc (OmniSoC_Base):
@demarcate
def analysis_setup(self):
"""Perform any SoC-specific setup prior to analysis.
"""
"""Perform any SoC-specific setup prior to analysis."""
super().analysis_setup()
+24 -10
View File
@@ -29,14 +29,29 @@ from utils.utils import demarcate, mibench
from roofline import Roofline
import logging
class gfx90a_soc (OmniSoC_Base):
def __init__(self,args,mspec):
super().__init__(args,mspec)
class gfx90a_soc(OmniSoC_Base):
def __init__(self, args, mspec):
super().__init__(args, mspec)
self.set_arch("gfx90a")
if hasattr(self.get_args(), 'roof_only') and self.get_args().roof_only:
self.set_perfmon_dir(os.path.join(str(config.omniperf_home), "omniperf_soc", "profile_configs", "roofline"))
if hasattr(self.get_args(), "roof_only") and self.get_args().roof_only:
self.set_perfmon_dir(
os.path.join(
str(config.omniperf_home),
"omniperf_soc",
"profile_configs",
"roofline",
)
)
else:
self.set_perfmon_dir(os.path.join(str(config.omniperf_home), "omniperf_soc", "profile_configs", self.get_arch()))
self.set_perfmon_dir(
os.path.join(
str(config.omniperf_home),
"omniperf_soc",
"profile_configs",
self.get_arch(),
)
)
self.set_compatible_profilers(["rocprofv1", "rocscope"])
# Per IP block max number of simultaneous counters. GFX IP Blocks
self.set_perfmon_config(
@@ -91,7 +106,6 @@ class gfx90a_soc (OmniSoC_Base):
super().analysis_setup()
# configure roofline for analysis
if roofline_parameters:
self.roofline_obj = Roofline(self.get_args(), self._mspec, roofline_parameters)
self.roofline_obj = Roofline(
self.get_args(), self._mspec, roofline_parameters
)
+13 -5
View File
@@ -29,12 +29,20 @@ from utils.utils import demarcate, mibench
from roofline import Roofline
import logging
class gfx940_soc (OmniSoC_Base):
def __init__(self,args,mspec):
super().__init__(args,mspec)
class gfx940_soc(OmniSoC_Base):
def __init__(self, args, mspec):
super().__init__(args, mspec)
self.set_arch("gfx940")
if hasattr(self.get_args(), 'roof_only') and self.get_args().roof_only:
self.set_perfmon_dir(os.path.join(str(config.omniperf_home), "omniperf_soc", "profile_configs", "roofline"))
if hasattr(self.get_args(), "roof_only") and self.get_args().roof_only:
self.set_perfmon_dir(
os.path.join(
str(config.omniperf_home),
"omniperf_soc",
"profile_configs",
"roofline",
)
)
else:
# NB: We're using generalized Mi300 perfmon configs
self.set_perfmon_dir(
+12 -4
View File
@@ -29,12 +29,20 @@ from utils.utils import demarcate, mibench
from roofline import Roofline
import logging
class gfx941_soc (OmniSoC_Base):
def __init__(self,args, mspec):
class gfx941_soc(OmniSoC_Base):
def __init__(self, args, mspec):
super().__init__(args, mspec)
self.set_arch("gfx941")
if hasattr(self.get_args(), 'roof_only') and self.get_args().roof_only:
self.set_perfmon_dir(os.path.join(str(config.omniperf_home), "omniperf_soc", "profile_configs", "roofline"))
if hasattr(self.get_args(), "roof_only") and self.get_args().roof_only:
self.set_perfmon_dir(
os.path.join(
str(config.omniperf_home),
"omniperf_soc",
"profile_configs",
"roofline",
)
)
else:
# NB: We're using generalized Mi300 perfmon configs
self.set_perfmon_dir(
+13 -5
View File
@@ -29,12 +29,20 @@ from utils.utils import demarcate, mibench
from roofline import Roofline
import logging
class gfx942_soc (OmniSoC_Base):
def __init__(self,args,mspec):
super().__init__(args,mspec)
class gfx942_soc(OmniSoC_Base):
def __init__(self, args, mspec):
super().__init__(args, mspec)
self.set_arch("gfx942")
if hasattr(self.get_args(), 'roof_only') and self.get_args().roof_only:
self.set_perfmon_dir(os.path.join(str(config.omniperf_home), "omniperf_soc", "profile_configs", "roofline"))
if hasattr(self.get_args(), "roof_only") and self.get_args().roof_only:
self.set_perfmon_dir(
os.path.join(
str(config.omniperf_home),
"omniperf_soc",
"profile_configs",
"roofline",
)
)
else:
# NB: We're using generalized Mi300 perfmon configs
self.set_perfmon_dir(
+67 -43
View File
@@ -41,31 +41,37 @@ class Roofline:
def __init__(self, args, mspec, run_parameters=None):
self.__args = args
self.__mspec = mspec
self.__run_parameters = run_parameters if run_parameters else {
'workload_dir': None, # in some cases (i.e. --specs) path will not be given
'device_id': 0,
'sort_type': 'kernels',
'mem_level': 'ALL',
'include_kernel_names': False,
'is_standalone': False
}
self.__run_parameters = (
run_parameters
if run_parameters
else {
"workload_dir": None, # in some cases (i.e. --specs) path will not be given
"device_id": 0,
"sort_type": "kernels",
"mem_level": "ALL",
"include_kernel_names": False,
"is_standalone": False,
}
)
self.__ai_data = None
self.__ceiling_data = None
self.__figure = go.Figure()
# Set roofline run parameters from args
if hasattr(self.__args, 'path') and not run_parameters:
self.__run_parameters['workload_dir'] = self.__args.path
if hasattr(self.__args, 'roof_only') and self.__args.roof_only == True:
self.__run_parameters['is_standalone'] = True
if hasattr(self.__args, 'kernel_names') and self.__args.kernel_names == True:
self.__run_parameters['include_kernel_names'] = True
if hasattr(self.__args, 'mem_level') and self.__args.mem_level != "ALL":
self.__run_parameters['mem_level'] = self.__args.mem_level
if hasattr(self.__args, 'sort') and self.__args.sort != "ALL":
self.__run_parameters['sort_type'] = self.__args.sort
if hasattr(self.__args, "path") and not run_parameters:
self.__run_parameters["workload_dir"] = self.__args.path
if hasattr(self.__args, "roof_only") and self.__args.roof_only == True:
self.__run_parameters["is_standalone"] = True
if hasattr(self.__args, "kernel_names") and self.__args.kernel_names == True:
self.__run_parameters["include_kernel_names"] = True
if hasattr(self.__args, "mem_level") and self.__args.mem_level != "ALL":
self.__run_parameters["mem_level"] = self.__args.mem_level
if hasattr(self.__args, "sort") and self.__args.sort != "ALL":
self.__run_parameters["sort_type"] = self.__args.sort
if (not isinstance(self.__run_parameters['workload_dir'], list)
and self.__run_parameters['workload_dir'] != None):
if (
not isinstance(self.__run_parameters["workload_dir"], list)
and self.__run_parameters["workload_dir"] != None
):
self.roof_setup()
self.validate_parameters()
@@ -78,11 +84,17 @@ class Roofline:
def roof_setup(self):
# set default workload path if not specified
if self.__run_parameters['workload_dir'] == os.path.join(os.getcwd(), 'workloads'):
self.__run_parameters['workload_dir'] = os.path.join(self.__run_parameters['workload_dir'], self.__args.name, self.__mspec.gpu_model)
if self.__run_parameters["workload_dir"] == os.path.join(
os.getcwd(), "workloads"
):
self.__run_parameters["workload_dir"] = os.path.join(
self.__run_parameters["workload_dir"],
self.__args.name,
self.__mspec.gpu_model,
)
# create new directory for roofline if it doesn't exist
if not os.path.isdir(self.__run_parameters['workload_dir']):
os.makedirs(self.__run_parameters['workload_dir'])
if not os.path.isdir(self.__run_parameters["workload_dir"]):
os.makedirs(self.__run_parameters["workload_dir"])
@demarcate
def empirical_roofline(
@@ -91,9 +103,9 @@ class Roofline:
):
"""Generate a set of empirical roofline plots given a directory containing required profiling and benchmarking data"""
# Create arithmetic intensity data that will populate the roofline model
logging.debug("[roofline] Path: %s" % self.__run_parameters['workload_dir'])
self.__ai_data = calc_ai(self.__run_parameters['sort_type'], ret_df)
logging.debug("[roofline] Path: %s" % self.__run_parameters["workload_dir"])
self.__ai_data = calc_ai(self.__run_parameters["sort_type"], ret_df)
logging.debug("[roofline] AI at each mem level:")
for i in self.__ai_data:
logging.debug("%s -> %s" % (i, self.__ai_data[i]))
@@ -131,21 +143,33 @@ class Roofline:
if self.__run_parameters["is_standalone"]:
dev_id = str(self.__run_parameters["device_id"])
fp32_fig.write_image(self.__run_parameters['workload_dir'] + "/empirRoof_gpu-{}_fp32.pdf".format(dev_id))
fp32_fig.write_image(
self.__run_parameters["workload_dir"]
+ "/empirRoof_gpu-{}_fp32.pdf".format(dev_id)
)
ml_combo_fig.write_image(
self.__run_parameters['workload_dir'] + "/empirRoof_gpu-{}_int8_fp16.pdf".format(dev_id)
self.__run_parameters["workload_dir"]
+ "/empirRoof_gpu-{}_int8_fp16.pdf".format(dev_id)
)
# only save a legend if kernel_names option is toggled
if self.__run_parameters['include_kernel_names']:
self.__figure.write_image(self.__run_parameters['workload_dir'] + "/kernelName_legend.pdf")
if self.__run_parameters["include_kernel_names"]:
self.__figure.write_image(
self.__run_parameters["workload_dir"] + "/kernelName_legend.pdf"
)
time.sleep(1)
# Re-save to remove loading MathJax pop up
fp32_fig.write_image(self.__run_parameters['workload_dir'] + "/empirRoof_gpu-{}_fp32.pdf".format(dev_id))
ml_combo_fig.write_image(
self.__run_parameters['workload_dir'] + "/empirRoof_gpu-{}_int8_fp16.pdf".format(dev_id)
fp32_fig.write_image(
self.__run_parameters["workload_dir"]
+ "/empirRoof_gpu-{}_fp32.pdf".format(dev_id)
)
if self.__run_parameters['include_kernel_names']:
self.__figure.write_image(self.__run_parameters['workload_dir'] + "/kernelName_legend.pdf")
ml_combo_fig.write_image(
self.__run_parameters["workload_dir"]
+ "/empirRoof_gpu-{}_int8_fp16.pdf".format(dev_id)
)
if self.__run_parameters["include_kernel_names"]:
self.__figure.write_image(
self.__run_parameters["workload_dir"] + "/kernelName_legend.pdf"
)
logging.info("[roofline] Empirical Roofline PDFs saved!")
else:
return html.Section(
@@ -332,7 +356,7 @@ class Roofline:
self.__run_parameters["mem_level"].remove("vL1D")
self.__run_parameters["mem_level"].append("L1")
app_path = os.path.join(self.__run_parameters['workload_dir'], "pmc_perf.csv")
app_path = os.path.join(self.__run_parameters["workload_dir"], "pmc_perf.csv")
roofline_exists = os.path.isfile(app_path)
if not roofline_exists:
logging.error("[roofline] Error: {} does not exist".format(app_path))
@@ -353,13 +377,13 @@ class Roofline:
if not os.path.isfile(sysinfo_path):
logging.info("[roofline] sysinfo.csv not found. Generating...")
gen_sysinfo(
workload_name=self.__args.name,
workload_dir=self.__workload_dir,
ip_blocks=self.__args.ipblocks,
app_cmd=self.__args.remaining,
skip_roof=self.__args.no_roof,
workload_name=self.__args.name,
workload_dir=self.__workload_dir,
ip_blocks=self.__args.ipblocks,
app_cmd=self.__args.remaining,
skip_roof=self.__args.no_roof,
roof_only=self.__args.roof_only,
mspec=self.__mspec
mspec=self.__mspec,
)
@abstractmethod
+3 -3
View File
@@ -227,9 +227,9 @@ def build_bar_chart(display_df, table_config, barchart_elements, norm_filt):
).update_xaxes(range=[0, 1638])
) # append second GB/s chart
else:
key = 'Avg'
if table_config['id'] in [1101]:
key = 'Pct of Peak'
key = "Avg"
if table_config["id"] in [1101]:
key = "Pct of Peak"
d_figs.append(
px.bar(
display_df,
+12 -9
View File
@@ -79,11 +79,11 @@ supported_denom = {
build_in_vars = {
"GRBM_GUI_ACTIVE_PER_XCD": "(GRBM_GUI_ACTIVE / $num_xcd)",
"GRBM_COUNT_PER_XCD": "(GRBM_COUNT / $num_xcd)",
"GRBM_SPI_BUSY_PER_XCD" : "(GRBM_SPI_BUSY / $num_xcd)",
"GRBM_SPI_BUSY_PER_XCD": "(GRBM_SPI_BUSY / $num_xcd)",
"numActiveCUs": "TO_INT(MIN((((ROUND(AVG(((4 * SQ_BUSY_CU_CYCLES) / $GRBM_GUI_ACTIVE_PER_XCD)), \
0) / $max_waves_per_cu) * 8) + MIN(MOD(ROUND(AVG(((4 * SQ_BUSY_CU_CYCLES) \
/ $GRBM_GUI_ACTIVE_PER_XCD)), 0), $max_waves_per_cu), 8)), $cu_per_gpu))",
"kernelBusyCycles": "ROUND(AVG((((End_Timestamp - Start_Timestamp) / 1000) * $max_sclk)), 0)"
"kernelBusyCycles": "ROUND(AVG((((End_Timestamp - Start_Timestamp) / 1000) * $max_sclk)), 0)",
}
supported_call = {
@@ -685,11 +685,11 @@ def eval_metric(dfs, dfs_type, sys_info, raw_pmc_df, debug):
ammolite__se_per_gpu = sys_info.se_per_gpu
ammolite__pipes_per_gpu = sys_info.pipes_per_gpu
ammolite__cu_per_gpu = sys_info.cu_per_gpu
ammolite__simd_per_cu = sys_info.simd_per_cu # not used
ammolite__simd_per_cu = sys_info.simd_per_cu # not used
ammolite__sqc_per_gpu = sys_info.sqc_per_gpu
ammolite__lds_banks_per_cu = sys_info.lds_banks_per_cu
ammolite__cur_sclk = sys_info.cur_sclk # not used
ammolite__mclk = sys_info.cur_mclk # not used
ammolite__mclk = sys_info.cur_mclk # not used
ammolite__max_sclk = sys_info.max_sclk
ammolite__max_waves_per_cu = sys_info.max_waves_per_cu
ammolite__hbm_bw = sys_info.hbm_bw
@@ -924,7 +924,9 @@ def load_kernel_top(workload, dir):
if file.exists():
tmp[id] = pd.read_csv(file)
else:
logging.info("Warning: Issue loading top kernels. Check pmc_kernel_top.csv")
logging.info(
"Warning: Issue loading top kernels. Check pmc_kernel_top.csv"
)
# NB: Special case for sysinfo. Probably room for improvement in this whole function design
elif "from_csv_columnwise" in df.columns and id == 101:
tmp[id] = workload.sys_info.transpose()
@@ -977,7 +979,8 @@ def build_comparable_columns(time_unit):
return comparable_columns
def correct_sys_info(mspec, specs_correction:dict):
def correct_sys_info(mspec, specs_correction: dict):
"""
Correct system spec items manually
"""
@@ -987,8 +990,8 @@ def correct_sys_info(mspec, specs_correction:dict):
for k, v in pairs.items():
if not hasattr(mspec, str(k)):
error(f"Invalid specs correction '{k}'. Please use --specs option to peak valid specs")
error(
f"Invalid specs correction '{k}'. Please use --specs option to peak valid specs"
)
setattr(mspec, str(k), v)
return mspec.get_class_members()
+376 -212
View File
@@ -53,6 +53,7 @@ VERSION_LOC = [
"version-utils",
]
def detect_arch(_rocminfo):
from omniperf_base import SUPPORTED_ARCHS
@@ -68,12 +69,15 @@ def detect_arch(_rocminfo):
else:
return (gpu_arch, idx1)
def generate_machine_specs(args, sysinfo:dict=None):
def generate_machine_specs(args, sysinfo: dict = None):
if not sysinfo is None:
sysinfo_ver = str(sysinfo['version'])
version = get_version(config.omniperf_home)['version']
if sysinfo_ver != version[:version.find(".")]:
logging.warning("WARNING: Detected mismatch in sysinfo versioning. You may need to reprofile to update data.")
sysinfo_ver = str(sysinfo["version"])
version = get_version(config.omniperf_home)["version"]
if sysinfo_ver != version[: version.find(".")]:
logging.warning(
"WARNING: Detected mismatch in sysinfo versioning. You may need to reprofile to update data."
)
return MachineSpecs(**sysinfo)
# read timestamp info
now = datetime.now()
@@ -87,7 +91,9 @@ def generate_machine_specs(args, sysinfo:dict=None):
vData = get_version(config.omniperf_home)
version = vData["version"]
# NB: Just taking major as specs version. May want to make this more specific in the future
specs_version = version[:version.find(".")] # version will always follow 'major.minor.patch' format
specs_version = version[
: version.find(".")
] # version will always follow 'major.minor.patch' format
##########################################
## A. Machine Specs
@@ -97,25 +103,28 @@ def generate_machine_specs(args, sysinfo:dict=None):
version = path("/proc/version").read_text()
os_release = path("/etc/os-release").read_text()
cpu_model = search(r"^model name\s*: (.*?)$", cpuinfo)
sbios = path("/sys/class/dmi/id/bios_vendor").read_text().strip() + \
path("/sys/class/dmi/id/bios_version").read_text().strip()
sbios = (
path("/sys/class/dmi/id/bios_vendor").read_text().strip()
+ path("/sys/class/dmi/id/bios_version").read_text().strip()
)
linux_kernel_version = search(r"version (\S*)", version)
amd_gpu_kernel_version = "" #TODO: Extract amdgpu kernel version
amd_gpu_kernel_version = "" # TODO: Extract amdgpu kernel version
cpu_memory = search(r"MemTotal:\s*(\S*)", meminfo)
gpu_memory = "" #TODO: Extract gpu memory
gpu_memory = "" # TODO: Extract gpu memory
linux_distro = search(r'PRETTY_NAME="(.*?)"', os_release)
if linux_distro is None:
linux_distro = ""
rocm_version = get_rocm_ver().strip()
#FIXME: use device
vbios = search(
r"VBIOS version: (.*?)$", run(["rocm-smi", "-v"], exit_on_error=True))
# FIXME: use device
vbios = search(r"VBIOS version: (.*?)$", run(["rocm-smi", "-v"], exit_on_error=True))
compute_partition = search(
r"Compute Partition:\s*(\w+)", run(["rocm-smi", "--showcomputepartition"]))
r"Compute Partition:\s*(\w+)", run(["rocm-smi", "--showcomputepartition"])
)
if compute_partition is None:
compute_partition = "NA"
memory_partition = search(
r"Memory Partition:\s*(\w+)", run(["rocm-smi", "--showmemorypartition"]))
r"Memory Partition:\s*(\w+)", run(["rocm-smi", "--showmemorypartition"])
)
if memory_partition is None:
memory_partition = "NA"
@@ -126,29 +135,34 @@ def generate_machine_specs(args, sysinfo:dict=None):
rocminfo_full = run(["rocminfo"])
_rocminfo = rocminfo_full.split("\n")
gpu_arch, idx = detect_arch(_rocminfo)
_rocminfo = _rocminfo[idx + 1 :] # update rocminfo for target section
specs = MachineSpecs(version=specs_version,
timestamp=timestamp,
_rocminfo=_rocminfo,
hostname=hostname,
cpu_model=cpu_model,
sbios=sbios,
linux_kernel_version=linux_kernel_version,
amd_gpu_kernel_version=amd_gpu_kernel_version,
cpu_memory=cpu_memory,
gpu_memory=gpu_memory,
linux_distro=linux_distro,
rocm_version=rocm_version,
vbios=vbios,
compute_partition=compute_partition,
memory_partition=memory_partition,
gpu_arch=gpu_arch)
_rocminfo = _rocminfo[idx + 1 :] # update rocminfo for target section
specs = MachineSpecs(
version=specs_version,
timestamp=timestamp,
_rocminfo=_rocminfo,
hostname=hostname,
cpu_model=cpu_model,
sbios=sbios,
linux_kernel_version=linux_kernel_version,
amd_gpu_kernel_version=amd_gpu_kernel_version,
cpu_memory=cpu_memory,
gpu_memory=gpu_memory,
linux_distro=linux_distro,
rocm_version=rocm_version,
vbios=vbios,
compute_partition=compute_partition,
memory_partition=memory_partition,
gpu_arch=gpu_arch,
)
# Load above SoC specs via module import
try:
soc_module = importlib.import_module('omniperf_soc.soc_'+ specs.gpu_arch)
soc_module = importlib.import_module("omniperf_soc.soc_" + specs.gpu_arch)
except ModuleNotFoundError as e:
error("Arch %s marked as supported, but couldn't find class implementation %s." % (specs.gpu_arch, e))
soc_class = getattr(soc_module, specs.gpu_arch+'_soc')
error(
"Arch %s marked as supported, but couldn't find class implementation %s."
% (specs.gpu_arch, e)
)
soc_class = getattr(soc_module, specs.gpu_arch + "_soc")
soc_obj = soc_class(args, specs)
# Update arch specific specs
specs.total_l2_chan: str = total_l2_banks(
@@ -157,6 +171,7 @@ def generate_machine_specs(args, sysinfo:dict=None):
specs.hbm_bw: str = str(int(specs.max_mclk) / 1000 * 32 * specs.get_hbm_channels())
return specs
@dataclass(kw_only=True)
class MachineSpecs:
##########################################
@@ -168,66 +183,135 @@ class MachineSpecs:
# _are_ included in profiling/analysis, so we mark them as 'optional'
# in the metadata to avoid erroring out on missing fields on
# serialization
workload_name: str = field(default=None, metadata={
'doc': 'The name of the workload data was collected for.',
'name': 'Workload Name',
'optional': True})
command: str = field(default=None, metadata={
'doc': 'The command the workload was executed with.',
'name': 'Command',
'optional': True})
ip_blocks: str = field(default=None, metadata={
'doc': 'The hardware blocks profiling information was collected for.',
'name': 'IP Blocks',
'optional': True
})
timestamp: str = field(default=None, metadata={
'doc': 'The time (in local system time) when data was collected',
'name': 'Timestamp'})
version: str = field(default=None, metadata={
'doc': 'The version of the machine specification file format.',
'name': 'MachineSpecs Version',
'intable': False})
timestamp: str = field(default=None, metadata={
'doc': 'The time (in local system time) when data was collected',
'name': 'Timestamp'})
workload_name: str = field(
default=None,
metadata={
"doc": "The name of the workload data was collected for.",
"name": "Workload Name",
"optional": True,
},
)
command: str = field(
default=None,
metadata={
"doc": "The command the workload was executed with.",
"name": "Command",
"optional": True,
},
)
ip_blocks: str = field(
default=None,
metadata={
"doc": "The hardware blocks profiling information was collected for.",
"name": "IP Blocks",
"optional": True,
},
)
timestamp: str = field(
default=None,
metadata={
"doc": "The time (in local system time) when data was collected",
"name": "Timestamp",
},
)
version: str = field(
default=None,
metadata={
"doc": "The version of the machine specification file format.",
"name": "MachineSpecs Version",
"intable": False,
},
)
timestamp: str = field(
default=None,
metadata={
"doc": "The time (in local system time) when data was collected",
"name": "Timestamp",
},
)
_rocminfo: list = field(default=None)
##########################################
## A. Machine Specs
##########################################
hostname: str = field(default=None, metadata={'doc': 'The hostname of the machine.',
'name': 'Hostname'})
cpu_model: str = field(default=None, metadata={'doc': 'The model name of the CPU used.',
'name': 'CPU Model'})
sbios: str = field(default=None, metadata={'doc': 'The system management bios version and vendor.',
'name': "SBIOS"})
linux_distro: str = field(default=None, metadata={'doc': 'The Linux distribution installed on the machine.',
'name': 'Linux Distribution'})
linux_kernel_version: str = field(default=None,
metadata={'doc': 'The Linux kernel version running on the machine.',
'name': 'Linux Kernel Version'})
amd_gpu_kernel_version: str = field(default=None, metadata={
'doc': '[RESERVED] The version of the AMDGPU driver installed on the machine. Unimplemented.',
'name': "AMD GPU Kernel Version"})
cpu_memory: str = field(default=None, metadata={
'doc': 'The total amount of memory available to the CPU.', 'unit': 'KB',
'name': 'CPU Memory'})
gpu_memory: str = field(default=None, metadata={
'doc': '[RESERVED] The total amount of memory available to accelerators/GPUs in the system. Unimplemented.',
'unit': 'KB',
'name': 'GPU Memory'})
rocm_version: str = field(default=None,
metadata={'doc': 'The ROCm version used during data-collection.',
'name': "ROCm Version"})
vbios: str = field(default=None,
metadata={'doc': 'The version of the accelerators/GPUs video bios in the system.',
'name': 'VBIOS'})
compute_partition: str = field(default=None,
metadata={'doc': 'The compute partitioning mode active on the accelerators/GPUs in the system (MI300 only).',
'name': 'Compute Partition'})
memory_partition: str = field(default=None,
metadata={'doc': 'The memory partitioning mode active on the accelerators/GPUs in the system (MI300 only).',
'name': 'Memory Partition'})
hostname: str = field(
default=None, metadata={"doc": "The hostname of the machine.", "name": "Hostname"}
)
cpu_model: str = field(
default=None,
metadata={"doc": "The model name of the CPU used.", "name": "CPU Model"},
)
sbios: str = field(
default=None,
metadata={
"doc": "The system management bios version and vendor.",
"name": "SBIOS",
},
)
linux_distro: str = field(
default=None,
metadata={
"doc": "The Linux distribution installed on the machine.",
"name": "Linux Distribution",
},
)
linux_kernel_version: str = field(
default=None,
metadata={
"doc": "The Linux kernel version running on the machine.",
"name": "Linux Kernel Version",
},
)
amd_gpu_kernel_version: str = field(
default=None,
metadata={
"doc": "[RESERVED] The version of the AMDGPU driver installed on the machine. Unimplemented.",
"name": "AMD GPU Kernel Version",
},
)
cpu_memory: str = field(
default=None,
metadata={
"doc": "The total amount of memory available to the CPU.",
"unit": "KB",
"name": "CPU Memory",
},
)
gpu_memory: str = field(
default=None,
metadata={
"doc": "[RESERVED] The total amount of memory available to accelerators/GPUs in the system. Unimplemented.",
"unit": "KB",
"name": "GPU Memory",
},
)
rocm_version: str = field(
default=None,
metadata={
"doc": "The ROCm version used during data-collection.",
"name": "ROCm Version",
},
)
vbios: str = field(
default=None,
metadata={
"doc": "The version of the accelerators/GPUs video bios in the system.",
"name": "VBIOS",
},
)
compute_partition: str = field(
default=None,
metadata={
"doc": "The compute partitioning mode active on the accelerators/GPUs in the system (MI300 only).",
"name": "Compute Partition",
},
)
memory_partition: str = field(
default=None,
metadata={
"doc": "The memory partitioning mode active on the accelerators/GPUs in the system (MI300 only).",
"name": "Memory Partition",
},
)
##########################################
## B. SoC Specs
@@ -235,93 +319,168 @@ class MachineSpecs:
gpu_model: str = field(
default=None,
metadata={
'doc': 'The product name of the accelerators/GPUs in the system.',
'name': 'GPU Model'})
"doc": "The product name of the accelerators/GPUs in the system.",
"name": "GPU Model",
},
)
gpu_arch: str = field(
default=None,
metadata={
'doc': 'The architecture name of the accelerators/GPUs in the system,\n'
'as used by (e.g.,) the AMDGPU backed of LLVM.',
'name': 'GPU Arch'})
gpu_l1: str = field(default=None, metadata={'doc':
"The size of the vL1D cache (per compute-unit) on the accelerators/GPUs in the system in KiB",
'name': 'GPU L1'})
gpu_l2: str = field(default=None, metadata={'doc':
"The size of the vL1D cache (per compute-unit) on the accelerators/GPUs in the system in KiB",
'name': 'GPU L2'})
cu_per_gpu: str = field(default=None, metadata={'doc':
"The total number of compute units per accelerator/GPU in the system. On systems with configurable\n"
"partitioning, (e.g., MI300) this is the total number of compute units in a partition.",
'name': 'CU per GPU'})
simd_per_cu: str = field(default=None, metadata={'doc':
"The number of SIMD processors in a compute unit for the accelerators/GPUs in the system.",
'name': 'SIMD per CU'})
se_per_gpu: str = field(default=None, metadata={'doc':
'The number of shader engines on the accelerators/GPUs in the system. On systems with configurable\n'
'partitioning, (e.g., MI300) this is the total number of shader engines in a partition.',
'name': 'SE per GPU'})
wave_size: str = field(default=None, metadata={'doc':
'The number work-items in a wavefront on the accelerators/GPUs in the system.',
'name': 'Wave Size'})
workgroup_max_size: str = field(default=None, metadata={'doc':
'The maximum number of work-items in a workgroup on the accelerators/GPUs in the system.',
'name': 'Workgroup Max Size'})
max_waves_per_cu: str = field(default=None, metadata={'doc':
'The maximum number of wavefronts that can be resident on a compute unit on the\n'
'accelerators/GPUs in the system',
'name': 'Max Waves per CU'})
max_sclk: str = field(default=None, metadata={'doc':
'The maximum engine (compute-unit) clock rate of the accelerators/GPUs in the system.',
'name': 'Max SCLK',
'unit': 'MHz'})
max_mclk: str = field(default=None, metadata={'doc':
'The maximum memory clock rate of the accelerators/GPUs in the system.',
'name': 'Max MCLK',
'unit': 'MHz'})
cur_sclk: str = field(default=None, metadata={'doc':
'[RESERVED] The current engine (compute unit) clock rate of the accelerators/GPUs in the system. Unused.',
'name': 'Cur SCLK',
'unit': 'MHz'})
cur_mclk: str = field(default=None, metadata={'doc':
'[RESERVED] The current memory clock rate of the accelerators/GPUs in the system. Unused.',
'name': 'Cur MCLK',
'unit': 'MHz'})
_l2_banks: str = None # NB: This only used in flatten_tcc_info_across_hbm_stacks()
total_l2_chan: str = field(default=None, metadata={
'doc': 'The maximum number of L2 cache channels on the accelerators/GPUs in the system. On systems with\n'
'configurable partitioning, (e.g., MI300) this is the total number of L2 cache channels in a partition.',
'name': 'Total L2 Channels'})
lds_banks_per_cu: str = field(default=None, metadata={'doc':
'The number of banks in the LDS for a compute unit on the accelerators/GPUs in the system.',
'name': 'LDS Banks per CU'})
sqc_per_gpu: str = field(default=None, metadata={
'doc': 'The number of L1I/sL1D caches on the accelerators/GPUs in the system. On systems with\n'
'configurable partitioning, (e.g., MI300) this is the total number of L1I/sL1D caches in a partition.',
'name': 'SQC per GPU'})
pipes_per_gpu: str = field(default=None, metadata={
'doc': 'The number of scheduler-pipes on the accelerators/GPUs in the system.',
'name': 'Pipes per GPU'})
hbm_bw: str = field(default=None, metadata={
'doc': 'The peak theoretical HBM bandwidth for the accelerators/GPUs in the system. On systems with\n'
'configurable partitioning, (e.g., MI300) this is the peak theoretical HBM bandwidth for a partition.',
'name': 'HBM BW',
'unit': 'MB/s'})
num_xcd: str = field(default=None, metadata={
'doc': 'The total number of accelerator complex dies in a compute partition on the accelerators/GPUs in the\n'
'system. For accelerators without partitioning (i.e., pre-MI300), this is considered to be one.',
'name': 'Num XCDs',
'unit': 'MB/s'})
"doc": "The architecture name of the accelerators/GPUs in the system,\n"
"as used by (e.g.,) the AMDGPU backed of LLVM.",
"name": "GPU Arch",
},
)
gpu_l1: str = field(
default=None,
metadata={
"doc": "The size of the vL1D cache (per compute-unit) on the accelerators/GPUs in the system in KiB",
"name": "GPU L1",
},
)
gpu_l2: str = field(
default=None,
metadata={
"doc": "The size of the vL1D cache (per compute-unit) on the accelerators/GPUs in the system in KiB",
"name": "GPU L2",
},
)
cu_per_gpu: str = field(
default=None,
metadata={
"doc": "The total number of compute units per accelerator/GPU in the system. On systems with configurable\n"
"partitioning, (e.g., MI300) this is the total number of compute units in a partition.",
"name": "CU per GPU",
},
)
simd_per_cu: str = field(
default=None,
metadata={
"doc": "The number of SIMD processors in a compute unit for the accelerators/GPUs in the system.",
"name": "SIMD per CU",
},
)
se_per_gpu: str = field(
default=None,
metadata={
"doc": "The number of shader engines on the accelerators/GPUs in the system. On systems with configurable\n"
"partitioning, (e.g., MI300) this is the total number of shader engines in a partition.",
"name": "SE per GPU",
},
)
wave_size: str = field(
default=None,
metadata={
"doc": "The number work-items in a wavefront on the accelerators/GPUs in the system.",
"name": "Wave Size",
},
)
workgroup_max_size: str = field(
default=None,
metadata={
"doc": "The maximum number of work-items in a workgroup on the accelerators/GPUs in the system.",
"name": "Workgroup Max Size",
},
)
max_waves_per_cu: str = field(
default=None,
metadata={
"doc": "The maximum number of wavefronts that can be resident on a compute unit on the\n"
"accelerators/GPUs in the system",
"name": "Max Waves per CU",
},
)
max_sclk: str = field(
default=None,
metadata={
"doc": "The maximum engine (compute-unit) clock rate of the accelerators/GPUs in the system.",
"name": "Max SCLK",
"unit": "MHz",
},
)
max_mclk: str = field(
default=None,
metadata={
"doc": "The maximum memory clock rate of the accelerators/GPUs in the system.",
"name": "Max MCLK",
"unit": "MHz",
},
)
cur_sclk: str = field(
default=None,
metadata={
"doc": "[RESERVED] The current engine (compute unit) clock rate of the accelerators/GPUs in the system. Unused.",
"name": "Cur SCLK",
"unit": "MHz",
},
)
cur_mclk: str = field(
default=None,
metadata={
"doc": "[RESERVED] The current memory clock rate of the accelerators/GPUs in the system. Unused.",
"name": "Cur MCLK",
"unit": "MHz",
},
)
_l2_banks: str = None # NB: This only used in flatten_tcc_info_across_hbm_stacks()
total_l2_chan: str = field(
default=None,
metadata={
"doc": "The maximum number of L2 cache channels on the accelerators/GPUs in the system. On systems with\n"
"configurable partitioning, (e.g., MI300) this is the total number of L2 cache channels in a partition.",
"name": "Total L2 Channels",
},
)
lds_banks_per_cu: str = field(
default=None,
metadata={
"doc": "The number of banks in the LDS for a compute unit on the accelerators/GPUs in the system.",
"name": "LDS Banks per CU",
},
)
sqc_per_gpu: str = field(
default=None,
metadata={
"doc": "The number of L1I/sL1D caches on the accelerators/GPUs in the system. On systems with\n"
"configurable partitioning, (e.g., MI300) this is the total number of L1I/sL1D caches in a partition.",
"name": "SQC per GPU",
},
)
pipes_per_gpu: str = field(
default=None,
metadata={
"doc": "The number of scheduler-pipes on the accelerators/GPUs in the system.",
"name": "Pipes per GPU",
},
)
hbm_bw: str = field(
default=None,
metadata={
"doc": "The peak theoretical HBM bandwidth for the accelerators/GPUs in the system. On systems with\n"
"configurable partitioning, (e.g., MI300) this is the peak theoretical HBM bandwidth for a partition.",
"name": "HBM BW",
"unit": "MB/s",
},
)
num_xcd: str = field(
default=None,
metadata={
"doc": "The total number of accelerator complex dies in a compute partition on the accelerators/GPUs in the\n"
"system. For accelerators without partitioning (i.e., pre-MI300), this is considered to be one.",
"name": "Num XCDs",
"unit": "MB/s",
},
)
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"
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 = {}
@@ -332,20 +491,25 @@ class MachineSpecs:
value = getattr(self, name)
if value is None:
# check if we've marked it optional
if field.metadata and 'optional' in field.metadata and field.metadata['optional']:
if (
field.metadata
and "optional" in field.metadata
and field.metadata["optional"]
):
pass
else:
#TODO: use proper logging function when that's merged
# TODO: use proper logging function when that's merged
logging.warning(
f"WARNING: Incomplete class definition for {self.gpu_arch}. "
f"Expecting populated {name} but detected None.")
f"Expecting populated {name} but detected None."
)
all_populated = False
data[name] = value
if not all_populated:
error("Missing specs fields for %s" % self.gpu_arch)
return pd.DataFrame(data, index=[0])
def __repr__(self):
topstr = "Machine Specifications: describing the state of the machine that Omniperf data was collected on.\n"
data = []
@@ -356,32 +520,30 @@ class MachineSpecs:
value = getattr(self, name)
if field.metadata:
# check out of table before any re-naming for pretty-printing
if 'intable' in field.metadata and not field.metadata['intable']:
if name == 'version':
topstr += f'Output version: {value}\n'
if "intable" in field.metadata and not field.metadata["intable"]:
if name == "version":
topstr += f"Output version: {value}\n"
else:
error(f"Unknown out of table printing field: {name}")
continue
if 'name' in field.metadata:
name = field.metadata['name']
if 'unit' in field.metadata:
_data['Unit'] = field.metadata['unit']
if 'doc' in field.metadata:
_data['Description'] = field.metadata['doc']
_data['Spec'] = name
_data['Value'] = value
if "name" in field.metadata:
name = field.metadata["name"]
if "unit" in field.metadata:
_data["Unit"] = field.metadata["unit"]
if "doc" in field.metadata:
_data["Description"] = field.metadata["doc"]
_data["Spec"] = name
_data["Value"] = value
data.append(_data)
df = pd.DataFrame(data)
columns = ['Spec', 'Value']
if 'Description' in df.columns:
columns += ['Description']
if 'Unit' in df.columns:
columns += ['Unit']
columns = ["Spec", "Value"]
if "Description" in df.columns:
columns += ["Description"]
if "Unit" in df.columns:
columns += ["Unit"]
df = df[columns]
df = df.fillna('')
return (
topstr +
get_table_string(df, transpose=False, decimal=2))
df = df.fillna("")
return topstr + get_table_string(df, transpose=False, decimal=2)
def get_rocm_ver():
@@ -403,14 +565,20 @@ def get_rocm_ver():
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)
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
def run(cmd,exit_on_error=False):
def run(cmd, exit_on_error=False):
try:
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError as e:
error(f"Unable to parse specs. Can't find ROCm asset: {e.filename}\nTry passing a path to an existing workload results in 'analyze' mode.")
error(
f"Unable to parse specs. Can't find ROCm asset: {e.filename}\nTry passing a path to an existing workload results in 'analyze' mode."
)
if exit_on_error:
if cmd[0] == "rocm-smi":
@@ -434,35 +602,28 @@ 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)
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)
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']:
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)
def total_xcds(archname, compute_partition):
# check MI300 has a valid compute partition
mi300a_archs = ["mi300a_a0", "mi300a_a1"]
mi300x_archs = ["mi300x_a0", "mi300x_a1"]
if archname.lower() in mi300a_archs + mi300x_archs \
and compute_partition == "NA":
error("Invalid compute partition found for {}".format(archname))
if archname.lower() in mi300a_archs + mi300x_archs and compute_partition == "NA":
error("Invalid compute partition found for {}".format(archname))
if archname.lower() not in mi300a_archs + mi300x_archs:
return 1
# from the whitepaper
@@ -484,8 +645,11 @@ def total_xcds(archname, compute_partition):
if compute_partition.lower() == "cpx":
if archname.lower() in mi300x_archs:
return 2
error("Unknown compute partition / arch found for {} / {}".format(
compute_partition, archname))
error(
"Unknown compute partition / arch found for {} / {}".format(
compute_partition, archname
)
)
if __name__ == "__main__":
+10 -6
View File
@@ -51,10 +51,12 @@ def string_multiple_lines(source, width, max_rows):
def get_table_string(df, transpose=False, decimal=2):
return tabulate(df.transpose() if transpose else df,
headers="keys",
tablefmt="fancy_grid",
floatfmt="." + str(decimal) + "f")
return tabulate(
df.transpose() if transpose else df,
headers="keys",
tablefmt="fancy_grid",
floatfmt="." + str(decimal) + "f",
)
def show_all(args, runs, archConfigs, output):
@@ -221,9 +223,11 @@ def show_all(args, runs, archConfigs, output):
# df when load it, because we need those items in column.
# For metric_table, we only need to show the data in column
# fash for now.
transpose = (type != "raw_csv_table"
transpose = (
type != "raw_csv_table"
and "columnwise" in table_config
and table_config["columnwise"] == True)
and table_config["columnwise"] == True
)
ss += (
get_table_string(df, transpose=transpose, decimal=args.decimal)
+ "\n"
+21 -13
View File
@@ -198,10 +198,11 @@ def capture_subprocess_output(subprocess_args, new_env=None):
return (success, output)
def run_prof(fname, profiler_options, workload_dir, mspec):
fbase = os.path.splitext(os.path.basename(fname))[0]
logging.debug("pmc file: %s" % str(os.path.basename(fname)))
# standard rocprof options
@@ -210,7 +211,12 @@ def run_prof(fname, profiler_options, workload_dir, mspec):
# set required env var for mi300
new_env = None
if (mspec.gpu_model.lower() == "mi300x_a0" or mspec.gpu_model.lower() == "mi300x_a1" or mspec.gpu_model.lower() == "mi300a_a0" or mspec.gpu_model.lower() == "mi300a_a1") and (
if (
mspec.gpu_model.lower() == "mi300x_a0"
or mspec.gpu_model.lower() == "mi300x_a1"
or mspec.gpu_model.lower() == "mi300a_a0"
or mspec.gpu_model.lower() == "mi300a_a1"
) and (
os.path.basename(fname) == "pmc_perf_13.txt"
or os.path.basename(fname) == "pmc_perf_14.txt"
or os.path.basename(fname) == "pmc_perf_15.txt"
@@ -233,9 +239,7 @@ def run_prof(fname, profiler_options, workload_dir, mspec):
# flatten tcc for applicable mi300 input
f = path(workload_dir + "/out/pmc_1/results_" + fbase + ".csv")
hbm_stack_num = get_hbm_stack_num(mspec.gpu_model, mspec.memory_partition)
df = flatten_tcc_info_across_hbm_stacks(
f, hbm_stack_num, int(mspec._l2_banks)
)
df = flatten_tcc_info_across_hbm_stacks(f, hbm_stack_num, int(mspec._l2_banks))
df.to_csv(f, index=False)
if os.path.exists(workload_dir + "/out"):
@@ -295,13 +299,16 @@ def replace_timestamps(workload_dir):
)
logging.warning(warning + "\n")
def gen_sysinfo(workload_name, workload_dir, ip_blocks, app_cmd, skip_roof, roof_only, mspec):
def gen_sysinfo(
workload_name, workload_dir, ip_blocks, app_cmd, skip_roof, roof_only, mspec
):
df = mspec.get_class_members()
# Append workload information to machine specs
df['command'] = app_cmd
df['workload_name'] = workload_name
df["command"] = app_cmd
df["workload_name"] = workload_name
blocks = []
if ip_blocks == None:
t = ["SQ", "LDS", "SQC", "TA", "TD", "TCP", "TCC", "SPI", "CPC", "CPF"]
@@ -310,11 +317,12 @@ def gen_sysinfo(workload_name, workload_dir, ip_blocks, app_cmd, skip_roof, roof
blocks += ip_blocks
if mspec.gpu_arch == "gfx90a" and (not skip_roof):
blocks.append("roofline")
df['ip_blocks'] = "|".join(blocks)
df["ip_blocks"] = "|".join(blocks)
# Save csv
df.to_csv(workload_dir + "/" + "sysinfo.csv", index=False)
def detect_roofline(mspec):
rocm_ver = mspec.rocm_version[:1]
@@ -380,9 +388,9 @@ def run_rocscope(args, fname):
logging.error(result.stderr.decode("ascii"))
sys.exit(1)
def mibench(args, mspec):
"""Run roofline microbenchmark to generate peak BW and FLOP measurements.
"""
"""Run roofline microbenchmark to generate peak BW and FLOP measurements."""
logging.info("[roofline] No roofline data found. Generating...")
distro_map = {"platform:el8": "rhel8", "15.3": "sle15sp3", "20.04": "ubuntu20_04"}