[HiCache][AMD] Add UMBP tiered DRAM + SSD L3 storage backend with hugepage host allocator (#25377)

Co-authored-by: TianDi101 ditian12@amd.com
Co-authored-by: Niko Ma nima@amd.com
Co-authored-by: Wu, Yutong yutong.wu@amd.com
Co-authored-by: figo fizhang@amd.com
Co-authored-by: AMD-yanfeiwang <yanfei.wang@amd.com>
Co-authored-by: Zhangheng <hzh0425@apache.org>
This commit is contained in:
inkcherry
2026-07-01 22:21:37 +08:00
committed by GitHub
co-authored by TianDi101 ditian12@amd.com Niko Ma nima@amd.com Wu, Yutong yutong.wu@amd.com figo fizhang@amd.com AMD-yanfeiwang Zhangheng
parent a0d9791810
commit 13dc5f2dc7
9 changed files with 2106 additions and 1 deletions
@@ -483,7 +483,7 @@ class HiCacheController:
if (
self.storage_backend_type
in ["hf3fs", "mooncake", "eic", "nixl", "simm"]
in ["hf3fs", "mooncake", "eic", "nixl", "simm", "mori"]
) or (
self.storage_backend_type == "dynamic"
and bool(self.storage_config.extra_config.get("interface_v1", 0))
@@ -40,6 +40,20 @@ def get_allocator_from_storage(allocator_type):
"Fallback to use default allocator."
)
return HostTensorAllocator()
elif allocator_type == "mori":
try:
from sglang.srt.mem_cache.storage.umbp.umbp_host_allocator import (
UMBPHostTensorAllocator,
)
return UMBPHostTensorAllocator()
except (ImportError, RuntimeError) as exc:
logger.warning(
"UMBPHostTensorAllocator unavailable (%s). "
"Falling back to torch.empty-based allocator.",
exc,
)
return HostTensorAllocator()
else:
return HostTensorAllocator()
@@ -185,6 +185,8 @@ class StorageBackendFactory:
return backend_class(storage_config, mem_pool_host)
elif backend_name == "simm":
return backend_class(storage_config, mem_pool_host)
elif backend_name == "mori":
return backend_class(storage_config, mem_pool_host)
else:
raise ValueError(f"Unknown built-in backend: {backend_name}")
@@ -229,3 +231,9 @@ StorageBackendFactory.register_backend(
"sglang.srt.mem_cache.storage.simm.hicache_simm",
"HiCacheSiMM",
)
StorageBackendFactory.register_backend(
"mori",
"sglang.srt.mem_cache.storage.umbp.umbp_store",
"UMBPStore",
)
@@ -0,0 +1,142 @@
import ctypes
import logging
import math
import os
from typing import Any, Dict
import torch
from sglang.srt.mem_cache.pool_host.common import HostTensorAllocator
logger = logging.getLogger(__name__)
def _bool_env(name: str, default: bool) -> bool:
raw = os.getenv(name)
if raw is None:
return default
return raw.strip().lower() in ("1", "true", "yes", "on")
def _int_env(name: str, default: int) -> int:
raw = os.getenv(name)
return int(raw) if raw is not None and raw != "" else default
class UMBPHostTensorAllocator(HostTensorAllocator):
"""Allocate the HiCache L2 host tensor from mori's UMBPHostMemAllocator."""
def __init__(self) -> None:
super().__init__()
try:
import mori.umbp as umbp_mod
except ImportError as exc:
raise RuntimeError(
"mori.umbp is not available. Build mori with BUILD_UMBP=ON "
"or fall back to the default torch host allocator."
) from exc
self._mod = umbp_mod
self._allocator = umbp_mod.UMBPHostMemAllocator()
self._use_hugepage = _bool_env("SGLANG_HICACHE_HOST_HUGEPAGE", True)
self._hugepage_size = _int_env(
"SGLANG_HICACHE_HOST_HUGEPAGE_SIZE", 2 * 1024 * 1024
)
self._numa_node = _int_env("SGLANG_HICACHE_HOST_NUMA_NODE", -1)
self._prefault = _bool_env("SGLANG_HICACHE_HOST_PREFAULT", True)
self._handles: Dict[int, Any] = {}
def allocate(
self, dims: tuple, dtype: torch.dtype, device: str = "cpu"
) -> torch.Tensor:
if device != "cpu":
raise ValueError(
"UMBPHostTensorAllocator only supports CPU host memory, "
f"got device={device}"
)
self.dims = dims
self.dtype = dtype
element_size = torch.empty((), dtype=dtype).element_size()
nbytes = math.prod(int(dim) for dim in dims) * element_size
requested_backing = (
self._mod.UMBPHostBufferBacking.AnonymousHugetlb
if self._use_hugepage
else self._mod.UMBPHostBufferBacking.Anonymous
)
handle = self._allocator.alloc(
nbytes,
requested_backing,
self._hugepage_size,
self._numa_node,
self._prefault,
)
if not handle:
raise RuntimeError(
f"UMBPHostMemAllocator.alloc({nbytes} bytes) failed "
f"(requested_backing={requested_backing}, "
f"numa_node={self._numa_node})."
)
self._handles[int(handle.ptr)] = handle
c_array = (ctypes.c_byte * nbytes).from_address(handle.ptr)
tensor = torch.frombuffer(c_array, dtype=torch.uint8, count=nbytes)
if dtype != torch.uint8:
tensor = tensor.view(dtype)
logger.info(
"UMBPHostTensorAllocator: allocated %.2f GB at 0x%x "
"requested_backing=%s actual_backing=%s actual_alignment=%d "
"mapped_size=%d numa_node=%d",
nbytes / 1e9,
handle.ptr,
requested_backing,
handle.actual_backing,
handle.actual_alignment,
handle.mapped_size,
self._numa_node,
)
if (
self._use_hugepage
and handle.actual_backing == self._mod.UMBPHostBufferBacking.Anonymous
):
logger.warning(
"UMBPHostTensorAllocator: requested AnonymousHugetlb backing "
"but kernel demoted to Anonymous (4 KiB pages). Check "
"vm.nr_hugepages and HugePages_Free in /proc/meminfo. "
"Performance and AINIC MR-size benefits will not apply."
)
return tensor.view(dims)
def mapped_size_for(self, ptr: int) -> int:
"""Actual mmap size for the allocation whose base address is *ptr*."""
handles = getattr(self, "_handles", None)
if handles is None:
return 0
h = handles.get(ptr)
return int(h.mapped_size) if h is not None else 0
@property
def mapped_size(self) -> int:
"""Largest mapped_size across all live allocations, or 0."""
handles = getattr(self, "_handles", None)
if not handles:
return 0
return max(int(h.mapped_size) for h in handles.values())
def __del__(self) -> None:
try:
handles = getattr(self, "_handles", None)
allocator = getattr(self, "_allocator", None)
if handles and allocator is not None:
for h in handles.values():
allocator.free(h)
self._handles.clear()
except Exception:
pass
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1923,6 +1923,7 @@ class ServerArgs:
"dynamic",
"eic",
"simm",
"mori",
],
),
] = None