parse_craypat.py
import os, re, subprocess, sys, csv
from pathlib import Path
D = os.environ.get("DISS_ROOT", "/work/project/project/user")
BASE = Path(f"{D}/runs/sweep")
OUT = Path(f"{D}/data")
OUT.mkdir(parents=True, exist_ok=True)
VAL_RE = re.compile(
r"^ (?P<name>[A-Z][A-Za-z0-9_:]*)\s+"
r"(?:(?:[\d.]+)\s*(?:[GMK]/sec|W)\s+)?"
r"(?P<val>\d[\d,]*(?:\.\d+)?)"
r"\s*(?:ops|instr|cycles|J|refs|misses|hits)?\s*$"
)
PAT_REPORT = "/opt/cray/pe/perftools/23.09.0/bin/pat_report"
def pat_report(expdir, mode):
try:
r = subprocess.run([PAT_REPORT, "-O", mode, str(expdir)],
capture_output=True, text=True, timeout=600)
return r.stdout
except Exception:
return ""
def parse_hwpc(text):
vals = {}
for line in text.splitlines():
if "PAT_RT_PERFCTR" in line:
break
m = VAL_RE.match(line)
if m:
try:
vals[m.group("name")] = float(m.group("val").replace(",", ""))
except ValueError:
pass
return vals
def parse_meta(outfile):
meta, walls = {}, {}
if not outfile or not outfile.exists():
return meta, walls
for line in outfile.read_text(errors="replace").splitlines():
if line.startswith("META "):
for kv in line.split()[1:]:
if "=" in kv:
k, v = kv.split("=", 1); meta[k] = v
m = re.match(r"=====END_SET (\w+) rc=(\d+) wall=([\d.]+)=====", line)
if m:
walls[m.group(1)] = {"rc": int(m.group(2)), "wall": float(m.group(3))}
return meta, walls
FUNC_RE = re.compile(r"^\|+\s+([\d.]+)%\s+\|\s+([\d.]+)\s+\|.*\|\s+([\d,.]+)\s+\|\s+(\S.*)$")
def parse_funcs(text):
out = []
for line in text.splitlines():
m = FUNC_RE.match(line)
if m:
out.append({"time_pct": float(m.group(1)), "time_s": float(m.group(2)),
"calls": float(m.group(3).replace(",", "")),
"function": m.group(4).strip()})
return out
def main():
runs, funcs = [], []
for d in sorted(BASE.glob("*_c*")):
if not d.is_dir():
continue
m = re.match(r"(\w+)_c(\d+)$", d.name)
if not m:
continue
app, nc = m.group(1), int(m.group(2))
prefixes = [app] + (["of"] if app == "openfoam" else [])
cand = []
for pre in prefixes:
cand += sorted(BASE.glob(f"{pre}_c{nc}-*.out"))
chosen = None
for f in sorted(cand, key=lambda p: p.stat().st_mtime, reverse=True):
if "SWEEP_DONE" in f.read_text(errors="replace"):
chosen = f
break
meta, walls = parse_meta(chosen or (cand[-1] if cand else None))
row = {"app": app, "ncore": nc}
row.update({f"wall_{k}": v["wall"] for k, v in walls.items()})
ok = sorted(v["wall"] for v in walls.values() if v["rc"] == 0)
if ok:
row["runtime_s"] = ok[len(ok)//2]
row["runtime_min_s"] = ok[0]
row["nsets_ok"] = len(ok)
for s in "ABCDE":
exp = d / f"exp_{s}"
if exp.exists():
row.update(parse_hwpc(pat_report(str(exp), "hwpc")))
if s == "A":
for f in parse_funcs(pat_report(str(exp), "profile")):
funcs.append({"app": app, "ncore": nc, **f})
runs.append(row)
ncnt = sum(1 for k in row if k[0].isupper())
print(f"parsed {app:8s} c{nc:<4d} counters={ncnt}", file=sys.stderr)
if runs:
lead = ["app","ncore","runtime_s","runtime_min_s","nsets_ok"]
keys = lead + sorted({k for r in runs for k in r} - set(lead))
with open(OUT/"runs.csv","w",newline="") as fh:
w = csv.DictWriter(fh, fieldnames=keys); w.writeheader(); w.writerows(runs)
print(f"wrote runs.csv ({len(runs)} rows, {len(keys)} cols)")
if funcs:
with open(OUT/"functions.csv","w",newline="") as fh:
w = csv.DictWriter(fh, fieldnames=["app","ncore","function","time_pct","time_s","calls"])
w.writeheader(); w.writerows(funcs)
print(f"wrote functions.csv ({len(funcs)} rows)")
if __name__ == "__main__":
main()