1#!/usr/bin/env python32"""E7 analysis: per node — chip, GPU cores, macOS, Metal tests (metallib / runtime MSL), runtime compile time, and3primitive benchmark medians (realized_fused and garch_nll_P64 at reference shapes) with Metal/CPU-MT speedups.4Writes experiments/results/e7_summary.{json,csv}."""5import os, sys, json, glob, re, collections, csv6import numpy as np7root = os.path.join(os.path.dirname(__file__), "..", "results"); raw = os.path.join(root, "raw", "e7")8rows = []9for node in sorted(os.listdir(raw)):10 d = os.path.join(raw, node)11 if not os.path.isdir(d): continue12 res = glob.glob(os.path.join(d, "results_*"))13 if not res: rows.append({"node": node, "status": "no results"}); continue14 r = res[0]; env = json.load(open(os.path.join(r, "environment.json"))) if os.path.exists(os.path.join(r, "environment.json")) else {}15 gpu = open(os.path.join(r, "gpu.txt")).read() if os.path.exists(os.path.join(r, "gpu.txt")) else ""16 cores = re.search(r"Total Number of Cores: (\d+)", gpu); chip = re.search(r"Chipset Model: (.*)", gpu)17 def last(p): return open(p).read().strip().splitlines()[-1] if os.path.exists(p) and open(p).read().strip() else "?"18 t_lib = last(os.path.join(r, "tests_metallib.txt")); t_rt = last(os.path.join(r, "tests_runtime_msl.txt"))19 def comp_rec(name):20 f = os.path.join(r, name); return [json.loads(l) for l in open(f) if '"shader_library_load"' in l] if os.path.exists(f) else []21 comp = comp_rec("bench_cold_compile.jsonl"); warm = comp_rec("bench_warm_compile.jsonl")22 bench = [json.loads(l) for l in open(os.path.join(r, "bench_primitives.jsonl")) if l.strip() and '"shader_library_load"' not in l] if os.path.exists(os.path.join(r, "bench_primitives.jsonl")) else []23 g = collections.defaultdict(list)24 for b in bench: g[(b["kernel"], b["S"], b["n"], b["backend"], b["threads"] == 1)].append(b["wall_seconds"])25 def med(k, S, n, be, one): v = g.get((k, S, n, be, one)); return float(np.median(v)) if v else None26 row = {"node": node, "chip": chip.group(1).strip() if chip else env.get("chip"), "gpu_cores": int(cores.group(1)) if cores else None, "cpu_threads": env.get("ncpu"), "mem_gb": round(int(env.get("mem_bytes", 0)) / 2**30) if str(env.get("mem_bytes", "0")).isdigit() else None,27 "macos": env.get("os"), "tests_metallib": t_lib, "tests_runtime_msl": t_rt, "cold_compile_s": comp[0]["seconds"] if comp else None, "cold_first_call_s": comp[0]["wall_first_call"] if comp else None, "warm_compile_s": warm[0]["seconds"] if warm else None, "library": comp[0]["note"].split()[0] if comp else None, "n_bench_records": len(bench)}28 for k, S, n in [("realized_fused", 1000, 3900), ("realized_fused", 100, 39000), ("regression_stats", 1000, 3900), ("garch_nll_P64", 1000, 390), ("garch_nll_P64", 100, 3900), ("rolling_sumsq", 1000, 3900)]:29 c1, cn, mt = med(k, S, n, "cpu", True), med(k, S, n, "cpu", False), med(k, S, n, "metal", False)30 tag = f"{k}@{S}x{n}"; row[tag + "_cpu1_ms"] = None if c1 is None else 1e3 * c1; row[tag + "_cpuMT_ms"] = None if cn is None else 1e3 * cn; row[tag + "_metal_ms"] = None if mt is None else 1e3 * mt; row[tag + "_speedup_MT"] = (cn / mt) if (cn and mt) else None31 rows.append(row)32json.dump(rows, open(os.path.join(root, "e7_summary.json"), "w"), indent=1)33keys = sorted({k for r in rows for k in r.keys()}, key=lambda k: (k not in ("node", "chip", "gpu_cores", "cpu_threads", "mem_gb", "macos"), k))34with open(os.path.join(root, "e7_summary.csv"), "w", newline="") as f: w = csv.DictWriter(f, fieldnames=keys); w.writeheader(); [w.writerow(r) for r in rows]35print(f"{'node':8s} {'chip':14s} {'gpu':>4s} {'thr':>4s} {'GB':>3s} {'macOS':7s} {'tests(lib)':28s} {'tests(rt)':28s} {'cold/warm_s':>12s} {'RVf 1000x3900 MT/metal ms':>26s} {'spd':>5s} {'garch64 1000x390 spd':>20s} {'roll spd':>8s}")36for r in rows:37 if "chip" not in r: print(r); continue38 f = lambda k: ("%.2f" % r[k]) if r.get(k) is not None else "-"39 print(f"{r['node']:8s} {str(r['chip'])[:14]:14s} {str(r['gpu_cores']):>4s} {str(r['cpu_threads']):>4s} {str(r['mem_gb']):>3s} {str(r['macos']):7s} {r['tests_metallib'][:28]:28s} {r['tests_runtime_msl'][:28]:28s} {f('cold_compile_s')+'/'+f('warm_compile_s'):>12s} {f('realized_fused@1000x3900_cpuMT_ms'):>12s}/{f('realized_fused@1000x3900_metal_ms'):<12s} {f('realized_fused@1000x3900_speedup_MT'):>5s} {f('garch_nll_P64@1000x390_speedup_MT'):>20s} {f('rolling_sumsq@1000x3900_speedup_MT'):>8s}")40