You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In vllm-omni's current diffusion pipeline architecture, the VAE (encoder/decoder) is loaded on the same GPU as the denoising model (DiT/UNet) within a single diffusion stage. VAE models can be very large — for example, QwenImage VAE and Wan VAE are 3D causal convolution networks with significant parameter counts and activation memory footprints. During the decode phase (latents → pixels), the VAE must coexist in GPU memory alongside the denoiser, leading to:
High peak GPU memory usage — the denoiser already occupies a large portion of VRAM; adding VAE on top can push close to or beyond device limits, especially for high-resolution / video generation.
Suboptimal memory utilization — VAE encode and decode are only needed at the beginning and end of the pipeline respectively; the denoiser does not need the VAE during its many iterative denoising steps. Despite this temporal non-overlap, both models occupy memory simultaneously.
OOM during VAE decode — several pipelines (e.g., Wan 2.2) already insert explicit torch.cuda.empty_cache() calls before VAE decode as a workaround for memory pressure, indicating this is a known pain point.
Current mitigations are insufficient
vae_use_tiling / vae_use_slicing reduce activation memory during VAE forward passes but do not address the weight memory of the VAE itself.
enable_cpu_offload (model-level offload) swaps DiT ↔ encoder modules but the current ModelLevelOffloadBackend does not include VAE in its sequential swap hooks — VAE is moved to GPU once and stays there.
Distributed VAE (patch parallelism) spreads VAE computation across GPUs within the same diffusion worker group, but does not eliminate VAE weight memory from the denoiser's GPU.
Proposed solution: VAE as a separate omni stage
vllm-omni's stage system (StageConfig / OmniStage) already supports running different stages on different GPU devices via per-stage runtime.devices configuration and set_stage_devices(). We propose leveraging this existing multi-stage infrastructure to split VAE into its own stage, separate from the denoising stage:
VAE Encode stage runs on a dedicated (possibly smaller / shared) GPU, encodes input images into latents, and passes them to the denoising stage.
Denoising stage runs the iterative denoising loop on its own GPU(s) without loading VAE weights at all, freeing significant VRAM for larger models, higher resolution, or batch processing.
VAE Decode stage receives final latents and decodes to pixel space. This could share the same GPU as VAE Encode (since they are temporally non-overlapping), or even share a GPU with the LLM stage.
Key benefits
Reduced per-GPU memory — The denoiser GPU no longer needs to hold VAE weights (often 100MB–2GB+ depending on precision and model).
Enable larger models / higher resolution — Freed VRAM can be used for longer context, higher resolution, or increased batch size on the denoiser.
Better multi-GPU utilization — VAE encode/decode can share a GPU with the LLM stage or other non-overlapping workloads, improving overall cluster utilization.
Consistent with existing architecture — Follows the same multi-stage pattern already used for LLM ↔ Diffusion separation.
Alternatives
Improve CPU offload to include VAE — Extend ModelLevelOffloadBackend to swap VAE ↔ DiT weights via CPU. Simpler but adds latency from CPU↔GPU transfers and doesn't improve GPU utilization across multiple devices.
Layer-wise offload for VAE — Offload individual VAE layers to CPU/disk. Very fine-grained but adds significant complexity and transfer overhead.
VAE quantization — Reduce VAE weight memory via INT8/INT4 quantization. Orthogonal optimization that can be combined with stage splitting, but may affect image quality.
Lazy VAE loading — Load VAE weights only when needed and unload after. Avoids holding weights but adds load/unload latency per request.
Additional context
Affected VAE implementations
VAE Class
Pipeline(s)
Notes
AutoencoderKLQwenImage
QwenImage, QwenImage Edit
3D causal conv VAE, large
DistributedAutoencoderKLWan
Wan 2.2 (t2v, i2v)
Video VAE, explicit OOM workarounds exist
AutoencoderKLFlux2
Flux2, Flux2 Klein
DistributedAutoencoderKL
SD3, Z-Image
Standard image VAE
AutoencoderKLConv3D
Hunyuan Image 3
3D conv VAE
AutoencoderKLLTX2Video + AutoencoderKLLTX2Audio
LTX2
Dual VAE (video + audio)
Bagel AutoEncoder
Bagel
Custom construction
Design considerations
New stage type or config: Could introduce a StageType.VAE or reuse StageType.DIFFUSION with a sub-mode flag.
Inter-stage data transfer: Latent tensors are relatively small compared to pixel-space tensors, making inter-stage transfer efficient.
Pipeline refactoring: Pipelines would need to be split into encode(), denoise(), decode() callable segments rather than monolithic __call__().
Backward compatibility: When no separate VAE stage is configured, behavior should remain identical to today (VAE loaded within the diffusion stage).
Shared GPU with LLM: Since LLM inference and VAE encode/decode are temporally non-overlapping in most omni workflows, they could share the same GPU device to minimize total GPU count.
Make sure you already searched for relevant issues, and asked the chatbot living at the bottom right corner of the documentation page, which can answer lots of frequently asked questions.
🚀 The feature, motivation and pitch
Problem
In vllm-omni's current diffusion pipeline architecture, the VAE (encoder/decoder) is loaded on the same GPU as the denoising model (DiT/UNet) within a single diffusion stage. VAE models can be very large — for example, QwenImage VAE and Wan VAE are 3D causal convolution networks with significant parameter counts and activation memory footprints. During the decode phase (latents → pixels), the VAE must coexist in GPU memory alongside the denoiser, leading to:
torch.cuda.empty_cache()calls before VAE decode as a workaround for memory pressure, indicating this is a known pain point.Current mitigations are insufficient
vae_use_tiling/vae_use_slicingreduce activation memory during VAE forward passes but do not address the weight memory of the VAE itself.enable_cpu_offload(model-level offload) swaps DiT ↔ encoder modules but the currentModelLevelOffloadBackenddoes not include VAE in its sequential swap hooks — VAE is moved to GPU once and stays there.Proposed solution: VAE as a separate omni stage
vllm-omni's stage system (
StageConfig/OmniStage) already supports running different stages on different GPU devices via per-stageruntime.devicesconfiguration andset_stage_devices(). We propose leveraging this existing multi-stage infrastructure to split VAE into its own stage, separate from the denoising stage:Key benefits
Alternatives
ModelLevelOffloadBackendto swap VAE ↔ DiT weights via CPU. Simpler but adds latency from CPU↔GPU transfers and doesn't improve GPU utilization across multiple devices.Additional context
Affected VAE implementations
AutoencoderKLQwenImageDistributedAutoencoderKLWanAutoencoderKLFlux2DistributedAutoencoderKLAutoencoderKLConv3DAutoencoderKLLTX2Video+AutoencoderKLLTX2AudioAutoEncoderDesign considerations
New stage type or config: Could introduce a
StageType.VAEor reuseStageType.DIFFUSIONwith a sub-mode flag.Inter-stage data transfer: Latent tensors are relatively small compared to pixel-space tensors, making inter-stage transfer efficient.
Pipeline refactoring: Pipelines would need to be split into
encode(),denoise(),decode()callable segments rather than monolithic__call__().Backward compatibility: When no separate VAE stage is configured, behavior should remain identical to today (VAE loaded within the diffusion stage).
Shared GPU with LLM: Since LLM inference and VAE encode/decode are temporally non-overlapping in most omni workflows, they could share the same GPU device to minimize total GPU count.
Make sure you already searched for relevant issues, and asked the chatbot living at the bottom right corner of the documentation page, which can answer lots of frequently asked questions.