Spatial multiplexing: part2, analysis (#542)
Co-authored-by: Yang Wang <ywang103@amd.com>
This commit is contained in:
committed by
Vignesh Edithal
parent
9643afa62d
commit
808a9a79ed
@@ -495,6 +495,14 @@ Examples:
|
||||
nargs="+",
|
||||
help="\t\tSpecify GPU id(s) for filtering.",
|
||||
)
|
||||
analyze_group.add_argument(
|
||||
"--spatial-multiplexing",
|
||||
dest="spatial_multiplexing",
|
||||
required=False,
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="\t\t\tMode of spatial multiplexing.",
|
||||
)
|
||||
analyze_group.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
|
||||
@@ -36,6 +36,7 @@ from utils.utils import (
|
||||
console_log,
|
||||
demarcate,
|
||||
is_workload_empty,
|
||||
merge_counters_spatial_multiplex,
|
||||
)
|
||||
|
||||
|
||||
@@ -57,6 +58,10 @@ class OmniAnalyze_Base:
|
||||
def get_socs(self):
|
||||
return self.__socs
|
||||
|
||||
@demarcate
|
||||
def spatial_multiplex_merge_counters(self, df):
|
||||
return merge_counters_spatial_multiplex(df)
|
||||
|
||||
@demarcate
|
||||
def generate_configs(self, arch, config_dir, list_stats, filter_metrics, sys_info):
|
||||
single_panel_config = file_io.is_single_panel_config(
|
||||
@@ -142,6 +147,7 @@ class OmniAnalyze_Base:
|
||||
sysinfo_path = (
|
||||
Path(d[0])
|
||||
if self.__args.nodes is None
|
||||
and self.__args.spatial_multiplexing is not True
|
||||
else file_io.find_1st_sub_dir(d[0])
|
||||
)
|
||||
sys_info = file_io.load_sys_info(sysinfo_path.joinpath("sysinfo.csv"))
|
||||
@@ -166,6 +172,7 @@ class OmniAnalyze_Base:
|
||||
sysinfo_path = (
|
||||
Path(d[0])
|
||||
if self.__args.nodes is None
|
||||
and self.__args.spatial_multiplexing is not True
|
||||
else file_io.find_1st_sub_dir(d[0])
|
||||
)
|
||||
w.sys_info = file_io.load_sys_info(sysinfo_path.joinpath("sysinfo.csv"))
|
||||
@@ -199,7 +206,11 @@ class OmniAnalyze_Base:
|
||||
# validate profiling data
|
||||
|
||||
# Todo: more err check
|
||||
if not (self.__args.nodes != None or self.__args.list_nodes):
|
||||
if not (
|
||||
self.__args.nodes != None
|
||||
or self.__args.list_nodes
|
||||
or self.__args.spatial_multiplexing
|
||||
):
|
||||
is_workload_empty(dir[0])
|
||||
# else:
|
||||
|
||||
|
||||
@@ -44,10 +44,16 @@ class cli_analysis(OmniAnalyze_Base):
|
||||
self._runs[d[0]].raw_pmc = file_io.create_df_pmc(
|
||||
d[0],
|
||||
self.get_args().nodes,
|
||||
self.get_args().spatial_multiplexing,
|
||||
self.get_args().kernel_verbose,
|
||||
self.get_args().verbose,
|
||||
)
|
||||
|
||||
if self.get_args().spatial_multiplexing:
|
||||
self._runs[d[0]].raw_pmc = self.spatial_multiplex_merge_counters(
|
||||
self._runs[d[0]].raw_pmc
|
||||
)
|
||||
|
||||
file_io.create_df_kernel_top_stats(
|
||||
df_in=self._runs[d[0]].raw_pmc,
|
||||
raw_data_dir=d[0],
|
||||
|
||||
@@ -113,9 +113,16 @@ class webui_analysis(OmniAnalyze_Base):
|
||||
base_data[base_run].raw_pmc = file_io.create_df_pmc(
|
||||
self.dest_dir,
|
||||
self.get_args().nodes,
|
||||
self.get_args().spatial_multiplexing,
|
||||
self.get_args().kernel_verbose,
|
||||
self.get_args().verbose,
|
||||
)
|
||||
|
||||
if self.get_args().spatial_multiplexing:
|
||||
base_data[base_run].raw_pmc = self.spatial_multiplex_merge_counters(
|
||||
base_data[base_run].raw_pmc
|
||||
)
|
||||
|
||||
console_debug("analysis", "gui dispatch filter is %s" % disp_filt)
|
||||
console_debug("analysis", "gui kernel filter is %s" % kernel_filter)
|
||||
console_debug("analysis", "gui gpu filter is %s" % gcd_filter)
|
||||
@@ -290,6 +297,12 @@ class webui_analysis(OmniAnalyze_Base):
|
||||
self.get_args().kernel_verbose,
|
||||
args.verbose,
|
||||
)
|
||||
|
||||
if self.get_args().spatial_multiplexing:
|
||||
self._runs[self.dest_dir].raw_pmc = self.spatial_multiplex_merge_counters(
|
||||
self._runs[self.dest_dir].raw_pmc
|
||||
)
|
||||
|
||||
file_io.create_df_kernel_top_stats(
|
||||
df_in=self._runs[self.dest_dir].raw_pmc,
|
||||
raw_data_dir=self.dest_dir,
|
||||
|
||||
@@ -219,6 +219,15 @@ class RocProfCompute:
|
||||
p.mkdir(parents=True, exist_ok=False)
|
||||
except FileExistsError:
|
||||
console_error("Directory already exists.")
|
||||
|
||||
elif self.__args.mode == "analyze":
|
||||
# block all filters during spatial-multiplexing
|
||||
if self.__args.spatial_multiplexing:
|
||||
self.__args.gpu_id = None
|
||||
self.__args.gpu_kernel = None
|
||||
self.__args.gpu_dispatch_id = None
|
||||
self.__args.nodes = None
|
||||
|
||||
return
|
||||
|
||||
@demarcate
|
||||
@@ -342,6 +351,7 @@ class RocProfCompute:
|
||||
sysinfo_path = (
|
||||
Path(d[0])
|
||||
if analyzer.get_args().nodes is None
|
||||
and analyzer.get_args().spatial_multiplexing is not True
|
||||
else file_io.find_1st_sub_dir(d[0])
|
||||
)
|
||||
sys_info = file_io.load_sys_info(sysinfo_path.joinpath("sysinfo.csv"))
|
||||
|
||||
+31
-16
@@ -176,7 +176,9 @@ def create_df_kernel_top_stats(
|
||||
|
||||
|
||||
@demarcate
|
||||
def create_df_pmc(raw_data_root_dir, nodes, kernel_verbose, verbose):
|
||||
def create_df_pmc(
|
||||
raw_data_root_dir, nodes, spatial_multiplexing, kernel_verbose, verbose
|
||||
):
|
||||
"""
|
||||
Load all raw pmc counters and join into one df.
|
||||
"""
|
||||
@@ -212,12 +214,7 @@ def create_df_pmc(raw_data_root_dir, nodes, kernel_verbose, verbose):
|
||||
console_debug("pmc_raw_data final_single_df %s" % final_df.info)
|
||||
return final_df
|
||||
|
||||
# regular single node case
|
||||
if nodes is None:
|
||||
return create_single_df_pmc(raw_data_root_dir, None, kernel_verbose, verbose)
|
||||
|
||||
# "empty list" means all nodes
|
||||
elif not nodes:
|
||||
if spatial_multiplexing:
|
||||
df = pd.DataFrame()
|
||||
# todo: more err check
|
||||
for subdir in Path(raw_data_root_dir).iterdir():
|
||||
@@ -230,15 +227,33 @@ def create_df_pmc(raw_data_root_dir, nodes, kernel_verbose, verbose):
|
||||
|
||||
# specified node list
|
||||
else:
|
||||
df = pd.DataFrame()
|
||||
# todo: more err check
|
||||
for subdir in nodes:
|
||||
p = Path(raw_data_root_dir)
|
||||
new_df = create_single_df_pmc(
|
||||
p.joinpath(subdir), subdir, kernel_verbose, verbose
|
||||
)
|
||||
df = pd.concat([df, new_df])
|
||||
return df
|
||||
# regular single node case
|
||||
if nodes is None:
|
||||
return create_single_df_pmc(raw_data_root_dir, None, kernel_verbose, verbose)
|
||||
|
||||
# "empty list" means all nodes
|
||||
elif not nodes:
|
||||
df = pd.DataFrame()
|
||||
# todo: more err check
|
||||
for subdir in Path(raw_data_root_dir).iterdir():
|
||||
if subdir.is_dir():
|
||||
new_df = create_single_df_pmc(
|
||||
subdir, str(subdir.name), kernel_verbose, verbose
|
||||
)
|
||||
df = pd.concat([df, new_df])
|
||||
return df
|
||||
|
||||
# specified node list
|
||||
else:
|
||||
df = pd.DataFrame()
|
||||
# todo: more err check
|
||||
for subdir in nodes:
|
||||
p = Path(raw_data_root_dir)
|
||||
new_df = create_single_df_pmc(
|
||||
p.joinpath(subdir), subdir, kernel_verbose, verbose
|
||||
)
|
||||
df = pd.concat([df, new_df])
|
||||
return df
|
||||
|
||||
|
||||
def collect_wave_occu_per_cu(in_dir, out_dir, numSE):
|
||||
|
||||
@@ -1091,3 +1091,125 @@ def set_locale_encoding():
|
||||
exit=False,
|
||||
)
|
||||
console_error(error)
|
||||
|
||||
|
||||
def reverse_multi_index_df_pmc(final_df):
|
||||
"""
|
||||
Util function to decompose multi-index dataframe.
|
||||
"""
|
||||
# Check if the columns have more than one level
|
||||
if len(final_df.columns.levels) < 2:
|
||||
raise ValueError("Input DataFrame does not have a multi-index column.")
|
||||
|
||||
# Extract the first level of the MultiIndex columns (the file names)
|
||||
coll_levels = final_df.columns.get_level_values(0).unique().tolist()
|
||||
|
||||
# Initialize the list of DataFrames
|
||||
dfs = []
|
||||
|
||||
# Loop through each 'coll_level' and rebuild the DataFrames
|
||||
for level in coll_levels:
|
||||
# Select columns that belong to the current 'coll_level'
|
||||
columns_for_level = final_df.xs(level, axis=1, level=0)
|
||||
|
||||
# Append the DataFrame for this level
|
||||
dfs.append(columns_for_level)
|
||||
|
||||
# Return the list of DataFrames and the column levels
|
||||
return dfs, coll_levels
|
||||
|
||||
|
||||
def merge_counters_spatial_multiplex(df_multi_index):
|
||||
"""
|
||||
For spatial multiplexing, this merges counter values for the same kernel that runs on different devices. For time stamp, start time stamp will use median while for end time stamp, it will be equal to the summation between median start stamp and median delta time.
|
||||
"""
|
||||
non_counter_column_index = [
|
||||
"Dispatch_ID",
|
||||
"GPU_ID",
|
||||
"Queue_ID",
|
||||
"PID",
|
||||
"TID",
|
||||
"Grid_Size",
|
||||
"Workgroup_Size",
|
||||
"LDS_Per_Workgroup",
|
||||
"Scratch_Per_Workitem",
|
||||
"Arch_VGPR",
|
||||
"Accum_VGPR",
|
||||
"SGPR",
|
||||
"Wave_Size",
|
||||
"Kernel_Name",
|
||||
"Start_Timestamp",
|
||||
"End_Timestamp",
|
||||
"Correlation_ID",
|
||||
"Kernel_ID",
|
||||
"Node",
|
||||
]
|
||||
|
||||
expired_column_index = [
|
||||
"Node",
|
||||
"PID",
|
||||
"TID",
|
||||
"Queue_ID",
|
||||
]
|
||||
|
||||
result_dfs = []
|
||||
|
||||
# TODO: will need optimize to avoid this convertion to single index format and do merge directly on multi-index dataframe
|
||||
dfs, coll_levels = reverse_multi_index_df_pmc(df_multi_index)
|
||||
|
||||
for df in dfs:
|
||||
kernel_name_column_name = "Kernel_Name"
|
||||
if not "Kernel_Name" in df and "Name" in df:
|
||||
kernel_name_column_name = "Name"
|
||||
|
||||
# Find the values in Kernel_Name that occur more than once
|
||||
kernel_single_occurances = df[kernel_name_column_name].value_counts().index
|
||||
|
||||
# Define a list to store the merged rows
|
||||
result_data = []
|
||||
|
||||
for kernel_name in kernel_single_occurances:
|
||||
# Get all rows for the current kernel_name
|
||||
group = df[df[kernel_name_column_name] == kernel_name]
|
||||
|
||||
# Create a dictionary to store the merged row for the current group
|
||||
merged_row = {}
|
||||
|
||||
# Process non-counter columns
|
||||
for col in [
|
||||
col for col in non_counter_column_index if col not in expired_column_index
|
||||
]:
|
||||
if col == "Start_Timestamp":
|
||||
# For Start_Timestamp, take the median
|
||||
merged_row[col] = group["Start_Timestamp"].median()
|
||||
elif col == "End_Timestamp":
|
||||
# For End_Timestamp, calculate the median delta time
|
||||
delta_time = group["End_Timestamp"] - group["Start_Timestamp"]
|
||||
median_delta_time = delta_time.median()
|
||||
merged_row[col] = merged_row["Start_Timestamp"] + median_delta_time
|
||||
else:
|
||||
# For other non-counter columns, take the first occurrence (0th row)
|
||||
merged_row[col] = group.iloc[0][col]
|
||||
|
||||
# Process counter columns (assumed to be all columns not in non_counter_column_index)
|
||||
counter_columns = [
|
||||
col for col in group.columns if col not in non_counter_column_index
|
||||
]
|
||||
for counter_col in counter_columns:
|
||||
# for counter columns, take the first non-none (or non-nan) value
|
||||
current_valid_counter_group = group[group[counter_col].notna()]
|
||||
first_valid_value = (
|
||||
current_valid_counter_group.iloc[0][counter_col]
|
||||
if len(current_valid_counter_group) > 0
|
||||
else None
|
||||
)
|
||||
merged_row[counter_col] = first_valid_value
|
||||
|
||||
# Append the merged row to the result list
|
||||
result_data.append(merged_row)
|
||||
|
||||
# Create a new DataFrame from the merged rows
|
||||
result_dfs.append(pd.DataFrame(result_data))
|
||||
|
||||
final_df = pd.concat(result_dfs, keys=coll_levels, axis=1, copy=False)
|
||||
return final_df
|
||||
|
||||
Reference in New Issue
Block a user