Download hy_scan_points.py from Cccccz/comparison: direct link, hf CLI and curl.
- Browser
- Download file 5.64 kB
-
https://huggingface.co/Cccccz/comparison/resolve/main/hy_scan_points.py
- Command line
-
hf download hf://Cccccz/comparison/hy_scan_points.py
-
curl -L -o hy_scan_points.py https://huggingface.co/Cccccz/comparison/resolve/main/hy_scan_points.py
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) | |