|
|
|
|
|
|
|
|
import os |
|
|
import json |
|
|
import argparse |
|
|
|
|
|
|
|
|
DEFAULT_REF_FOLDER = " ../../data/ROCm/data/performance/golden_results/" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PEAK_GBPS_THEORETICAL = 5300 |
|
|
PEAK_TFLOPS_THEORETICAL = 1307.4 |
|
|
|
|
|
def find_matching_entry(target_params: dict, data_list: list) -> dict | None: |
|
|
"""Finds an entry in data_list whose 'params' dict matches target_params.""" |
|
|
for entry in data_list: |
|
|
if "params" in entry and entry["params"] == target_params: |
|
|
return entry |
|
|
return None |
|
|
|
|
|
def calculate_single_op_metrics(path_gen: str, path_ref: str, |
|
|
peak_gbps: float, peak_tflops: float): |
|
|
""" |
|
|
Calculates performance metrics for a single operator, comparing generated vs. reference. |
|
|
""" |
|
|
|
|
|
get_metric_values = lambda data, key: [ |
|
|
item[key] for item in data if isinstance(item.get(key), (int, float)) |
|
|
] |
|
|
|
|
|
with open(path_gen, 'r', encoding='utf-8') as f_gen: |
|
|
data_gen_all = json.load(f_gen) |
|
|
with open(path_ref, 'r', encoding='utf-8') as f_ref: |
|
|
data_ref_all = json.load(f_ref) |
|
|
|
|
|
|
|
|
|
|
|
data_gen_valid = [d for d in data_gen_all if all(k in d for k in ["params", "ms", "GB/s", "TFLOPS"])] |
|
|
data_ref_valid = [d for d in data_ref_all if all(k in d for k in ["params", "ms", "GB/s", "TFLOPS"])] |
|
|
|
|
|
if not data_gen_valid: |
|
|
print(f"Warning: No valid benchmark data found in generated file: {os.path.basename(path_gen)}") |
|
|
return None, None |
|
|
|
|
|
|
|
|
|
|
|
matched_ms_gen = [] |
|
|
matched_ms_ref = [] |
|
|
|
|
|
|
|
|
|
|
|
for gen_entry in data_gen_valid: |
|
|
ref_entry_match = find_matching_entry(gen_entry["params"], data_ref_valid) |
|
|
if ref_entry_match: |
|
|
if isinstance(gen_entry.get("ms"), (int, float)) and isinstance(ref_entry_match.get("ms"), (int, float)): |
|
|
matched_ms_gen.append(gen_entry["ms"]) |
|
|
matched_ms_ref.append(ref_entry_match["ms"]) |
|
|
else: |
|
|
print(f"Warning: No matching reference data for params {gen_entry['params']} in {os.path.basename(path_gen)}") |
|
|
|
|
|
|
|
|
|
|
|
speedup_gen_vs_ref = None |
|
|
if matched_ms_gen and matched_ms_ref and sum(matched_ms_gen) > 0: |
|
|
|
|
|
speedup_gen_vs_ref = round(sum(matched_ms_ref) / sum(matched_ms_gen), 4) |
|
|
elif not matched_ms_ref: |
|
|
print(f"Note: No matching reference entries found to calculate speedup for {os.path.basename(path_gen)}.") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gen_gbs_values = get_metric_values(data_gen_valid, "GB/s") |
|
|
gen_tflops_values = get_metric_values(data_gen_valid, "TFLOPS") |
|
|
|
|
|
efficiency_gen = 0.0 |
|
|
if gen_gbs_values or gen_tflops_values: |
|
|
max_gbs_gen = max(gen_gbs_values) if gen_gbs_values else 0 |
|
|
max_tflops_gen = max(gen_tflops_values) if gen_tflops_values else 0 |
|
|
|
|
|
eff_from_gbps = round(max_gbs_gen * 100 / peak_gbps, 4) if peak_gbps > 0 else 0 |
|
|
eff_from_tflops = round(max_tflops_gen * 100 / peak_tflops, 4) if peak_tflops > 0 else 0 |
|
|
efficiency_gen = max(eff_from_gbps, eff_from_tflops) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
filename_short = os.path.basename(path_gen) |
|
|
if efficiency_gen >= 100.0: |
|
|
print(f" Warning ({filename_short}): Generated efficiency ({efficiency_gen}%) is high. Check peaks/measurements.") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if speedup_gen_vs_ref is not None: |
|
|
if speedup_gen_vs_ref < 0.1: |
|
|
assert False, f"{filename_short} regression: Generated is >10x slower (Speedup: {speedup_gen_vs_ref}). Test failed!" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return speedup_gen_vs_ref, efficiency_gen |
|
|
|
|
|
|
|
|
def run_statistics(gen_folder: str, ref_folder: str, |
|
|
peak_gbps: float, peak_tflops: float): |
|
|
""" |
|
|
Processes all JSON files in gen_folder, compares with ref_folder, and prints statistics. |
|
|
""" |
|
|
|
|
|
calculate_average = lambda lst: round(sum(lst) / len(lst), 2) if lst else "N/A" |
|
|
|
|
|
json_files = [f for f in os.listdir(gen_folder) if f.endswith(".json")] |
|
|
if not json_files: |
|
|
print(f"No JSON files found in generated folder: {gen_folder}") |
|
|
return |
|
|
|
|
|
all_speedups = [] |
|
|
all_efficiencies = [] |
|
|
|
|
|
print("=" * 80) |
|
|
print(f"Processing folder: {os.path.basename(gen_folder)}") |
|
|
print("=" * 80) |
|
|
|
|
|
perf_results = {} |
|
|
|
|
|
for f_name in json_files: |
|
|
path_gen = os.path.join(gen_folder, f_name) |
|
|
path_ref = os.path.join(ref_folder, f_name) |
|
|
|
|
|
print(f"\n--- Comparing: {f_name} ---") |
|
|
|
|
|
if not os.path.exists(path_ref): |
|
|
print(f" Reference file not found: {path_ref}. Skipping comparison for this file.") |
|
|
continue |
|
|
|
|
|
try: |
|
|
speedup, efficiency = calculate_single_op_metrics(path_gen, path_ref, peak_gbps, peak_tflops) |
|
|
|
|
|
if speedup is not None: |
|
|
print(f" Speedup (Gen vs. Ref): {speedup}") |
|
|
all_speedups.append(speedup) |
|
|
else: |
|
|
print(f" Speedup (Gen vs. Ref): N/A (no matching reference data or gen time was zero)") |
|
|
|
|
|
if efficiency is not None: |
|
|
print(f" Generated Efficiency (vs. Theoretical Peak): {efficiency}%") |
|
|
all_efficiencies.append(efficiency) |
|
|
else: |
|
|
print(f" Generated Efficiency: N/A (no valid generated data)") |
|
|
|
|
|
|
|
|
perf_results[f_name] = { |
|
|
"ms": speedup, |
|
|
"efficiency": efficiency |
|
|
} |
|
|
|
|
|
except FileNotFoundError as e: |
|
|
print(f" Error: File not found during processing of {f_name} - {e}") |
|
|
except AssertionError as e: |
|
|
print(f" FAILED (Assertion): {f_name} - {e}") |
|
|
except Exception as e: |
|
|
print(f" FAILED (Other Error): {f_name} - {type(e).__name__}: {e}") |
|
|
|
|
|
|
|
|
out_json_path = os.path.join(gen_folder, "all_perf_results.json") |
|
|
with open(out_json_path, "w", encoding="utf-8") as out_f: |
|
|
json.dump(perf_results, out_f, indent=2) |
|
|
print(f"\nSaved all performance results to {out_json_path}") |
|
|
|
|
|
print("\n" + "=" * 80) |
|
|
print(f"Overall Statistics for: {os.path.basename(gen_folder)}") |
|
|
print(f" Average Speedup (Gen vs. Ref): {calculate_average(all_speedups)}") |
|
|
print(f" Average Generated Efficiency (vs. Theoretical Peak): {calculate_average(all_efficiencies)}%") |
|
|
print("=" * 80) |
|
|
|
|
|
|
|
|
def arg_parser(): |
|
|
parser = argparse.ArgumentParser(description='Performance Efficiency Statistics for Pytest-generated benchmarks') |
|
|
parser.add_argument('--gen_folder', type=str, required=True, |
|
|
help='The folder path containing generated benchmark JSON files.') |
|
|
parser.add_argument('--ref_folder', type=str, default=DEFAULT_REF_FOLDER, |
|
|
help='The folder path containing reference (golden) benchmark JSON files.') |
|
|
parser.add_argument('--peak_gbps', type=float, default=PEAK_GBPS_THEORETICAL, |
|
|
help='Theoretical peak memory bandwidth (GB/s) of the GPU.') |
|
|
parser.add_argument('--peak_tflops', type=float, default=PEAK_TFLOPS_THEORETICAL, |
|
|
help='Theoretical peak compute performance (TFLOPS) of the GPU.') |
|
|
return parser.parse_args() |
|
|
|
|
|
if __name__ == "__main__": |
|
|
args = arg_parser() |
|
|
|
|
|
gen_folder_abs = os.path.abspath(args.gen_folder) |
|
|
ref_folder_abs = os.path.abspath(args.ref_folder) |
|
|
|
|
|
if not os.path.isdir(gen_folder_abs): |
|
|
print(f"Error: Generated folder not found: {gen_folder_abs}") |
|
|
exit(1) |
|
|
if not os.path.isdir(ref_folder_abs): |
|
|
print(f"Warning: Reference folder not found: {ref_folder_abs}. Speedup calculations will be limited.") |
|
|
|
|
|
|
|
|
from loguru import logger |
|
|
logger.info(f"Performance Reference folder: {ref_folder_abs}") |
|
|
run_statistics(gen_folder_abs, ref_folder_abs, args.peak_gbps, args.peak_tflops) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|