feat: add -ngl to cap diffusion layers resident in VRAM - #1830
Closed
mosaic-devel wants to merge 1 commit into
Closed
feat: add -ngl to cap diffusion layers resident in VRAM#1830mosaic-devel wants to merge 1 commit into
mosaic-devel wants to merge 1 commit into
Conversation
Residency is currently expressed only as a byte budget (--max-vram). That works, but it is awkward to reason about when the question is simply "how much of this model fits on my card", and it has no equivalent to the layer count that users of similar projects already reach for. Add -ngl / --gpu-layers, which caps how many diffusion layers stay resident; the remainder stream from RAM per step. It reuses the existing graph-cut segmentation and streaming machinery, so the only real change is that annotate_residency can now be driven by a count as well as by bytes. Segment merging is disabled when a count is given, so one segment corresponds to one layer. The count is an upper bound rather than a demand: residency is also capped by free VRAM (minus the same safety margin and worst-streamed-segment reserve the byte path already uses). Asking for more layers than fit therefore fills the device and streams the rest instead of failing part-way through a denoise pass, which makes -ngl 999 a reasonable way to say "use as much VRAM as there is". Since streaming reads weights out of host memory, -ngl implies --offload-to-cpu and --stream-layers. With no count given (-1, the default) the byte-budget behaviour is unchanged.
Owner
|
Thanks for the contribution and for exploring GPU layer offloading support. I understand the motivation of adding -ngl for consistency with llama.cpp, but I don't think layer-based offloading maps well to diffusion models. Unlike transformer models, diffusion architectures don't expose a stable concept of "layer" to users. The same -ngl value has very different VRAM requirements across SD1.5/SDXL/Flux/etc. This makes the option difficult to discover and configure. Users usually know their available VRAM, not the number of diffusion layers they can fit. I would prefer keeping VRAM-based control as the user-facing API and making layer-based partitioning an internal/debug option. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Residency is currently expressed only as a byte budget (
--max-vram). That works, but it is awkward when the question is simply "how much of this model fits on my card", and there is no equivalent to the layer count users of similar projects already reach for.Solution
-ngl/--gpu-layerscaps how many diffusion layers stay resident in VRAM; the remainder stream from RAM per step.sd-cli -m model.safetensors -p "a cat" -ngl 20This reuses the existing graph-cut segmentation and streaming machinery. The substantive change is that
annotate_residencycan now be driven by a count as well as by bytes; segment merging is disabled when a count is given, so one segment corresponds to one layer.The count is an upper bound, not a demand. Residency is also capped by free VRAM, reusing the same safety margin and worst-streamed-segment reserve the byte path already applies. Asking for more layers than fit fills the device and streams the rest, rather than failing part-way through a denoise pass — so
-ngl 999is a reasonable way to say "use as much VRAM as there is". This differs deliberately from llama.cpp, where the number is absolute and over-asking OOMs.Since streaming reads weights out of host memory,
-nglimplies--offload-to-cpuand--stream-layers. With no count given (-1, the default), byte-budget behaviour is unchanged —remainingstays negative and never gates the loop.Verification
Vulkan (RX 6600 XT, 8 GB, RADV), Krea 2 Turbo, 29 segments:
-ngl 8— honoured exactly:-ngl 999— clamped to fit instead of OOMing:(The budget is 4.2 GB rather than the full 8 GB because the text encoder is resident at plan time; moving it to CPU frees more.)
Both produce correct images. Without
-nglthe byte-budget path is untouched and behaves as before.Caveats
--stream-layerslimitations: single-device only, and the streamed tail re-reads per segment, so it trades speed for headroom.Note
This was written with AI assistance. Errors are mine; happy to rework or drop any part of it.