import os, warnings, sys, itertools
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.neural_network import MLPRegressor
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, FunctionTransformer
from sklearn.impute import SimpleImputer
from scipy.stats import wilcoxon
warnings.filterwarnings("ignore")
D = os.environ.get("DISS_ROOT", "/work/project/project/user")
OUT = Path(f"{D}/analysis/out"); OUT.mkdir(parents=True, exist_ok=True)
F_PEAK = 2.25e9
SEEDS = (0, 1, 2)
COUNTERS = ["PAPI_TOT_CYC", "PAPI_TOT_INS", "PAPI_FP_OPS", "PAPI_FP_INS",
"PAPI_L1_DCA", "PAPI_L2_DCH", "PAPI_L2_DCM", "PAPI_L2_DCR",
"PAPI_TLB_DM", "PAPI_BR_INS", "PAPI_BR_MSP",
"UNC_L3_CACHE_MISSES", "UNC_L3_MISS_LATENCY",
"PACKAGE_ENERGY", "PP0_ENERGY",
"L2_PREFETCH_HIT_L2", "L2_PREFETCH_HIT_L3",
"REQUESTS_TO_L2_GROUP1:L2_HW_PF", "REQUESTS_TO_L2_GROUP1:RD_BLK_X",
"DISPATCH_RESOURCE_STALL_CYCLES_1:LOAD_QUEUE_RSRC_STALL",
"DISPATCH_RESOURCE_STALL_CYCLES_1:STORE_QUEUE_RSRC_STALL",
"DISPATCH_RESOURCE_STALL_CYCLES_1:FP_REG_FILE_RSRC_STALL"]
def load_merged():
df = pd.read_csv(f"{D}/data/runs_expanded.csv")
df = df[df.runtime_s.notna() & (df.runtime_s > 0)]
keys = ["app", "size", "ncore", "nthread", "nrank"]
agg = {c: "median" for c in COUNTERS if c in df.columns}
agg["runtime_s"] = "median"
m = df.groupby(keys, as_index=False).agg(agg)
m = m[m.PAPI_TOT_CYC.notna()].reset_index(drop=True)
return m
def build_features(df):
X = pd.DataFrame(index=df.index)
g = lambda c: pd.to_numeric(df.get(c), errors="coerce")
cyc, ins = g("PAPI_TOT_CYC"), g("PAPI_TOT_INS")
fpops = g("PAPI_FP_OPS")
l1a = g("PAPI_L1_DCA")
l2h, l2m = g("PAPI_L2_DCH"), g("PAPI_L2_DCM")
l3m, l3lat = g("UNC_L3_CACHE_MISSES"), g("UNC_L3_MISS_LATENCY")
tlb = g("PAPI_TLB_DM")
pkg, pp0 = g("PACKAGE_ENERGY"), g("PP0_ENERGY")
s_ld = g("DISPATCH_RESOURCE_STALL_CYCLES_1:LOAD_QUEUE_RSRC_STALL")
s_st = g("DISPATCH_RESOURCE_STALL_CYCLES_1:STORE_QUEUE_RSRC_STALL")
s_fp = g("DISPATCH_RESOURCE_STALL_CYCLES_1:FP_REG_FILE_RSRC_STALL")
pf2, pf3 = g("L2_PREFETCH_HIT_L2"), g("L2_PREFETCH_HIT_L3")
X["log_ncore"] = np.log2(df.ncore.clip(lower=1))
X["log_nrank"] = np.log2(df.nrank.clip(lower=1))
X["log_nthread"] = np.log2(df.nthread.clip(lower=1))
X["ipc"] = ins / cyc
X["flops_per_instr"] = fpops / ins
X["flops_per_cycle"] = fpops / cyc
X["l2_hit_rate"] = l2h / (l2h + l2m)
X["l2_miss_per_instr"] = l2m / ins
X["l1_access_per_instr"] = l1a / ins
X["l3_miss_per_instr"] = l3m / ins
X["l3_lat_per_miss"] = l3lat / l3m
X["tlb_miss_per_instr"] = tlb / ins
X["stall_load_frac"] = s_ld / cyc
X["stall_store_frac"] = s_st / cyc
X["stall_fp_frac"] = s_fp / cyc
X["prefetch_l2_frac"] = pf2 / (pf2 + pf3)
X["energy_per_instr"] = pkg / ins
X["core_energy_frac"] = pp0 / pkg
X["arith_intensity"] = fpops / (l3m * 64.0)
X["log_instr_per_rank"] = np.log10((ins / df.nrank.clip(lower=1)).clip(lower=1))
X["log_t_analytic"] = np.log10((cyc / F_PEAK).clip(lower=1e-6))
return X.replace([np.inf, -np.inf], np.nan)
def slog(A):
return np.sign(A) * np.log1p(np.abs(A))
def mk(kind, seed=0, **kw):
pre = [("impute", SimpleImputer(strategy="median")),
("log", FunctionTransformer(slog, validate=False)),
("scale", StandardScaler())]
if kind == "mlp":
est = MLPRegressor(hidden_layer_sizes=kw["h"], alpha=kw["a"],
solver="lbfgs", max_iter=5000, random_state=seed)
elif kind == "rf":
est = RandomForestRegressor(n_estimators=400, min_samples_leaf=2,
random_state=seed, n_jobs=-1)
elif kind == "gbq":
est = GradientBoostingRegressor(loss="quantile", alpha=0.5,
n_estimators=300, max_depth=3,
learning_rate=0.05, random_state=seed)
return Pipeline(pre + [("est", est)])
def fac(p, a):
p = np.clip(p, 1e-9, None)
return np.maximum(p / a, a / p)
GRID = [{"h": h, "a": a}
for h in [(16,), (32, 16), (64, 32)]
for a in [0.1, 1.0, 3.0, 10.0]]
def fit_predict(kind, Xtr, ytr, Xte, seeds=SEEDS, **kw):
ps = []
for s in seeds:
m = mk(kind, s, **kw); m.fit(Xtr, ytr); ps.append(m.predict(Xte))
return np.mean(ps, axis=0)
def main():
df = load_merged()
X = build_features(df)
y = df.runtime_s.values
t_an = (df.PAPI_TOT_CYC / F_PEAK).values
eta = t_an / y
apps = sorted(df.app.unique())
L = []
say = lambda s="": (print(s), L.append(s))
say("Corrected evaluation")
say("=" * 78)
say(f"F2: merged {191 if len(df)==191 else len(df)} configurations "
f"(was 815 pseudo-rows)")
miss = X.isna().mean().mean() * 100
say(f" feature matrix now {100-miss:.1f}% populated "
f"(was ~42% on per-set rows)")
say(f"{len(df)} configs, {X.shape[1]} features, {len(apps)} applications")
say("")
rows = {}
def record(name, pred_fn):
r = np.full(len(y), np.nan)
for a in apps:
te = (df.app == a).values; tr = ~te
r[te] = fac(pred_fn(X[tr], np.log10(eta[tr]), X[te], t_an[te]), y[te])
rows[name] = r
say(f" {name:34s} median {np.nanmedian(r):.3f} "
f"p90 {np.nanpercentile(r,90):.3f}")
say("--- LOAO (train on 7 apps, predict the 8th) ---")
record("Constant (median log-eta) [F1]",
lambda Xtr, ytr, Xte, ta: ta / (10 ** np.full(len(Xte), np.median(ytr))))
record("Random Forest",
lambda Xtr, ytr, Xte, ta: ta / (10 ** fit_predict("rf", Xtr, ytr, Xte, (0,))))
record("GBM quantile(0.5) [F5]",
lambda Xtr, ytr, Xte, ta: ta / (10 ** fit_predict("gbq", Xtr, ytr, Xte, (0,))))
record("MLP (32,16) a=1 [tuned on test]",
lambda Xtr, ytr, Xte, ta: ta / (10 ** fit_predict("mlp", Xtr, ytr, Xte, h=(32,16), a=1.0)))
def nested(Xtr, ytr, Xte, ta, tr_apps):
best, bg = None, None
for gcfg in GRID:
errs = []
for ia in tr_apps:
ite = (tr_apps == ia)
if ite.sum() == 0 or (~ite).sum() == 0:
continue
p = fit_predict("mlp", Xtr[~ite], ytr[~ite], Xtr[ite], (0,), **gcfg)
errs.append(np.median(np.abs(p - ytr[ite])))
e = np.mean(errs) if errs else 1e9
if best is None or e < best:
best, bg = e, gcfg
return ta / (10 ** fit_predict("mlp", Xtr, ytr, Xte, **bg)), bg
r = np.full(len(y), np.nan); picks = []
for a in apps:
te = (df.app == a).values; tr = ~te
p, bg = nested(X[tr], np.log10(eta[tr]), X[te], t_an[te], df.app[tr].values)
r[te] = fac(p, y[te]); picks.append(f"{a}:{bg['h']}a{bg['a']}")
rows["MLP nested selection [F3]"] = r
say(f" {'MLP nested selection [F3]':34s} median {np.nanmedian(r):.3f} "
f"p90 {np.nanpercentile(r,90):.3f}")
say(f" inner loop picked: {', '.join(picks)}")
def shrunk(Xtr, ytr, Xte, ta, w=0.5):
p = fit_predict("mlp", Xtr, ytr, Xte, h=(32, 16), a=1.0)
return ta / (10 ** (w * p + (1 - w) * np.median(ytr)))
record("MLP shrunk 0.5 to median [F5]", shrunk)
def ens(Xtr, ytr, Xte, ta):
a1 = fit_predict("mlp", Xtr, ytr, Xte, h=(32, 16), a=1.0)
a2 = fit_predict("rf", Xtr, ytr, Xte, (0,))
return ta / (10 ** (0.5 * a1 + 0.5 * a2))
record("MLP + RF ensemble", ens)
say("")
say("--- F6: paired Wilcoxon at CONFIG granularity (n=%d) ---" % len(y))
base = rows["Constant (median log-eta) [F1]"]
for k, v in rows.items():
if k.startswith("Constant"):
continue
m = ~np.isnan(v) & ~np.isnan(base)
try:
_, p = wilcoxon(v[m], base[m])
except ValueError:
p = float("nan")
better = np.nanmedian(v) < np.nanmedian(base)
say(f" {k:34s} vs constant: wins {int((v[m]<base[m]).sum()):3d}/{int(m.sum())}"
f" p={p:.4g} ({'better' if better else 'WORSE'})")
say("")
say("--- per-application median (LOAO) ---")
tb = pd.DataFrame(rows); tb["app"] = df.app.values
say(tb.groupby("app").median().round(3).to_string())
(OUT / "retrain2_summary.txt").write_text("\n".join(L) + "\n")
tb.to_csv(OUT / "retrain2_perrow.csv", index=False)
if __name__ == "__main__":
main()