wxDai commited on
Commit
e27b3ae
·
verified ·
1 Parent(s): 4b68197

revert to clean eager app.py (remove all AOTI/dynamo machinery); 480p24 has ample headroom, eager is sufficient; align with local run_server

Browse files
Files changed (1) hide show
  1. app.py +38 -151
app.py CHANGED
@@ -255,116 +255,6 @@ except Exception as e: # noqa: BLE001
255
  # run_session_blocking (in xvideo/serving/zerogpu_engine.py) reuses the vendored
256
  # gate/PE/streaming logic; it is NOT a modification of the existing server.
257
  # ---------------------------------------------------------------------------
258
- _AOTI_STATE = {}
259
-
260
-
261
- def _fork_load_aoti() -> None:
262
- """Inside a GPU fork: fetch (idempotent, /data-cached) and load the dev-box
263
- precompiled AOTInductor VAE encode packages (same sm_120 arch + torch 2.9.1),
264
- then monkeypatch the encode entrypoints. Executes via dlopen — zero dynamo,
265
- which is inert in ZeroGPU forks. Fetch happens IN THE FORK so a download
266
- problem can never wedge the app boot."""
267
- if _AOTI_STATE.get("installed") or _RUNTIME is None:
268
- return
269
- from torch._inductor import aoti_load_package
270
- aoti_dir = _DATA / "aoti"
271
- aoti_dir.mkdir(parents=True, exist_ok=True)
272
- RES = ((480, 840), (840, 480), (720, 1248), (1248, 720))
273
- wanted = []
274
- for H, W in RES:
275
- for t in (9, 1):
276
- wanted.append(("encode", t, H, W, f"vae_encode_t{t}_{H}x{W}.pt2"))
277
- for t in (2, 1):
278
- wanted.append(("decode", t, H, W, f"vae_decode_t{t}_{H}x{W}.pt2"))
279
- fetch = bool(_AOTI_STATE.pop("fetch", False))
280
- if fetch:
281
- for kind, t, H, W, fname in wanted:
282
- p = aoti_dir / fname
283
- if p.is_file():
284
- continue
285
- try:
286
- from huggingface_hub import hf_hub_download
287
- hf_hub_download("wxDai/joyomni-aoti-sm120", fname, repo_type="dataset",
288
- local_dir=str(aoti_dir))
289
- print(f"[fork] fetched aoti {fname}", flush=True)
290
- except Exception: # noqa: BLE001
291
- _AOTI_STATE.setdefault("log", []).append(f"fetch_fail {fname}"[:120])
292
-
293
- enc_files, dec_files = {}, {}
294
- for kind, t, H, W, fname in wanted:
295
- p = aoti_dir / fname
296
- if not p.is_file():
297
- continue
298
- if kind == "encode":
299
- enc_files[(t, H, W)] = str(p)
300
- else:
301
- dec_files[(t, H * 2 // 3 // 16, W * 2 // 3 // 16)] = str(p)
302
- if not enc_files and not dec_files:
303
- return
304
- loaded = {}
305
-
306
- def _get(files, key):
307
- if key in loaded:
308
- return loaded[key]
309
- path = files.get(key)
310
- if path is None:
311
- loaded[key] = None
312
- return None
313
- try:
314
- loaded[key] = aoti_load_package(path)
315
- _AOTI_STATE.setdefault("log", []).append(f"lazy-loaded {path.rsplit('/', 1)[-1]}")
316
- print(f"[fork] aoti lazy-loaded {path.rsplit('/', 1)[-1]}", flush=True)
317
- except Exception as e: # noqa: BLE001
318
- loaded[key] = None
319
- _AOTI_STATE.setdefault("log", []).append(f"load_fail {key}: {e!r}"[:180])
320
- return loaded[key]
321
-
322
- def _unwrap_fn(fn):
323
- while hasattr(fn, "_torchdynamo_orig_callable"):
324
- fn = fn._torchdynamo_orig_callable
325
- return fn
326
-
327
- seen = set()
328
- for vae in (_RUNTIME.pipeline.vae, _RUNTIME.pseudo_encode_vae, _RUNTIME.decode_vae):
329
- if vae is None or id(vae) in seen:
330
- continue
331
- seen.add(id(vae))
332
- if enc_files and hasattr(vae, "_encode"):
333
- fb_e = _unwrap_fn(vae._encode)
334
-
335
- def _encode_dispatch(x, _fb=fb_e):
336
- fn = None
337
- if x.shape[0] == 1 and x.shape[1] == 3:
338
- fn = _get(enc_files, (int(x.shape[2]), int(x.shape[3]), int(x.shape[4])))
339
- if fn is None:
340
- return _fb(x)
341
- try:
342
- return fn(x)
343
- except Exception: # noqa: BLE001
344
- return _fb(x)
345
-
346
- vae._encode = _encode_dispatch
347
- if dec_files and hasattr(vae, "_decode"):
348
- fb_d = _unwrap_fn(vae._decode)
349
-
350
- def _decode_dispatch(z, _fb=fb_d):
351
- fn = None
352
- if z.shape[0] == 1:
353
- fn = _get(dec_files, (int(z.shape[2]), int(z.shape[3]), int(z.shape[4])))
354
- if fn is None:
355
- return _fb(z)
356
- try:
357
- return fn(z)
358
- except Exception: # noqa: BLE001
359
- return _fb(z)
360
-
361
- vae._decode = _decode_dispatch
362
- _AOTI_STATE["installed"] = True
363
- _AOTI_STATE.setdefault("log", []).append(
364
- f"registered enc={len(enc_files)} dec={len(dec_files)} (lazy)")
365
- print(f"[fork] aoti lazy dispatch registered enc={len(enc_files)} dec={len(dec_files)}", flush=True)
366
-
367
-
368
  def _fork_enable_compile() -> None:
369
  """ZeroGPU's CUDA-emulation layer disables torch dynamo in the parent process;
370
  forks inherit that, so every torch.compile wrapper silently runs eager (measured:
@@ -413,13 +303,17 @@ def _fork_enable_compile() -> None:
413
  setattr(_vae, _attr, _plain)
414
  _unwrapped += _n
415
  globals()["_FORK_UNWRAPPED"] = _unwrapped
416
- print(f"[fork] unwrapped {_unwrapped} poisoned compile wrappers; VAE runs eager+AOTI (dynamo-independent)", flush=True)
 
 
 
 
 
 
 
 
417
  except Exception as e: # noqa: BLE001
418
  print(f"[fork] compile re-enable failed: {e!r}", flush=True)
419
- try:
420
- _fork_load_aoti()
421
- except Exception as e: # noqa: BLE001
422
- print(f"[fork] aoti install failed: {e!r}", flush=True)
423
 
424
 
425
  def make_gpu_session(in_q, out_q, paths):
@@ -462,7 +356,6 @@ def _run_session_in_fork(in_q, out_q, paths):
462
 
463
  @spaces.GPU(duration=290, size="xlarge")
464
  def warmup():
465
- _AOTI_STATE["fetch"] = True
466
  """Warm up the pipeline inside the fork (real GPU): both orientations at both
467
  resolution tiers, same coverage as the standalone server's load-time warmup.
468
  720p is included so its VAE compile artifacts land in /data — without this,
@@ -488,13 +381,6 @@ def gpu_probe():
488
 
489
  _fork_enable_compile()
490
  out = {"device": torch.cuda.get_device_name(0)}
491
- try:
492
- _adir = _DATA / "aoti"
493
- out["aoti_state"] = {"installed": _AOTI_STATE.get("installed"),
494
- "log": _AOTI_STATE.get("log", [])[-6:],
495
- "files": sorted(p.name for p in _adir.glob("*.pt2")) if _adir.is_dir() else []}
496
- except Exception as e: # noqa: BLE001
497
- out["aoti_state"] = {"err": repr(e)[:120]}
498
  try:
499
  import os as _os
500
  out["env_torch"] = {k: v for k, v in _os.environ.items()
@@ -647,8 +533,25 @@ def gpu_probe():
647
  out["census_err"] = repr(e)[:150]
648
 
649
  try:
 
650
  from xvideo.models.vae import vae_compile as _vc
 
651
  vae_e = _RUNTIME.pipeline.vae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
652
  x = torch.zeros(1, 3, 9, 480, 840, device=dev, dtype=torch.bfloat16)
653
  x = _vc.prep_input(x)
654
  with torch.no_grad(), torch.autocast(device_type="cuda", dtype=torch.bfloat16, enabled=True):
@@ -660,27 +563,15 @@ def gpu_probe():
660
  vae_e.encode(x)
661
  torch.cuda.synchronize()
662
  out["vae_encode_direct_ms"] = round((_t.perf_counter() - t0) / 10 * 1000, 2)
 
 
 
 
 
 
663
  except Exception as e: # noqa: BLE001
664
  out["vae_probe_err"] = repr(e)[:200]
665
 
666
- try:
667
- from xvideo.models.vae import vae_compile as _vc
668
- vae_d = _RUNTIME.decode_vae
669
- lat_c = int(getattr(vae_d, "latent_channels", 16) or 16)
670
- z = torch.zeros(1, lat_c, 2, 60, 105, device=dev, dtype=torch.bfloat16)
671
- z = _vc.prep_input(z)
672
- with torch.no_grad(), torch.autocast(device_type="cuda", dtype=torch.bfloat16, enabled=True):
673
- for _ in range(3):
674
- vae_d.decode(z, return_dict=False)[0]
675
- torch.cuda.synchronize()
676
- t0 = _t.perf_counter()
677
- for _ in range(10):
678
- vae_d.decode(z, return_dict=False)[0]
679
- torch.cuda.synchronize()
680
- out["vae_decode_direct_ms"] = round((_t.perf_counter() - t0) / 10 * 1000, 2)
681
- except Exception as e: # noqa: BLE001
682
- out["vae_decode_err"] = repr(e)[:200]
683
-
684
  try:
685
  import torch.nn.functional as F
686
  torch.backends.cudnn.benchmark = True
@@ -805,8 +696,7 @@ async def do_warmup():
805
 
806
 
807
  @spaces.GPU(duration=120, size="xlarge")
808
- def engine_bench(fresh_cache: bool = False, pace: float = 12.0, chunks: int = 16,
809
- height: int = 480, width: int = 840):
810
  """Engine-only throughput probe inside the fork: no session wrapper, no
811
  queues — the exact mirror of the dev-box bench. Separates 'fork environment'
812
  from 'session machinery' when chasing Space-vs-local speed gaps. Samples SM
@@ -843,13 +733,13 @@ def engine_bench(fresh_cache: bool = False, pace: float = 12.0, chunks: int = 16
843
 
844
  _th.Thread(target=_clk, daemon=True).start()
845
 
846
- settings = StreamingSettings(height=height, width=width, num_inference_steps=2,
847
  seed=42, max_temporal_ids=8, profile_timings=True,
848
  output_codec="mjpeg")
849
  sess = _RUNTIME.create_v2v_session(
850
  "Turn the scene into an oil painting with warm colors.", settings=settings)
851
  rng = np.random.default_rng(7)
852
- base = rng.integers(0, 256, size=(height, width, 3), dtype=np.uint8)
853
  recs = []
854
  t0 = _t.time()
855
  i = 0
@@ -860,7 +750,7 @@ def engine_bench(fresh_cache: bool = False, pace: float = 12.0, chunks: int = 16
860
  now = _t.time()
861
  if target > now:
862
  _t.sleep(target - now)
863
- frame = _Image.fromarray(np.roll(base, (i * 7) % width, axis=1), mode="RGB")
864
  for r in sess.push_frame(frame, {"seq": i + 1, "t_capture_ms": 0.0}):
865
  p = r.profile or {}
866
  recs.append({k: round(float(p[k]), 4) for k in
@@ -888,7 +778,6 @@ def engine_bench(fresh_cache: bool = False, pace: float = 12.0, chunks: int = 16
888
  v = [c[k] for c in st if k in c]
889
  return round(sum(v) / len(v), 4) if v else None
890
  return {"chunks": len(recs), "fresh_cache": bool(fresh_cache), "pace": pace,
891
- "aoti_log": _AOTI_STATE.get("log", [])[-8:],
892
  "dit": _m("dit_denoise_s"), "enc": _m("vae_encode_s"),
893
  "ref": _m("reference_prepare_s"), "dec": _m("vae_decode_s"),
894
  "jpg": _m("jpeg_encode_s"), "f2t": _m("frames_to_tensor_s"),
@@ -906,13 +795,11 @@ async def do_gpu_probe():
906
 
907
 
908
  @app.get("/engine-bench")
909
- async def do_engine_bench(fresh_cache: int = 0, pace: float = 12.0, chunks: int = 16,
910
- height: int = 480, width: int = 840):
911
  loop = asyncio.get_event_loop()
912
  try:
913
  return {"result": await loop.run_in_executor(
914
- None, lambda: engine_bench(bool(fresh_cache), float(pace), int(chunks),
915
- int(height), int(width)))}
916
  except Exception as e: # noqa: BLE001
917
  return JSONResponse({"error": str(e)[:300]}, status_code=500)
918
 
 
255
  # run_session_blocking (in xvideo/serving/zerogpu_engine.py) reuses the vendored
256
  # gate/PE/streaming logic; it is NOT a modification of the existing server.
257
  # ---------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  def _fork_enable_compile() -> None:
259
  """ZeroGPU's CUDA-emulation layer disables torch dynamo in the parent process;
260
  forks inherit that, so every torch.compile wrapper silently runs eager (measured:
 
303
  setattr(_vae, _attr, _plain)
304
  _unwrapped += _n
305
  globals()["_FORK_UNWRAPPED"] = _unwrapped
306
+ _vc._configured.clear()
307
+ _vc._configured_encode.clear()
308
+ _vc._configured_encode_dynamic.clear()
309
+ if _RUNTIME is not None:
310
+ _vc.maybe_setup_decode(_RUNTIME.decode_vae)
311
+ _vc.maybe_setup_encode(_RUNTIME.pipeline.vae)
312
+ if _RUNTIME.pseudo_encode_vae is not _RUNTIME.pipeline.vae:
313
+ _vc.maybe_setup_encode(_RUNTIME.pseudo_encode_vae)
314
+ print(f"[fork] dynamo re-enabled; unwrapped {_unwrapped} poisoned compile wrappers; VAE re-wrapped fresh", flush=True)
315
  except Exception as e: # noqa: BLE001
316
  print(f"[fork] compile re-enable failed: {e!r}", flush=True)
 
 
 
 
317
 
318
 
319
  def make_gpu_session(in_q, out_q, paths):
 
356
 
357
  @spaces.GPU(duration=290, size="xlarge")
358
  def warmup():
 
359
  """Warm up the pipeline inside the fork (real GPU): both orientations at both
360
  resolution tiers, same coverage as the standalone server's load-time warmup.
361
  720p is included so its VAE compile artifacts land in /data — without this,
 
381
 
382
  _fork_enable_compile()
383
  out = {"device": torch.cuda.get_device_name(0)}
 
 
 
 
 
 
 
384
  try:
385
  import os as _os
386
  out["env_torch"] = {k: v for k, v in _os.environ.items()
 
533
  out["census_err"] = repr(e)[:150]
534
 
535
  try:
536
+ import torch._dynamo.utils as _du
537
  from xvideo.models.vae import vae_compile as _vc
538
+ vae_d = _RUNTIME.decode_vae
539
  vae_e = _RUNTIME.pipeline.vae
540
+ tgt = getattr(vae_d, "_decode", None) or vae_d.decode
541
+ out["decode_wrap"] = repr(type(tgt))[:90]
542
+ out["decode_is_compiled"] = hasattr(tgt, "_torchdynamo_orig_callable") or "Optimized" in repr(type(tgt))
543
+ lat_c = int(getattr(vae_d, "latent_channels", 16) or 16)
544
+ z = torch.zeros(1, lat_c, 2, 60, 105, device=dev, dtype=torch.bfloat16)
545
+ z = _vc.prep_input(z)
546
+ with torch.no_grad(), torch.autocast(device_type="cuda", dtype=torch.bfloat16, enabled=True):
547
+ for _ in range(3):
548
+ vae_d.decode(z, return_dict=False)[0]
549
+ torch.cuda.synchronize()
550
+ t0 = _t.perf_counter()
551
+ for _ in range(15):
552
+ vae_d.decode(z, return_dict=False)[0]
553
+ torch.cuda.synchronize()
554
+ out["vae_decode_direct_ms"] = round((_t.perf_counter() - t0) / 15 * 1000, 2)
555
  x = torch.zeros(1, 3, 9, 480, 840, device=dev, dtype=torch.bfloat16)
556
  x = _vc.prep_input(x)
557
  with torch.no_grad(), torch.autocast(device_type="cuda", dtype=torch.bfloat16, enabled=True):
 
563
  vae_e.encode(x)
564
  torch.cuda.synchronize()
565
  out["vae_encode_direct_ms"] = round((_t.perf_counter() - t0) / 10 * 1000, 2)
566
+ fr = _du.counters.get("frames", {})
567
+ out["dynamo_frames"] = {"total": fr.get("total"), "ok": fr.get("ok")}
568
+ st = _du.counters.get("stats", {})
569
+ out["dynamo_unique_graphs"] = st.get("unique_graphs")
570
+ gb = _du.counters.get("graph_break", {})
571
+ out["dynamo_graph_breaks"] = sum(gb.values()) if gb else 0
572
  except Exception as e: # noqa: BLE001
573
  out["vae_probe_err"] = repr(e)[:200]
574
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  try:
576
  import torch.nn.functional as F
577
  torch.backends.cudnn.benchmark = True
 
696
 
697
 
698
  @spaces.GPU(duration=120, size="xlarge")
699
+ def engine_bench(fresh_cache: bool = False, pace: float = 12.0, chunks: int = 16):
 
700
  """Engine-only throughput probe inside the fork: no session wrapper, no
701
  queues — the exact mirror of the dev-box bench. Separates 'fork environment'
702
  from 'session machinery' when chasing Space-vs-local speed gaps. Samples SM
 
733
 
734
  _th.Thread(target=_clk, daemon=True).start()
735
 
736
+ settings = StreamingSettings(height=480, width=840, num_inference_steps=2,
737
  seed=42, max_temporal_ids=8, profile_timings=True,
738
  output_codec="mjpeg")
739
  sess = _RUNTIME.create_v2v_session(
740
  "Turn the scene into an oil painting with warm colors.", settings=settings)
741
  rng = np.random.default_rng(7)
742
+ base = rng.integers(0, 256, size=(480, 840, 3), dtype=np.uint8)
743
  recs = []
744
  t0 = _t.time()
745
  i = 0
 
750
  now = _t.time()
751
  if target > now:
752
  _t.sleep(target - now)
753
+ frame = _Image.fromarray(np.roll(base, (i * 7) % 840, axis=1), mode="RGB")
754
  for r in sess.push_frame(frame, {"seq": i + 1, "t_capture_ms": 0.0}):
755
  p = r.profile or {}
756
  recs.append({k: round(float(p[k]), 4) for k in
 
778
  v = [c[k] for c in st if k in c]
779
  return round(sum(v) / len(v), 4) if v else None
780
  return {"chunks": len(recs), "fresh_cache": bool(fresh_cache), "pace": pace,
 
781
  "dit": _m("dit_denoise_s"), "enc": _m("vae_encode_s"),
782
  "ref": _m("reference_prepare_s"), "dec": _m("vae_decode_s"),
783
  "jpg": _m("jpeg_encode_s"), "f2t": _m("frames_to_tensor_s"),
 
795
 
796
 
797
  @app.get("/engine-bench")
798
+ async def do_engine_bench(fresh_cache: int = 0, pace: float = 12.0, chunks: int = 16):
 
799
  loop = asyncio.get_event_loop()
800
  try:
801
  return {"result": await loop.run_in_executor(
802
+ None, lambda: engine_bench(bool(fresh_cache), float(pace), int(chunks)))}
 
803
  except Exception as e: # noqa: BLE001
804
  return JSONResponse({"error": str(e)[:300]}, status_code=500)
805