Skip to content

Sleep Mode & ACK Protocol

vLLM-Omni’s Sleep Mode allows you to temporarily release most GPU memory used by a model—such as model weights and key-value (KV) caches—without stopping the server or unloading the Docker container.

This feature is inherited from vLLM’s Sleep Mode and extended with the Omni ACK Protocol to support multi-stage pipelines and heterogeneous hardware backends (NVIDIA, AMD, Intel, Huawei). It is especially useful in RLHF, dynamic model switching, or cost-saving scenarios.


1. Feature Documentation

Overview

Omni Sleep Mode provides a mechanism to "sleep" specific model stages. When a stage enters sleep, its physical VRAM is reclaimed by the system, while the process state is preserved for rapid "wake-up" without full re-initialization.

Sleep Levels

We support two levels of hibernation to balance recovery speed and memory efficiency:

Level Name Mechanism Recovery Speed Memory Freed
Level 1 Weight Offloading Offloads weights to Host CPU RAM. Fast (DMA) Substantial
Level 2 Full De-mapping Physically releases memory pages via VRAM scavenging. Moderate Maximum (up to 95%+)

Supported Platforms

Omni Sleep Mode is optimized for high-performance computing backends:

  • NVIDIA: Supported via Virtual Memory Management (VMM).
  • AMD (ROCm): Fully supported with physical page de-mapping.
  • Intel XPU: Supported via Level Zero memory management.
  • Huawei NPU: Supported via Ascend memory scavenging.

Hardware Requirements

  • Memory Considerations: System RAM must be sufficient to hold offloaded weights during sleep.
  • TP Support: Tensor Parallel groups synchronize sleep/wake transitions across all workers.

Quiescing a busy diffusion stage: pause_generation(mode="keep")

sleep() on a diffusion stage is a worker-level RPC: it offloads memory but does not stop the stage's scheduler, so a stage that still has queued requests must be made quiet first. Aborting discards the batch that is running. pause_generation(mode="keep") keeps it instead:

  • The batch that is already running finishes on the weights it started with and its outputs are delivered normally.
  • Requests that have not started stay queued and run after resume_generation(), on whatever weights are loaded by then. A request never continues denoising across a weight change.
  • The call returns only after every replica has acknowledged: the batch that was running has finished executing, its asynchronous output copies are done, and the device is synchronized; its outputs are then delivered on the normal path. On a non-preemptible backend that acknowledgement arrives at the next batch boundary, so expect it to take as long as the remaining batch.
  • Control RPCs (list_loras, sleep, wake_up, resume_generation) keep working while paused.

Recommended order for a weight update on a diffusion stage:

await engine.pause_generation(mode="keep", clear_cache=False)
await engine.sleep(level=1)          # optional: free memory for the trainer
await engine.wake_up()               # admission stays closed until resume
# install new weights here
await engine.resume_generation()

Support matrix and limits:

Configuration mode="keep"
Request-level execution (step_execution=False) with the in-process (uni) executor Supported
Request-level execution with the multi-process (mp) executor, including asynchronous output Supported; the acknowledgement waits for the output copies
Request-level execution with asynchronous KV prefetch Supported; the acknowledgement joins any prefetch still in flight
Step-level execution (step_execution=True / streaming output) Not supported: the call raises NotImplementedError
mode="abort" / mode="wait" / wait_for_inflight_requests=True on a diffusion stage Unchanged: frontend admission is paused only

Behavior notes:

  • Callers that already passed mode="keep" for a diffusion stage used to get an immediate return that only closed frontend admission. They now also stop the backend and wait for the ACK.
  • A timeout or error from pause_generation means the pause did not complete; the scheduler stays closed and the caller must retry the pause or call resume_generation() explicitly. Do not sleep or mutate weights after a failed pause.
  • resume_generation(stage_ids=...) that leaves another paused stage closed (an AR stage, or a keep-paused diffusion stage) keeps frontend admission closed as well, so new requests cannot queue on a stage that will not schedule them. Admission reopens once every stage paused by pause_generation has been resumed.
  • clear_cache has no diffusion-specific effect.
  • Without a pause call nothing changes: no barrier runs and batching, abort and output ordering are as before.

2. Usage Examples

Python API Example

You can programmatically control the lifecycle of stages using the AsyncOmni engine.

import asyncio
from vllm_omni.entrypoints.async_omni import AsyncOmni

async def run_sleep_demo():
    # 1. initialization
    engine = AsyncOmni(
        model="ByteDance-Seed/BAGEL-7B-MoT",
        enable_sleep_mode=True
    )

    # 2. sleep mode level2
    acks = await engine.sleep(stage_ids=[0], level=2)
    print(f"Freed {acks[0].freed_bytes / 1024**3:.2f} GiB on Stage 0")

    # 3. wake up
    await engine.wake_up(stage_ids=[0])

if __name__ == "__main__":
    asyncio.run(run_sleep_demo())

server command Example

Start the server with sleep mode enabled:

The first method

vllm serve ByteDance-Seed/BAGEL-7B-MoT \
--omni \
--enable-sleep-mode \
--trust-remote-code \
--gpu-memory-utilization 0.7

The second method

python3 -m vllm_omni.entrypoints.openai.api_server \
    --model ByteDance-Seed/BAGEL-7B-MoT \
    --omni \
    --enable-sleep-mode \
    --trust-remote-code \
--gpu-memory-utilization 0.7

Test Scenarios & Commands

Scenario 1: LLM Engine Sleep

Objective: Verify VRAM reclamation for Stage 0 (Thinker).

Trigger sleep (Level 1 or Level 2) via client:

curl -X POST http://localhost:8000/v1/omni/sleep \
     -H "Content-Type: application/json" \
     -d '{"stage_ids": [0], "level": 2}'

Tip: Open a new terminal and run rocm-smi or nvidia-smi or to observe the immediate drop in VRAM usage.

Scenario 2: Diffusion Sleep

Objective: Verify VRAM reclamation for Stage 1 (Diffusion).

Trigger sleep (Level 1 or Level 2) via client:

curl -X POST http://localhost:8000/v1/omni/sleep \
     -H "Content-Type: application/json" \
     -d '{"stage_ids": [1], "level": 2}'

Scenario 3: Multi-Stage Coordinated Stress Test

Objective: Test concurrent sleep and rapid wake-up across multiple stages.

Concurrent Sleep (Stage 0 & 1):

curl -X POST http://localhost:8000/v1/omni/sleep \
     -H "Content-Type: application/json" \
     -d '{"stage_ids": [0, 1], "level": 2}'

Rapid Wake-up:

curl -X POST http://localhost:8000/v1/omni/wakeup \
     -H "Content-Type: application/json" \
     -d '{"stage_ids": [0, 1]}'

Scenario 4: Full Lifecycle Memory Audit & Functional Integrity

Objective: Audit the complete flow from Sleep to Wake-up followed by an Inference validation.

Check Initial State: Observe baseline VRAM usage.

Trigger Deep Sleep (Level 2):

curl -X POST http://localhost:8000/v1/omni/sleep \
     -H "Content-Type: application/json" \
     -d '{"stage_ids": [0], "level": 2}'

Wake-up Model:

curl -X POST http://localhost:8000/v1/omni/wakeup \
     -H "Content-Type: application/json" \
     -d '{"stage_ids": [0]}'

Verify Functional Integrity (Inference): Ensure the model still generates valid output after reloading weights.

curl -X POST http://localhost:8000/v1/images/generations \
     -H "Content-Type: application/json" \
     -d '{
       "prompt": "A huge swimming pool, with many people swimming.",
       "model": "ByteDance-Seed/BAGEL-7B-MoT",
       "response_format": "b64_json",
       "extra_body": {"sampling_params": {"num_inference_steps": 4, "seed": 42}}
     }' > post.json

3. API Reference

Methods

Method Arguments Return Type Description
sleep stage_ids: List[int], level: int List[OmniACK] Triggers hibernation for specified stages.
wake_up stage_ids: List[int] List[OmniACK] Reloads weights and re-maps memory.
pause_generation mode: str, stage_ids: List[int] None Stops admission; with mode="keep" also stops diffusion schedulers and returns after their ACK.
resume_generation stage_ids: List[int] None Reopens paused schedulers, then admission.

OmniACK Dataclass Fields

Field Type Description
task_id str Unique identifier for the operation.
status str SUCCESS or ERROR.
stage_id int The ID of the stage that responded.
rank int The rank ID within the Tensor Parallel group.
freed_bytes int Actual amount of physical VRAM reclaimed.
metadata dict Additional platform-specific metrics.

Metadata Field Analysis The metadata field is a dynamic dictionary containing hardware-specific telemetry and audit data, primarily used for verifying memory reclamation on various backends (e.g., AMD ROCm, NVIDIA CUDA).

"metadata": {
    "source": "Platform_AMD_Instinct_MI300X",
    "total_freed_gib": "78.57",
    "rank_residual_gib": "2.07"
}

Core Utility

VRAM Reclamation Audit (total_freed_gib): Converts raw freed_bytes into human-readable GiB. It serves as the primary metric to verify that Level 2 sleep has successfully purged model weights from VRAM.

Residual & Fragmentation Monitoring (rank_residual_gib): Reports the remaining VRAM footprint after memory de-mapping. A low residual value (e.g., 2.07 GiB) confirms a successful "clean" state, ensuring the device is ready for high-memory co-located tasks like training or diffusion pipelines.

Backend Traceability (source): Identifies the underlying hardware driver or audit source. This is critical for debugging synchronization issues in multi-stage, distributed environments.

Performance Analytics (Roadmap): Future updates will include latency_ms (context-switch overhead) and cuda_graph_recalled (graph engine status) to optimize performance in high-frequency sleep/wake scenarios.