comparison / hy_scan_points.py
Cccccz's picture
Add files using upload-large-folder tool
38a51ff verified
Raw History Blame Contribute Delete
5.64 kB
#!/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("--method", default="motioncache",
choices=["teacache", "flowcache", "taylorseer", "motioncache"])
ap.add_argument("--values", required=True)
ap.add_argument("--coefficients", default="results/hy_coeff.json")
ap.add_argument("--frames", type=int, default=61)
ap.add_argument("--num-cases", type=int, default=2)
ap.add_argument("--out", default=None)
ap.add_argument("--first-chunk", default=None, help="schedule for chunk 0, e.g. FFFF")
args = ap.parse_args()
COEFF = None
if args.coefficients and args.method in ("teacache", "flowcache"):
p = args.coefficients if os.path.isabs(args.coefficients) else os.path.join(ROOT, args.coefficients)
COEFF = json.load(open(p))["coefficients"]
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, coefficients=COEFF,
first_chunk_schedule=args.first_chunk, **kw)
ms, recs, chunk_ms = [], [], []
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"])
per = t["per_step_denoise_ms"]
n = H.NUM_STEPS
chunk_ms.append([sum(per[b * n:(b + 1) * n]) for b in range(len(per) // n)])
# activity stats over the cacheable steps only (chunk 0 excluded when it is forced full)
recs += [r for r in ctrl.records if r["step"] not in ctrl.forced
and not (args.first_chunk and r["block"] == 0)]
# per-chunk denoise ms of the fastest (least contended) run, for extrapolation
best = chunk_ms[ms.index(min(ms))]
return min(ms), recs, ctrl.summary(), best
def extrapolate(chunks, n_target=8):
"""Denoise ms for n_target chunks from a shorter run: chunks beyond the measured
ones cost what the last measured chunk cost (per-chunk cost is flat once the
context window is full)."""
return sum(chunks) + (n_target - len(chunks)) * chunks[-1]
PARAM = {"teacache": "thresh", "flowcache": "thresh", "motioncache": "thresh",
"taylorseer": "interval"}
pname = PARAM[args.method]
base_ms, _, _, base_chunks = run("none") # warm-up + reference point
print(f"baseline FFFF min denoise {base_ms:.0f} ms ({args.frames} frames, schedule {args.schedule})", flush=True)
print(f"{pname[:8]:>8}{'Full':>8}{'compute_x':>10}{'measured_x':>11}{'ext8_Full':>10}{'ext8_x':>8}{'active%':>9}{'empty%':>8}{'full%':>7}{'sel_mean%':>10}{'sel_p90%':>9}")
rows = []
for t in [float(v) for v in args.values.split(",")]:
b_ms, _, _, b_chunks = run("none")
ms, recs, summ, s_chunks = run(args.method, **{pname: t})
n_chunks = len(b_chunks)
# extrapolated 8-chunk (125-frame) figures: chunk 0 (forced full) is measured, the
# cacheable chunks' per-chunk cost is taken from the last measured chunk
ext_ms = extrapolate(s_chunks); ext_b = extrapolate(b_chunks)
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 = {"value": t, "param": pname, "method": args.method, "compute_equivalent": summ["compute_equivalent_forwards"],
"compute_speedup": summ["flops_speedup_estimate"], "measured_speedup": b_ms / ms, "paired_baseline_ms": b_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,
"measured_chunks": n_chunks, "ext8_measured_speedup": ext_b / ext_ms,
"ext8_compute_equivalent": summ["compute_equivalent_forwards"]
+ (8 - n_chunks) * (summ["compute_equivalent_forwards"] - 4.0) / max(n_chunks - 1, 1)}
rows.append(row)
print(f"{t:>8.4g}{row['compute_equivalent']:>8.2f}{row['compute_speedup']:>10.3f}{row['measured_speedup']:>11.3f}"
f"{row['ext8_compute_equivalent']:>10.2f}{row['ext8_measured_speedup']:>8.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", "method": args.method, "param": pname,
"schedule": args.schedule, "first_chunk_schedule": args.first_chunk, "frames": args.frames,
"baseline_ms": base_ms, "rows": rows}, open(out, "w"), indent=2)
print("wrote", out)