From d6f45411eb5d93e76199a928cd691ffe181e0756 Mon Sep 17 00:00:00 2001 From: colramos-amd Date: Fri, 1 Mar 2024 11:52:31 -0600 Subject: [PATCH] Python formatting Signed-off-by: colramos-amd --- src/argparser.py | 4 +- src/omniperf_analyze/analysis_base.py | 4 +- src/omniperf_analyze/analysis_cli.py | 12 +- src/omniperf_analyze/analysis_webui.py | 20 +- src/omniperf_base.py | 47 +- src/omniperf_profile/profiler_base.py | 11 +- src/omniperf_soc/soc_base.py | 72 +-- src/omniperf_soc/soc_gfx906.py | 18 +- src/omniperf_soc/soc_gfx908.py | 19 +- src/omniperf_soc/soc_gfx90a.py | 34 +- src/omniperf_soc/soc_gfx940.py | 18 +- src/omniperf_soc/soc_gfx941.py | 16 +- src/omniperf_soc/soc_gfx942.py | 18 +- src/roofline.py | 110 +++-- src/utils/gui.py | 6 +- src/utils/parser.py | 21 +- src/utils/specs.py | 588 ++++++++++++++++--------- src/utils/tty.py | 16 +- src/utils/utils.py | 34 +- 19 files changed, 688 insertions(+), 380 deletions(-) diff --git a/src/argparser.py b/src/argparser.py index f1961407f6..90319185b9 100644 --- a/src/argparser.py +++ b/src/argparser.py @@ -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", diff --git a/src/omniperf_analyze/analysis_base.py b/src/omniperf_analyze/analysis_base.py index 8d65e156ac..563a413acb 100644 --- a/src/omniperf_analyze/analysis_base.py +++ b/src/omniperf_analyze/analysis_base.py @@ -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 diff --git a/src/omniperf_analyze/analysis_cli.py b/src/omniperf_analyze/analysis_cli.py index 7009d44115..426a7feb5a 100644 --- a/src/omniperf_analyze/analysis_cli.py +++ b/src/omniperf_analyze/analysis_cli.py @@ -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, ) diff --git a/src/omniperf_analyze/analysis_webui.py b/src/omniperf_analyze/analysis_webui.py index 172c64937a..c5d41c3631 100644 --- a/src/omniperf_analyze/analysis_webui.py +++ b/src/omniperf_analyze/analysis_webui.py @@ -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): diff --git a/src/omniperf_base.py b/src/omniperf_base.py index 57ef9176e7..0f98ba03a6 100644 --- a/src/omniperf_base.py +++ b/src/omniperf_base.py @@ -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) diff --git a/src/omniperf_profile/profiler_base.py b/src/omniperf_profile/profiler_base.py index 86d13e9f05..a8f830746e 100644 --- a/src/omniperf_profile/profiler_base.py +++ b/src/omniperf_profile/profiler_base.py @@ -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, diff --git a/src/omniperf_soc/soc_base.py b/src/omniperf_soc/soc_base.py index e5d197943e..b28d107ed7 100644 --- a/src/omniperf_soc/soc_base.py +++ b/src/omniperf_soc/soc_base.py @@ -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): diff --git a/src/omniperf_soc/soc_gfx906.py b/src/omniperf_soc/soc_gfx906.py index ec218f3bd8..66be19cc99 100644 --- a/src/omniperf_soc/soc_gfx906.py +++ b/src/omniperf_soc/soc_gfx906.py @@ -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 diff --git a/src/omniperf_soc/soc_gfx908.py b/src/omniperf_soc/soc_gfx908.py index 2a7db8d915..05790e2da5 100644 --- a/src/omniperf_soc/soc_gfx908.py +++ b/src/omniperf_soc/soc_gfx908.py @@ -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() diff --git a/src/omniperf_soc/soc_gfx90a.py b/src/omniperf_soc/soc_gfx90a.py index 7c0f5e1c2e..ffff6ffd08 100644 --- a/src/omniperf_soc/soc_gfx90a.py +++ b/src/omniperf_soc/soc_gfx90a.py @@ -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 + ) diff --git a/src/omniperf_soc/soc_gfx940.py b/src/omniperf_soc/soc_gfx940.py index b8c8213d0b..0f573538bb 100644 --- a/src/omniperf_soc/soc_gfx940.py +++ b/src/omniperf_soc/soc_gfx940.py @@ -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( diff --git a/src/omniperf_soc/soc_gfx941.py b/src/omniperf_soc/soc_gfx941.py index 638bc2bedb..884d15d1e7 100644 --- a/src/omniperf_soc/soc_gfx941.py +++ b/src/omniperf_soc/soc_gfx941.py @@ -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( diff --git a/src/omniperf_soc/soc_gfx942.py b/src/omniperf_soc/soc_gfx942.py index e7db703832..b0e03c6946 100644 --- a/src/omniperf_soc/soc_gfx942.py +++ b/src/omniperf_soc/soc_gfx942.py @@ -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( diff --git a/src/roofline.py b/src/roofline.py index bd1f4e5690..68064429cf 100644 --- a/src/roofline.py +++ b/src/roofline.py @@ -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 diff --git a/src/utils/gui.py b/src/utils/gui.py index 8008dfbbca..d177f84d37 100644 --- a/src/utils/gui.py +++ b/src/utils/gui.py @@ -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, diff --git a/src/utils/parser.py b/src/utils/parser.py index a54ad75655..f522e318ff 100644 --- a/src/utils/parser.py +++ b/src/utils/parser.py @@ -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() - - diff --git a/src/utils/specs.py b/src/utils/specs.py index f116c44ee8..5f0eaad193 100644 --- a/src/utils/specs.py +++ b/src/utils/specs.py @@ -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__": diff --git a/src/utils/tty.py b/src/utils/tty.py index 05fceb3363..1e266704ce 100644 --- a/src/utils/tty.py +++ b/src/utils/tty.py @@ -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" diff --git a/src/utils/utils.py b/src/utils/utils.py index c1ed0e9ce1..f372e8598e 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -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"}