Files
wehub-resource-sync eec33d25b2
pre-commit / pre-commit (push) Failing after 1s
Build Wheel / build (3.11) (push) Failing after 1s
Build Wheel / build (3.12) (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:29:08 +08:00

151 lines
5.8 KiB
Python

import numpy as np
import pytest
import torch
from vllm.distributed.parallel_state import cleanup_dist_env_and_memory
from tests.diffusion.offloader.test_diffusion_cpu_offload import (
_GATED_MODELS,
_skip_if_gated_repo_inaccessible,
)
from tests.helpers.env import DeviceMemoryMonitor
from tests.helpers.runtime import OmniRunner
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
from vllm_omni.platforms import current_omni_platform
AUDIO_MODEL = {
"stabilityai/stable-audio-open-1.0": {"cuda": 1500, "rocm": 1500},
}
IMAGE_VIDEO_MODELS = {
"riverclouds/qwen_image_random": {"cuda": 4500, "rocm": None},
# "Wan-AI/Wan2.2-T2V-A14B-Diffusers": {"cuda": 45000, "rocm": None},
}
MODELS = {**AUDIO_MODEL, **IMAGE_VIDEO_MODELS}
AUDIO_MODEL_PARAMS = {
"runner_params": {},
"sampler_params": {},
}
IMAGE_VIDEO_MODELS_PARAMS = {
"runner_params": {"boundary_ratio": 0.875, "flow_shift": 5.0},
"sampler_params": {"height": 480, "width": 640, "num_frames": 5},
}
def check_audio_determinism(audio1, audio2, atol=1e-2):
device = current_omni_platform.device_type
if isinstance(audio1, np.ndarray):
audio1 = torch.from_numpy(audio1).to(device)
if isinstance(audio2, np.ndarray):
audio2 = torch.from_numpy(audio2).to(device)
if not torch.allclose(audio1, audio2, atol=atol):
diff = torch.abs(audio1 - audio2)
print(f"Max difference: {diff.max().item()}")
print(f"Mean difference: {diff.mean().item()}")
raise AssertionError(f"Audio outputs differ beyond tolerance atol={atol}")
return True
def run_inference(
model_name: str,
layerwise_offload: bool = False,
num_inference_steps: int = 3,
) -> float:
current_omni_platform.empty_cache()
device_index = current_omni_platform.current_device()
monitor = DeviceMemoryMonitor(device_index=device_index, interval=0.02)
monitor.start()
if model_name in AUDIO_MODEL:
params = AUDIO_MODEL_PARAMS
else:
params = IMAGE_VIDEO_MODELS_PARAMS
with OmniRunner(
model_name,
enable_layerwise_offload=layerwise_offload,
# TODO: we might want to add overlapped feature e2e tests
# cache_backend="cache_dit",
**params["runner_params"],
) as runner:
current_omni_platform.reset_peak_memory_stats()
# Refer to tests/e2e/offline_inference/test_wan22.py
# Use minimal settings for testing
output = runner.omni.generate(
"A cat sitting on a table",
OmniDiffusionSamplingParams(
generator=torch.Generator(device=current_omni_platform.device_type).manual_seed(42),
guidance_scale=1.0,
num_inference_steps=num_inference_steps,
**params["sampler_params"],
),
)
peak = monitor.peak_used_mb
monitor.stop()
return peak, output
@pytest.mark.parametrize("model_name", list(MODELS.keys()))
def test_layerwise_offload_diffusion_model(model_name: str):
"""Test that layerwise offloading reduces GPU memory usage.
This test verifies that layerwise offloading significantly reduces peak
GPU memory usage compared to loading the entire model on GPU. The layerwise
offloader keeps only a single transformer block on GPU at a time, with
prefetching for compute-memory overlap.
"""
if model_name in _GATED_MODELS:
_skip_if_gated_repo_inaccessible(model_name)
try:
# Run without layerwise offloading (baseline)
no_offload_peak_memory, output_no_offload = run_inference(model_name, layerwise_offload=False)
cleanup_dist_env_and_memory()
# Run with layerwise offloading (1 layer on device)
layerwise_offload_peak_memory, output_offload = run_inference(model_name, layerwise_offload=True)
cleanup_dist_env_and_memory()
except ValueError as exc:
# omni_snapshot_download wraps GatedRepoError in a ValueError.
# If the pre-flight guard above did not catch it (e.g. partial
# HF_TOKEN where config.json is accessible but weight shards are
# blocked), skip instead of failing.
if "Access to model" in str(exc) and "is restricted" in str(exc):
pytest.skip(
f"Skipping: gated HF repo {model_name!r} inaccessible "
f"({exc}). See docs/contributing/ci/hf_credentials.md."
)
pytest.fail(f"Inference failed: {exc}")
except Exception:
pytest.fail("Inference failed")
print(f"Layerwise offload peak memory (1 GPU layer): {layerwise_offload_peak_memory} MB")
print(f"No offload peak memory: {no_offload_peak_memory} MB")
if model_name == "stabilityai/stable-audio-open-1.0":
audio_offload = output_offload[0].request_output.multimodal_output.get("audio")
audio_no_offload = output_no_offload[0].request_output.multimodal_output.get("audio")
# Match the sibling cpu-offload test's tolerance: layerwise offload moves
# blocks across the PCIe bus on a side stream, which can perturb cuBLAS
# algorithm selection and produce ~ULP-level drift larger than 1e-3.
check_audio_determinism(audio_offload, audio_no_offload, atol=1e-2)
is_rocm = torch.version.hip is not None
platform = "rocm" if is_rocm else "cuda"
expected_saved_memory = MODELS[model_name][platform]
if expected_saved_memory is None:
pytest.skip(f"Threshold not defined for {platform} on {model_name}")
# Verify that layerwise offloading significantly reduces memory usage
# Passes only if the actual savings exceeds the expected savings
assert layerwise_offload_peak_memory + expected_saved_memory < no_offload_peak_memory, (
f"Layerwise offload peak memory {layerwise_offload_peak_memory} MB "
f"should be significantly less than no offload peak memory {no_offload_peak_memory} MB"
)