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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions