From 5352eafde4a23ea11bd077536d5c4c419143ecd6 Mon Sep 17 00:00:00 2001 From: colramos-amd Date: Tue, 20 Feb 2024 13:34:56 -0600 Subject: [PATCH] Reorganize the specs module to reduce duplicate code Signed-off-by: colramos-amd [ROCm/rocprofiler-compute commit: 3b1b8d7b5b202d8279360be9bf1ebfdc61164b5e] --- projects/rocprofiler-compute/src/argparser.py | 1 + .../src/omniperf_analyze/analysis_base.py | 5 +- .../src/omniperf_analyze/analysis_cli.py | 12 +- .../src/omniperf_analyze/analysis_webui.py | 23 +- .../rocprofiler-compute/src/omniperf_base.py | 79 +-- .../src/omniperf_profile/profiler_base.py | 11 +- .../src/omniperf_soc/soc_base.py | 147 ++++-- .../src/omniperf_soc/soc_gfx906.py | 40 +- .../src/omniperf_soc/soc_gfx908.py | 43 +- .../src/omniperf_soc/soc_gfx90a.py | 53 +- .../src/omniperf_soc/soc_gfx940.py | 42 +- .../src/omniperf_soc/soc_gfx941.py | 42 +- .../src/omniperf_soc/soc_gfx942.py | 42 +- projects/rocprofiler-compute/src/roofline.py | 108 ++-- .../src/utils/db_connector.py | 6 +- .../rocprofiler-compute/src/utils/parser.py | 24 +- .../src/utils/roofline_calc.py | 2 +- .../rocprofiler-compute/src/utils/schema.py | 1 - .../rocprofiler-compute/src/utils/specs.py | 479 +++++++----------- .../rocprofiler-compute/src/utils/utils.py | 116 +---- 20 files changed, 495 insertions(+), 781 deletions(-) diff --git a/projects/rocprofiler-compute/src/argparser.py b/projects/rocprofiler-compute/src/argparser.py index 150095e8d1..f1961407f6 100644 --- a/projects/rocprofiler-compute/src/argparser.py +++ b/projects/rocprofiler-compute/src/argparser.py @@ -90,6 +90,7 @@ 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.") profile_group.add_argument( "-n", diff --git a/projects/rocprofiler-compute/src/omniperf_analyze/analysis_base.py b/projects/rocprofiler-compute/src/omniperf_analyze/analysis_base.py index 18cab8b55a..893edb1ed6 100644 --- a/projects/rocprofiler-compute/src/omniperf_analyze/analysis_base.py +++ b/projects/rocprofiler-compute/src/omniperf_analyze/analysis_base.py @@ -135,7 +135,7 @@ class OmniAnalyze_Base: # load required configs for d in self.__args.path: sys_info = file_io.load_sys_info(Path(d[0], "sysinfo.csv")) - arch = sys_info.iloc[0]["gpu_soc"] + arch = sys_info.iloc[0]["arch"] args = self.__args self.generate_configs( arch, @@ -155,10 +155,9 @@ class OmniAnalyze_Base: w.sys_info, self.__args.specs_correction ) w.avail_ips = w.sys_info["ip_blocks"].item().split("|") - arch = w.sys_info.iloc[0]["gpu_soc"] + arch = w.sys_info.iloc[0]["arch"] w.dfs = copy.deepcopy(self._arch_configs[arch].dfs) w.dfs_type = self._arch_configs[arch].dfs_type - w.soc_spec = self.get_socs()[arch].get_soc_param() self._runs[d[0]] = w return self._runs diff --git a/projects/rocprofiler-compute/src/omniperf_analyze/analysis_cli.py b/projects/rocprofiler-compute/src/omniperf_analyze/analysis_cli.py index b240b72460..179c0ce840 100644 --- a/projects/rocprofiler-compute/src/omniperf_analyze/analysis_cli.py +++ b/projects/rocprofiler-compute/src/omniperf_analyze/analysis_cli.py @@ -70,17 +70,13 @@ 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_soc"] - ], - self._output, + self._arch_configs[self._runs[self.get_args().path[0][0]].sys_info.iloc[0]["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_soc"] - ], - self._output, + self._arch_configs[self._runs[self.get_args().path[0][0]].sys_info.iloc[0]["arch"]], + self._output ) diff --git a/projects/rocprofiler-compute/src/omniperf_analyze/analysis_webui.py b/projects/rocprofiler-compute/src/omniperf_analyze/analysis_webui.py index 5192e7c88d..6c4b74acd2 100644 --- a/projects/rocprofiler-compute/src/omniperf_analyze/analysis_webui.py +++ b/projects/rocprofiler-compute/src/omniperf_analyze/analysis_webui.py @@ -23,7 +23,7 @@ ##############################################################################el from omniperf_analyze.analysis_base import OmniAnalyze_Base -from utils.utils import demarcate +from utils.utils import demarcate, error from utils import file_io, parser from utils.gui import build_bar_chart, build_table_chart @@ -163,12 +163,12 @@ class webui_analysis(OmniAnalyze_Base): # update roofline for visualization in GUI self.get_socs()[self.arch].analysis_setup( roofline_parameters={ - "path_to_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,12 +284,10 @@ 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_soc"] - + self.arch = self._runs[self.dest_dir].sys_info.iloc[0]['GPU'] + else: - self.error( - "Multiple runs not yet supported in GUI. Retry without --gui flag." - ) + error("Multiple runs not yet supported in GUI. Retry without --gui flag.") @demarcate def run_analysis(self): @@ -308,6 +306,7 @@ class webui_analysis(OmniAnalyze_Base): input_filters, self._arch_configs[self.arch], ) + # Here I expect that self._runs[].raw_pmc will no longer be populated if args.random_port: self.app.run_server( debug=False, host="0.0.0.0", port=random.randint(1024, 49151) diff --git a/projects/rocprofiler-compute/src/omniperf_base.py b/projects/rocprofiler-compute/src/omniperf_base.py index 71cf5ade48..86c2d60afd 100644 --- a/projects/rocprofiler-compute/src/omniperf_base.py +++ b/projects/rocprofiler-compute/src/omniperf_base.py @@ -28,16 +28,8 @@ import sys import os from pathlib import Path import shutil -from utils.specs import get_machine_specs -from utils.utils import ( - demarcate, - trace_logger, - get_version, - get_version_display, - detect_rocprof, - error, - get_submodules, -) +from utils.specs import MachineSpecs +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 @@ -68,6 +60,7 @@ class Omniperf: } self.__options = {} self.__supported_archs = SUPPORTED_ARCHS + self.__mspec: MachineSpecs = None # to be initalized in load_soc_specs() self.setup_logging() self.set_version() @@ -163,33 +156,22 @@ class Omniperf: return @demarcate - def detect_soc(self, sys_info=pd.DataFrame()): - """Load OmniSoC instance for Omniperf run""" - # in case of analyze mode, we can explicitly specify an arch - # rather than detect from rocminfo - if sys_info.empty: - mspec = get_machine_specs(0) - arch = mspec.arch - target = mspec.GPU - else: - arch = sys_info.iloc[0]["gpu_soc"] - target = sys_info.iloc[0]["name"] + def load_soc_specs(self, sysinfo=None): + """Load OmniSoC instance for Omniperf run + """ + if not sysinfo is None: + self.__mspec = MachineSpecs(self.__args, sysinfo) - # instantiate underlying SoC support class - # in case of analyze mode, __soc can accommodate multiple archs + arch = self.__mspec.arch + + # NB: This checker is a bit redundent. We already check this in specs module if arch not in self.__supported_archs.keys(): error("%s is an unsupported SoC" % arch) - else: - self.__soc_name.add(target) - if hasattr(self.__args, "target"): - self.__args.target = target - soc_module = importlib.import_module("omniperf_soc.soc_" + arch) - soc_class = getattr(soc_module, arch + "_soc") - self.__soc[arch] = soc_class(self.__args) - - logging.info("SoC = %s" % self.__soc_name) - return arch + 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 @demarcate def parse_args(self): @@ -207,7 +189,7 @@ class Omniperf: self.__args = parser.parse_args() if self.__args.specs: - print(get_machine_specs(0)) + print(MachineSpecs(self.__args)) sys.exit(0) if self.__args.mode == None: parser.print_help(sys.stderr) @@ -218,47 +200,36 @@ class Omniperf: @demarcate def run_profiler(self): self.print_graphic() - targ_arch = self.detect_soc() + self.load_soc_specs() # 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.__args.target - ) + self.__args.path = os.path.join(self.__args.path, self.__args.name, self.__mspec.GPU) 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[targ_arch] - ) + profiler = rocprof_v1_profiler(self.__args, self.__profiler_mode, self.__soc[self.__mspec.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[targ_arch] - ) + profiler = rocprof_v2_profiler(self.__args, self.__profiler_mode, self.__soc[self.__mspec.arch]) elif self.__profiler_mode == "rocscope": from omniperf_profile.profiler_rocscope import rocscope_profiler - - profiler = rocscope_profiler( - self.__args, self.__profiler_mode, self.__soc[targ_arch] - ) + profiler = rocscope_profiler(self.__args, self.__profiler_mode, self.__soc[self.__mspec.arch]) else: logging.error("Unsupported profiler") sys.exit(1) # ----------------------- # run profiling workflow - # ----------------------- - self.__soc[targ_arch].profiling_setup() + #----------------------- + self.__soc[self.__mspec.arch].profiling_setup() profiler.pre_processing() profiler.run_profiling(self.__version["ver"], config.prog) profiler.post_processing() - self.__soc[targ_arch].post_profiling() + self.__soc[self.__mspec.arch].post_profiling() return @@ -305,7 +276,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")) - self.detect_soc(sys_info) + self.load_soc_specs(sys_info) analyzer.set_soc(self.__soc) analyzer.pre_processing() diff --git a/projects/rocprofiler-compute/src/omniperf_profile/profiler_base.py b/projects/rocprofiler-compute/src/omniperf_profile/profiler_base.py index 3915425bca..c144f61c58 100644 --- a/projects/rocprofiler-compute/src/omniperf_profile/profiler_base.py +++ b/projects/rocprofiler-compute/src/omniperf_profile/profiler_base.py @@ -312,7 +312,7 @@ class OmniProfiler_Base: # log basic info logging.info(str(prog) + " ver: " + str(version)) logging.info("Path: " + str(os.path.abspath(self.__args.path))) - logging.info("Target: " + str(self.__args.target)) + logging.info("Target: " + str(self._soc._mspec.GPU)) logging.info("Command: " + str(self.__args.remaining)) logging.info("Kernel Selection: " + str(self.__args.kernel)) logging.info("Dispatch Selection: " + str(self.__args.dispatch)) @@ -374,14 +374,10 @@ class OmniProfiler_Base: if self.__profiler == "rocprofv1" or self.__profiler == "rocprofv2": run_prof( - fname=fname, - # workload_dir=self.get_args().path, - # perfmon_dir=self.__perfmon_dir, - # cmd=self.__args.remaining, - # target=self.__args.target, + fname=fname, profiler_options=options, - target=self.__args.target, workload_dir=self.get_args().path, + mspec=self._soc._mspec, ) elif self.__profiler == "rocscope": @@ -403,6 +399,7 @@ class OmniProfiler_Base: app_cmd=self.__args.remaining, skip_roof=self.__args.no_roof, roof_only=self.__args.roof_only, + mspec=self._soc._mspec, ) diff --git a/projects/rocprofiler-compute/src/omniperf_soc/soc_base.py b/projects/rocprofiler-compute/src/omniperf_soc/soc_base.py index a684998cc9..5a31c969e5 100644 --- a/projects/rocprofiler-compute/src/omniperf_soc/soc_base.py +++ b/projects/rocprofiler-compute/src/omniperf_soc/soc_base.py @@ -33,53 +33,46 @@ import numpy as np from utils.utils import demarcate from pathlib import Path +from omniperf_base import SUPPORTED_ARCHS -class OmniSoC_Base: - def __init__(self, args): +class OmniSoC_Base(): + def __init__(self,args,mspec): # new info field will contain rocminfo or sysinfo to populate properties self.__args = args - self.__name = None # SoC name + 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 - if self.__args.path == os.path.join(os.getcwd(), "workloads"): - self.__workload_dir = os.path.join( - self.__args.path, self.__args.name, self.__args.target - ) - else: - self.__workload_dir = self.__args.path + 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) + else: + self.__workload_dir = self.__args.path def __hash__(self): - return hash(self.__name) - + return hash(self.__arch) def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented - return self.__name == other.get_soc() + return self.__arch == other.get_soc() def set_perfmon_dir(self, path: str): self.__perfmon_dir = path def set_perfmon_config(self, config: dict): self.__perfmon_config = config - - def set_soc_param(self, param: dict): - self.__soc_params = param - 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_soc_name(self, soc: str): - self.__name = soc - - def get_soc_name(self): - return self.__name - + def set_arch(self, arch: str): + self.__arch = arch + def get_arch(self): + return self.__arch def get_args(self): return self.__args @@ -95,6 +88,83 @@ class OmniSoC_Base: # assume no SoC specific options and return empty list by default return [] + @demarcate + def populate_mspec(self): + from utils.specs import search, run + + if not hasattr(self._mspec, "_rocminfo"): + return + + # load stats from rocminfo + self._mspec.L1 = "" + self._mspec.L2 = "" + for idx2, linetext in enumerate(self._mspec._rocminfo): + key = search(r"^\s*L1:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.L1 = key + continue + + key = search(r"^\s*L2:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.L2 = key + continue + + key = search(r"^\s*Max Clock Freq\. \(MHz\):\s+([0-9]+)", linetext) + if key != None: + self._mspec.max_sclk = key + continue + + key = search(r"^\s*Compute Unit:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.CU = key + continue + + key = search(r"^\s*SIMDs per CU:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.SIMD = key + continue + + key = search(r"^\s*Shader Engines:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.SE = key + continue + + key = search(r"^\s*Wavefront Size:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.wave_size = key + continue + + key = search(r"^\s*Workgroup Max Size:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.workgroup_max_size = key + continue + + key = search(r"^\s*Max Waves Per CU:\s+ ([a-zA-Z0-9]+)\s*", linetext) + if key != None: + self._mspec.max_waves_per_cu = key + break + + # 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) + + # 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) + self._mspec.cur_sclk = self._mspec.max_sclk + self._mspec.cur_mclk = self._mspec.max_mclk + + self._mspec.compute_partition = "" + self._mspec.memory_partition = "" + + # specify gpu name for gfx942 hardware + self._mspec.GPU = list(SUPPORTED_ARCHS[self._mspec.arch].keys())[0].upper() + if self._mspec.GPU == "MI300": + self._mspec.GPU = list(SUPPORTED_ARCHS[self._mspec.arch].values())[0][0] + if (self._mspec.arch == "gfx942") and ("MI300A" in self._mspec._rocminfo): + self._mspec.GPU = "MI300A_A1" + if (self._mspec.arch == "gfx942") and ("MI300A" not in self._mspec._rocminfo): + self._mspec.GPU = "MI300X_A1" + @demarcate def perfmon_filter(self, roofline_perfmon_only: bool): """Filter default performance counter set based on user arguments""" @@ -114,9 +184,7 @@ 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.__name + "/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: @@ -152,19 +220,22 @@ class OmniSoC_Base: # ---------------------------------------------------- @abstractmethod def profiling_setup(self): - """Perform any SoC-specific setup prior to profiling.""" - logging.debug("[profiling] perform SoC profiling setup for %s" % self.__name) + """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.""" - logging.debug("[profiling] perform SoC post processing for %s" % self.__name) + """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.""" - logging.debug("[analysis] perform SoC analysis setup for %s" % self.__name) - + """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/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx906.py b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx906.py index d31d4e82bf..bf1ed96cf4 100644 --- a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx906.py +++ b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx906.py @@ -27,32 +27,11 @@ import config from omniperf_soc.soc_base import OmniSoC_Base from utils.utils import demarcate, error -SOC_PARAM = { - "numSE": 4, - "numCU": 60, - "numSIMD": 240, - "numPipes": 4, - "numWavesPerCU": 40, - "numSQC": 15, - "L2Banks": 16, - "LDSBanks": 32, - "Freq": 1725, - "mclk": 1000, -} - - -class gfx906_soc(OmniSoC_Base): - def __init__(self, args): - super().__init__(args) - self.set_soc_name("gfx906") - self.set_perfmon_dir( - os.path.join( - str(config.omniperf_home), - "omniperf_soc", - "profile_configs", - self.get_soc_name(), - ) - ) +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_compatible_profilers(["rocprofv1", "rocscope"]) # Per IP block max number of simultaneous counters. GFX IP Blocks self.set_perfmon_config( @@ -70,7 +49,12 @@ class gfx906_soc(OmniSoC_Base): "TCC_channels": 16, } ) - self.set_soc_param(SOC_PARAM) + + # Set arch specific specs + self._mspec.L2Banks = 16 + self._mspec.LDSBanks = 32 + self._mspec.numSQC = 15 + self._mspec.numPipes = 4 # ----------------------- # Required child methods @@ -80,7 +64,7 @@ class gfx906_soc(OmniSoC_Base): """Perform any SoC-specific setup prior to profiling.""" super().profiling_setup() if self.get_args().roof_only: - error("%s does not support roofline analysis" % self.get_soc_name()) + error("%s does not support roofline analysis" % self.get_arch()) # Perfmon filtering self.perfmon_filter() diff --git a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx908.py b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx908.py index b80898fb66..a08c6a4964 100644 --- a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx908.py +++ b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx908.py @@ -27,32 +27,11 @@ import config from omniperf_soc.soc_base import OmniSoC_Base from utils.utils import demarcate, error -SOC_PARAM = { - "numSE": 8, - "numCU": 120, - "numSIMD": 480, - "numPipes": 4, - "numWavesPerCU": 40, - "numSQC": 30, - "L2Banks": 32, - "LDSBanks": 32, - "Freq": 1502, - "mclk": 1200, -} - - -class gfx908_soc(OmniSoC_Base): - def __init__(self, args): - super().__init__(args) - self.set_soc_name("gfx908") - self.set_perfmon_dir( - os.path.join( - str(config.omniperf_home), - "omniperf_soc", - "profile_configs", - self.get_soc_name(), - ) - ) +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_compatible_profilers(["rocprofv1", "rocscope"]) # Per IP block max number of simultaneous counters. GFX IP Blocks self.set_perfmon_config( @@ -70,7 +49,15 @@ class gfx908_soc(OmniSoC_Base): "TCC_channels": 32, } ) - self.set_soc_param(SOC_PARAM) + + # Set arch specific specs + self._mspec.L2Banks = 32 + self._mspec.LDSBanks = 32 + self._mspec.numSQC = 30 + self._mspec.numPipes = 4 + # --showmclkrange is broken in Mi100, hardcode freq + self._info['max_mclk'] = 1200 + self._info['cur_mclk'] = 1200 @demarcate def get_profiler_options(self): @@ -85,7 +72,7 @@ class gfx908_soc(OmniSoC_Base): """Perform any SoC-specific setup prior to profiling.""" super().profiling_setup() if self.get_args().roof_only: - error("%s does not support roofline analysis" % self.get_soc_name()) + error("%s does not support roofline analysis" % self.get_arch()) # Perfmon filtering self.perfmon_filter(self.get_args().roof_only) diff --git a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx90a.py b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx90a.py index 498fb613e6..2013c40f64 100644 --- a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx90a.py +++ b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx90a.py @@ -29,42 +29,14 @@ from utils.utils import demarcate, mibench from roofline import Roofline import logging -SOC_PARAM = { - "numSE": 8, - "numCU": 110, - "numSIMD": 440, - "numPipes": 4, - "numWavesPerCU": 32, - "numSQC": 56, - "L2Banks": 32, - "LDSBanks": 32, - "Freq": 1700, - "mclk": 1600, -} - - -class gfx90a_soc(OmniSoC_Base): - def __init__(self, args): - super().__init__(args) - self.set_soc_name("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", - ) - ) +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")) else: - self.set_perfmon_dir( - os.path.join( - str(config.omniperf_home), - "omniperf_soc", - "profile_configs", - self.get_soc_name(), - ) - ) + 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,8 +54,13 @@ class gfx90a_soc(OmniSoC_Base): "TCC_channels": 32, } ) - self.set_soc_param(SOC_PARAM) - self.roofline_obj = Roofline(args) + self.roofline_obj = Roofline(args, self._mspec) + + # Set arch specific specs + self._mspec.L2Banks = 32 + self._mspec.LDSBanks = 32 + self._mspec.numSQC = 56 + self._mspec.numPipes = 4 # ----------------------- # Required child methods @@ -104,7 +81,7 @@ class gfx90a_soc(OmniSoC_Base): "[roofline] Checking for roofline.csv in " + str(self.get_args().path) ) if not os.path.isfile(os.path.join(self.get_args().path, "roofline.csv")): - mibench(self.get_args()) + mibench(self.get_args(), self._mspec) self.roofline_obj.post_processing() else: logging.info("[roofline] Skipping roofline") diff --git a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx940.py b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx940.py index 38970a400f..16a9104e10 100644 --- a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx940.py +++ b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx940.py @@ -29,33 +29,12 @@ from utils.utils import demarcate, mibench from roofline import Roofline import logging -SOC_PARAM = { - "numSE": 8, - "numCU": 38, - "numSIMD": 4, - "numPipes": 4, - "numWavesPerCU": 32, - "numSQC": 56, - "L2Banks": 16, - "LDSBanks": 32, - "Freq": 1950, - "mclk": 1300, -} - - -class gfx940_soc(OmniSoC_Base): - def __init__(self, args): - super().__init__(args) - self.set_soc_name("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", - ) - ) +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")) else: # NB: We're using generalized Mi300 perfmon configs self.set_perfmon_dir( @@ -80,8 +59,13 @@ class gfx940_soc(OmniSoC_Base): "TCC_channels": 32, } ) - self.set_soc_param(SOC_PARAM) - self.roofline_obj = Roofline(args) + self.roofline_obj = Roofline(args, self._mspec) + + # Set arch specific specs + self._mspec.L2Banks = 16 + self._mspec.LDSBanks = 32 + self._mspec.numSQC = 56 + self._mspec.numPipes = 4 # ----------------------- # Required child methods diff --git a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx941.py b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx941.py index f796a004d9..3389e173dc 100644 --- a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx941.py +++ b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx941.py @@ -29,33 +29,12 @@ from utils.utils import demarcate, mibench from roofline import Roofline import logging -SOC_PARAM = { - "numSE": 8, - "numCU": 38, - "numSIMD": 4, - "numPipes": 4, - "numWavesPerCU": 32, - "numSQC": 56, - "L2Banks": 16, - "LDSBanks": 32, - "Freq": 1950, - "mclk": 1300, -} - - -class gfx941_soc(OmniSoC_Base): - def __init__(self, args): - super().__init__(args) - self.set_soc_name("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", - ) - ) +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")) else: # NB: We're using generalized Mi300 perfmon configs self.set_perfmon_dir( @@ -80,8 +59,13 @@ class gfx941_soc(OmniSoC_Base): "TCC_channels": 32, } ) - self.set_soc_param(SOC_PARAM) - self.roofline_obj = Roofline(args) + self.roofline_obj = Roofline(args, self._mspec) + + # Set arch specific specs + self._mspec.L2Banks = 16 + self._mspec.LDSBanks = 32 + self._mspec.numSQC = 56 + self._mspec.numPipes = 4 # ----------------------- # Required child methods diff --git a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx942.py b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx942.py index 4135d337ce..8d0288b141 100644 --- a/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx942.py +++ b/projects/rocprofiler-compute/src/omniperf_soc/soc_gfx942.py @@ -29,33 +29,12 @@ from utils.utils import demarcate, mibench from roofline import Roofline import logging -SOC_PARAM = { - "numSE": 8, - "numCU": 38, - "numSIMD": 4, - "numPipes": 4, - "numWavesPerCU": 32, - "numSQC": 56, - "L2Banks": 16, - "LDSBanks": 32, - "Freq": 1950, - "mclk": 1300, -} - - -class gfx942_soc(OmniSoC_Base): - def __init__(self, args): - super().__init__(args) - self.set_soc_name("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", - ) - ) +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")) else: # NB: We're using generalized Mi300 perfmon configs self.set_perfmon_dir( @@ -80,8 +59,13 @@ class gfx942_soc(OmniSoC_Base): "TCC_channels": 32, } ) - self.set_soc_param(SOC_PARAM) - self.roofline_obj = Roofline(args) + self.roofline_obj = Roofline(args, self.__mspec) + + # Set arch specific specs + self._mspec.L2Banks = 16 + self._mspec.LDSBanks = 32 + self._mspec.numSQC = 56 + self._mspec.numPipes = 4 # ----------------------- # Required child methods diff --git a/projects/rocprofiler-compute/src/roofline.py b/projects/rocprofiler-compute/src/roofline.py index e0424e8617..173f3d6e8c 100644 --- a/projects/rocprofiler-compute/src/roofline.py +++ b/projects/rocprofiler-compute/src/roofline.py @@ -38,34 +38,35 @@ SYMBOLS = [0, 1, 2, 3, 4, 5, 13, 17, 18, 20] class Roofline: - def __init__(self, args, run_parameters=None): + def __init__(self, args, mspec, run_parameters=None): self.__args = args - self.__run_parameters = ( - run_parameters - if run_parameters - else { - "path_to_dir": self.__args.path, - "device_id": 0, - "sort_type": "kernels", - "mem_level": "ALL", - "include_kernel_names": False, - "is_standalone": False, - } - ) + 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.__ai_data = None self.__ceiling_data = None self.__figure = go.Figure() - if not isinstance(self.__run_parameters["path_to_dir"], list): - self.roof_setup() # Set roofline run parameters from args - 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'): + 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): + self.roof_setup() self.validate_parameters() @@ -77,13 +78,11 @@ class Roofline: def roof_setup(self): # set default workload path if not specified - if self.__run_parameters["path_to_dir"] == os.path.join(os.getcwd(), "workloads"): - self.__run_parameters["path_to_dir"] = os.path.join( - self.__run_parameters["path_to_dir"], self.__args.name, self.__args.target - ) + 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) # create new directory for roofline if it doesn't exist - if not os.path.isdir(self.__run_parameters["path_to_dir"]): - os.makedirs(self.__run_parameters["path_to_dir"]) + if not os.path.isdir(self.__run_parameters['workload_dir']): + os.makedirs(self.__run_parameters['workload_dir']) @demarcate def empirical_roofline( @@ -92,9 +91,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["path_to_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])) @@ -132,33 +131,21 @@ class Roofline: if self.__run_parameters["is_standalone"]: dev_id = str(self.__run_parameters["device_id"]) - fp32_fig.write_image( - self.__run_parameters["path_to_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["path_to_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["path_to_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["path_to_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["path_to_dir"] - + "/empirRoof_gpu-{}_int8_fp16.pdf".format(dev_id) + 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["path_to_dir"] + "/kernelName_legend.pdf" - ) + 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( @@ -345,7 +332,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["path_to_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)) @@ -366,12 +353,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 ) @abstractmethod @@ -383,7 +371,7 @@ class Roofline: ) roof_path = os.path.join(self.__args.path, "roofline.csv") if not os.path.isfile(roof_path): - mibench(self.__args) + mibench(self.__args, self.__mspec) # check for profiling data logging.info( @@ -401,7 +389,7 @@ class Roofline: elif self.__args.no_roof: logging.info("[roofline] Skipping roofline.") else: - mibench(self.__args) + mibench(self.__args, self.__mspec) # NB: Currently the post_prossesing() method is the only one being used by omniperf, # we include pre_processing() and profile() methods for those who wish to borrow the roofline module diff --git a/projects/rocprofiler-compute/src/utils/db_connector.py b/projects/rocprofiler-compute/src/utils/db_connector.py index e8b4e70ada..e94c8c37c5 100644 --- a/projects/rocprofiler-compute/src/utils/db_connector.py +++ b/projects/rocprofiler-compute/src/utils/db_connector.py @@ -54,11 +54,7 @@ class DatabaseConnector: self.client: MongoClient = None @demarcate - def prep_import(self, profile_and_export=False): - if profile_and_export: - self.connection_info["workload"] = os.path.join( - self.connection_info["workload"], self.args.target - ) + def prep_import(self): # Extract SoC and workload name from sysinfo.csv sys_info = os.path.join(self.connection_info["workload"], "sysinfo.csv") diff --git a/projects/rocprofiler-compute/src/utils/parser.py b/projects/rocprofiler-compute/src/utils/parser.py index 36e67e9af9..81f55cebc7 100644 --- a/projects/rocprofiler-compute/src/utils/parser.py +++ b/projects/rocprofiler-compute/src/utils/parser.py @@ -663,7 +663,7 @@ def build_metric_value_string(dfs, dfs_type, normal_unit): # print(tabulate(df, headers='keys', tablefmt='fancy_grid')) -def eval_metric(dfs, dfs_type, sys_info, soc_spec, raw_pmc_df, debug): +def eval_metric(dfs, dfs_type, sys_info, raw_pmc_df, debug): """ Execute the expr string for each metric in the df. """ @@ -679,25 +679,18 @@ def eval_metric(dfs, dfs_type, sys_info, soc_spec, raw_pmc_df, debug): print("WARNING: Dectected GRBM_GUI_ACTIVE == 0\nHaulting execution.") sys.exit(1) - # NB: - # Following with Omniperf 0.2.0, we are using HW spec from sys_info instead. - # The soc_spec is not in using right now, but can be used to do verification - # against sys_info, forced theoretical evaluation, or supporting tool-chains - # broken. - ammolite__numSE = sys_info.numSE + ammolite__numSE = sys_info.SE ammolite__numPipes = sys_info.numPipes - ammolite__numCU = sys_info.numCU - ammolite__numSIMD = sys_info.numSIMD - ammolite__numWavesPerCU = sys_info.maxWavesPerCU # todo: check do we still need it + ammolite__numCU = sys_info.CU + ammolite__numSIMD = sys_info.SIMD + ammolite__numWavesPerCU = sys_info.max_waves_per_cu # todo: check do we still need it ammolite__numSQC = sys_info.numSQC ammolite__L2Banks = sys_info.L2Banks - ammolite__LDSBanks = soc_spec[ - "LDSBanks" - ] # todo: eventually switch this over to sys_info. its a new spec so trying not to break compatibility + ammolite__LDSBanks = sys_info.LDSBanks ammolite__freq = sys_info.cur_sclk # todo: check do we still need it ammolite__mclk = sys_info.cur_mclk - ammolite__sclk = sys_info.sclk - ammolite__maxWavesPerCU = sys_info.maxWavesPerCU + ammolite__sclk = sys_info.max_sclk + ammolite__maxWavesPerCU = sys_info.max_waves_per_cu ammolite__hbmBW = sys_info.hbmBW ammolite__totalL2Banks = calc_builtin_var("$totalL2Banks", sys_info) @@ -947,7 +940,6 @@ def load_table_data(workload, dir, is_gui, debug, verbose, skipKernelTop=False): workload.dfs, workload.dfs_type, workload.sys_info.iloc[0], - workload.soc_spec, apply_filters(workload, dir, is_gui, debug), debug, ) diff --git a/projects/rocprofiler-compute/src/utils/roofline_calc.py b/projects/rocprofiler-compute/src/utils/roofline_calc.py index e8367d1184..cadf0db08c 100644 --- a/projects/rocprofiler-compute/src/utils/roofline_calc.py +++ b/projects/rocprofiler-compute/src/utils/roofline_calc.py @@ -474,7 +474,7 @@ def calc_ai(sort_type, ret_df): def constuct_roof(roofline_parameters, dtype): - benchmark_results = os.path.join(roofline_parameters["path_to_dir"], "roofline.csv") + benchmark_results = os.path.join(roofline_parameters["workload_dir"], "roofline.csv") # ----------------------------------------------------- # Initialize roofline data dictionary from roofline.csv # ----------------------------------------------------- diff --git a/projects/rocprofiler-compute/src/utils/schema.py b/projects/rocprofiler-compute/src/utils/schema.py index f10190f8b7..ea5f23ba4b 100644 --- a/projects/rocprofiler-compute/src/utils/schema.py +++ b/projects/rocprofiler-compute/src/utils/schema.py @@ -59,7 +59,6 @@ class ArchConfig: @dataclass class Workload: sys_info: pd.DataFrame = None - soc_spec: dict = None # TODO: might move it to ArchConfig raw_pmc: pd.DataFrame = None dfs: Dict[int, pd.DataFrame] = field(default_factory=dict) dfs_type: Dict[int, str] = field(default_factory=dict) diff --git a/projects/rocprofiler-compute/src/utils/specs.py b/projects/rocprofiler-compute/src/utils/specs.py index 0583c5c483..56bdf2e0b4 100644 --- a/projects/rocprofiler-compute/src/utils/specs.py +++ b/projects/rocprofiler-compute/src/utils/specs.py @@ -31,46 +31,158 @@ import socket import subprocess import importlib import logging +import pandas as pd +from datetime import datetime from dataclasses import dataclass from pathlib import Path as path from textwrap import dedent from utils.utils import error, get_hbm_stack_num +VERSION_LOC = [ + "version", + "version-dev", + "version-hip-libraries", + "version-hiprt", + "version-hiprt-devel", + "version-hip-sdk", + "version-libs", + "version-utils", +] @dataclass class MachineSpecs: - hostname: str - CPU: str - sbios: str - kernel_version: str - ram: str - distro: str - rocm_version: str - GPU: str - arch: str - vbios: str - L1: str - L2: str - CU: str - SIMD: str - SE: str - wave_size: str - workgroup_max_size: str - max_sclk: str - max_mclk: str - cur_sclk: str - cur_mclk: str - max_waves_per_cu: str - L2Banks: str - totalL2Banks: str - LDSBanks: str - numSQC: str - numPipes: str - hbmBW: str - compute_partition: str - memory_partition: str + def __init__(self, args, sysinfo=None): + if not sysinfo is None: + self.arch = sysinfo.iloc[0]["arch"] + return + # read timestamp info + now = datetime.now() + local_now = now.astimezone() + local_tz = local_now.tzinfo + local_tzname = local_tz.tzname(local_now) + self.timestamp = now.strftime("%c") + " (" + local_tzname + ")" + # read rocminfo + rocminfo_full = run(["rocminfo"]) + self._rocminfo = rocminfo_full.split("\n") + + ########################################## + ## A. Machine Specs + ########################################## + cpuinfo = path("/proc/cpuinfo").read_text() + meminfo = path("/proc/meminfo").read_text() + version = path("/proc/version").read_text() + os_release = path("/etc/os-release").read_text() + + self.hostname: str = socket.gethostname() + self.CPU: str = search(r"^model name\s*: (.*?)$", cpuinfo) + self.sbios: str = ( + path("/sys/class/dmi/id/bios_vendor").read_text().strip() + + path("/sys/class/dmi/id/bios_version").read_text().strip() + ) + self.kernel_version: str = search(r"version (\S*)", version) + self.ram: str = search(r"MemTotal:\s*(\S*)", meminfo) + self.distro: str = search(r'PRETTY_NAME="(.*?)"', os_release) + if self.distro is None: + self.distro = "" + self.rocm_version: str = get_rocm_ver().strip() + #FIXME: use device + self.vbios: str = search( + r"VBIOS version: (.*?)$", run(["rocm-smi", "-v"], exit_on_error=True) + ) + self.compute_partition: str = search( + r"Compute Partition:\s*(\w+)", run(["rocm-smi", "--showcomputepartition"]) + ) + if self.compute_partition is None: + self.compute_partition = "NA" + self.memory_partition: str = search( + r"Memory Partition:\s*(\w+)", run(["rocm-smi", "--showmemorypartition"]) + ) + if self.memory_partition is None: + self.memory_partition = "NA" + + ########################################## + ## B. SoC Specs + ########################################## + self.arch: str = self.detect_arch()[0] + self.L1: str = None + self.L2: str = None + self.CU: str = None + self.SIMD: str = None + self.SE: str = None + self.wave_size: str = None + self.workgroup_max_size: str = None + self.max_sclk: str = None + self.max_mclk: str = None + self.cur_sclk: str = None + self.cur_mclk: str = None + self.max_waves_per_cu: str = None + self.GPU: str = None + self.L2Banks: str = None + self.LDSBanks: str = None + self.numSQC: str = None + self.numPipes: str = None + self.totalL2Banks: str = None + self.hbmBW: str = None + # Load above SoC specs via module import + try: + soc_module = importlib.import_module('omniperf_soc.soc_'+ self.arch) + except ModuleNotFoundError as e: + error("Arch %s marked as supported, but couldn't find class implementation %s." % (self.arch, e)) + soc_class = getattr(soc_module, self.arch+'_soc') + self._rocminfo = self._rocminfo[self.detect_arch()[1] + 1 :] # update rocminfo for target section + soc_obj = soc_class(args, self) + # Update arch specific specs + self.totalL2Banks: str = total_l2_banks( + self.GPU, int(self.L2Banks), self.memory_partition + ) + self.hbmBW: str = str(int(self.max_mclk) / 1000 * 32 * self.get_hbm_channels()) + + + def detect_arch(self): + from omniperf_base import SUPPORTED_ARCHS + + for idx1, linetext in enumerate(self._rocminfo): + gpu_arch = search(r"^\s*Name\s*:\s+ ([a-zA-Z0-9]+)\s*$", linetext) + if gpu_arch in SUPPORTED_ARCHS.keys(): + break + if str(gpu_arch) in SUPPORTED_ARCHS.keys(): + gpu_arch = str(gpu_arch) + break + if not gpu_arch in SUPPORTED_ARCHS.keys(): + error("[profiling] Cannot find a supported arch in rocminfo") + else: + return (gpu_arch, idx1) + + def get_hbm_channels(self): + hbmchannels = int(self.totalL2Banks) + if ( + self.GPU.lower() == "mi300a_a0" + or self.GPU.lower() == "mi300a_a1" + ) and self.memory_partition.lower() == "nps1": + # we have an extra 32 channels for the CCD + hbmchannels += 32 + return hbmchannels + + def get_class_members(self): + all_populated = True + data = {} + # dataclass uses an OrderedDict for member variables, ensuring order consistency + for attr_name in self.__dict__.keys(): + if not attr_name.startswith("_"): + attr_value = getattr(self, attr_name) + if attr_value is None: + #TODO: use proper logging function when that's merged + logging.warning(f"WARNING: Incomplete class definition for {self.arch}. Expecting populated {attr_name} but detected None.") + all_populated = False + data[attr_name] = attr_value + + if not all_populated: + error("Missing specs fields for %s" % self.arch) + return pd.DataFrame(data, index=[0]) + + def __str__(self): return dedent( f"""\ @@ -110,145 +222,27 @@ class MachineSpecs: ) -def gpuinfo(): - from omniperf_base import SUPPORTED_ARCHS - - gpu_info = { - "gpu_name": None, - "gpu_arch": None, - "L1": None, - "L2": None, - "max_sclk": None, - "max_mclk": None, - "num_CU": None, - "num_SIMD": None, - "numPipes": None, - "num_SE": None, - "wave_size": None, - "grp_size": None, - "max_waves_per_cu": None, - "L2Banks": None, - "LDSBanks": None, - "numSQC": None, - "compute_partition": None, - "memory_partition": None, - } - - # Fixme: find better way to differentiate cards, GPU vs APU, etc. - rocminfo_full = run(["rocminfo"]) - rocminfo = rocminfo_full.split("\n") - - for idx1, linetext in enumerate(rocminfo): - gpu_arch = search(r"^\s*Name\s*:\s+ ([a-zA-Z0-9]+)\s*$", linetext) - if gpu_arch in SUPPORTED_ARCHS.keys(): - break - if str(gpu_arch) in SUPPORTED_ARCHS.keys(): - gpu_arch = str(gpu_arch) - break - if not gpu_arch in SUPPORTED_ARCHS.keys(): - return gpu_info - - gpu_info["L1"], gpu_info["L1"] = "", "" - for idx2, linetext in enumerate(rocminfo[idx1 + 1 :]): - key = search(r"^\s*L1:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["L1"] = key - continue - - key = search(r"^\s*L2:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["L2"] = key - continue - - key = search(r"^\s*Max Clock Freq\. \(MHz\):\s+([0-9]+)", linetext) - if key != None: - gpu_info["max_sclk"] = key - continue - - key = search(r"^\s*Compute Unit:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["num_CU"] = key - continue - - key = search(r"^\s*SIMDs per CU:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["num_SIMD"] = key - continue - - key = search(r"^\s*Shader Engines:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["num_SE"] = key - continue - - key = search(r"^\s*Wavefront Size:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["wave_size"] = key - continue - - key = search(r"^\s*Workgroup Max Size:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["grp_size"] = key - continue - - key = search(r"^\s*Max Waves Per CU:\s+ ([a-zA-Z0-9]+)\s*", linetext) - if key != None: - gpu_info["max_waves_per_cu"] = key - break - - try: - soc_module = importlib.import_module("omniperf_soc.soc_" + gpu_arch) - except ModuleNotFoundError as e: - error( - "Arch %s marked as supported, but couldn't find class implementation %s." - % (gpu_arch, e) - ) - - # load arch specific info - try: - gpu_name = list(SUPPORTED_ARCHS[gpu_arch].keys())[0].upper() - gpu_info["L2Banks"] = str(soc_module.SOC_PARAM["L2Banks"]) - gpu_info["numSQC"] = str(soc_module.SOC_PARAM["numSQC"]) - gpu_info["LDSBanks"] = str(soc_module.SOC_PARAM["LDSBanks"]) - gpu_info["numPipes"] = str(soc_module.SOC_PARAM["numPipes"]) - except KeyError as e: - error( - "Incomplete class definition for %s. Expected a field for %s in SOC_PARAM." - % (gpu_arch, e) - ) - - # we get the max mclk from rocm-smi --showmclkrange - rocm_smi_mclk = run(["rocm-smi", "--showmclkrange"], exit_on_error=True) - gpu_info["max_mclk"] = search(r"(\d+)Mhz\s*$", rocm_smi_mclk) - # check that we got the mclk from smi - if gpu_info["max_mclk"] is None: - if gpu_name == "MI100": - # hardcoded due to rocm-smi limitation - gpu_info["max_mclk"] = str(1200) - else: - error( - "Could not obtain maximum mclk from rocm-smi for GPU: {}".format(gpu_info) +def get_rocm_ver(): + rocm_found = False + for itr in VERSION_LOC: + _path = os.path.join(os.getenv("ROCM_PATH", "/opt/rocm"), ".info", itr) + if os.path.exists(_path): + rocm_ver = path(_path).read_text() + rocm_found = True + break + if not rocm_found: + # check if ROCM_VER is supplied externally + ROCM_VER_USER = os.getenv("ROCM_VER") + if ROCM_VER_USER is not None: + logging.info( + "Overriding missing ROCm version detection with ROCM_VER = %s" + % ROCM_VER_USER ) - - # specify gpu name for gfx942 hardware - if gpu_name == "MI300": - gpu_name = list(SUPPORTED_ARCHS[gpu_arch].values())[0][0] - if (gpu_info["gpu_arch"] == "gfx942") and ("MI300A" in rocminfo_full): - gpu_name = "MI300A_A1" - if (gpu_arch == "gfx942") and ("MI300A" not in rocminfo_full): - gpu_name = "MI300X_A1" - - gpu_info["gpu_name"] = gpu_name - gpu_info["gpu_arch"] = gpu_arch - gpu_info["compute_partition"] = "" - gpu_info["memory_partition"] = "" - - # verify all fields are filled - for key, value in gpu_info.items(): - if value is None: - logging.info("Warning: %s is missing from gpu_info dictionary." % key) - - return gpu_info - + rocm_ver = ROCM_VER_USER + else: + _rocm_path = os.getenv("ROCM_PATH", "/opt/rocm") + error("Unable to detect a complete local ROCm installation.\nThe expected %s/.info/ versioning directory is missing. Please ensure you have valid ROCm installation." % _rocm_path) + return rocm_ver def run(cmd, exit_on_error=False): p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -275,143 +269,20 @@ def total_l2_banks(archname, L2Banks, memory_partition): # Fixme: support all supported partitioning mode # Fixme: "name" is a bad name! totalL2Banks = L2Banks - if archname.lower() == "mi300a_a0" or archname.lower() == "mi300a_a1": - totalL2Banks = L2Banks * get_hbm_stack_num(archname, memory_partition) - elif archname.lower() == "mi300x_a0" or archname.lower() == "mi300x_a1": - totalL2Banks = L2Banks * get_hbm_stack_num(archname, memory_partition) - return totalL2Banks - - -def get_machine_specs(devicenum): - cpuinfo = path("/proc/cpuinfo").read_text() - meminfo = path("/proc/meminfo").read_text() - version = path("/proc/version").read_text() - os_release = path("/etc/os-release").read_text() - - version_loc = [ - "version", - "version-dev", - "version-hip-libraries", - "version-hiprt", - "version-hiprt-devel", - "version-hip-sdk", - "version-libs", - "version-utils", - ] - - rocmFound = False - for itr in version_loc: - _path = os.path.join(os.getenv("ROCM_PATH", "/opt/rocm"), ".info", itr) - if os.path.exists(_path): - rocm_ver = path(_path).read_text() - rocmFound = True - break - - if not rocmFound: - # check if ROCM_VER is supplied externally - ROCM_VER_USER = os.getenv("ROCM_VER") - if ROCM_VER_USER is not None: - print( - "Overriding missing ROCm version detection with ROCM_VER = %s" - % ROCM_VER_USER - ) - rocm_ver = ROCM_VER_USER - else: - _rocm_path = os.getenv("ROCM_PATH", "/opt/rocm") - print("Error: Unable to detect a complete local ROCm installation.") - print( - "\nThe expected %s/.info/ versioning directory is missing. Please" - % _rocm_path - ) - print("ensure you have valid ROCm installation.") - sys.exit(1) - - gpu_info = gpuinfo() - - rocm_smi = run(["rocm-smi"], exit_on_error=True) - - device = rf"^\s*{devicenum}(.*)" - - hostname = socket.gethostname() - sbios = ( - path("/sys/class/dmi/id/bios_vendor").read_text().strip() - + path("/sys/class/dmi/id/bios_version").read_text().strip() - ) - CPU = search(r"^model name\s*: (.*?)$", cpuinfo) - kernel_version = search(r"version (\S*)", version) - ram = search(r"MemTotal:\s*(\S*)", meminfo) - distro = search(r'PRETTY_NAME="(.*?)"', os_release) - if distro is None: - distro = "" - - rocm_version = rocm_ver.strip() - - # 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) - cur_sclk = gpu_info["max_sclk"] - cur_mclk = gpu_info["max_mclk"] - - # FIXME with 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"]) - ) - if compute_partition == None: - compute_partition = "NA" - - memory_partition = search( - r"Memory Partition:\s*(\w+)", run(["rocm-smi", "--showmemorypartition"]) - ) - if memory_partition == None: - memory_partition = "NA" - - totalL2Banks = total_l2_banks( - gpu_info["gpu_name"], int(gpu_info["L2Banks"]), memory_partition - ) - hbmchannels = totalL2Banks if ( - gpu_info["gpu_name"].lower() == "mi300a_a0" - or gpu_info["gpu_name"].lower() == "mi300a_a1" - ) and memory_partition.lower() == "nps1": - # we have an extra 32 channels for the CCD - hbmchannels += 32 - hbmBW = str(int(gpu_info["max_mclk"]) / 1000 * 32 * hbmchannels) - totalL2Banks = str(totalL2Banks) - - return MachineSpecs( - hostname, - CPU, - sbios, - kernel_version, - ram, - distro, - rocm_version, - gpu_info["gpu_name"], - gpu_info["gpu_arch"], - vbios, - gpu_info["L1"], - gpu_info["L2"], - gpu_info["num_CU"], - gpu_info["num_SIMD"], - gpu_info["num_SE"], - gpu_info["wave_size"], - gpu_info["grp_size"], - gpu_info["max_sclk"], - gpu_info["max_mclk"], - cur_sclk, - cur_mclk, - gpu_info["max_waves_per_cu"], - gpu_info["L2Banks"], - totalL2Banks, - gpu_info["LDSBanks"], - gpu_info["numSQC"], - gpu_info["numPipes"], - hbmBW, - compute_partition, - memory_partition, - ) + 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) if __name__ == "__main__": - print(get_machine_specs(0)) + print(MachineSpecs()) diff --git a/projects/rocprofiler-compute/src/utils/utils.py b/projects/rocprofiler-compute/src/utils/utils.py index 8e4bed5958..1d5bfd2e9b 100644 --- a/projects/rocprofiler-compute/src/utils/utils.py +++ b/projects/rocprofiler-compute/src/utils/utils.py @@ -198,12 +198,10 @@ def capture_subprocess_output(subprocess_args, new_env=None): return (success, output) - -def run_prof(fname, profiler_options, target, workload_dir): +def run_prof(fname, profiler_options, workload_dir, mspec): fbase = os.path.splitext(os.path.basename(fname))[0] - m_specs = specs.get_machine_specs(0) - + logging.debug("pmc file: %s" % str(os.path.basename(fname))) # standard rocprof options @@ -212,12 +210,7 @@ def run_prof(fname, profiler_options, target, workload_dir): # set required env var for mi300 new_env = None - if ( - target.lower() == "mi300x_a0" - or target.lower() == "mi300x_a1" - or target.lower() == "mi300a_a0" - or target.lower() == "mi300a_a1" - ) and ( + if (mspec.GPU.lower() == "mi300x_a0" or mspec.GPU.lower() == "mi300x_a1" or mspec.GPU.lower() == "mi300a_a0" or mspec.GPU.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" @@ -239,8 +232,10 @@ def run_prof(fname, profiler_options, target, workload_dir): if new_env: # flatten tcc for applicable mi300 input f = path(workload_dir + "/out/pmc_1/results_" + fbase + ".csv") - hbm_stack_num = get_hbm_stack_num(target, m_specs.memory_partition) - df = flatten_tcc_info_across_hbm_stacks(f, hbm_stack_num, int(m_specs.L2Banks)) + hbm_stack_num = get_hbm_stack_num(mspec.GPU, mspec.memory_partition) + df = flatten_tcc_info_across_hbm_stacks( + f, hbm_stack_num, int(mspec.L2Banks) + ) df.to_csv(f, index=False) if os.path.exists(workload_dir + "/out"): @@ -300,88 +295,27 @@ def replace_timestamps(workload_dir): ) logging.warning(warning + "\n") - -def gen_sysinfo(workload_name, workload_dir, ip_blocks, app_cmd, skip_roof, roof_only): - # Record system information - mspec = specs.get_machine_specs(0) - sysinfo = open(workload_dir + "/" + "sysinfo.csv", "w") - - # write header - header = "workload_name," - header += "command," - header += "host_name,host_cpu,sbios,host_distro,host_kernel,host_rocmver,date," - header += "gpu_soc,vbios,numSE,numCU,numSIMD,waveSize,maxWavesPerCU,maxWorkgroupSize," - header += "L1,L2,sclk,mclk,cur_sclk,cur_mclk,L2Banks,totalL2Banks,LDSBanks,name,numSQC,numPipes," - header += "hbmBW,compute_partition,memory_partition," - header += "ip_blocks\n" - sysinfo.write(header) - - # timestamp - now = datetime.now() - local_now = now.astimezone() - local_tz = local_now.tzinfo - local_tzname = local_tz.tzname(local_now) - timestamp = now.strftime("%c") + " (" + local_tzname + ")" - # host info - param = [workload_name] - param += ['"' + app_cmd + '"'] - param += [ - mspec.hostname, - mspec.CPU, - '"' + mspec.sbios + '"', - mspec.distro, - mspec.kernel_version, - mspec.rocm_version, - timestamp, - ] - - # GPU info - param += [ - mspec.arch, - mspec.vbios, - mspec.SE, - mspec.CU, - mspec.SIMD, - mspec.wave_size, - mspec.max_waves_per_cu, - mspec.workgroup_max_size, - ] - param += [ - mspec.L1, - mspec.L2, - mspec.max_sclk, - mspec.max_mclk, - mspec.cur_sclk, - mspec.cur_mclk, - mspec.L2Banks, - mspec.totalL2Banks, - mspec.LDSBanks, - mspec.GPU, - mspec.numSQC, - mspec.numPipes, - mspec.hbmBW, - mspec.compute_partition, - mspec.memory_partition, - ] - +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.insert(0, 'command', app_cmd) + df.insert(0,'workload_name', workload_name) + blocks = [] - if mspec.arch == "gfx90a" and (not skip_roof): - blocks.append("roofline") - - # ip block info if ip_blocks == None: t = ["SQ", "LDS", "SQC", "TA", "TD", "TCP", "TCC", "SPI", "CPC", "CPF"] blocks += t else: blocks += ip_blocks - param.append("|".join(blocks)) + if mspec.arch == "gfx90a" and (not skip_roof): + blocks.append("roofline") + df['ip_blocks'] = "|".join(blocks) - sysinfo.write(",".join(param)) - sysinfo.close() + # Save csv + df.to_csv(workload_dir + "/" + "sysinfo.csv", index=False) - -def detect_roofline(): - mspec = specs.get_machine_specs(0) +def detect_roofline(mspec): rocm_ver = mspec.rocm_version[:1] os_release = path("/etc/os-release").read_text() @@ -446,13 +380,13 @@ def run_rocscope(args, fname): logging.error(result.stderr.decode("ascii")) sys.exit(1) - -def mibench(args): - """Run roofline microbenchmark to generate peak BW and FLOP measurements.""" +def mibench(args, mspec): + """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"} - target_binary = detect_roofline() + target_binary = detect_roofline(mspec) if target_binary["rocm_ver"] == "override": path_to_binary = target_binary["path"] else: @@ -462,7 +396,7 @@ def mibench(args): + "-" + distro_map[target_binary["distro"]] + "-" - + args.target.lower() + + mspec.GPU.lower() + "-rocm" + target_binary["rocm_ver"] )