someone-in-the-world Claude Sonnet 5 commited on
Commit
994c9a0
·
1 Parent(s): a104380

Try full pipeline GPU residency before falling back to cpu offload

Browse files

Each ZeroGPU call runs in a fresh worker (offload hooks are always unset
at entry), so enable_model_cpu_offload bought no cross-call reuse — it
just replaced one bulk pipe.to(device) transfer with several slower,
hook-managed component transfers, adding ~55s of pure data movement to a
~62s call per the FireRed-Dev logs. Try the fast full-GPU path first and
only fall back to offload on an actual CUDA OOM (small MIG slices).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +20 -9
app.py CHANGED
@@ -402,13 +402,22 @@ def _infer_gpu(pil_images, prompt, seed, guidance_scale, steps, width, height, m
402
  print(f"[infer] GPU: {p.name}, total={p.total_memory/1024**3:.1f}GB, cap={p.major}.{p.minor}")
403
  torch.cuda.reset_peak_memory_stats()
404
 
405
- # Sequential offload: only the component currently doing work (text_encoder,
406
- # then transformer, then vae see model_cpu_offload_seq) sits on GPU at a
407
- # time, instead of the full ~37GB pipeline resident simultaneously. Needed
408
- # to fit on smaller MIG slices (e.g. the 47GB 2g.48gb partition) without OOM.
 
 
409
  if getattr(pipe.transformer, "_hf_hook", None) is None:
410
- pipe.enable_model_cpu_offload(device=device)
411
- print(f"[infer] enabled sequential cpu offload on {device}")
 
 
 
 
 
 
 
412
  print(f"[infer] {_gpu_mem_str(_cuda_ok)} — t={time.perf_counter()-t0:.1f}s")
413
 
414
  print(f"[infer] {len(pil_images)} image(s) pre-decoded, output={width}x{height}, seed={seed}")
@@ -452,9 +461,11 @@ def _infer_gpu(pil_images, prompt, seed, guidance_scale, steps, width, height, m
452
  timer.print_timings()
453
  raise
454
  finally:
455
- # No manual pipe.to("cpu") that fights the offload hooks' own device
456
- # bookkeeping. Hooks already return each component to CPU after its
457
- # forward; empty_cache() just reclaims the freed GPU blocks.
 
 
458
  gc.collect()
459
  torch.cuda.empty_cache()
460
  print(f"[infer] ===== END t={time.perf_counter()-t0:.1f}s =====")
 
402
  print(f"[infer] GPU: {p.name}, total={p.total_memory/1024**3:.1f}GB, cap={p.major}.{p.minor}")
403
  torch.cuda.reset_peak_memory_stats()
404
 
405
+ # Each ZeroGPU call runs in a fresh worker (hooks are always unset here),
406
+ # so cpu_offload buys no cross-call reuse it only trades one bulk
407
+ # to(device) transfer for several slower hook-managed ones. Try moving
408
+ # the full ~37GB pipeline to GPU in one shot first (fast); only fall back
409
+ # to enable_model_cpu_offload (keeps one component resident at a time) if
410
+ # that OOMs on a smaller MIG slice (e.g. the 47GB 2g.48gb partition).
411
  if getattr(pipe.transformer, "_hf_hook", None) is None:
412
+ try:
413
+ pipe.to(device)
414
+ print(f"[infer] moved full pipe to {device} — t={time.perf_counter()-t0:.1f}s")
415
+ except torch.cuda.OutOfMemoryError:
416
+ print(f"[infer] OOM moving full pipe to {device}, falling back to cpu offload")
417
+ pipe.to("cpu")
418
+ torch.cuda.empty_cache()
419
+ pipe.enable_model_cpu_offload(device=device)
420
+ print(f"[infer] enabled cpu offload on {device} (fallback)")
421
  print(f"[infer] {_gpu_mem_str(_cuda_ok)} — t={time.perf_counter()-t0:.1f}s")
422
 
423
  print(f"[infer] {len(pil_images)} image(s) pre-decoded, output={width}x{height}, seed={seed}")
 
461
  timer.print_timings()
462
  raise
463
  finally:
464
+ # No manual pipe.to("cpu"): in the offload-fallback case that fights the
465
+ # hooks' own device bookkeeping (they return each component to CPU after
466
+ # its forward), and in the normal fast-path case the worker's GPU access
467
+ # is reclaimed by ZeroGPU when this call returns regardless — paying for
468
+ # a D2H transfer here would just be wasted GPU-billed time.
469
  gc.collect()
470
  torch.cuda.empty_cache()
471
  print(f"[infer] ===== END t={time.perf_counter()-t0:.1f}s =====")