Skip to content

[BUG] AMD: Windows ROCm image gen silently exhausts host RAM when the memory plan under-budgets #8188

Description

@LeoBorcherding

What happens

On Windows ROCm an under-estimated diffusion memory plan does not fail. The generation keeps allocating, WDDM backs the overflow out of system RAM, and the machine becomes unusable (available RAM under 1.2 GB, pagefile past 50 GB). On Linux the same shortfall raises torch.OutOfMemoryError and the job dies cleanly.

The trigger is that the image memory plan is resolution-blind.

Why

estimate_image_runtime_mib (studio/backend/core/inference/diffusion_memory.py:273-292) scales headroom by pixel area against DEFAULT_IMAGE_WIDTH/HEIGHT, both 1024. Its only caller is _plan_memory (studio/backend/core/inference/diffusion.py:3006), and that caller passes no dimensions (diffusion.py:3130):

runtime_headroom = estimate_image_runtime_mib(width = None, height = None, family = variant_hint)

pixel_scale is therefore always exactly 1.0. Every image at every resolution is budgeted as one 1 MP frame. studio/backend/core/inference/video.py:1186 passes real values, so image is the odd one out.

The arithmetic

Z-Image-Turbo GGUF Q4_K_S at 1088x1920, 9 steps:

quantity value
requested area 2,088,960 px, 1.99x of 1024^2
pixel_scale actually used 1.0
planned headroom 8192 x 1.0 x 0.85 = 6,963 MiB
headroom actually needed ~13,872 MiB
shortfall ~6.7 GiB

The planner picked its most conservative tier and still overran, because it was conservative about a workload half the real size:

diffusion.loaded: repo=unsloth/Z-Image-Turbo-GGUF base=Tongyi-MAI/Z-Image-Turbo
device=cuda offload=model tiling=True reasons=companions exceed budget;
whole-module offload of every component

Why Windows is worse than Linux

Measured from Windows performance counters on the backend process during the overrun:

counter value
GPU Process Memory\Local Usage (dedicated VRAM) 13,215 MB
GPU Process Memory\Non Local Usage (system RAM as GPU memory) 14,025 MB
total held by the process ~27 GB on a 15.92 GiB card
system Available RAM 1,228 MB
Committed 80.5 GB of an 89.1 GB limit
pagefile grown to 52.5 GB

Under WDDM the driver satisfies the overflow from shared system memory instead of failing. No OOM, no error, no failed job, just a desktop that stops responding.

Nothing in the inference path bounds this: torch.cuda.set_per_process_memory_fraction appears exactly once in the backend (studio/backend/core/training/worker.py:3734), inside run_training_process. No inference process reaches it.

Environment

  • GPU: Radeon RX 9060 XT (gfx1200), discrete, 15.92 GiB
  • OS: Windows 11
  • torch 2.11.0+rocm7.13.0
  • Model: unsloth/Z-Image-Turbo-GGUF, Q4_K_S, base Tongyi-MAI/Z-Image-Turbo

Reproduce

Load Z-Image-Turbo GGUF Q4_K_S on a ~16 GB Windows ROCm card, generate at 1088x1920. Watch GPU Process Memory\Non Local Usage for the backend process climb into the tens of GB while available RAM collapses, with no exception raised.

At 1024x1024 the same model completes, because that is the size the plan silently assumed.

Fix

Re-check at generate time, where the real resolution is known. The load-time width=None is defensible on its own (no request exists yet); the defect is that nothing revisited it. Compare the activation term alone against the current free budget: weights can be offloaded, activations cannot, so a runtime estimate over budget is an overrun at every tier rather than a tuning guess.

Longer term the ROCm OOM guard should extend to inference processes. Cross-importing from the training worker is already an established pattern (studio/backend/core/inference/llama_cpp.py:4864 does from core.training.worker import _rocm_classify_unified_memory).

Related

Open question

It is not yet established whether the ~14 GB of non-local memory is caching-allocator bytes that WDDM placed in system RAM, or pinned host buffers created by enable_model_cpu_offload. This matters for the fix: set_per_process_memory_fraction bounds the former and not the latter.

Evidence leaning toward some of it sitting outside the allocator: the separate video OOM on this host reported "Of the allocated memory 9.09 GiB is allocated by PyTorch" while the process held substantially more GPU-addressable memory than that.

Settling it requires sampling torch.cuda.memory_reserved() against the live GPU Process Memory\Non Local Usage counter during a large generation.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions