From fca1001a4cb62a72f470763ab61d3ce6340969c9 Mon Sep 17 00:00:00 2001 From: colramos425 Date: Wed, 18 Jan 2023 11:46:12 -0600 Subject: [PATCH] Modify GUI roofline to accomidate standalone option Signed-off-by: colramos425 --- src/omniperf | 119 +++++++++--------- src/omniperf_analyze/omniperf_analyze.py | 15 +++ .../utils/gui_components/roofline.py | 62 +++++---- src/omniperf_analyze/utils/roofline_calc.py | 2 +- 4 files changed, 111 insertions(+), 87 deletions(-) diff --git a/src/omniperf b/src/omniperf index d8a8ef3d4e..5eca89f45c 100755 --- a/src/omniperf +++ b/src/omniperf @@ -37,7 +37,7 @@ from utils import specs from utils.perfagg import perfmon_filter, pmc_filter from utils import remove_workload from utils import csv_converter # Import workload -from utils import plot_roofline # standalone roofline +from omniperf_analyze.omniperf_analyze import roofline_only from omniperf_analyze.omniperf_analyze import analyze # CLI analysis from common import ( @@ -212,7 +212,61 @@ def mongo_import(args, profileAndImport): ################################################ # Roofline Helpers ################################################ +def roof_setup(args, my_parser): + if args.path == os.getcwd() + "/workloads": + args.path += "/" + args.name + "/" + str(get_soc()) + # Verify valid axes parameters + if args.axes: + if len(args.axes) != 4: + throw_parse_error( + my_parser, + "Invalid argument for --axes.\nMust contain four values formatted as: --axes xmin xmax ymin ymax", + ) + if args.axes[0] > args.axes[1] or args.axes[2] > args.axes[3]: + throw_parse_error( + my_parser, + "Invalid argument for --axes.\nBreaks required conditions: (xmax > xmin && ymax > ymin)", + ) + + # We need to make a directory for a new roofline + if not os.path.isdir(args.path): + os.makedirs(args.path) + # does roof data exist? + print("Checking for roofline.csv in ", args.path) + roof_path = args.path + "/roofline.csv" + roofline_exists = os.path.isfile(roof_path) + if not roofline_exists: + if get_soc() != "mi200": + throw_parse_error( + my_parser, "Invalid SoC.\nRoofline only availible on MI200." + ) + mibench(args) + + # does sysinfo exist? + print("Checking for sysinfo.csv in ", args.path) + sysinfo_path = args.path + "/sysinfo.csv" + sysinfo_exists = os.path.isfile(sysinfo_path) + if not sysinfo_exists: + print("sysinfo not found") + gen_sysinfo(args.name, args.path, [], args.remaining, False) + + # does app data exist? + print("Checking for pmc_perf.csv in ", args.path) + app_path = args.path + "/pmc_perf.csv" + app_exists = os.path.isfile(app_path) + if not app_exists: + if get_soc() != "mi200": + throw_parse_error( + my_parser, "Invalid SoC.\nRoofline only availible on MI200." + ) + if not args.remaining: + throw_parse_error( + my_parser, + "Cannot find existing application data.\nAttempting to generate application data from -- .\n-- option is required to generate application data.", + ) + else: + characterize_app(args.path, args.remaining, args.verbose) def detect_roofline(): mspec = specs.get_machine_specs(0) @@ -248,7 +302,6 @@ def detect_roofline(): target_binary = {"rocm_ver": rocm_ver, "distro": distro} return target_binary - def mibench(args): print("No roofline data found. Generating...") @@ -282,7 +335,6 @@ def mibench(args): ] ) - def characterize_app(path, cmd, verbose): target = get_soc() workload_dir = path @@ -314,12 +366,9 @@ def characterize_app(path, cmd, verbose): # Update pmc_perf.csv timestamps replace_timestamps(workload_dir) - ################################################ # Profiling Helpers ################################################ - - def run_prof(fname, workload_dir, perfmon_dir, cmd, verbose): global rocprof_cmd @@ -514,62 +563,10 @@ def main(): elif args.roof_only: print("\n--------\nRoofline only\n--------\n") - if args.path == os.getcwd() + "/workloads": - args.path += "/" + args.name + "/" + str(get_soc()) - # Verify valid axes parameters - if args.axes: - if len(args.axes) != 4: - throw_parse_error( - my_parser, - "Invalid argument for --axes.\nMust contain four values formatted as: --axes xmin xmax ymin ymax", - ) - - if args.axes[0] > args.axes[1] or args.axes[2] > args.axes[3]: - throw_parse_error( - my_parser, - "Invalid argument for --axes.\nBreaks required conditions: (xmax > xmin && ymax > ymin)", - ) - - # We need to make a directory for a new roofline - if not os.path.isdir(args.path): - os.makedirs(args.path) - # does roof data exist? - print("Checking for roofline.csv in ", args.path) - roof_path = args.path + "/roofline.csv" - roofline_exists = os.path.isfile(roof_path) - if not roofline_exists: - if get_soc() != "mi200": - throw_parse_error( - my_parser, "Invalid SoC.\nRoofline only availible on MI200." - ) - mibench(args) - - # does sysinfo exist? - print("Checking for sysinfo.csv in ", args.path) - sysinfo_path = args.path + "/sysinfo.csv" - sysinfo_exists = os.path.isfile(sysinfo_path) - if not sysinfo_exists: - print("sysinfo not found") - gen_sysinfo(args.name, args.path, [], args.remaining, False) - - # does app data exist? - print("Checking for pmc_perf.csv in ", args.path) - app_path = args.path + "/pmc_perf.csv" - app_exists = os.path.isfile(app_path) - if not app_exists: - if get_soc() != "mi200": - throw_parse_error( - my_parser, "Invalid SoC.\nRoofline only availible on MI200." - ) - if not args.remaining: - throw_parse_error( - my_parser, - "Cannot find existing application data.\nAttempting to generate application data from -- .\n-- option is required to generate application data.", - ) - else: - characterize_app(args.path, args.remaining, args.verbose) + # Setup prerequisits for roofline + roof_setup(args, my_parser) # Generate roofline - plot_roofline.empirical_roof(args) + roofline_only(args.path, args.verbose, args.device) # Profile only else: diff --git a/src/omniperf_analyze/omniperf_analyze.py b/src/omniperf_analyze/omniperf_analyze.py index 4741a3acfa..d15dec4c92 100644 --- a/src/omniperf_analyze/omniperf_analyze.py +++ b/src/omniperf_analyze/omniperf_analyze.py @@ -41,6 +41,7 @@ import argparse import os.path from pathlib import Path from omniperf_analyze.utils import parser, file_io +from omniperf_analyze.utils.gui_components.roofline import get_roofline def initialize_run(args, normalization_filter=None): @@ -209,6 +210,20 @@ def run_cli(args, runs): ) +def roofline_only(path_to_dir, verbose, dev_id): + import pandas as pd + from collections import OrderedDict + + app_path = path_to_dir + "/pmc_perf.csv" + roofline_exists = os.path.isfile(app_path) + if not roofline_exists: + print("Error: {} does not exist") + sys.exit(0) + t_df = OrderedDict() + t_df["pmc_perf"] = pd.read_csv(app_path) + get_roofline(path_to_dir, t_df, dev_id, verbose, True) + + def analyze(args): if args.dependency: print("pip3 install astunparse numpy tabulate pandas pyyaml") diff --git a/src/omniperf_analyze/utils/gui_components/roofline.py b/src/omniperf_analyze/utils/gui_components/roofline.py index 183bd9dcbb..669dc284a1 100644 --- a/src/omniperf_analyze/utils/gui_components/roofline.py +++ b/src/omniperf_analyze/utils/gui_components/roofline.py @@ -164,8 +164,9 @@ def generate_plots(roof_info, ai_data, verbose, fig=None): return fig -def get_roofline(path_to_dir, ret_df, verbose): +def get_roofline(path_to_dir, ret_df, dev_id, verbose, isStandalone=False): # Roofline settings + # TODO: Make "sort" attribute dynamic so user can select desired sort fp32_details = { "path": path_to_dir, "sort": "kernels", @@ -185,6 +186,7 @@ def get_roofline(path_to_dir, ret_df, verbose): ai_data = roofline_calc.plot_application("kernels", ret_df, verbose) if verbose >= 1: # print AI data for each mem level + print("AI at each mem level") for i in ai_data: print(i, "->", ai_data[i]) print("\n") @@ -193,27 +195,37 @@ def get_roofline(path_to_dir, ret_df, verbose): fp16_fig = generate_plots(fp16_details, ai_data, verbose) ml_combo_fig = generate_plots(int8_details, ai_data, verbose, fp16_fig) - return html.Section( - id="roofline", - children=[ - html.Div( - className="float-container", - children=[ - html.Div( - className="float-child", - children=[ - html.H3(children="Empirical Roofline Analysis (FP32/FP64)"), - dcc.Graph(figure=fp32_fig), - ], - ), - html.Div( - className="float-child", - children=[ - html.H3(children="Empirical Roofline Analysis (FP16/INT8)"), - dcc.Graph(figure=ml_combo_fig), - ], - ), - ], - ) - ], - ) + if isStandalone: + dev_id = "ALL" if dev_id == -1 else str(dev_id) + + fp32_fig.write_image(path_to_dir + "/empirRoof_gpu-{}_fp32".format(dev_id)) + ml_combo_fig.write_image(path_to_dir + "empirRoof_gpu-{}_fp8_fp16".format(dev_id)) + else: + return html.Section( + id="roofline", + children=[ + html.Div( + className="float-container", + children=[ + html.Div( + className="float-child", + children=[ + html.H3( + children="Empirical Roofline Analysis (FP32/FP64)" + ), + dcc.Graph(figure=fp32_fig), + ], + ), + html.Div( + className="float-child", + children=[ + html.H3( + children="Empirical Roofline Analysis (FP16/INT8)" + ), + dcc.Graph(figure=ml_combo_fig), + ], + ), + ], + ) + ], + ) diff --git a/src/omniperf_analyze/utils/roofline_calc.py b/src/omniperf_analyze/utils/roofline_calc.py index df64248358..ca8b5022a5 100644 --- a/src/omniperf_analyze/utils/roofline_calc.py +++ b/src/omniperf_analyze/utils/roofline_calc.py @@ -89,7 +89,7 @@ def get_color(catagory): # Plot BW at each cache level # ------------------------------------------------------------------------------------- def plot_roof(roof_details, roof_data, verbose): - + # TODO: This is where filtering by memory level will need to occur for standalone graphPoints = {"hbm": [], "l2": [], "l1": [], "lds": [], "valu": [], "mfma": []} cacheHierarchy = ["HBM", "L2", "L1", "LDS"]