Skip to content

Measure the allocator gc-gate fix on an 8 GB card #26

Description

@TroyHernandez

@user9208212 — task for your Claude, on the 8 GB NVIDIA box.

Context

#24 and #25 fix a chronic torch allocator gc gate: under
backend:native the allocated/reserved ratio gate (0.95) is always
exceeded, so the R gc callback fired on nearly every CUDA allocation —
up to ~90% of image-generation wall time was R gc. The fix (see
.flux_gc_gates() in R/memory_flux.R) disables the ratio gate and
uses an absolute allocated_rate gate at 0.65 of total VRAM plus a
footprint-based reserved net. Measured wins on a 16 GB RTX 5060 Ti at
1024px: Z-Image 143→24 s, FLUX.2 klein 48→13 s, SDXL 234→100 s,
SD 2.1 (768px) 101→26 s.

All constants are tuned on 16 GB. On 8 GB the sizing may be wrong in
both directions:

  • allocated_rate 0.65 = 5.2 GB, which is likely below the live
    working set for SDXL (unet fp16 alone is ~5 GB) — the gate could turn
    chronic again and the win could vanish.
  • With fewer gcs, garbage rides higher between collections: we saw the
    SD21 sawtooth peak at 13.3 GB where the baseline sat at 5.4 GB. That
    headroom does not exist on 8 GB — OOM is a real possibility.

The task

  1. Install the branch:
    remotes::install_github("cornball-ai/diffuseR@feature/sd-gc-gates")
  2. For each model that fits, run one warm generation per arm with the
    script below: GATES=0 pins the old torch defaults (a true baseline
    through the same code path); GATES=1 uses the new gates.
    • SD 2.1, 768px (core ask — fits comfortably)
    • FLUX.2 klein fp8, 512px and 1024px if you have the
      cornball safetensors fork (remotes::install_github("cornball-ai/safetensors");
      needed for the float8 dtype) — pass text_device = "cpu" to
      flux2_load_pipeline(), the Qwen3 encoder won't fit on 8 GB
    • SDXL, 768px optional, decoder = "cpu"; skip if it OOMs
  3. If GATES=1 OOMs where GATES=0 doesn't, retry with a smaller
    absolute gate before the library loads, e.g.
    options(torch.cuda_allocator_allocated_rate = 0.5) — finding the
    right 8 GB sizing is the point of this task.
# GATES=0 baseline / GATES=1 new gates; PIPE=sd21 (or sdxl)
library(diffuseR)
if (Sys.getenv("GATES") == "0") {
    options(torch.cuda_allocator_allocated_reserved_rate = 0.8)
    options(torch.cuda_allocator_allocated_rate = 0.8)
    options(torch.cuda_allocator_reserved_rate = 0.2)
}
options(timeout = 1800)
torch::local_no_grad()

PROMPT <- "A retro 1970s radio station studio interior, warm wood panels, vinyl records on the walls, soft neon glow, detailed illustration"
devices <- list(unet = "cuda", decoder = "cpu",
                text_encoder = "cuda", encoder = "cpu")
m2d <- models2devices("sd21", devices = devices,
                      unet_dtype_str = "float16", download_models = TRUE)
pipeline <- load_pipeline("sd21", m2d = m2d, unet_dtype_str = "float16",
                          use_native_unet = TRUE, use_native_decoder = TRUE,
                          use_native_text_encoder = TRUE)

gen <- function(seed) {
    g0 <- gc.time()
    tw <- system.time(
        txt2img_sd21(PROMPT, pipeline = pipeline, devices = devices,
                     img_dim = 768, num_inference_steps = 50,
                     guidance_scale = 7.5, seed = seed,
                     use_native_unet = TRUE, use_native_decoder = TRUE,
                     use_native_text_encoder = TRUE,
                     save_file = FALSE, verbose = FALSE)
    )
    g1 <- gc.time()
    gc(FALSE); torch::cuda_empty_cache()
    c(wall = tw[["elapsed"]], gc = g1[3] - g0[3])
}

invisible(gen(1L))  # warmup
r <- gen(42L)
ms <- torch::cuda_memory_stats()
cat(sprintf("gates=%s: %.1f s (gc %.1f s), peak %.2f GB alloc / %.2f GB reserved\n",
            Sys.getenv("GATES"), r[["wall"]], r[["gc"]],
            ms$allocated_bytes$all$peak / 1e9,
            ms$reserved_bytes$all$peak / 1e9))

Report back (in this issue)

Per model × arm: warm wall seconds, gc seconds, peak alloc/reserved,
OOM yes/no — plus the GPU model, driver version, and
packageVersion("torch"). If you find a gate sizing that works better
on 8 GB, include it; we'll fold it into .flux_gc_gates() as a
VRAM-aware default.

Metadata

Metadata

Assignees

No one assigned

    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