Skip to content

Commit

Permalink
add new script to analyze entire noc trace directories
Browse files Browse the repository at this point in the history
  • Loading branch information
bgrady-tt committed Jan 23, 2025
1 parent 50a8d0c commit 9e1b907
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 9 deletions.
2 changes: 2 additions & 0 deletions tt_npe/cpp/include/npeEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class npeEngine {
const std::vector<PETransferID> &live_transfer_ids,
size_t curr_cycle) const;

void computeSummaryStats(npeStats &stats) const;

void emitSimStats(
const std::string &filepath,
const std::vector<PETransferState> &transfer_state,
Expand Down
87 changes: 87 additions & 0 deletions tt_npe/scripts/analyze_noc_trace_dir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC

import argparse
import tempfile
import os
import glob
import sys
import re
from convert_noc_events_to_workload import convert_noc_traces_to_npe_workload

import tt_npe_pybind as npe

def get_cli_args():
parser = argparse.ArgumentParser(
description='Analyzes all JSON noc traces in a directory using tt-npe',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)

parser.add_argument(
'noc_trace_dir',
type=str,
help='The directory containing the JSON files of the NoC trace events'
)
return parser.parse_args()

def runNPE(opname,yaml_workload_file):
# populate Config struct from cli args
cfg = npe.Config()
cfg.workload_yaml_filepath = yaml_workload_file

wl = npe.createWorkloadFromYAML(cfg.workload_yaml_filepath)
if wl is None:
print(f"E: Could not create tt-npe workload from file '{yaml_workload_file}'; aborting ... ")
sys.exit(1)

npe_api = npe.InitAPI(cfg)
if npe_api is None:
print(f"E: tt-npe could not be initialized, check that config is sound?")
sys.exit(1)

# run workload simulation using npe_api handle
result = npe_api.runNPE(wl)
match type(result):
case npe.Stats:
#print(f"Overall Average Link Utilization: {result.overall_avg_link_util:5.1f}%")
#print(f"Overall Maximum Link Utilization: {result.overall_max_link_util:5.1f}%")
#print(f"Overall Average NIU Utilization: {result.overall_avg_niu_util:5.1f}%")
#print(f"Overall Maximum NIU Utilization: {result.overall_max_niu_util:5.1f}%")
print(f"{opname+',':42} {result.overall_avg_link_util:14.1f}, {result.overall_max_link_util:14.1f}, {result.overall_avg_niu_util:14.1f}, {result.overall_max_niu_util:14.1f}")
case npe.Exception:
print(f"E: tt-npe crashed during perf estimation: {result}")

def main():
args = get_cli_args()
# find all json files in the directory
# for each json file, call convert_noc_traces_to_npe_workload
# with the input and output file paths

# Check if the directory exists
if not os.path.isdir(args.noc_trace_dir):
raise FileNotFoundError(f"The directory {args.noc_trace_dir} does not exist")

# Print header
print(f"{'opname':42} {'AVG LINK UTIL':>14}, {'MAX LINK UTIL':>14}, {'AVG NIU UTIL':>14}, {'MAX NIU UTIL':>14}")

noc_trace_files = glob.glob(os.path.join(args.noc_trace_dir, "*.json"))
for noc_trace_file in noc_trace_files:
# create temp file name for output yaml file
tmp_workload_file = tempfile.mktemp(".yaml", "tmp", ".")
try:
convert_noc_traces_to_npe_workload(
noc_trace_file, tmp_workload_file, coalesce_packets=False, quiet=True
)
opname = os.path.basename(noc_trace_file).split(".")[0]
opname = re.sub("noc_trace_dev\d*_", "", opname)
runNPE(opname,tmp_workload_file)
except Exception as e:
print(f"Error processing {noc_trace_file}: {e}")
finally:
os.remove(tmp_workload_file)


if __name__ == "__main__":
main()
26 changes: 17 additions & 9 deletions tt_npe/scripts/convert_noc_events_to_workload.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ def load_json_file(file_path: Union[str, Path]) -> Union[Dict, List]:
except UnicodeDecodeError:
raise UnicodeDecodeError(f"File {path} is not encoded in UTF-8")

def convert_noc_traces_to_npe_workload(event_data_json, output_filepath, coalesce_packets):
def convert_noc_traces_to_npe_workload(input_filepath, output_filepath, coalesce_packets, quiet):

log = lambda *args: print(*args) if not quiet else None

event_data_json = load_json_file(input_filepath)

t0_timestamp = 2e30
per_core_ts = {}
Expand Down Expand Up @@ -87,7 +91,7 @@ def convert_noc_traces_to_npe_workload(event_data_json, output_filepath, coalesc
if delta > max_kernel_cycles:
max_kernel_cycles = delta
# print(f"{proc},{x},{y} is new max at {max_kernel_cycles} cycles")
print(f"Longest running kernel took {max_kernel_cycles} cycles")
log(f"Longest running kernel took {max_kernel_cycles} cycles")

# setup workload dict
workload = {
Expand Down Expand Up @@ -125,7 +129,7 @@ def convert_noc_traces_to_npe_workload(event_data_json, output_filepath, coalesc
continue

if (noc_event_type in ["WRITE_", "READ"]) and num_bytes == 0:
print("WARNING: skipping event with 0 bytes!")
log("WARNING: skipping event with 0 bytes!")
continue

# handle SET_STATE/WITH_STATE events
Expand Down Expand Up @@ -164,7 +168,7 @@ def convert_noc_traces_to_npe_workload(event_data_json, output_filepath, coalesc
ts = event.get("timestamp")
phase_cycle_offset = int(ts) - t0_timestamp
except Exception as e:
print(f"skipping conversion; timestamp could not be parsed '{ts}'")
log(f"skipping conversion; timestamp could not be parsed '{ts}'")
continue

transfer = {}
Expand All @@ -191,12 +195,12 @@ def convert_noc_traces_to_npe_workload(event_data_json, output_filepath, coalesc
last_transfer = transfers[f"tr{idx}"] = transfer
idx += 1

print(f"Total transfers exported : {len(transfers)}")
log(f"Total transfers exported : {len(transfers)}")
if coalesce_packets:
print(f"Total transfers coalesced : {transfers_coalesced}")
log(f"Total transfers coalesced : {transfers_coalesced}")

# Write YAML file
print(f"writing output yaml workload to : {output_filepath}")
log(f"writing output yaml workload to : {output_filepath}")
with open(output_filepath, 'w') as file:
yaml.dump(workload, file, default_flow_style=False, sort_keys=False, allow_unicode=True)

Expand All @@ -222,12 +226,16 @@ def get_cli_args():
action='store_true',
help='Coalesce adjacent reads/write calls into single logical transfers'
)
parser.add_argument(
'-q,--quiet',
action='store_true',
help='Silence stdout'
)
return parser.parse_args()

def main():
args = get_cli_args()
json_data = load_json_file(args.input_filepath)
convert_noc_traces_to_npe_workload(json_data, args.output_filepath, args.coalesce_packets)
convert_noc_traces_to_npe_workload(args.input_filepath, args.output_filepath, args.coalesce_packets, args.quiet)


if __name__ == "__main__":
Expand Down

0 comments on commit 9e1b907

Please sign in to comment.