Spaces:
Running on Zero
Running on Zero
Larger batches with OOM fallback; size GPU duration from measurements
Browse files
app.py
CHANGED
|
@@ -356,17 +356,29 @@ def _badge(flag):
|
|
| 356 |
# Inference
|
| 357 |
# ----------------------------------------------------------------------------
|
| 358 |
def _gpu_duration(*args, **kwargs):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
num_steps = kwargs.get("num_steps", DEFAULT_NUM_STEPS)
|
| 360 |
num_frames = kwargs.get("num_frames", DEFAULT_NUM_FRAMES)
|
|
|
|
| 361 |
if len(args) > 4:
|
| 362 |
num_steps = args[4]
|
| 363 |
if len(args) > 5:
|
| 364 |
num_frames = args[5]
|
|
|
|
|
|
|
| 365 |
try:
|
| 366 |
-
work = int(num_steps) * int(num_frames)
|
|
|
|
| 367 |
except Exception:
|
| 368 |
-
work = DEFAULT_NUM_STEPS * DEFAULT_NUM_FRAMES
|
| 369 |
-
return int(min(180,
|
| 370 |
|
| 371 |
|
| 372 |
@spaces.GPU(duration=_gpu_duration)
|
|
@@ -448,7 +460,10 @@ def analyze(
|
|
| 448 |
t_prep = time.perf_counter() - t1
|
| 449 |
|
| 450 |
seq_len = int(samples[0]["input_ids"].shape[-1])
|
| 451 |
-
|
|
|
|
|
|
|
|
|
|
| 452 |
|
| 453 |
def run_batch(batch):
|
| 454 |
kwargs = dict(
|
|
@@ -465,12 +480,23 @@ def analyze(
|
|
| 465 |
|
| 466 |
t2 = time.perf_counter()
|
| 467 |
values = []
|
| 468 |
-
|
| 469 |
-
chunk = samples[
|
| 470 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 471 |
progress(
|
| 472 |
-
0.2 + 0.55 *
|
| 473 |
-
desc=f"Scoring prefix {
|
| 474 |
)
|
| 475 |
t_value = time.perf_counter() - t2
|
| 476 |
|
|
|
|
| 356 |
# Inference
|
| 357 |
# ----------------------------------------------------------------------------
|
| 358 |
def _gpu_duration(*args, **kwargs):
|
| 359 |
+
"""Size the ZeroGPU reservation from the measured cost of one run.
|
| 360 |
+
|
| 361 |
+
Reference points measured on this Space (448 px, 24 frames/prefix,
|
| 362 |
+
batch 8): 32 prefixes over a 429-frame video = ~30 s wall clock end to end,
|
| 363 |
+
including decode and rendering. Cost is dominated by the value pass, which
|
| 364 |
+
scales with ``num_steps × num_frames`` and roughly with the square of the
|
| 365 |
+
image side (the eager attention is O(L²)).
|
| 366 |
+
"""
|
| 367 |
num_steps = kwargs.get("num_steps", DEFAULT_NUM_STEPS)
|
| 368 |
num_frames = kwargs.get("num_frames", DEFAULT_NUM_FRAMES)
|
| 369 |
+
side = kwargs.get("max_image_side", DEFAULT_MAX_SIDE)
|
| 370 |
if len(args) > 4:
|
| 371 |
num_steps = args[4]
|
| 372 |
if len(args) > 5:
|
| 373 |
num_frames = args[5]
|
| 374 |
+
if len(args) > 6:
|
| 375 |
+
side = args[6]
|
| 376 |
try:
|
| 377 |
+
work = min(int(num_steps) * int(num_frames), WORK_BUDGET)
|
| 378 |
+
factor = (float(side) / DEFAULT_MAX_SIDE) ** 2.5
|
| 379 |
except Exception:
|
| 380 |
+
work, factor = DEFAULT_NUM_STEPS * DEFAULT_NUM_FRAMES, 1.0
|
| 381 |
+
return int(min(180, max(30, 18 + work * 0.045 * factor)))
|
| 382 |
|
| 383 |
|
| 384 |
@spaces.GPU(duration=_gpu_duration)
|
|
|
|
| 460 |
t_prep = time.perf_counter() - t1
|
| 461 |
|
| 462 |
seq_len = int(samples[0]["input_ids"].shape[-1])
|
| 463 |
+
# `pred_slot_isolated_eager` materialises a full B×32×L×L attention matrix,
|
| 464 |
+
# so the batch size is memory- rather than compute-bound. Start optimistic
|
| 465 |
+
# and halve on OOM (see the loop below).
|
| 466 |
+
batch_size = 8 if seq_len <= 3600 else (4 if seq_len <= 5400 else 2)
|
| 467 |
|
| 468 |
def run_batch(batch):
|
| 469 |
kwargs = dict(
|
|
|
|
| 480 |
|
| 481 |
t2 = time.perf_counter()
|
| 482 |
values = []
|
| 483 |
+
while len(values) < len(samples):
|
| 484 |
+
chunk = samples[len(values) : len(values) + batch_size]
|
| 485 |
+
try:
|
| 486 |
+
values.extend(run_batch(chunk))
|
| 487 |
+
except torch.cuda.OutOfMemoryError:
|
| 488 |
+
torch.cuda.empty_cache()
|
| 489 |
+
if batch_size == 1:
|
| 490 |
+
raise gr.Error(
|
| 491 |
+
"Ran out of GPU memory. Try a smaller 'Max image side' or fewer "
|
| 492 |
+
"'Frames per prefix' in Advanced settings."
|
| 493 |
+
)
|
| 494 |
+
batch_size = max(1, batch_size // 2)
|
| 495 |
+
print(f"[oom] falling back to batch_size={batch_size}", flush=True)
|
| 496 |
+
continue
|
| 497 |
progress(
|
| 498 |
+
0.2 + 0.55 * len(values) / len(samples),
|
| 499 |
+
desc=f"Scoring prefix {len(values)}/{len(samples)}…",
|
| 500 |
)
|
| 501 |
t_value = time.perf_counter() - t2
|
| 502 |
|