Diffusers
Safetensors
zeyuren2002's picture
Add files using upload-large-folder tool
f2c12ee verified
"""
GPU memory utility helpers.
Shared cleanup and memory checking logic used by both the backend API and
the Gradio UI to keep memory-management behavior consistent.
"""
from __future__ import annotations
import gc
from typing import Any, Dict, Optional
import torch
def get_gpu_memory_info() -> Optional[Dict[str, Any]]:
"""Return a snapshot of current GPU memory usage or None if CUDA not available.
Keys in returned dict: total_gb, allocated_gb, reserved_gb, free_gb, utilization
"""
if not torch.cuda.is_available():
return None
try:
device = torch.cuda.current_device()
total_memory = torch.cuda.get_device_properties(device).total_memory
allocated_memory = torch.cuda.memory_allocated(device)
reserved_memory = torch.cuda.memory_reserved(device)
free_memory = total_memory - reserved_memory
return {
"total_gb": total_memory / 1024 ** 3,
"allocated_gb": allocated_memory / 1024 ** 3,
"reserved_gb": reserved_memory / 1024 ** 3,
"free_gb": free_memory / 1024 ** 3,
"utilization": (reserved_memory / total_memory) * 100,
}
except Exception:
return None
def cleanup_cuda_memory() -> None:
"""Perform a robust GPU cleanup sequence.
This includes synchronizing, emptying caches, collecting IPC handles and
running the Python garbage collector. Use this instead of a raw
``torch.cuda.empty_cache()`` where you need reliable freeing of GPU memory
between model loads or in error handling paths.
"""
try:
if torch.cuda.is_available():
mem_before = get_gpu_memory_info()
torch.cuda.synchronize()
torch.cuda.empty_cache()
# Collect cross-process cuda resources
try:
torch.cuda.ipc_collect()
except Exception:
# Older PyTorch versions or non-cuda devices may not support
# ipc_collect (no-op if not available)
pass
gc.collect()
mem_after = get_gpu_memory_info()
if mem_before and mem_after:
freed = mem_before["reserved_gb"] - mem_after["reserved_gb"]
print(
f"CUDA cleanup: freed {freed:.2f}GB, "
f"available: {mem_after['free_gb']:.2f}GB/{mem_after['total_gb']:.2f}GB"
)
else:
print("CUDA memory cleanup completed")
except Exception as e:
print(f"Warning: CUDA cleanup failed: {e}")
def check_memory_availability(required_gb: float = 2.0) -> tuple[bool, str]:
"""Return whether at least ``required_gb`` seems available on the current GPU.
The returned tuple is (is_available, message) with a human-friendly message.
"""
try:
if not torch.cuda.is_available():
return False, "CUDA is not available"
mem_info = get_gpu_memory_info()
if mem_info is None:
return True, "Cannot check memory, proceeding anyway"
if mem_info["free_gb"] < required_gb:
return (
False,
(
f"Insufficient GPU memory: {mem_info['free_gb']:.2f}GB available, "
f"{required_gb:.2f}GB required. Total: {mem_info['total_gb']:.2f}GB, "
f"Used: {mem_info['reserved_gb']:.2f}GB ({mem_info['utilization']:.1f}%)"
),
)
return (
True,
(
f"Memory check passed: {mem_info['free_gb']:.2f}GB available, "
f"{required_gb:.2f}GB required"
),
)
except Exception as e:
return True, f"Memory check failed: {e}, proceeding anyway"
def estimate_memory_requirement(num_images: int, process_res: int) -> float:
"""Heuristic estimate for memory usage (GB) based on image count and resolution.
This mirrors the simple policy used by the backend service so other code
(e.g., Gradio UI) can make consistent decisions when checking available
memory before loading a model or running inference.
Args:
num_images: Number of images to process.
process_res: Processing resolution.
Returns:
Estimated memory requirement in GB.
"""
base_memory = 2.0
per_image_memory = (process_res / 504) ** 2 * 0.5
total_memory = base_memory + (num_images * per_image_memory * 0.1)
return total_memory