import warnings
from pathlib import Path
import numpy as np
import pandas as pd
from scipy.stats import wilcoxon
from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
warnings.filterwarnings("ignore")
D = "/work/project/project/user"
DATA = f"{D}/repo/user/dissertation/data"
OUT = Path(f"{D}/analysis/out")
OUT.mkdir(parents=True, exist_ok=True)
F_PEAK = 2.25e9
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"]
CFG_FEATURES = ["log_ncore", "log_nrank", "log_nthread"]
def load_merged():
df = pd.read_csv(f"{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)
return m[m.PAPI_TOT_CYC.notna()].reset_index(drop=True)
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 rf_pipeline(seed=0):
return Pipeline([("impute", SimpleImputer(strategy="median")),
("log", FunctionTransformer(slog, validate=False)),
("scale", StandardScaler()),
("est", RandomForestRegressor(n_estimators=400,
min_samples_leaf=2,
random_state=seed,
n_jobs=-1))])
def fac(pred, act):
pred = np.clip(np.asarray(pred, float), 1e-9, None)
return np.maximum(pred / act, act / pred)
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())
lines = []
say = lambda s="": (print(s), lines.append(s))
say("Baseline ladder: what each layer of measurement actually buys")
say("=" * 78)
say(f"{len(df)} merged configurations, {len(apps)} applications: "
f"{', '.join(apps)}")
say(f"f_peak = {F_PEAK:.3g} Hz (ARCHER2 EPYC 7742)")
say("Protocol: leave-one-application-out, error = max(pred/act, act/pred)")
say("")
lr = np.corrcoef(np.log10(df.PAPI_TOT_CYC.values), np.log10(y))[0, 1]
say(f"log-log correlation r(PAPI_TOT_CYC, runtime) = {lr:.4f} "
f"(r^2 = {lr**2:.4f})")
say(f"eta = t_analytic/runtime: median {np.median(eta):.3f}, "
f"IQR {np.percentile(eta,25):.3f}-{np.percentile(eta,75):.3f}, "
f"sd(log10 eta) {np.std(np.log10(eta)):.3f}")
say("")
rungs = []
def add(label, cost, pred_fn):
r = np.full(len(y), np.nan)
for a in apps:
te = (df.app == a).values
r[te] = fac(pred_fn(~te, te), y[te])
rungs.append((label, cost, r))
return r
add("0 median training runtime", "nothing",
lambda tr, te: np.full(te.sum(), np.median(y[tr])))
def cfg_only(tr, te):
m = rf_pipeline()
m.fit(X.loc[tr, CFG_FEATURES], np.log10(y[tr]))
return 10 ** m.predict(X.loc[te, CFG_FEATURES])
add("1 configuration only (RF)", "free metadata", cfg_only)
add("2 t_analytic, eta = 1", "1 counter, on target",
lambda tr, te: t_an[te])
add("3 t_analytic / constant eta", "1 counter, on target",
lambda tr, te: t_an[te] / 10 ** np.median(np.log10(eta[tr])))
def rf_eta(tr, te):
m = rf_pipeline()
m.fit(X[tr], np.log10(eta[tr]))
return t_an[te] / 10 ** m.predict(X[te])
add("4 t_analytic / RF(counters)", "5 counter sets, on target", rf_eta)
say("--- the ladder ---")
say(f"{'rung':32s} {'measurement cost':22s} {'median':>8s} {'p90':>8s} "
f"{'d.med':>8s} {'d.p90':>8s} {'p':>10s}")
tab = []
prev = None
for label, cost, r in rungs:
med, p90 = np.nanmedian(r), np.nanpercentile(r, 90)
if prev is None:
dmed = dp90 = np.nan
pval = np.nan
else:
dmed = np.nanmedian(prev) - med
dp90 = np.nanpercentile(prev, 90) - p90
msk = ~np.isnan(r) & ~np.isnan(prev)
try:
pval = wilcoxon(r[msk], prev[msk]).pvalue
except ValueError:
pval = np.nan
say(f"{label:32s} {cost:22s} {med:8.3f} {p90:8.3f} "
f"{dmed:8.3f} {dp90:8.3f} {pval:10.2}")
tab.append(dict(rung=label, cost=cost, median=round(med, 4),
p90=round(p90, 4),
delta_median_vs_prev=None if np.isnan(dmed) else round(dmed, 4),
delta_p90_vs_prev=None if np.isnan(dp90) else round(dp90, 4),
wilcoxon_p_vs_prev=None if np.isnan(pval) else float(f"{pval:.3g}")))
prev = r
say("")
say("d.med and d.p90 are the reduction in error contributed by that rung")
say("relative to the rung above it, so positive is an improvement.")
say("")
r0 = rungs[0][2]
r2 = rungs[2][2]
r4 = rungs[4][2]
span = np.nanmedian(r0) - np.nanmedian(r4)
say("--- attribution of the total gain ---")
say(f"total gain, rung 0 to rung 4: {span:.3f} error factor")
for i in range(1, len(rungs)):
d = np.nanmedian(rungs[i - 1][2]) - np.nanmedian(rungs[i][2])
say(f" {rungs[i][0]:32s} contributes {d:7.3f} "
f"({100*d/span:5.1f}% of the total)")
say("")
say("Rung 2 is a measurement, not a prediction. Everything from rung 2")
say("downwards requires an instrumented run of the target configuration.")
say("")
say("--- per-application median error ---")
per = pd.DataFrame({lab: r for lab, _, r in rungs})
per["app"] = df.app.values
say(per.groupby("app").median().round(3).to_string())
(OUT / "baseline_ladder.txt").write_text("\n".join(lines) + "\n")
pd.DataFrame(tab).to_csv(OUT / "baseline_ladder.csv", index=False)
per.to_csv(OUT / "baseline_ladder_perconfig.csv", index=False)
print(f"\nwrote {OUT}/baseline_ladder.txt and .csv")
if __name__ == "__main__":
main()