Opens a larger view. Escape closes it.

hardware-counters

interaction_probe.py

"""
Can we model how a specific application responds to a specific microarchitecture?

The target quantity is the per-application speedup, which varies 1.18x (HPCG)
to 4.32x (LULESH) between Zen 2 and Zen 5 against a clock ratio of 1.649.
Static specs predict the machine-level part (1.608 error) but not the
application-level part (oracle 1.130).

The hypothesis worth testing before building anything: the application's
BEHAVIOURAL SIGNATURE on the source machine should predict how much it
benefits from the target microarchitecture. A compute-bound code with high
arithmetic intensity should track the clock ratio. A memory-latency-bound code
should track the memory subsystem instead, and Zen 5's wider cores buy it
little.

This probe asks whether that signal is present at all, before any model is
fitted. If the correlations are near zero there is nothing to model.
"""
import os
import numpy as np, pandas as pd

D = os.environ.get("DISS_ROOT", "/work/project/project/user")
KEYS = ["app", "size", "ncore", "nthread"]
CTR = ["PAPI_TOT_CYC", "PAPI_TOT_INS", "PAPI_FP_OPS", "PAPI_L1_DCA",
       "PAPI_L2_DCM", "PAPI_L2_DCH", "PAPI_TLB_DM", "UNC_L3_CACHE_MISSES",
       "UNC_L3_MISS_LATENCY",
       "DISPATCH_RESOURCE_STALL_CYCLES_1:LOAD_QUEUE_RSRC_STALL",
       "DISPATCH_RESOURCE_STALL_CYCLES_1:STORE_QUEUE_RSRC_STALL"]

def merge(path):
    df = pd.read_csv(path)
    df = df[df.runtime_s > 0]
    agg = {c: "median" for c in CTR if c in df.columns}
    agg["runtime_s"] = "median"
    return df.groupby(KEYS, as_index=False).agg(agg)

A, C = merge(f"{D}/data/runs_expanded.csv"), merge(f"{D}/data/cirrus_persets.csv")
m = pd.merge(A, C, on=KEYS, suffixes=("_a2", "_cir")).dropna(subset=["runtime_s_a2",
                                                                    "runtime_s_cir"])
m["speedup"] = m.runtime_s_a2 / m.runtime_s_cir
m["log_speedup"] = np.log2(m.speedup)
print(f"matched configurations: {len(m)}, apps: {m.app.nunique()}\n")

# behavioural signature computed from SOURCE machine counters only
g = lambda c: pd.to_numeric(m.get(f"{c}_a2"), errors="coerce")
cyc, ins = g("PAPI_TOT_CYC"), g("PAPI_TOT_INS")
sig = pd.DataFrame({
    "ipc":               ins / cyc,
    "flops_per_instr":   g("PAPI_FP_OPS") / ins,
    "l2_miss_per_instr": g("PAPI_L2_DCM") / ins,
    "l2_hit_rate":       g("PAPI_L2_DCH") / (g("PAPI_L2_DCH") + g("PAPI_L2_DCM")),
    "l3_miss_per_instr": g("UNC_L3_CACHE_MISSES") / ins,
    "l3_lat_per_miss":   g("UNC_L3_MISS_LATENCY") / g("UNC_L3_CACHE_MISSES"),
    "tlb_miss_per_instr": g("PAPI_TLB_DM") / ins,
    "stall_load_frac":   g("DISPATCH_RESOURCE_STALL_CYCLES_1:LOAD_QUEUE_RSRC_STALL") / cyc,
    "stall_store_frac":  g("DISPATCH_RESOURCE_STALL_CYCLES_1:STORE_QUEUE_RSRC_STALL") / cyc,
    "arith_intensity":   g("PAPI_FP_OPS") / (g("UNC_L3_CACHE_MISSES") * 64.0),
    "log_ncore":         np.log2(m.ncore),
}).replace([np.inf, -np.inf], np.nan)

print("correlation of each SOURCE-machine signature feature with log2(speedup)")
print("pooled over all configurations, and averaged within application")
print(f"{'feature':20s} {'pooled r':>9s} {'within-app r':>13s} {'n':>5s}")
print("-" * 52)
res = []
for c in sig.columns:
    v = sig[c]
    ok = v.notna() & m.log_speedup.notna()
    if ok.sum() < 30:
        continue
    r_pool = np.corrcoef(v[ok], m.log_speedup[ok])[0, 1]
    # within-application correlation: does the feature explain variation that
    # is NOT just "which application is this"
    parts = []
    for a, gg in m[ok].groupby("app"):
        vv = sig.loc[gg.index, c]
        if vv.notna().sum() > 5 and vv.std() > 0 and gg.log_speedup.std() > 0:
            parts.append(np.corrcoef(vv, gg.log_speedup)[0, 1])
    r_within = np.nanmean(parts) if parts else np.nan
    res.append((c, abs(r_pool)))
    print(f"{c:20s} {r_pool:+9.3f} {r_within:+13.3f} {int(ok.sum()):5d}")

print("\nstrongest pooled predictors of speedup:")
for c, r in sorted(res, key=lambda x: -x[1])[:4]:
    print(f"  {c} (|r| = {r:.3f})")

print("\n=== how much of the speedup is 'which application'? ===")
ss_tot = ((m.log_speedup - m.log_speedup.mean()) ** 2).sum()
ss_res = sum(((gg.log_speedup - gg.log_speedup.mean()) ** 2).sum()
             for _, gg in m.groupby("app"))
print(f"  application identity explains {100*(1-ss_res/ss_tot):.1f}% of log2(speedup) variance")
print(f"  the remaining {100*ss_res/ss_tot:.1f}% is within-application "
      f"(problem size and core count)")
print("\nIf application identity dominates, a model must infer the per-app")
print("response from the signature, which is exactly the LOAO test.")