Analysis report block based filtering for profiling (#566)
* Analysis report block based filtering for profiling
* Profiling mode changes
- `-b` option now additionally accepts metric id(s), similar to `-b` option in analyze mode (e.g. 6, 6.2, 6.23)
- Only counters mentioned in the selected analysis report blocks will be collected
- Add parsing logic to identify hardware counters from analysis report blocks
- Add filtering logic to only write filtered counters in perfmon files
- Log not collected counters in one line
- `--list-metrics` option added in profile mode to list possible metric id(s) similar to analyze mode
- Write arguments provided during profiling in profiling_configuration.yaml file
* Analysis mode changes
- During analysis mode, only show report blocks selected during profiling
- If `-b` option is provided in analysis mode, then follow provided filters
- Do not show empty tables in analysis report
* Miscellaneous changes
- Update CHANGELOG
- Add test cases
- Instruction mix report block filter
- Instruction mix and Memory chart report block filter
- Instruction mix report block filter and CPC hardware block filter
- TA hardware block filter
- --list-metrics in profile mode should work
- Move binary handler fixtures to conftest.py to avoid importing
fixtures
- cmake file in tests directory has been updated to compile sample/vmem.hip for testing
* Public documentation changes
- Use the term "Hardware report block" instead of "Hardware block"
- Add documentation for "--list-metrics" option in profile mode
- Add example of filtering by hardware report block such as instruction
mix and wavefront launch statistics
- Add deprecation warning for hardware component (sq, tcc) based filtering
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
0aefd15b7b
Коммит
55cf0e237e
@@ -36,7 +36,7 @@ import yaml
|
||||
import config
|
||||
from utils import schema
|
||||
from utils.kernel_name_shortener import kernel_name_shortener
|
||||
from utils.utils import console_debug, console_error, demarcate
|
||||
from utils.utils import console_debug, console_error, console_log, demarcate
|
||||
|
||||
# TODO: use pandas chunksize or dask to read really large csv file
|
||||
# from dask import dataframe as dd
|
||||
@@ -85,6 +85,21 @@ def load_panel_configs(dir):
|
||||
return od
|
||||
|
||||
|
||||
def load_profiling_config(config_dir):
|
||||
"""
|
||||
Load profiling config from yaml file.
|
||||
"""
|
||||
try:
|
||||
with open(Path(config_dir).joinpath("profiling_config.yaml")) as file:
|
||||
prof_config = yaml.safe_load(file)
|
||||
return prof_config
|
||||
except FileNotFoundError:
|
||||
console_log(
|
||||
f"Could not find profiling_config.yaml in {config_dir} for filtering analysis report"
|
||||
)
|
||||
return dict()
|
||||
|
||||
|
||||
@demarcate
|
||||
def create_df_kernel_top_stats(
|
||||
df_in,
|
||||
|
||||
@@ -492,7 +492,9 @@ def build_dfs(archConfigs, filter_metrics, sys_info):
|
||||
if type == "metric_table":
|
||||
headers = ["Metric_ID"]
|
||||
data_source_idx = str(data_config["id"] // 100)
|
||||
if data_source_idx != 0 or data_source_idx in filter_metrics:
|
||||
if data_source_idx != 0 or (
|
||||
filter_metrics and data_source_idx in filter_metrics
|
||||
):
|
||||
metric_list[data_source_idx] = panel["title"]
|
||||
if (
|
||||
"cli_style" in data_config
|
||||
|
||||
+58
-7
@@ -29,7 +29,7 @@ import pandas as pd
|
||||
from tabulate import tabulate
|
||||
|
||||
from utils import parser
|
||||
from utils.utils import console_log, console_warning
|
||||
from utils.utils import console_log, console_warning, convert_metric_id_to_panel_idx
|
||||
|
||||
hidden_columns = ["Tips", "coll_level"]
|
||||
hidden_sections = [1900, 2000]
|
||||
@@ -60,11 +60,20 @@ def get_table_string(df, transpose=False, decimal=2):
|
||||
)
|
||||
|
||||
|
||||
def show_all(args, runs, archConfigs, output):
|
||||
def show_all(args, runs, archConfigs, output, profiling_config):
|
||||
"""
|
||||
Show all panels with their data in plain text mode.
|
||||
"""
|
||||
comparable_columns = parser.build_comparable_columns(args.time_unit)
|
||||
filter_panel_ids = [
|
||||
convert_metric_id_to_panel_idx(section)
|
||||
for section in [
|
||||
name
|
||||
for name, type in profiling_config.get("filter_blocks", {}).items()
|
||||
if type == "metric_id"
|
||||
]
|
||||
]
|
||||
comparable_columns = parser.build_comparable_columns(args.time_unit)
|
||||
|
||||
for panel_id, panel in archConfigs.panel_configs.items():
|
||||
# Skip panels that don't support baseline comparison
|
||||
@@ -74,6 +83,27 @@ def show_all(args, runs, archConfigs, output):
|
||||
|
||||
for data_source in panel["data source"]:
|
||||
for type, table_config in data_source.items():
|
||||
# If block filtering was used during analysis, then dont use profiling config
|
||||
# If block filtering was used in profiling config, only show those panels
|
||||
# If block filtering not used in profiling config, show all panels
|
||||
# Skip this table if table id or panel id is not present in block filters
|
||||
# However, always show panel id <= 100
|
||||
if (
|
||||
not args.filter_metrics
|
||||
and filter_panel_ids
|
||||
and table_config["id"] not in filter_panel_ids
|
||||
and panel_id not in filter_panel_ids
|
||||
and panel_id > 100
|
||||
):
|
||||
table_id_str = (
|
||||
str(table_config["id"] // 100)
|
||||
+ "."
|
||||
+ str(table_config["id"] % 100)
|
||||
)
|
||||
console_log(
|
||||
f"Not showing table not selected during profiling: {table_id_str} {table_config['title']}"
|
||||
)
|
||||
continue
|
||||
# take the 1st run as baseline
|
||||
base_run, base_data = next(iter(runs.items()))
|
||||
base_df = base_data.dfs[table_config["id"]]
|
||||
@@ -207,7 +237,25 @@ def show_all(args, runs, archConfigs, output):
|
||||
+ str(table_config["id"] % 100)
|
||||
)
|
||||
|
||||
if "title" in table_config and table_config["title"]:
|
||||
# Check if any column in df is empty
|
||||
is_empty_columns_exist = any(
|
||||
[
|
||||
df.columns[col_idx]
|
||||
for col_idx in range(len(df.columns))
|
||||
if df.replace("", None).iloc[:, col_idx].isnull().all()
|
||||
]
|
||||
)
|
||||
# Do not print the table if any column is empty
|
||||
if is_empty_columns_exist:
|
||||
console_log(
|
||||
f"Not showing table with empty column(s): {table_id_str} {table_config['title']}"
|
||||
)
|
||||
|
||||
if (
|
||||
"title" in table_config
|
||||
and table_config["title"]
|
||||
and not is_empty_columns_exist
|
||||
):
|
||||
ss += table_id_str + " " + table_config["title"] + "\n"
|
||||
|
||||
if args.df_file_dir:
|
||||
@@ -238,10 +286,13 @@ def show_all(args, runs, archConfigs, output):
|
||||
and "columnwise" in table_config
|
||||
and table_config["columnwise"] == True
|
||||
)
|
||||
ss += (
|
||||
get_table_string(df, transpose=transpose, decimal=args.decimal)
|
||||
+ "\n"
|
||||
)
|
||||
if not is_empty_columns_exist:
|
||||
ss += (
|
||||
get_table_string(
|
||||
df, transpose=transpose, decimal=args.decimal
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
if ss:
|
||||
print("\n" + "-" * 80, file=output)
|
||||
|
||||
@@ -191,7 +191,7 @@ def capture_subprocess_output(subprocess_args, new_env=None, profileMode=False):
|
||||
global rocprof_args
|
||||
# Format command for debug messages, formatting for rocprofv1 and rocprofv2
|
||||
command = " ".join(rocprof_args)
|
||||
console_debug("subprocess", "Running: " + command)
|
||||
console_debug("subprocess", "Running: " + command + " " + " ".join(subprocess_args))
|
||||
# Start subprocess
|
||||
# bufsize = 1 means output is line buffered
|
||||
# universal_newlines = True is required for line buffering
|
||||
@@ -820,7 +820,7 @@ def gen_sysinfo(
|
||||
df["workload_name"] = workload_name
|
||||
|
||||
blocks = []
|
||||
if ip_blocks == None:
|
||||
if not ip_blocks:
|
||||
t = ["SQ", "LDS", "SQC", "TA", "TD", "TCP", "TCC", "SPI", "CPC", "CPF"]
|
||||
blocks += t
|
||||
else:
|
||||
@@ -1249,3 +1249,16 @@ def merge_counters_spatial_multiplex(df_multi_index):
|
||||
|
||||
final_df = pd.concat(result_dfs, keys=coll_levels, axis=1, copy=False)
|
||||
return final_df
|
||||
|
||||
|
||||
def convert_metric_id_to_panel_idx(metric_id):
|
||||
# "4.02" -> 402
|
||||
# "4.23" -> 423
|
||||
# "4" -> 400
|
||||
tokens = metric_id.split(".")
|
||||
if len(tokens) == 1:
|
||||
return int(tokens[0]) * 100
|
||||
elif len(tokens) == 2:
|
||||
return int(tokens[0]) * 100 + int(tokens[1])
|
||||
else:
|
||||
raise Exception(f"Invalid metric id: {metric_id}")
|
||||
|
||||
Ссылка в новой задаче
Block a user