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

Use sequential CPU offload instead of moving full pipeline to GPU at once

Browse files

pipe.to(device) staged all ~37GB of weights (transformer + text_encoder +
vae) onto GPU simultaneously, which OOM'd on smaller MIG slices (e.g. the
47GB 2g.48gb partition ZeroGPU sometimes hands out). enable_model_cpu_offload
uses the pipeline's declared model_cpu_offload_seq to keep only the active
component resident on GPU at a time.

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

Files changed (1) hide show
  1. app.py +11 -8
app.py CHANGED
@@ -402,12 +402,14 @@ 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
- print(f"[infer] moving pipe to {device}... t={time.perf_counter()-t0:.1f}s")
406
- pipe.to(device)
407
- print(f"[infer] pipe on {device} {_gpu_mem_str(_cuda_ok)} t={time.perf_counter()-t0:.1f}s")
408
-
409
- torch.cuda.empty_cache()
410
- print(f"[infer] cache cleared — {_gpu_mem_str(_cuda_ok)}")
 
 
411
 
412
  print(f"[infer] {len(pil_images)} image(s) pre-decoded, output={width}x{height}, seed={seed}")
413
 
@@ -450,8 +452,9 @@ def _infer_gpu(pil_images, prompt, seed, guidance_scale, steps, width, height, m
450
  timer.print_timings()
451
  raise
452
  finally:
453
- print(f"[infer] moving pipe to CPU... t={time.perf_counter()-t0:.1f}s")
454
- pipe.to("cpu")
 
455
  gc.collect()
456
  torch.cuda.empty_cache()
457
  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
+ # 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}")
415
 
 
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 =====")