Buckets:

Mercity/FluxDistill / scripts /20_nunchaku_profile.py
Pranav2748's picture
download
raw
6.25 kB
"""Profile Nunchaku FLUX.2-klein-9B low-bit (FP4 / INT4) fused SVDQuant kernels on Blackwell.
Loads the SVDQuant-quantized transformer (svdq-{prec}_r32-...safetensors) into a stock
`Flux2KleinPipeline`, runs a warm-up + N timed text-to-image generations, and prints a
per-step + end-to-end latency / VRAM / throughput breakdown.
Timing is done by monkeypatching `transformer.forward` (one call == one denoise step) and
`vae.decode`, each wrapped in cuda.synchronize() + perf_counter() for clean, async-free
numbers. The warm-up run (kernel autotune / lazy CUDA init / cudnn pick) is excluded.
Usage:
PYTHONPATH=. python3 scripts/20_nunchaku_profile.py [precision] [steps] [H] [W] [n_timed]
precision : fp4 | int4 (default: nunchaku.utils.get_precision() -> fp4 on sm_120)
steps : denoise steps (default 4 — klein is distilled / CFG-free)
"""
import os, sys, time, json, statistics as st
import torch
PREC = sys.argv[1] if len(sys.argv) > 1 and sys.argv[1] not in ("-",) else None
STEPS = int(sys.argv[2]) if len(sys.argv) > 2 else 4
H = int(sys.argv[3]) if len(sys.argv) > 3 else 1024
W = int(sys.argv[4]) if len(sys.argv) > 4 else 1024
NRUN = int(sys.argv[5]) if len(sys.argv) > 5 else 3
REPO = "models/klein-9b-nunchaku"
NAME = "FLUX.2-klein-9B-Nunchaku"
PROMPT = ("a photorealistic storefront at golden hour with a glowing neon sign that reads "
"\"NUNCHAKU\", wet pavement reflections, shallow depth of field, 50mm")
from diffusers import Flux2KleinPipeline
from nunchaku.models.transformers.transformer_flux2 import NunchakuFlux2Transformer2DModel
from nunchaku.utils import get_precision
prec = PREC or get_precision()
ckpt = f"{REPO}/svdq-{prec}_r32-{NAME}.safetensors"
print(f"=== Nunchaku FLUX.2-klein-9B | precision={prec.upper()} r32 | {W}x{H} | {STEPS} steps | {NRUN} timed runs ===")
print(f"device={torch.cuda.get_device_name(0)} | torch={torch.__version__} cuda={torch.version.cuda}")
torch.cuda.reset_peak_memory_stats()
t0 = time.perf_counter()
transformer = NunchakuFlux2Transformer2DModel.from_pretrained(ckpt, torch_dtype=torch.bfloat16)
pipe = Flux2KleinPipeline.from_pretrained(REPO, torch_dtype=torch.bfloat16, transformer=transformer)
pipe.to("cuda")
torch.cuda.synchronize()
print(f"[load] {time.perf_counter()-t0:.1f}s | post-load VRAM {torch.cuda.max_memory_allocated()/1e9:.2f} GB")
# ---- instrument per-step transformer + vae decode (sync'd) ----
step_times, dec_times = [], []
_dumped = {"done": False}
DUMP_FWD = os.environ.get("DUMP_FWD") # path: save the first forward's inputs for the bf16 replay bench
_fwd = transformer.forward
def _to_cpu(x):
if torch.is_tensor(x): return x.detach().to("cpu")
if isinstance(x, (list, tuple)): return type(x)(_to_cpu(v) for v in x)
if isinstance(x, dict): return {k: _to_cpu(v) for k, v in x.items()}
return x
def timed_fwd(*a, **k):
if DUMP_FWD and not _dumped["done"]:
torch.save({"args": _to_cpu(a), "kwargs": _to_cpu(k)}, DUMP_FWD)
_dumped["done"] = True
print(f"[dump] saved first transformer.forward inputs -> {DUMP_FWD}")
torch.cuda.synchronize(); s = time.perf_counter()
out = _fwd(*a, **k)
torch.cuda.synchronize(); step_times.append(time.perf_counter() - s)
return out
transformer.forward = timed_fwd
_dec = pipe.vae.decode
def timed_dec(*a, **k):
torch.cuda.synchronize(); s = time.perf_counter()
out = _dec(*a, **k)
torch.cuda.synchronize(); dec_times.append(time.perf_counter() - s)
return out
pipe.vae.decode = timed_dec
def run(seed=0):
step_times.clear(); dec_times.clear()
torch.cuda.reset_peak_memory_stats()
g = torch.Generator("cpu").manual_seed(seed)
torch.cuda.synchronize(); t = time.perf_counter()
img = pipe(prompt=PROMPT, guidance_scale=1.0, num_inference_steps=STEPS,
height=H, width=W, generator=g).images[0]
torch.cuda.synchronize(); total = time.perf_counter() - t
return dict(img=img, total=total, peak=torch.cuda.max_memory_allocated()/1e9,
steps=list(step_times), denoise=sum(step_times), decode=sum(dec_times))
print("[warmup] running (excluded) ...")
w = run(seed=999)
print(f"[warmup] total {w['total']:.2f}s | steps {[f'{x*1000:.0f}' for x in w['steps']]}ms")
runs = [run(seed=i) for i in range(NRUN)]
med = st.median
ns = len(runs[0]['steps'])
per_step = [med([r['steps'][i] for r in runs]) for i in range(ns)]
tot, den = med([r['total'] for r in runs]), med([r['denoise'] for r in runs])
dec = med([r['decode'] for r in runs])
ovh = med([r['total'] - r['denoise'] - r['decode'] for r in runs])
peak = med([r['peak'] for r in runs])
stepN = med([med([r['steps'][i] for r in runs]) for i in range(1, ns)]) if ns > 1 else per_step[0]
print("\n================ RESULTS (median of %d runs) ================" % NRUN)
print(f"precision : {prec.upper()} (Nunchaku SVDQuant W4A4, low-rank r32)")
print(f"resolution / steps : {W}x{H} / {STEPS}")
print(f"per-step transformer : " + ", ".join(f"{t*1000:.0f}ms" for t in per_step))
print(f" step1+ mean : {stepN*1000:.0f}ms (step0 warms caches; later steps are the true kernel cost)")
print(f"denoise (sum steps) : {den*1000:.0f}ms -> {STEPS/den:.2f} steps/s (transformer-only)")
print(f"vae decode : {dec*1000:.0f}ms")
print(f"text-encode+overhead : {ovh*1000:.0f}ms")
print(f"END-TO-END : {tot:.2f}s -> {1/tot:.2f} img/s")
print(f"peak VRAM : {peak:.2f} GB")
os.makedirs("outputs/nunchaku", exist_ok=True)
out = f"outputs/nunchaku/klein9b_{prec}_{W}x{H}_{STEPS}s.png"
runs[-1]['img'].save(out)
res = dict(model="klein-9b", engine="nunchaku", precision=prec, rank=32, H=H, W=W, steps=STEPS,
per_step_ms=[round(x*1000,1) for x in per_step], denoise_ms=round(den*1000,1),
steps_per_s=round(STEPS/den,3), decode_ms=round(dec*1000,1), overhead_ms=round(ovh*1000,1),
total_s=round(tot,3), img_per_s=round(1/tot,3), peak_vram_gb=round(peak,2),
device=torch.cuda.get_device_name(0))
with open(f"outputs/nunchaku/klein9b_{prec}_{W}x{H}_{STEPS}s.json", "w") as f:
json.dump(res, f, indent=2)
print(f"saved sample -> {out}")
print(f"saved metrics -> outputs/nunchaku/klein9b_{prec}_{W}x{H}_{STEPS}s.json")

Xet Storage Details

Size:
6.25 kB
·
Xet hash:
4df97ff2d464104bf05ce0526abe0613303240e2eb595a7185d82344744e74cc

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.