Refactor cache init logic (#13800)

This commit is contained in:
Liangsheng Yin
2025-11-24 11:41:46 +08:00
committed by GitHub
parent 75222bfed9
commit b2f7b08c49
17 changed files with 307 additions and 419 deletions
-1
View File
@@ -358,7 +358,6 @@ jobs:
runs-on: 1-gpu-runner runs-on: 1-gpu-runner
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 5
matrix: matrix:
part: [0, 1] part: [0, 1]
steps: steps:
+1
View File
@@ -282,6 +282,7 @@ class Envs:
SGLANG_ENABLE_OVERLAP_PLAN_STREAM = EnvBool(False) SGLANG_ENABLE_OVERLAP_PLAN_STREAM = EnvBool(False)
# VLM # VLM
SGLANG_VLM_CACHE_SIZE_MB = EnvInt(100)
SGLANG_IMAGE_MAX_PIXELS = EnvInt(16384 * 28 * 28) SGLANG_IMAGE_MAX_PIXELS = EnvInt(16384 * 28 * 28)
SGLANG_RESIZE_RESAMPLE = EnvStr("") SGLANG_RESIZE_RESAMPLE = EnvStr("")
@@ -95,12 +95,7 @@ class SchedulePolicy:
self.schedule_low_priority_values_first = schedule_low_priority_values_first self.schedule_low_priority_values_first = schedule_low_priority_values_first
# It is used to find the matching prefix for in-batch prefix caching. # It is used to find the matching prefix for in-batch prefix caching.
self.waiting_queue_radix_tree = RadixCache( self.waiting_queue_radix_tree = RadixCache.create_simulated()
req_to_token_pool=None,
token_to_kv_pool_allocator=None,
page_size=1,
disable=False,
)
def calc_priority(self, waiting_queue: List[Req]) -> bool: def calc_priority(self, waiting_queue: List[Req]) -> bool:
if self.policy == CacheAgnosticPolicy.FCFS: if self.policy == CacheAgnosticPolicy.FCFS:
+43 -86
View File
@@ -148,12 +148,9 @@ from sglang.srt.managers.scheduler_update_weights_mixin import (
) )
from sglang.srt.managers.session_controller import Session from sglang.srt.managers.session_controller import Session
from sglang.srt.managers.utils import GenerationBatchResult, validate_input_length from sglang.srt.managers.utils import GenerationBatchResult, validate_input_length
from sglang.srt.mem_cache.chunk_cache import ChunkCache, SWAChunkCache from sglang.srt.mem_cache.cache_init_params import CacheInitParams
from sglang.srt.mem_cache.common import release_kv_cache from sglang.srt.mem_cache.common import release_kv_cache
from sglang.srt.mem_cache.hiradix_cache import HiRadixCache
from sglang.srt.mem_cache.mamba_radix_cache import MambaRadixCache
from sglang.srt.mem_cache.radix_cache import RadixCache from sglang.srt.mem_cache.radix_cache import RadixCache
from sglang.srt.mem_cache.swa_radix_cache import SWARadixCache
from sglang.srt.model_executor.forward_batch_info import ForwardMode from sglang.srt.model_executor.forward_batch_info import ForwardMode
from sglang.srt.multiplex.multiplexing_mixin import SchedulerMultiplexMixin from sglang.srt.multiplex.multiplexing_mixin import SchedulerMultiplexMixin
from sglang.srt.parser.reasoning_parser import ReasoningParser from sglang.srt.parser.reasoning_parser import ReasoningParser
@@ -419,8 +416,8 @@ class Scheduler(
# Init metrics stats # Init metrics stats
self.init_metrics(tp_rank, pp_rank, dp_rank) self.init_metrics(tp_rank, pp_rank, dp_rank)
# Init memory pool and cache # Init cache using the existing memory pool
self.init_memory_pool_and_cache() self.init_cache_with_memory_pool()
# Init running status # Init running status
self.waiting_queue: List[Req] = [] self.waiting_queue: List[Req] = []
@@ -693,117 +690,81 @@ class Scheduler(
reasoning_parser.detector.think_end_token, add_special_tokens=False reasoning_parser.detector.think_end_token, add_special_tokens=False
)[0] )[0]
def init_memory_pool_and_cache(self): def init_cache_with_memory_pool(self):
server_args = self.server_args server_args = self.server_args
self.req_to_token_pool, self.token_to_kv_pool_allocator = ( self.req_to_token_pool, self.token_to_kv_pool_allocator = (
self.tp_worker.get_memory_pool() self.tp_worker.get_memory_pool()
) )
params = CacheInitParams(
disable=server_args.disable_radix_cache,
req_to_token_pool=self.req_to_token_pool,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator,
page_size=self.page_size,
is_eagle=self.spec_algorithm.is_eagle(),
tp_cache_group=(
self.attn_tp_cpu_group
if self.server_args.enable_dp_attention
else self.tp_cpu_group
),
eviction_policy=server_args.radix_eviction_policy,
enable_metrics=self.enable_metrics,
enable_kv_cache_events=self.enable_kv_cache_events,
)
if ( if (
server_args.chunked_prefill_size is not None server_args.chunked_prefill_size is not None
and server_args.disable_radix_cache and server_args.disable_radix_cache
): ):
if self.is_hybrid: if not self.is_hybrid:
ChunkCacheClass = SWAChunkCache from sglang.srt.mem_cache.chunk_cache import ChunkCache
self.tree_cache = ChunkCache(params)
else: else:
ChunkCacheClass = ChunkCache
self.tree_cache = ChunkCacheClass( from sglang.srt.mem_cache.chunk_cache import SWAChunkCache
req_to_token_pool=self.req_to_token_pool,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator, self.tree_cache = SWAChunkCache(params)
page_size=self.page_size,
)
else: else:
if envs.SGLANG_EXPERIMENTAL_CPP_RADIX_TREE.get(): if envs.SGLANG_EXPERIMENTAL_CPP_RADIX_TREE.get():
# lazy import to avoid JIT overhead # lazy import to avoid JIT overhead
from sglang.srt.mem_cache.radix_cache_cpp import RadixCacheCpp from sglang.srt.mem_cache.radix_cache_cpp import RadixCacheCpp
logger.info("Using experimental C++ radix tree implementation.") logger.info("Using experimental C++ radix tree implementation.")
self.tree_cache = RadixCacheCpp( self.tree_cache = RadixCacheCpp(params=params, server_args=server_args)
disable=False,
use_hicache=self.enable_hierarchical_cache,
req_to_token_pool=self.req_to_token_pool,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator,
tp_cache_group=self.tp_cpu_group,
page_size=self.page_size,
hicache_ratio=server_args.hicache_ratio,
hicache_size=server_args.hicache_size,
hicache_write_policy=server_args.hicache_write_policy,
enable_metrics=self.enable_metrics,
enable_kv_cache_events=self.enable_kv_cache_events,
)
elif self.enable_hierarchical_cache: elif self.enable_hierarchical_cache:
self.tree_cache = HiRadixCache( from sglang.srt.mem_cache.hiradix_cache import HiRadixCache
req_to_token_pool=self.req_to_token_pool,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator, self.tree_cache = HiRadixCache(params=params, server_args=server_args)
tp_cache_group=(
self.attn_tp_cpu_group
if self.server_args.enable_dp_attention
else self.tp_cpu_group
),
page_size=self.page_size,
eviction_policy=server_args.radix_eviction_policy,
hicache_ratio=server_args.hicache_ratio,
hicache_size=server_args.hicache_size,
hicache_write_policy=server_args.hicache_write_policy,
hicache_io_backend=server_args.hicache_io_backend,
hicache_mem_layout=server_args.hicache_mem_layout,
enable_metrics=self.enable_metrics,
hicache_storage_backend=server_args.hicache_storage_backend,
hicache_storage_prefetch_policy=server_args.hicache_storage_prefetch_policy,
model_name=server_args.served_model_name,
storage_backend_extra_config=server_args.hicache_storage_backend_extra_config,
is_eagle=self.spec_algorithm.is_eagle(),
)
self.tp_worker.register_hicache_layer_transfer_counter( self.tp_worker.register_hicache_layer_transfer_counter(
self.tree_cache.cache_controller.layer_done_counter self.tree_cache.cache_controller.layer_done_counter
) )
elif self.is_hybrid: elif self.is_hybrid:
from sglang.srt.mem_cache.swa_radix_cache import SWARadixCache
self.tree_cache = SWARadixCache( self.tree_cache = SWARadixCache(
req_to_token_pool=self.req_to_token_pool, params=params, sliding_window_size=self.sliding_window_size
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator,
sliding_window_size=self.sliding_window_size,
page_size=self.page_size,
disable=server_args.disable_radix_cache,
is_eagle=self.spec_algorithm.is_eagle(),
enable_metrics=self.enable_metrics,
) )
elif self.is_hybrid_gdn: elif self.is_hybrid_gdn:
self.tree_cache = MambaRadixCache( from sglang.srt.mem_cache.mamba_radix_cache import MambaRadixCache
req_to_token_pool=self.req_to_token_pool,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator, self.tree_cache = MambaRadixCache(params)
page_size=self.page_size,
disable=server_args.disable_radix_cache,
enable_metrics=self.enable_metrics,
)
elif server_args.enable_lmcache: elif server_args.enable_lmcache:
from sglang.srt.mem_cache.storage.lmcache.lmc_radix_cache import ( from sglang.srt.mem_cache.storage.lmcache.lmc_radix_cache import (
LMCRadixCache, LMCRadixCache,
) )
self.tree_cache = LMCRadixCache( self.tree_cache = LMCRadixCache(
req_to_token_pool=self.req_to_token_pool, params=params,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator,
page_size=self.page_size,
disable=server_args.disable_radix_cache,
enable_metrics=self.enable_metrics,
model_config=self.model_config, model_config=self.model_config,
tp_size=self.tp_size, tp_size=self.tp_size,
rank=self.tp_rank, rank=self.tp_rank,
tp_group=self.tp_group, tp_group=self.tp_group,
eviction_policy=server_args.radix_eviction_policy,
) )
else: else:
self.tree_cache = RadixCache( self.tree_cache = RadixCache(params)
req_to_token_pool=self.req_to_token_pool,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator,
page_size=self.page_size,
disable=server_args.disable_radix_cache,
enable_metrics=self.enable_metrics,
enable_kv_cache_events=self.enable_kv_cache_events,
eviction_policy=server_args.radix_eviction_policy,
is_eagle=self.spec_algorithm.is_eagle(),
)
if ( if (
server_args.disaggregation_mode == "decode" server_args.disaggregation_mode == "decode"
@@ -812,11 +773,7 @@ class Scheduler(
self.decode_offload_manager = DecodeKVCacheOffloadManager( self.decode_offload_manager = DecodeKVCacheOffloadManager(
req_to_token_pool=self.req_to_token_pool, req_to_token_pool=self.req_to_token_pool,
token_to_kv_pool_allocator=self.token_to_kv_pool_allocator, token_to_kv_pool_allocator=self.token_to_kv_pool_allocator,
tp_group=( tp_group=params.tp_cache_group,
self.attn_tp_cpu_group
if self.server_args.enable_dp_attention
else self.tp_cpu_group
),
tree_cache=self.tree_cache, tree_cache=self.tree_cache,
server_args=self.server_args, server_args=self.server_args,
) )
@@ -835,7 +792,7 @@ class Scheduler(
) )
) )
embedding_cache_size = int(os.environ.get("SGLANG_VLM_CACHE_SIZE_MB", "100")) embedding_cache_size = envs.SGLANG_VLM_CACHE_SIZE_MB.get()
init_mm_embedding_cache(embedding_cache_size * 1024 * 1024) init_mm_embedding_cache(embedding_cache_size * 1024 * 1024)
def init_disaggregation(self): def init_disaggregation(self):
@@ -0,0 +1,26 @@
from __future__ import annotations
import dataclasses
from typing import TYPE_CHECKING, Optional
import torch
if TYPE_CHECKING:
from sglang.srt.mem_cache.allocator import BaseTokenToKVPoolAllocator
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
@dataclasses.dataclass
class CacheInitParams:
disable: bool
req_to_token_pool: ReqToTokenPool
token_to_kv_pool_allocator: BaseTokenToKVPoolAllocator
page_size: int
is_eagle: bool = False
tp_cache_group: Optional[torch.distributed.ProcessGroup] = None
eviction_policy: str = "lru"
disable_finished_insert: bool = False
enable_metrics: bool = False
enable_kv_cache_events: bool = False
+9 -22
View File
@@ -6,27 +6,19 @@ from typing import TYPE_CHECKING, Any, Optional
import torch import torch
from sglang.srt.mem_cache.allocator import ( from sglang.srt.mem_cache.allocator import SWATokenToKVPoolAllocator
BaseTokenToKVPoolAllocator,
SWATokenToKVPoolAllocator,
)
from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
if TYPE_CHECKING: if TYPE_CHECKING:
from sglang.srt.managers.schedule_batch import Req from sglang.srt.managers.schedule_batch import Req
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
class ChunkCache(BasePrefixCache): class ChunkCache(BasePrefixCache):
def __init__( def __init__(self, params: CacheInitParams):
self, self.req_to_token_pool = params.req_to_token_pool
req_to_token_pool: ReqToTokenPool, self.token_to_kv_pool_allocator = params.token_to_kv_pool_allocator
token_to_kv_pool_allocator: BaseTokenToKVPoolAllocator, self.page_size = params.page_size
page_size: int,
):
self.req_to_token_pool = req_to_token_pool
self.token_to_kv_pool_allocator = token_to_kv_pool_allocator
self.page_size = page_size
if self.token_to_kv_pool_allocator: if self.token_to_kv_pool_allocator:
self.device = self.token_to_kv_pool_allocator.device self.device = self.token_to_kv_pool_allocator.device
else: else:
@@ -89,14 +81,9 @@ class ChunkCache(BasePrefixCache):
class SWAChunkCache(ChunkCache): class SWAChunkCache(ChunkCache):
"""ChunkCache with support for hybrid KV cache operations.""" """ChunkCache with support for hybrid KV cache operations."""
def __init__( def __init__(self, params: CacheInitParams):
self, assert isinstance(params.token_to_kv_pool_allocator, SWATokenToKVPoolAllocator)
req_to_token_pool: ReqToTokenPool, super().__init__(params)
token_to_kv_pool_allocator: SWATokenToKVPoolAllocator,
page_size: int,
):
super().__init__(req_to_token_pool, token_to_kv_pool_allocator, page_size)
assert isinstance(token_to_kv_pool_allocator, SWATokenToKVPoolAllocator)
def evict_swa( def evict_swa(
self, self,
+40 -62
View File
@@ -1,20 +1,17 @@
from __future__ import annotations
import heapq import heapq
import json import json
import logging import logging
import threading import threading
import time import time
from typing import List, Optional from typing import TYPE_CHECKING, List, Optional
import torch import torch
from sglang.srt.managers.cache_controller import HiCacheController, PrefetchOperation from sglang.srt.managers.cache_controller import HiCacheController, PrefetchOperation
from sglang.srt.mem_cache.allocator import BaseTokenToKVPoolAllocator
from sglang.srt.mem_cache.base_prefix_cache import MatchResult from sglang.srt.mem_cache.base_prefix_cache import MatchResult
from sglang.srt.mem_cache.memory_pool import ( from sglang.srt.mem_cache.memory_pool import MHATokenToKVPool, MLATokenToKVPool
MHATokenToKVPool,
MLATokenToKVPool,
ReqToTokenPool,
)
from sglang.srt.mem_cache.memory_pool_host import ( from sglang.srt.mem_cache.memory_pool_host import (
MHATokenToKVPoolHost, MHATokenToKVPoolHost,
MLATokenToKVPoolHost, MLATokenToKVPoolHost,
@@ -22,62 +19,49 @@ from sglang.srt.mem_cache.memory_pool_host import (
from sglang.srt.mem_cache.radix_cache import RadixCache, RadixKey, TreeNode from sglang.srt.mem_cache.radix_cache import RadixCache, RadixKey, TreeNode
from sglang.srt.metrics.collector import StorageMetricsCollector from sglang.srt.metrics.collector import StorageMetricsCollector
if TYPE_CHECKING:
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
from sglang.srt.server_args import ServerArgs
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class HiRadixCache(RadixCache): class HiRadixCache(RadixCache):
def __init__( def __init__(self, params: CacheInitParams, server_args: ServerArgs):
self, if server_args.hicache_io_backend == "direct":
req_to_token_pool: ReqToTokenPool, # FIXME: move this logic into server_args parsing
token_to_kv_pool_allocator: BaseTokenToKVPoolAllocator, if server_args.hicache_mem_layout == "page_first":
tp_cache_group: torch.distributed.ProcessGroup, server_args.hicache_mem_layout = "page_first_direct"
page_size: int,
hicache_ratio: float,
hicache_size: int,
hicache_write_policy: str,
hicache_io_backend: str,
hicache_mem_layout: str,
enable_metrics: bool,
eviction_policy: str = "lru",
hicache_storage_backend: Optional[str] = None,
hicache_storage_prefetch_policy: Optional[str] = "best_effort",
model_name: Optional[str] = None,
storage_backend_extra_config: Optional[str] = None,
is_eagle: bool = False,
):
if hicache_io_backend == "direct":
if hicache_mem_layout == "page_first":
hicache_mem_layout = "page_first_direct"
logger.warning( logger.warning(
"Page first layout is not supported with direct IO backend, switching to page first direct layout" "Page first layout is not supported with direct IO backend, switching to page first direct layout"
) )
self.kv_cache = token_to_kv_pool_allocator.get_kvcache() self.page_size = params.page_size
self.kv_cache = params.token_to_kv_pool_allocator.get_kvcache()
if isinstance(self.kv_cache, MHATokenToKVPool): if isinstance(self.kv_cache, MHATokenToKVPool):
self.token_to_kv_pool_host = MHATokenToKVPoolHost( self.token_to_kv_pool_host = MHATokenToKVPoolHost(
self.kv_cache, self.kv_cache,
hicache_ratio, server_args.hicache_ratio,
hicache_size, server_args.hicache_size,
page_size, self.page_size,
hicache_mem_layout, server_args.hicache_mem_layout,
) )
elif isinstance(self.kv_cache, MLATokenToKVPool): elif isinstance(self.kv_cache, MLATokenToKVPool):
self.token_to_kv_pool_host = MLATokenToKVPoolHost( self.token_to_kv_pool_host = MLATokenToKVPoolHost(
self.kv_cache, self.kv_cache,
hicache_ratio, server_args.hicache_ratio,
hicache_size, server_args.hicache_size,
page_size, self.page_size,
hicache_mem_layout, server_args.hicache_mem_layout,
) )
else: else:
raise ValueError(f"HiRadixCache only supports MHA and MLA yet") raise ValueError(f"HiRadixCache only supports MHA and MLA yet")
self.tp_group = tp_cache_group self.tp_group = params.tp_cache_group
self.tp_world_size = torch.distributed.get_world_size(group=self.tp_group) self.tp_world_size = torch.distributed.get_world_size(group=self.tp_group)
self.enable_storage = hicache_storage_backend is not None self.enable_storage = server_args.hicache_storage_backend is not None
self.enable_storage_metrics = self.enable_storage and enable_metrics self.enable_storage_metrics = self.enable_storage and params.enable_metrics
( (
extra_config, extra_config,
@@ -85,35 +69,37 @@ class HiRadixCache(RadixCache):
prefetch_timeout_base, prefetch_timeout_base,
prefetch_timeout_per_ki_token, prefetch_timeout_per_ki_token,
hicache_storage_pass_prefix_keys, hicache_storage_pass_prefix_keys,
) = self._parse_storage_backend_extra_config(storage_backend_extra_config) ) = self._parse_storage_backend_extra_config(
server_args.hicache_storage_backend_extra_config
)
self.prefetch_threshold = prefetch_threshold self.prefetch_threshold = prefetch_threshold
self.prefetch_timeout_base = prefetch_timeout_base self.prefetch_timeout_base = prefetch_timeout_base
self.prefetch_timeout_per_page = ( self.prefetch_timeout_per_page = (
page_size / 1024 * prefetch_timeout_per_ki_token self.page_size / 1024 * prefetch_timeout_per_ki_token
) )
self.hicache_storage_pass_prefix_keys = hicache_storage_pass_prefix_keys self.hicache_storage_pass_prefix_keys = hicache_storage_pass_prefix_keys
# TODO: support more timeout check functions # TODO: support more timeout check functions
self.is_prefetch_timeout = self._prefetch_timeout_check_linear_func self.is_prefetch_timeout = self._prefetch_timeout_check_linear_func
self.prefetch_stop_policy = hicache_storage_prefetch_policy self.prefetch_stop_policy = server_args.hicache_storage_prefetch_policy
self.load_cache_event = threading.Event() self.load_cache_event = threading.Event()
self.cache_controller = HiCacheController( self.cache_controller = HiCacheController(
token_to_kv_pool_allocator, params.token_to_kv_pool_allocator,
self.token_to_kv_pool_host, self.token_to_kv_pool_host,
page_size, self.page_size,
self.tp_group, self.tp_group,
load_cache_event=self.load_cache_event, load_cache_event=self.load_cache_event,
write_policy=hicache_write_policy, write_policy=server_args.hicache_write_policy,
io_backend=hicache_io_backend, io_backend=server_args.hicache_io_backend,
storage_backend=hicache_storage_backend, storage_backend=server_args.hicache_storage_backend,
prefetch_threshold=self.prefetch_threshold, prefetch_threshold=self.prefetch_threshold,
model_name=model_name, model_name=server_args.served_model_name,
storage_backend_extra_config=extra_config, storage_backend_extra_config=extra_config,
) )
if self.enable_storage_metrics: if self.enable_storage_metrics:
# TODO: support pp # TODO: support pp
labels = { labels = {
"storage_backend": hicache_storage_backend, "storage_backend": server_args.hicache_storage_backend,
"tp_rank": self.cache_controller.tp_rank, "tp_rank": self.cache_controller.tp_rank,
"dp_rank": self.cache_controller.dp_rank, "dp_rank": self.cache_controller.dp_rank,
} }
@@ -128,19 +114,11 @@ class HiRadixCache(RadixCache):
self.ongoing_backup = {} self.ongoing_backup = {}
# todo: dynamically adjust the threshold # todo: dynamically adjust the threshold
self.write_through_threshold = ( self.write_through_threshold = (
1 if hicache_write_policy == "write_through" else 2 1 if server_args.hicache_write_policy == "write_through" else 2
) )
self.load_back_threshold = 10 self.load_back_threshold = 10
super().__init__( super().__init__(params=params)
req_to_token_pool,
token_to_kv_pool_allocator,
page_size,
disable=False,
eviction_policy=eviction_policy,
is_eagle=is_eagle,
enable_metrics=enable_metrics,
)
def _parse_storage_backend_extra_config( def _parse_storage_backend_extra_config(
self, storage_backend_extra_config: Optional[str] self, storage_backend_extra_config: Optional[str]
@@ -28,7 +28,6 @@ from numpy import float64
from sglang.srt.mem_cache.allocator import TokenToKVPoolAllocator from sglang.srt.mem_cache.allocator import TokenToKVPoolAllocator
from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult
from sglang.srt.mem_cache.memory_pool import HybridReqToTokenPool
from sglang.srt.mem_cache.radix_cache import ( from sglang.srt.mem_cache.radix_cache import (
RadixKey, RadixKey,
_key_match_page_size1, _key_match_page_size1,
@@ -37,6 +36,7 @@ from sglang.srt.mem_cache.radix_cache import (
if TYPE_CHECKING: if TYPE_CHECKING:
from sglang.srt.managers.schedule_batch import Req from sglang.srt.managers.schedule_batch import Req
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
import logging import logging
@@ -320,28 +320,23 @@ class LRUList:
class MambaRadixCache(BasePrefixCache): class MambaRadixCache(BasePrefixCache):
def __init__( def __init__(self, params: CacheInitParams):
self, assert isinstance(params.token_to_kv_pool_allocator, TokenToKVPoolAllocator)
req_to_token_pool: HybridReqToTokenPool, self.req_to_token_pool = params.req_to_token_pool
token_to_kv_pool_allocator: TokenToKVPoolAllocator, self.token_to_kv_pool_allocator = params.token_to_kv_pool_allocator
page_size: int,
disable: bool = False,
enable_metrics: bool = False,
):
assert isinstance(token_to_kv_pool_allocator, TokenToKVPoolAllocator)
self.req_to_token_pool = req_to_token_pool
self.token_to_kv_pool_allocator = token_to_kv_pool_allocator
assert page_size == 1, "Only support page_size=1 in mamba radix cache now." assert (
self.page_size = page_size params.page_size == 1
self.disable = disable ), "Only support page_size=1 in mamba radix cache now."
self.page_size = params.page_size
self.disable = params.disable
if self.token_to_kv_pool_allocator: if self.token_to_kv_pool_allocator:
self.device = self.token_to_kv_pool_allocator.device self.device = self.token_to_kv_pool_allocator.device
else: else:
self.device = torch.device("cpu") self.device = torch.device("cpu")
if enable_metrics: if params.enable_metrics:
self.init_metrics_collector() self.init_metrics_collector()
self.key_match_fn = _key_match_page_size1 self.key_match_fn = _key_match_page_size1
+42 -34
View File
@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
from sglang.srt.mem_cache.utils import convert_to_bigram_key from sglang.srt.mem_cache.utils import convert_to_bigram_key
""" """
@@ -26,7 +27,7 @@ import sys
import time import time
from collections import defaultdict from collections import defaultdict
from functools import lru_cache, partial from functools import lru_cache, partial
from typing import TYPE_CHECKING, Iterator, List, Optional, Tuple, Union from typing import TYPE_CHECKING, Any, Iterator, List, Optional, Tuple, Union
import torch import torch
@@ -35,7 +36,6 @@ from sglang.srt.disaggregation.kv_events import (
BlockRemoved, BlockRemoved,
BlockStored, BlockStored,
) )
from sglang.srt.mem_cache.allocator import BaseTokenToKVPoolAllocator
from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult
from sglang.srt.mem_cache.evict_policy import ( from sglang.srt.mem_cache.evict_policy import (
EvictionStrategy, EvictionStrategy,
@@ -46,7 +46,6 @@ from sglang.srt.mem_cache.evict_policy import (
MRUStrategy, MRUStrategy,
PriorityStrategy, PriorityStrategy,
) )
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
if TYPE_CHECKING: if TYPE_CHECKING:
from sglang.srt.managers.schedule_batch import Req from sglang.srt.managers.schedule_batch import Req
@@ -187,28 +186,19 @@ def get_child_key(key: RadixKey, page_size: int = 1):
class RadixCache(BasePrefixCache): class RadixCache(BasePrefixCache):
def __init__( def __init__(self, params: CacheInitParams):
self, self.disable = params.disable
req_to_token_pool: ReqToTokenPool, self.req_to_token_pool = params.req_to_token_pool
token_to_kv_pool_allocator: BaseTokenToKVPoolAllocator, self.token_to_kv_pool_allocator = params.token_to_kv_pool_allocator
page_size: int, self.page_size = params.page_size
disable: bool = False, self.enable_kv_cache_events = params.enable_kv_cache_events
enable_metrics: bool = False, self.is_eagle = params.is_eagle
enable_kv_cache_events: bool = False, self.disable_finished_insert = params.disable_finished_insert
eviction_policy: str = "lru", self.eviction_policy = params.eviction_policy.lower()
is_eagle: bool = False,
disable_finished_insert: bool = False,
):
self.req_to_token_pool = req_to_token_pool
self.token_to_kv_pool_allocator = token_to_kv_pool_allocator
self.page_size = page_size
self.disable = disable
self.enable_kv_cache_events = enable_kv_cache_events
self.kv_event_queue = []
self.is_eagle = is_eagle
self.disable_finished_insert = disable_finished_insert
if enable_metrics: self.kv_event_queue = []
if params.enable_metrics:
self.init_metrics_collector() self.init_metrics_collector()
if self.token_to_kv_pool_allocator: if self.token_to_kv_pool_allocator:
@@ -220,27 +210,45 @@ class RadixCache(BasePrefixCache):
self.key_match_fn = _key_match_page_size1 self.key_match_fn = _key_match_page_size1
self.get_child_key_fn = get_child_key self.get_child_key_fn = get_child_key
else: else:
self.key_match_fn = partial(_key_match_paged, page_size=page_size) self.key_match_fn = partial(_key_match_paged, page_size=self.page_size)
self.get_child_key_fn = partial(get_child_key, page_size=page_size) self.get_child_key_fn = partial(get_child_key, page_size=self.page_size)
if eviction_policy.lower() == "lru": if self.eviction_policy == "lru":
self.eviction_strategy: EvictionStrategy = LRUStrategy() self.eviction_strategy: EvictionStrategy = LRUStrategy()
elif eviction_policy.lower() == "lfu": elif self.eviction_policy == "lfu":
self.eviction_strategy: EvictionStrategy = LFUStrategy() self.eviction_strategy: EvictionStrategy = LFUStrategy()
elif eviction_policy.lower() == "fifo": elif self.eviction_policy == "fifo":
self.eviction_strategy: EvictionStrategy = FIFOStrategy() self.eviction_strategy: EvictionStrategy = FIFOStrategy()
elif eviction_policy.lower() == "mru": elif self.eviction_policy == "mru":
self.eviction_strategy: EvictionStrategy = MRUStrategy() self.eviction_strategy: EvictionStrategy = MRUStrategy()
elif eviction_policy.lower() == "filo": elif self.eviction_policy == "filo":
self.eviction_strategy: EvictionStrategy = FILOStrategy() self.eviction_strategy: EvictionStrategy = FILOStrategy()
elif eviction_policy.lower() == "priority": elif self.eviction_policy == "priority":
self.eviction_strategy: EvictionStrategy = PriorityStrategy() self.eviction_strategy: EvictionStrategy = PriorityStrategy()
else: else:
raise ValueError( raise ValueError(
f"Unknown eviction policy: {eviction_policy}. Supported policies: 'lru', 'lfu', 'fifo', 'mru', 'filo', 'priority'." f"Unknown eviction policy: {self.eviction_policy}. Supported policies: 'lru', 'lfu', 'fifo', 'mru', 'filo', 'priority'."
) )
self.reset() self.reset()
@classmethod
def create_simulated(
self,
disable: bool = False,
mock_allocator: Optional[Any] = None,
page_size: int = 1,
enable_kv_cache_events: bool = False,
) -> RadixCache:
"""Init a radix cache without memory pools for simulation purpose."""
params = CacheInitParams(
disable=disable,
req_to_token_pool=None,
token_to_kv_pool_allocator=mock_allocator,
page_size=page_size,
enable_kv_cache_events=enable_kv_cache_events,
)
return RadixCache(params)
##### Public API ##### ##### Public API #####
def reset(self): def reset(self):
@@ -743,7 +751,7 @@ class RadixCache(BasePrefixCache):
if __name__ == "__main__": if __name__ == "__main__":
tree = RadixCache(None, None, page_size=1, disable=False) tree = RadixCache.create_simulated()
# Example token id sequences (as lists of ints) # Example token id sequences (as lists of ints)
tree.insert(RadixKey(token_ids=[1, 2, 3], extra_key=None)) tree.insert(RadixKey(token_ids=[1, 2, 3], extra_key=None))
+46 -56
View File
@@ -6,24 +6,68 @@ from typing import TYPE_CHECKING, List, Set
import torch import torch
from sglang.srt.mem_cache.allocator import BaseTokenToKVPoolAllocator
from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult
from sglang.srt.mem_cache.cpp_radix_tree.radix_tree import ( from sglang.srt.mem_cache.cpp_radix_tree.radix_tree import (
IOHandle, IOHandle,
RadixTreeCpp, RadixTreeCpp,
TreeNodeCpp, TreeNodeCpp,
) )
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
from sglang.srt.mem_cache.radix_cache import RadixKey from sglang.srt.mem_cache.radix_cache import RadixKey
if TYPE_CHECKING: if TYPE_CHECKING:
from sglang.srt.managers.schedule_batch import Req from sglang.srt.managers.schedule_batch import Req
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
from sglang.srt.server_args import ServerArgs
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class RadixCacheCpp(BasePrefixCache): class RadixCacheCpp(BasePrefixCache):
def __init__(
self,
params: CacheInitParams,
server_args: ServerArgs,
enable_write_cancel: bool = False,
):
self.disable = params.disable
self.enable_write_cancel = enable_write_cancel
assert (
params.enable_kv_cache_events is False
), "HiRadixCache does not support kv cache events yet"
# record the nodes with ongoing write through
self.ongoing_write_through: Set[IOHandle] = set()
# record the node segments with ongoing load back
self.ongoing_load_back: Set[IOHandle] = set()
# todo: dynamically adjust the threshold
self.write_through_threshold = (
1 if server_args.hicache_write_policy == "write_through" else 2
)
self.device = self.token_to_kv_pool_allocator.device
self.token_to_kv_pool_allocator = params.token_to_kv_pool_allocator
self.req_to_token_pool = params.req_to_token_pool
self.page_size = params.page_size
self.kv_cache = self.token_to_kv_pool_allocator.get_kvcache()
self.tp_group = params.tp_cache_group
if params.enable_metrics:
self.init_metrics_collector()
if not server_args.enable_hierarchical_cache:
self.tree = RadixTreeCpp(
disabled=self.disable,
page_size=self.page_size,
host_size=None, # no host cache, this should be removed in the future
write_through_threshold=self.write_through_threshold,
)
self.cache_controller = None
return # early return if hicache is not used
raise NotImplementedError("Host cache is not supported yet")
def _merge_tensor(self, l: List[torch.Tensor]) -> torch.Tensor: def _merge_tensor(self, l: List[torch.Tensor]) -> torch.Tensor:
""" """
Merge a list of tensors into a single tensor. Merge a list of tensors into a single tensor.
@@ -39,60 +83,6 @@ class RadixCacheCpp(BasePrefixCache):
else: else:
return torch.cat(l) return torch.cat(l)
def __init__(
self,
disable: bool,
use_hicache: bool,
req_to_token_pool: ReqToTokenPool,
token_to_kv_pool_allocator: BaseTokenToKVPoolAllocator,
tp_cache_group: torch.distributed.ProcessGroup,
page_size: int,
hicache_ratio: float,
hicache_size: int,
hicache_write_policy: str,
enable_metrics: bool = False,
enable_kv_cache_events: bool = False,
hicache_oracle: bool = False,
enable_write_cancel: bool = False,
):
self.disable = disable
self.enable_write_cancel = enable_write_cancel
assert (
enable_kv_cache_events is False
), "HiRadixCache does not support kv cache events yet"
self.kv_cache = token_to_kv_pool_allocator.get_kvcache()
# record the nodes with ongoing write through
self.ongoing_write_through: Set[IOHandle] = set()
# record the node segments with ongoing load back
self.ongoing_load_back: Set[IOHandle] = set()
# todo: dynamically adjust the threshold
self.write_through_threshold = (
1 if hicache_write_policy == "write_through" else 2
)
self.device = token_to_kv_pool_allocator.device
self.token_to_kv_pool_allocator = token_to_kv_pool_allocator
self.req_to_token_pool = req_to_token_pool
self.page_size = page_size
self.tp_group = tp_cache_group
if enable_metrics:
self.init_metrics_collector()
if not use_hicache:
self.tree = RadixTreeCpp(
disabled=self.disable,
page_size=page_size,
host_size=None, # no host cache, this should be removed in the future
write_through_threshold=self.write_through_threshold,
)
self.cache_controller = None
return # early return if hicache is not used
raise NotImplementedError("Host cache is not supported yet")
def reset(self): def reset(self):
if self.cache_controller is not None: if self.cache_controller is not None:
# need to clear the acks before resetting the cache controller # need to clear the acks before resetting the cache controller
@@ -6,9 +6,7 @@ from typing import TYPE_CHECKING, Optional
import torch import torch
from sglang.srt.mem_cache.allocator import BaseTokenToKVPoolAllocator
from sglang.srt.mem_cache.base_prefix_cache import MatchResult from sglang.srt.mem_cache.base_prefix_cache import MatchResult
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
from sglang.srt.mem_cache.radix_cache import RadixCache, RadixKey, TreeNode from sglang.srt.mem_cache.radix_cache import RadixCache, RadixKey, TreeNode
try: try:
@@ -25,6 +23,7 @@ except ImportError as e:
if TYPE_CHECKING: if TYPE_CHECKING:
from sglang.srt.configs.model_config import ModelConfig from sglang.srt.configs.model_config import ModelConfig
from sglang.srt.managers.schedule_batch import Req from sglang.srt.managers.schedule_batch import Req
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -69,27 +68,13 @@ class LMCRadixCache(RadixCache):
def __init__( def __init__(
self, self,
req_to_token_pool: ReqToTokenPool, params: CacheInitParams,
token_to_kv_pool_allocator: BaseTokenToKVPoolAllocator,
page_size: int,
disable: bool = False,
enable_metrics: bool = False,
enable_kv_cache_events: bool = False,
model_config: Optional["ModelConfig"] = None, model_config: Optional["ModelConfig"] = None,
tp_size: int = 1, tp_size: int = 1,
rank: int = 0, rank: int = 0,
tp_group: Optional[torch.distributed.ProcessGroup] = None, tp_group: Optional[torch.distributed.ProcessGroup] = None,
eviction_policy: str = "lru",
): ):
super().__init__( super().__init__(params)
req_to_token_pool=req_to_token_pool,
token_to_kv_pool_allocator=token_to_kv_pool_allocator,
page_size=page_size,
disable=disable,
enable_metrics=enable_metrics,
enable_kv_cache_events=enable_kv_cache_events,
eviction_policy=eviction_policy,
)
kvcache = self.token_to_kv_pool_allocator.get_kvcache() kvcache = self.token_to_kv_pool_allocator.get_kvcache()
self.lmcache_connector = LMCacheLayerwiseConnector( self.lmcache_connector = LMCacheLayerwiseConnector(
@@ -271,12 +256,17 @@ class LMCRadixCache(RadixCache):
if __name__ == "__main__": if __name__ == "__main__":
cache = LMCRadixCache( from sglang.srt.mem_cache.cache_init_params import CacheInitParams
params = CacheInitParams(
req_to_token_pool=None, req_to_token_pool=None,
token_to_kv_pool_allocator=None, token_to_kv_pool_allocator=None,
page_size=1, page_size=1,
disable=False, disable=False,
enable_kv_cache_events=False, enable_kv_cache_events=False,
)
cache = LMCRadixCache(
params=params,
model_config=None, model_config=None,
tp_size=1, tp_size=1,
rank=0, rank=0,
+12 -21
View File
@@ -30,7 +30,7 @@ from numpy import float64
from sglang.srt.mem_cache.allocator import SWATokenToKVPoolAllocator from sglang.srt.mem_cache.allocator import SWATokenToKVPoolAllocator
from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache, MatchResult
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool from sglang.srt.mem_cache.cache_init_params import CacheInitParams
from sglang.srt.mem_cache.radix_cache import ( from sglang.srt.mem_cache.radix_cache import (
RadixKey, RadixKey,
_key_match_page_size1, _key_match_page_size1,
@@ -329,22 +329,13 @@ class LRUList:
class SWARadixCache(BasePrefixCache): class SWARadixCache(BasePrefixCache):
def __init__( def __init__(self, params: CacheInitParams, sliding_window_size: int):
self, assert isinstance(params.token_to_kv_pool_allocator, SWATokenToKVPoolAllocator)
req_to_token_pool: ReqToTokenPool, self.req_to_token_pool = params.req_to_token_pool
token_to_kv_pool_allocator: SWATokenToKVPoolAllocator, self.token_to_kv_pool_allocator = params.token_to_kv_pool_allocator
sliding_window_size: int, self.page_size = params.page_size
page_size: int, self.disable = params.disable
disable: bool = False, self.is_eagle = params.is_eagle
is_eagle: bool = False,
enable_metrics: bool = False,
):
assert isinstance(token_to_kv_pool_allocator, SWATokenToKVPoolAllocator)
self.req_to_token_pool = req_to_token_pool
self.token_to_kv_pool_allocator = token_to_kv_pool_allocator
self.page_size = page_size
self.disable = disable
self.is_eagle = is_eagle
if self.token_to_kv_pool_allocator: if self.token_to_kv_pool_allocator:
self.device = self.token_to_kv_pool_allocator.device self.device = self.token_to_kv_pool_allocator.device
@@ -355,15 +346,15 @@ class SWARadixCache(BasePrefixCache):
self.key_match_fn = _key_match_page_size1 self.key_match_fn = _key_match_page_size1
self.get_child_key_fn = get_child_key self.get_child_key_fn = get_child_key
else: else:
self.key_match_fn = partial(_key_match_paged, page_size=page_size) self.key_match_fn = partial(_key_match_paged, page_size=self.page_size)
self.get_child_key_fn = partial(get_child_key, page_size=page_size) self.get_child_key_fn = partial(get_child_key, page_size=self.page_size)
if is_eagle: if self.is_eagle:
self.key_convert_fn = convert_to_bigram_key self.key_convert_fn = convert_to_bigram_key
else: else:
self.key_convert_fn = lambda key: key self.key_convert_fn = lambda key: key
if enable_metrics: if params.enable_metrics:
self.init_metrics_collector() self.init_metrics_collector()
self.sliding_window_size = sliding_window_size self.sliding_window_size = sliding_window_size
+8 -4
View File
@@ -56,6 +56,7 @@ from json import JSONDecodeError
from multiprocessing.reduction import ForkingPickler from multiprocessing.reduction import ForkingPickler
from pathlib import Path from pathlib import Path
from typing import ( from typing import (
TYPE_CHECKING,
Any, Any,
Callable, Callable,
Dict, Dict,
@@ -94,6 +95,9 @@ from typing_extensions import Literal
from sglang.srt.environ import envs from sglang.srt.environ import envs
from sglang.srt.metrics.func_timer import enable_func_timer from sglang.srt.metrics.func_timer import enable_func_timer
if TYPE_CHECKING:
from sglang.srt.server_args import ServerArgs
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
show_time_cost = False show_time_cost = False
@@ -2754,7 +2758,7 @@ class Withable(Generic[T]):
self._value = None self._value = None
def require_mlp_tp_gather(server_args): def require_mlp_tp_gather(server_args: ServerArgs):
""" """
Check if the input of MLP is obtained by all-gather rather than all-reduce. This only happens when each MLP TP group contains multiple attention DP groups. Check if the input of MLP is obtained by all-gather rather than all-reduce. This only happens when each MLP TP group contains multiple attention DP groups.
""" """
@@ -2777,7 +2781,7 @@ def require_mlp_tp_gather(server_args):
return False return False
def require_attn_tp_gather(server_args): def require_attn_tp_gather(server_args: ServerArgs):
""" """
Check if the input of attention is scattered. Check if the input of attention is scattered.
""" """
@@ -2791,11 +2795,11 @@ def require_attn_tp_gather(server_args):
return False return False
def require_gathered_buffer(server_args): def require_gathered_buffer(server_args: ServerArgs):
return require_mlp_tp_gather(server_args) or require_attn_tp_gather(server_args) return require_mlp_tp_gather(server_args) or require_attn_tp_gather(server_args)
def require_mlp_sync(server_args): def require_mlp_sync(server_args: ServerArgs):
return server_args.enable_dp_attention or require_gathered_buffer(server_args) return server_args.enable_dp_attention or require_gathered_buffer(server_args)
+24 -25
View File
@@ -14,7 +14,7 @@ from sglang.test.test_utils import CustomTestCase
class TestSchedulePolicy(CustomTestCase): class TestSchedulePolicy(CustomTestCase):
def setUp(self): def setUp(self):
self.tree_cache = RadixCache(None, None, False) self.tree_cache = RadixCache.create_simulated()
def test_init_with_cache_aware_policy(self): def test_init_with_cache_aware_policy(self):
policy = SchedulePolicy( policy = SchedulePolicy(
@@ -47,10 +47,10 @@ class TestSchedulePolicy(CustomTestCase):
) )
def test_init_with_disabled_cache(self): def test_init_with_disabled_cache(self):
disabled_tree_cache = RadixCache(None, None, disable=True, page_size=1) tree_cache = RadixCache.create_simulated(disable=True)
policy = SchedulePolicy( policy = SchedulePolicy(
policy="lpm", policy="lpm",
tree_cache=disabled_tree_cache, tree_cache=tree_cache,
enable_hierarchical_cache=True, enable_hierarchical_cache=True,
enable_priority_scheduling=False, enable_priority_scheduling=False,
schedule_low_priority_values_first=False, schedule_low_priority_values_first=False,
@@ -58,7 +58,7 @@ class TestSchedulePolicy(CustomTestCase):
self.assertEqual(policy.policy, CacheAgnosticPolicy.FCFS) self.assertEqual(policy.policy, CacheAgnosticPolicy.FCFS)
def test_calc_priority_fcfs(self): def test_calc_priority_fcfs(self):
tree_cache = RadixCache(None, None, False) tree_cache = RadixCache.create_simulated()
waiting_queue = [ waiting_queue = [
Req(1, "a b", [1, 2], SamplingParams()), Req(1, "a b", [1, 2], SamplingParams()),
Req(3, "a b c", [1, 2, 3], SamplingParams()), Req(3, "a b c", [1, 2, 3], SamplingParams()),
@@ -79,16 +79,15 @@ class TestSchedulePolicy(CustomTestCase):
self.assertEqual(waiting_queue[2].rid, 2) self.assertEqual(waiting_queue[2].rid, 2)
def test_calc_priority_priority_enabled_fcfs_scheduling(self): def test_calc_priority_priority_enabled_fcfs_scheduling(self):
tree_cache = RadixCache(None, None, False) tree_cache = RadixCache.create_simulated()
r1 = Req(1, "a b", [1, 2], SamplingParams())
r2 = Req(3, "a b c", [1, 2, 3], SamplingParams())
r3 = Req(2, "a", [1], SamplingParams())
r1.priority, r1.time_stats.wait_queue_entry_time = 1, 1
r2.priority, r2.time_stats.wait_queue_entry_time = 0, 1
r3.priority, r3.time_stats.wait_queue_entry_time = 0, 0
waiting_queue = [ waiting_queue = [r1, r2, r3]
Req(1, "a b", [1, 2], SamplingParams()),
Req(3, "a b c", [1, 2, 3], SamplingParams()),
Req(2, "a", [1], SamplingParams()),
]
waiting_queue[0].priority, waiting_queue[0].queue_time_start = 1, 1
waiting_queue[1].priority, waiting_queue[1].queue_time_start = 0, 1
waiting_queue[2].priority, waiting_queue[2].queue_time_start = 0, 0
policy = SchedulePolicy( policy = SchedulePolicy(
policy="fcfs", policy="fcfs",
@@ -98,6 +97,7 @@ class TestSchedulePolicy(CustomTestCase):
schedule_low_priority_values_first=False, schedule_low_priority_values_first=False,
) )
policy.calc_priority(waiting_queue) policy.calc_priority(waiting_queue)
# Check if priority enabled fcfs ordering is applied. # Check if priority enabled fcfs ordering is applied.
self.assertEqual(waiting_queue[0].rid, 1) self.assertEqual(waiting_queue[0].rid, 1)
self.assertEqual(waiting_queue[1].rid, 2) self.assertEqual(waiting_queue[1].rid, 2)
@@ -106,16 +106,15 @@ class TestSchedulePolicy(CustomTestCase):
def test_calc_priority_priority_enabled_fcfs_scheduling_with_low_priority_values_first( def test_calc_priority_priority_enabled_fcfs_scheduling_with_low_priority_values_first(
self, self,
): ):
tree_cache = RadixCache(None, None, False) tree_cache = RadixCache.create_simulated()
r1 = Req(1, "a b", [1, 2], SamplingParams())
r2 = Req(3, "a b c", [1, 2, 3], SamplingParams())
r3 = Req(2, "a", [1], SamplingParams())
r1.priority, r1.time_stats.wait_queue_entry_time = -1, 1
r2.priority, r2.time_stats.wait_queue_entry_time = 0, 1
r3.priority, r3.time_stats.wait_queue_entry_time = 0, 0
waiting_queue = [ waiting_queue = [r1, r2, r3]
Req(1, "a b", [1, 2], SamplingParams()),
Req(3, "a b c", [1, 2, 3], SamplingParams()),
Req(2, "a", [1], SamplingParams()),
]
waiting_queue[0].priority, waiting_queue[0].queue_time_start = -1, 0
waiting_queue[1].priority, waiting_queue[1].queue_time_start = 0, 1
waiting_queue[2].priority, waiting_queue[2].queue_time_start = 0, 0
policy = SchedulePolicy( policy = SchedulePolicy(
policy="fcfs", policy="fcfs",
@@ -131,7 +130,7 @@ class TestSchedulePolicy(CustomTestCase):
self.assertEqual(waiting_queue[2].rid, 3) self.assertEqual(waiting_queue[2].rid, 3)
def test_calc_priority_longest_output_first_scheduling(self): def test_calc_priority_longest_output_first_scheduling(self):
tree_cache = RadixCache(None, None, False) tree_cache = RadixCache.create_simulated()
waiting_queue = [ waiting_queue = [
Req(1, "a b", [1, 2], SamplingParams(max_new_tokens=1000)), Req(1, "a b", [1, 2], SamplingParams(max_new_tokens=1000)),
@@ -153,7 +152,7 @@ class TestSchedulePolicy(CustomTestCase):
self.assertEqual(waiting_queue[2].rid, 3) self.assertEqual(waiting_queue[2].rid, 3)
def test_calc_priority_priority_enabled_longest_output_first_scheduling(self): def test_calc_priority_priority_enabled_longest_output_first_scheduling(self):
tree_cache = RadixCache(None, None, False) tree_cache = RadixCache.create_simulated()
waiting_queue = [ waiting_queue = [
Req(1, "a b", [1, 2], SamplingParams(max_new_tokens=1), priority=1), Req(1, "a b", [1, 2], SamplingParams(max_new_tokens=1), priority=1),
@@ -177,7 +176,7 @@ class TestSchedulePolicy(CustomTestCase):
def test_calc_priority_priority_enabled_longest_output_first_scheduling_with_low_priority_values_first( def test_calc_priority_priority_enabled_longest_output_first_scheduling_with_low_priority_values_first(
self, self,
): ):
tree_cache = RadixCache(None, None, False) tree_cache = RadixCache.create_simulated()
waiting_queue = [ waiting_queue = [
Req(1, "a b", [1, 2], SamplingParams(max_new_tokens=1), priority=0), Req(1, "a b", [1, 2], SamplingParams(max_new_tokens=1), priority=0),
+4 -2
View File
@@ -6,6 +6,7 @@ import torch
from sglang.srt.configs.mamba_utils import Mamba2CacheParams, Mamba2StateShape from sglang.srt.configs.mamba_utils import Mamba2CacheParams, Mamba2StateShape
from sglang.srt.managers.schedule_batch import Req from sglang.srt.managers.schedule_batch import Req
from sglang.srt.mem_cache.allocator import TokenToKVPoolAllocator from sglang.srt.mem_cache.allocator import TokenToKVPoolAllocator
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
from sglang.srt.mem_cache.mamba_radix_cache import MambaRadixCache from sglang.srt.mem_cache.mamba_radix_cache import MambaRadixCache
from sglang.srt.mem_cache.memory_pool import HybridLinearKVPool, HybridReqToTokenPool from sglang.srt.mem_cache.memory_pool import HybridLinearKVPool, HybridReqToTokenPool
from sglang.srt.mem_cache.radix_cache import RadixKey from sglang.srt.mem_cache.radix_cache import RadixKey
@@ -186,13 +187,14 @@ class TestMamba(unittest.TestCase):
kvcache=pool, kvcache=pool,
need_sort=False, need_sort=False,
) )
# setup radix cache params = CacheInitParams(
tree = MambaRadixCache(
req_to_token_pool=req_to_token_pool, req_to_token_pool=req_to_token_pool,
token_to_kv_pool_allocator=allocator, token_to_kv_pool_allocator=allocator,
page_size=1, page_size=1,
disable=False, disable=False,
) )
# setup radix cache
tree = MambaRadixCache(params=params)
def make_dummy_req(): def make_dummy_req():
sampling_params = SamplingParams( sampling_params = SamplingParams(
+17 -56
View File
@@ -240,11 +240,9 @@ class TestRadixCache(unittest.TestCase):
with self.subTest( with self.subTest(
page_size=page_size, disable=disable, enable_events=enable_events page_size=page_size, disable=disable, enable_events=enable_events
): ):
cache = RadixCache( cache = RadixCache.create_simulated(
req_to_token_pool=None,
token_to_kv_pool_allocator=None,
page_size=page_size,
disable=disable, disable=disable,
page_size=page_size,
enable_kv_cache_events=enable_events, enable_kv_cache_events=enable_events,
) )
@@ -257,9 +255,7 @@ class TestRadixCache(unittest.TestCase):
def test_reset(self): def test_reset(self):
"""Test reset method.""" """Test reset method."""
cache = RadixCache( cache = RadixCache.create_simulated()
req_to_token_pool=None, token_to_kv_pool_allocator=None, page_size=1
)
# Insert some data # Insert some data
cache.insert(RadixKey([1, 2, 3]), torch.tensor([10, 20, 30], dtype=torch.int64)) cache.insert(RadixKey([1, 2, 3]), torch.tensor([10, 20, 30], dtype=torch.int64))
@@ -275,12 +271,7 @@ class TestRadixCache(unittest.TestCase):
"""Test basic insert and match operations.""" """Test basic insert and match operations."""
for disable_cache in [False, True]: for disable_cache in [False, True]:
with self.subTest(disable_cache=disable_cache): with self.subTest(disable_cache=disable_cache):
cache = RadixCache( cache = RadixCache.create_simulated(disable=disable_cache)
req_to_token_pool=None,
token_to_kv_pool_allocator=None,
page_size=1,
disable=disable_cache,
)
key = RadixKey([1, 2, 3]) key = RadixKey([1, 2, 3])
value = torch.tensor([10, 20, 30], dtype=torch.int64) value = torch.tensor([10, 20, 30], dtype=torch.int64)
@@ -309,9 +300,7 @@ class TestRadixCache(unittest.TestCase):
def test_insert_with_none_value(self): def test_insert_with_none_value(self):
"""Test insert with None value (should use token_ids as list).""" """Test insert with None value (should use token_ids as list)."""
cache = RadixCache( cache = RadixCache.create_simulated()
req_to_token_pool=None, token_to_kv_pool_allocator=None, page_size=1
)
key = RadixKey([1, 2, 3]) key = RadixKey([1, 2, 3])
prefix_len = cache.insert(key, None) prefix_len = cache.insert(key, None)
@@ -322,9 +311,7 @@ class TestRadixCache(unittest.TestCase):
def test_total_size(self): def test_total_size(self):
"""Test total_size calculation.""" """Test total_size calculation."""
cache = RadixCache( cache = RadixCache.create_simulated()
req_to_token_pool=None, token_to_kv_pool_allocator=None, page_size=1
)
self.assertEqual(cache.total_size(), 0) self.assertEqual(cache.total_size(), 0)
@@ -344,11 +331,8 @@ class TestRadixCache(unittest.TestCase):
for page_size, enable_events in test_cases: for page_size, enable_events in test_cases:
with self.subTest(page_size=page_size, enable_events=enable_events): with self.subTest(page_size=page_size, enable_events=enable_events):
cache = RadixCache( cache = RadixCache.create_simulated(
req_to_token_pool=None, page_size=page_size, enable_kv_cache_events=enable_events
token_to_kv_pool_allocator=None,
page_size=page_size,
enable_kv_cache_events=enable_events,
) )
# Insert data # Insert data
@@ -374,11 +358,8 @@ class TestRadixCache(unittest.TestCase):
mock_allocator = unittest.mock.Mock() mock_allocator = unittest.mock.Mock()
mock_allocator.device = torch.device("cpu") mock_allocator.device = torch.device("cpu")
cache = RadixCache( cache = RadixCache.create_simulated(
req_to_token_pool=None, mock_allocator=mock_allocator, enable_kv_cache_events=True
token_to_kv_pool_allocator=mock_allocator,
page_size=1,
enable_kv_cache_events=True,
) )
# Insert and then evict data # Insert and then evict data
@@ -400,9 +381,7 @@ class TestRadixCache(unittest.TestCase):
def test_extra_key_isolation(self): def test_extra_key_isolation(self):
"""Test that keys with different extra_key values are isolated.""" """Test that keys with different extra_key values are isolated."""
cache = RadixCache( cache = RadixCache.create_simulated()
req_to_token_pool=None, token_to_kv_pool_allocator=None, page_size=1
)
# Insert same token sequence with different extra keys # Insert same token sequence with different extra keys
cache.insert( cache.insert(
@@ -442,9 +421,7 @@ class TestRadixCache(unittest.TestCase):
def test_lock_ref_operations(self): def test_lock_ref_operations(self):
"""Test lock reference counting operations.""" """Test lock reference counting operations."""
cache = RadixCache( cache = RadixCache.create_simulated()
req_to_token_pool=None, token_to_kv_pool_allocator=None, page_size=1
)
# Insert sequence # Insert sequence
cache.insert(RadixKey([1, 2, 3]), torch.tensor([10, 20, 30], dtype=torch.int64)) cache.insert(RadixKey([1, 2, 3]), torch.tensor([10, 20, 30], dtype=torch.int64))
@@ -471,11 +448,7 @@ class TestRadixCache(unittest.TestCase):
mock_allocator = unittest.mock.Mock() mock_allocator = unittest.mock.Mock()
mock_allocator.device = torch.device("cpu") mock_allocator.device = torch.device("cpu")
cache = RadixCache( cache = RadixCache.create_simulated(mock_allocator=mock_allocator)
req_to_token_pool=None,
token_to_kv_pool_allocator=mock_allocator,
page_size=1,
)
# Insert sequences # Insert sequences
cache.insert(RadixKey([1, 2]), torch.tensor([10, 20], dtype=torch.int64)) cache.insert(RadixKey([1, 2]), torch.tensor([10, 20], dtype=torch.int64))
@@ -500,11 +473,7 @@ class TestRadixCache(unittest.TestCase):
for page_size, sequence_length in test_cases: for page_size, sequence_length in test_cases:
with self.subTest(page_size=page_size, sequence_length=sequence_length): with self.subTest(page_size=page_size, sequence_length=sequence_length):
cache = RadixCache( cache = RadixCache.create_simulated(page_size=page_size)
req_to_token_pool=None,
token_to_kv_pool_allocator=None,
page_size=page_size,
)
tokens = list(range(sequence_length)) tokens = list(range(sequence_length))
cache.insert(RadixKey(tokens), torch.tensor(tokens, dtype=torch.int64)) cache.insert(RadixKey(tokens), torch.tensor(tokens, dtype=torch.int64))
@@ -518,9 +487,7 @@ class TestRadixCache(unittest.TestCase):
def test_pretty_print_basic(self): def test_pretty_print_basic(self):
"""Test pretty_print produces output.""" """Test pretty_print produces output."""
cache = RadixCache( cache = RadixCache.create_simulated()
req_to_token_pool=None, token_to_kv_pool_allocator=None, page_size=1
)
cache.insert(RadixKey([1, 2, 3]), torch.tensor([10, 20, 30], dtype=torch.int64)) cache.insert(RadixKey([1, 2, 3]), torch.tensor([10, 20, 30], dtype=torch.int64))
@@ -532,9 +499,7 @@ class TestRadixCache(unittest.TestCase):
def test_all_values_flatten(self): def test_all_values_flatten(self):
"""Test all_values_flatten method.""" """Test all_values_flatten method."""
cache = RadixCache( cache = RadixCache.create_simulated()
req_to_token_pool=None, token_to_kv_pool_allocator=None, page_size=1
)
cache.insert(RadixKey([1, 2]), torch.tensor([10, 20], dtype=torch.int64)) cache.insert(RadixKey([1, 2]), torch.tensor([10, 20], dtype=torch.int64))
cache.insert(RadixKey([3, 4]), torch.tensor([30, 40], dtype=torch.int64)) cache.insert(RadixKey([3, 4]), torch.tensor([30, 40], dtype=torch.int64))
@@ -549,11 +514,7 @@ class TestRadixCache(unittest.TestCase):
"""Advanced prefix matching: splits inside nodes and across pages.""" """Advanced prefix matching: splits inside nodes and across pages."""
for page_size in [1, 2]: for page_size in [1, 2]:
with self.subTest(page_size=page_size): with self.subTest(page_size=page_size):
cache = RadixCache( cache = RadixCache.create_simulated(page_size=page_size)
req_to_token_pool=None,
token_to_kv_pool_allocator=None,
page_size=page_size,
)
# Insert a long sequence that will be split later. # Insert a long sequence that will be split later.
seq1 = [1, 2, 3, 4, 5, 6, 7, 8] seq1 = [1, 2, 3, 4, 5, 6, 7, 8]
+14 -9
View File
@@ -3,6 +3,7 @@ import unittest
import torch import torch
from sglang.srt.mem_cache.allocator import SWAKVPool, SWATokenToKVPoolAllocator from sglang.srt.mem_cache.allocator import SWAKVPool, SWATokenToKVPoolAllocator
from sglang.srt.mem_cache.cache_init_params import CacheInitParams
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
from sglang.srt.mem_cache.radix_cache import RadixKey from sglang.srt.mem_cache.radix_cache import RadixKey
from sglang.srt.mem_cache.swa_radix_cache import SWARadixCache from sglang.srt.mem_cache.swa_radix_cache import SWARadixCache
@@ -110,11 +111,13 @@ class TestSWA(unittest.TestCase):
) )
# setup radix cache # setup radix cache
tree = SWARadixCache( tree = SWARadixCache(
req_to_token_pool=req_to_token_pool, params=CacheInitParams(
token_to_kv_pool_allocator=allocator, req_to_token_pool=req_to_token_pool,
token_to_kv_pool_allocator=allocator,
disable=False,
page_size=1,
),
sliding_window_size=sliding_window_size, sliding_window_size=sliding_window_size,
page_size=1,
disable=False,
) )
# test # test
@@ -241,12 +244,14 @@ class TestSWA(unittest.TestCase):
) )
# setup radix cache # setup radix cache
tree = SWARadixCache( tree = SWARadixCache(
req_to_token_pool=req_to_token_pool, params=CacheInitParams(
token_to_kv_pool_allocator=allocator, req_to_token_pool=req_to_token_pool,
token_to_kv_pool_allocator=allocator,
page_size=1,
disable=False,
is_eagle=True,
),
sliding_window_size=sliding_window_size, sliding_window_size=sliding_window_size,
page_size=1,
disable=False,
is_eagle=True,
) )
# test # test