File size: 3,264 Bytes
38a51ff | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #!/usr/bin/env python
"""MotionCache threshold scan on the HY-WorldPlay base -- same diagnostic as
mc_scan.py: what the compute fraction hides, i.e. whether the cacheable steps are
making *partial* token decisions or degenerating into whole-step skips.
CUDA_VISIBLE_DEVICES=4 python hy_mc_scan.py --schedule FxxF \
--thresholds 0.05,0.1,0.2,0.4,0.7,1.0 --out results/hy_mc_scan_FxxF.json
"""
import argparse, json, os, statistics as st, sys
ROOT = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, ROOT)
from hycache import harness as H
H.enter_repo()
import torch
from hycache.measure import make_ctrl
ap = argparse.ArgumentParser()
ap.add_argument("--schedule", default="FxxF")
ap.add_argument("--thresholds", required=True)
ap.add_argument("--frames", type=int, default=61)
ap.add_argument("--num-cases", type=int, default=2)
ap.add_argument("--out", default=None)
args = ap.parse_args()
pipe = H.load_pipeline()
cases = H.load_cases()
jobs = [(cases[i], H.ACTIONS[0]) for i in range(args.num_cases + 1)] # first = warm-up
forced = set(range(H.NUM_STEPS)) if False else None
def run(method, **kw):
ctrl = make_ctrl(pipe, method, schedule=args.schedule, **kw)
ms, recs = [], []
for i, (case, action) in enumerate(jobs):
_, _, t = H.generate(pipe, case, action, controller=ctrl, num_frames=args.frames, decode=False)
if i == 0:
continue
ms.append(t["denoise_dit_ms"])
recs += [r for r in ctrl.records if r["step"] not in ctrl.forced]
return min(ms), recs, ctrl.summary()
base_ms, _, _ = run("none")
print(f"baseline FFFF min denoise {base_ms:.0f} ms ({args.frames} frames, schedule {args.schedule})", flush=True)
print(f"{'thresh':>8}{'Full':>8}{'compute_x':>10}{'measured_x':>11}{'active%':>9}{'empty%':>8}{'full%':>7}{'sel_mean%':>10}{'sel_p90%':>9}")
rows = []
for t in [float(v) for v in args.thresholds.split(",")]:
ms, recs, summ = run("motioncache", thresh=t)
fr = [r["compute_fraction"] for r in recs]
act = [f for f in fr if 1e-9 < f < 1 - 1e-9]
q = lambda v, p: sorted(v)[min(int(p * (len(v) - 1)), len(v) - 1)] if v else float("nan")
row = {"thresh": t, "compute_equivalent": summ["compute_equivalent_forwards"],
"compute_speedup": summ["flops_speedup_estimate"], "measured_speedup": base_ms / ms,
"active_ratio": len(act) / len(fr), "empty_ratio": sum(f <= 1e-9 for f in fr) / len(fr),
"full_ratio": sum(f >= 1 - 1e-9 for f in fr) / len(fr),
"sel_mean": st.fmean(act) if act else 0.0, "sel_p90": q(act, .9), "min_denoise_ms": ms}
rows.append(row)
print(f"{t:>8.4g}{row['compute_equivalent']:>8.2f}{row['compute_speedup']:>10.3f}{row['measured_speedup']:>11.3f}"
f"{100*row['active_ratio']:>9.1f}{100*row['empty_ratio']:>8.1f}{100*row['full_ratio']:>7.1f}"
f"{100*row['sel_mean']:>10.1f}{100*row['sel_p90']:>9.1f}", flush=True)
if args.out:
out = args.out if os.path.isabs(args.out) else os.path.join(ROOT, args.out)
os.makedirs(os.path.dirname(out), exist_ok=True)
json.dump({"base": "hy_worldplay", "schedule": args.schedule, "frames": args.frames,
"baseline_ms": base_ms, "rows": rows}, open(out, "w"), indent=2)
print("wrote", out)
|