Add roofline cli_generate_plot method (#737)

Add option to print out roofline plot in terminal using plotext.
Takes in one datatype and returns the str from plot.build() which contains the visual plot of roofline analysis for said datatype.

---------

Signed-off-by: Carrie Fallows <Carrie.Fallows@amd.com>
This commit is contained in:
cfallows-amd
2025-06-11 15:38:21 -04:00
کامیت شده توسط GitHub
والد dcbc7b15e2
کامیت 0415bb9740
+219 -1
مشاهده پرونده
@@ -30,10 +30,17 @@ from pathlib import Path
import numpy as np
import pandas as pd
import plotext as plt
import plotly.graph_objects as go
from dash import dcc, html
from utils.logger import console_debug, console_error, console_log, demarcate
from utils.logger import (
console_debug,
console_error,
console_log,
console_warning,
demarcate,
)
from utils.roofline_calc import (
MFMA_DATATYPES,
PEAK_OPS_DATATYPES,
@@ -408,6 +415,217 @@ class Roofline:
return fig
@demarcate
def cli_generate_plot(self, dtype):
"""
Plot CLI mode roofline analysis in terminal using plotext
:param dtype: The datatype to be profiled
:type method: str
:return: Build the current figure using plot.build(), or None if datatype is not valid for the architecture
:rtype: str or None
"""
console_debug("roofline", "Generating roofline plot for CLI")
if not (str(dtype) in SUPPORTED_DATATYPES[self.__mspec.gpu_arch]):
console_error(
"{} is not a supported datatype for roofline profiling on {}".format(
str(dtype), self.__mspec.gpu_model
),
exit=False,
)
return
# Check proper datatype input - takes single str
if not isinstance(dtype, str):
console_error("Unsupported datatype input - must be str")
if (
not isinstance(self.__run_parameters["workload_dir"], list)
and self.__run_parameters["workload_dir"] != None
):
self.roof_setup()
# Change vL1D to a interpretable str, if required
if "vL1D" in self.__run_parameters["mem_level"]:
self.__run_parameters["mem_level"].remove("vL1D")
self.__run_parameters["mem_level"].append("L1")
app_path = str(
Path(self.__run_parameters["workload_dir"]).joinpath("pmc_perf.csv")
)
roofline_exists = Path(app_path).is_file()
if not roofline_exists:
console_error("roofline", "{} does not exist".format(app_path))
t_df = OrderedDict()
t_df["pmc_perf"] = pd.read_csv(app_path)
color_scheme = {
"HBM": "blue+",
"L2": "green+",
"L1": "red+",
"LDS": "orange+",
"VALU": "white",
"MFMA": "magenta+",
}
kernel_markers = {
0: "star",
1: "cross",
2: "sd",
3: "shamrock",
4: "at",
5: "atom",
}
self.__ceiling_data = constuct_roof(
roofline_parameters=self.__run_parameters,
dtype=dtype,
)
self.__ai_data = calc_ai(self.__mspec, self.__run_parameters["sort_type"], t_df)
plt.clf()
plt.plotsize(plt.tw(), plt.th())
ops_flops = "OP" if (dtype[:1] == "I") else "FLOP" # For printing purposes
# Plot BW Lines
if self.__run_parameters["mem_level"] == "ALL":
cache_hierarchy = ["HBM", "L2", "L1", "LDS"]
else:
cache_hierarchy = self.__run_parameters["mem_level"]
for cache_level in cache_hierarchy:
plt.plot(
self.__ceiling_data[cache_level.lower()][0],
self.__ceiling_data[cache_level.lower()][1],
label="{}-{}".format(cache_level, dtype),
marker="braille",
color=color_scheme[cache_level],
)
plt.text(
str(round(self.__ceiling_data[cache_level.lower()][2])) + " GB/s",
x=self.__ceiling_data[cache_level.lower()][0][0],
y=self.__ceiling_data[cache_level.lower()][1][0],
background="black",
color="white",
alignment="left",
)
console_debug(
"roofline",
cache_level
+ ": [{},{}], [{},{}], {}".format(
str(self.__ceiling_data[cache_level.lower()][0][0]),
str(self.__ceiling_data[cache_level.lower()][0][1]),
str(self.__ceiling_data[cache_level.lower()][1][0]),
str(self.__ceiling_data[cache_level.lower()][1][1]),
str(self.__ceiling_data[cache_level.lower()][2]),
),
)
# Plot VALU and MFMA Peak
if dtype in PEAK_OPS_DATATYPES:
plt.plot(
self.__ceiling_data["valu"][0],
[
self.__ceiling_data["valu"][1][0] - 0.1,
self.__ceiling_data["valu"][1][1] - 0.1,
],
label="Peak VALU-{}".format(dtype),
marker="braille",
color=color_scheme["VALU"],
)
plt.text(
str(round(self.__ceiling_data["valu"][2])) + " G{}/s".format(ops_flops),
x=self.__ceiling_data["valu"][0][1] - 800,
y=self.__ceiling_data["valu"][1][1],
background="black",
color="white",
alignment="right",
)
console_debug(
"roofline",
"VALU: [{},{}], [{},{}], {}".format(
str(self.__ceiling_data["valu"][0][0]),
str(self.__ceiling_data["valu"][0][1]),
str(self.__ceiling_data["valu"][1][0]),
str(self.__ceiling_data["valu"][1][1]),
str(self.__ceiling_data["valu"][2]),
),
)
else:
console_warning("No PEAK measurement available for {}".format(dtype))
if dtype in MFMA_DATATYPES:
plt.plot(
self.__ceiling_data["mfma"][0],
[
self.__ceiling_data["mfma"][1][0] - 0.1,
self.__ceiling_data["mfma"][1][1] - 0.1,
],
label="Peak MFMA-{}".format(dtype),
marker="braille",
color=color_scheme["MFMA"],
)
plt.text(
str(round(self.__ceiling_data["mfma"][2])) + " G{}/s".format(ops_flops),
x=self.__ceiling_data["mfma"][0][1] - 800,
y=self.__ceiling_data["mfma"][1][1],
background="black",
color="white",
alignment="right",
)
console_debug(
"roofline",
"MFMA: [{},{}], [{},{}], {}".format(
str(self.__ceiling_data["mfma"][0][0]),
str(self.__ceiling_data["mfma"][0][1]),
str(self.__ceiling_data["mfma"][1][0]),
str(self.__ceiling_data["mfma"][1][1]),
str(self.__ceiling_data["mfma"][2]),
),
)
else:
console_warning("No MFMA measurement available for {}".format(dtype))
# Plot Application AI
for cache_level in cache_hierarchy:
key = "ai_" + cache_level.lower()
if key in self.__ai_data:
for i in range(len(self.__ai_data["kernelNames"])):
# Zero intensity level means no data reported for this cache level
if self.__ai_data[key][0][i] > 0 and self.__ai_data[key][1][i] > 0:
plt.plot(
[self.__ai_data[key][0][i]],
[self.__ai_data[key][1][i]],
label="AI_"
+ cache_level
+ "_{}".format(self.__ai_data["kernelNames"][i]),
color=color_scheme[cache_level],
marker=kernel_markers[i % len(kernel_markers)],
)
console_debug(
"roofline",
"AI_{}: {}, {}".format(
self.__ai_data["kernelNames"][i],
self.__ai_data[key][0][i],
self.__ai_data[key][1][i],
),
)
plt.xlabel("Arithmetic Intensity ({})s/Byte)".format(ops_flops))
plt.ylabel("Performance (GFLOP/sec)")
plt.title("Roofline ({})".format(dtype))
# Canvas config
plt.theme("pro")
plt.xscale("log")
plt.yscale("log")
# Build figure
# Print plot using `plt._utility.write(self.cli_generate_plot(dtype))`
return plt.build()
@demarcate
def standalone_roofline(self):
if (