from __future__ import annotations
import importlib
import logging
import threading
from contextlib import contextmanager
from typing import TYPE_CHECKING, Iterator
if TYPE_CHECKING:
import torch
from .arena import VmmArena
logger = logging.getLogger(__name__)
def _probe_extension():
try:
ext = importlib.import_module("modelexpress.vmm._alloc_ext")
return ext, True, None
except ImportError as e:
return None, False, e
_vmm_alloc_ext, ARENA_AVAILABLE, _import_error = _probe_extension()
_active_arena: "VmmArena | None" = None
_active_lock = threading.Lock()
_pluggable_alloc = None _mem_pool = None _callbacks_initialized = False
_init_lock = threading.Lock()
class ArenaUnavailableError(RuntimeError):
def _mx_malloc(size: int, device: int, stream: int) -> int:
del device, stream
arena = _active_arena
if arena is None:
logger.error("mx_malloc(%d) called with no active arena", size)
return 0
try:
return arena.malloc(int(size))
except Exception as e:
logger.error("arena.malloc(%d) failed: %s", size, e)
return 0
def _mx_free(ptr: int, size: int, device: int, stream: int) -> None:
del size, device, stream
arena = _active_arena
if arena is None:
logger.warning(
"mx_free(0x%x) called with no active arena, ignoring", ptr
)
return
try:
arena.free(int(ptr))
except Exception as e:
logger.error("arena.free(0x%x) failed: %s", ptr, e)
def _ensure_callbacks_initialized() -> None:
global _pluggable_alloc, _mem_pool, _callbacks_initialized
if _callbacks_initialized:
return
with _init_lock:
if _callbacks_initialized:
return
if not ARENA_AVAILABLE:
raise ArenaUnavailableError(
"_vmm_alloc_ext C extension is not available "
f"(import failed: {_import_error}). The arena allocator "
"fast path is disabled. Install a C++ compiler and reinstall "
"modelexpress, or unset MX_VMM_ARENA to use the pool-reg path."
)
cumem = _vmm_alloc_ext from torch.cuda import CUDAPluggableAllocator
from torch.cuda.memory import MemPool
_pluggable_alloc = CUDAPluggableAllocator(
cumem.__file__, "mx_malloc", "mx_free"
)
_mem_pool = MemPool(allocator=_pluggable_alloc.allocator())
_callbacks_initialized = True
logger.debug(
"CUDAPluggableAllocator + MemPool initialized for VmmArena hook"
)
def install_pluggable_allocator() -> None:
_ensure_callbacks_initialized()
@contextmanager
def use_arena(arena: "VmmArena", device: "torch.device | int") -> Iterator[None]:
global _active_arena
import torch
_ensure_callbacks_initialized()
with _active_lock:
if _active_arena is not None:
raise RuntimeError(
"another VmmArena is already active; use_arena does not "
"support nesting or concurrent activation"
)
_vmm_alloc_ext.set_active_arena(arena.capsule)
_active_arena = arena
try:
with torch.cuda.use_mem_pool(_mem_pool, device=device):
yield
finally:
with _active_lock:
_vmm_alloc_ext.set_active_arena(None)
_active_arena = None
def is_active() -> bool:
return _active_arena is not None
def active_arena() -> "VmmArena | None":
return _active_arena