[Feature] Support JIT set kv cache (#16273)

This commit is contained in:
DarkSharpness
2026-01-11 09:34:09 +08:00
committed by GitHub
parent a2c2c09d7d
commit d112f6a25b
9 changed files with 516 additions and 33 deletions

View File

@@ -15,19 +15,6 @@ limitations under the License.
from __future__ import annotations
import dataclasses
from dataclasses import dataclass
from typing import List
from sglang.srt.configs.mamba_utils import BaseLinearStateParams
from sglang.srt.environ import envs
from sglang.srt.layers.attention.nsa import index_buf_accessor
from sglang.srt.layers.attention.nsa.quant_k_cache import (
quantize_k_cache,
quantize_k_cache_separate,
)
from sglang.srt.utils.torch_memory_saver_adapter import TorchMemorySaverAdapter
"""
Memory pool.
@@ -38,16 +25,26 @@ KVCache actually holds the physical kv cache.
"""
import abc
import dataclasses
import logging
from contextlib import contextmanager, nullcontext
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
import numpy as np
import torch
import triton
import triton.language as tl
from sglang.jit_kernel.kvcache import can_use_store_cache, store_cache
from sglang.srt.configs.mamba_utils import BaseLinearStateParams
from sglang.srt.constants import GPU_MEMORY_TYPE_KV_CACHE
from sglang.srt.environ import envs
from sglang.srt.layers.attention.nsa import index_buf_accessor
from sglang.srt.layers.attention.nsa.quant_k_cache import (
quantize_k_cache,
quantize_k_cache_separate,
)
from sglang.srt.layers.radix_attention import RadixAttention
from sglang.srt.mem_cache.utils import (
get_mla_kv_buffer_triton,
@@ -56,6 +53,10 @@ from sglang.srt.mem_cache.utils import (
set_mla_kv_scale_buffer_triton,
)
from sglang.srt.utils import is_cuda, is_npu, next_power_of_2
from sglang.srt.utils.custom_op import register_custom_op
from sglang.srt.utils.torch_memory_saver_adapter import TorchMemorySaverAdapter
store_cache = register_custom_op(store_cache, mutates_args=["k_cache", "v_cache"])
if TYPE_CHECKING:
from sglang.srt.managers.cache_controller import LayerDoneCounter
@@ -75,6 +76,43 @@ def get_tensor_size_bytes(t: Union[torch.Tensor, List[torch.Tensor]]):
return np.prod(t.shape) * t.dtype.itemsize
def _set_kv_buffer_impl(
k: torch.Tensor,
v: torch.Tensor,
k_cache: torch.Tensor,
v_cache: torch.Tensor,
indices: torch.Tensor,
row_dim: int, # head_num * head_dim
store_dtype: torch.dtype,
device_module: Any,
alt_stream: Optional[torch.cuda.Stream] = None,
same_kv_dim: bool = True,
) -> None:
row_bytes = row_dim * store_dtype.itemsize
if _is_cuda and same_kv_dim and can_use_store_cache(row_bytes):
return store_cache(
k.view(-1, row_dim),
v.view(-1, row_dim),
k_cache.view(-1, row_dim),
v_cache.view(-1, row_dim),
indices,
row_bytes=row_bytes,
)
from sglang.srt.model_executor.cuda_graph_runner import get_is_capture_mode
if get_is_capture_mode() and alt_stream is not None:
current_stream = device_module.current_stream()
alt_stream.wait_stream(current_stream)
k_cache[indices] = k
with device_module.stream(alt_stream):
v_cache[indices] = v
current_stream.wait_stream(alt_stream)
else: # fallback to naive implementation
k_cache[indices] = k
v_cache[indices] = v
class ReqToTokenPool:
"""A memory pool that maps a request to its token locations."""
@@ -661,6 +699,10 @@ class MHATokenToKVPool(KVCache):
self._finalize_allocation_log(size)
# for store_cache JIT kernel
self.row_dim = self.head_num * self.head_dim
self.same_kv_dim = self.head_dim == self.v_head_dim
def _init_kv_copy_and_warmup(self):
# Heuristics for KV copy tiling
_KV_COPY_STRIDE_THRESHOLD_LARGE = 8192
@@ -868,8 +910,6 @@ class MHATokenToKVPool(KVCache):
v_scale: Optional[float] = None,
layer_id_override: Optional[int] = None,
):
from sglang.srt.model_executor.cuda_graph_runner import get_is_capture_mode
if layer_id_override is not None:
layer_id = layer_id_override
else:
@@ -886,17 +926,18 @@ class MHATokenToKVPool(KVCache):
cache_k = cache_k.view(self.store_dtype)
cache_v = cache_v.view(self.store_dtype)
if get_is_capture_mode() and self.alt_stream is not None:
# Overlap the copy of K and V cache for small batch size
current_stream = self.device_module.current_stream()
self.alt_stream.wait_stream(current_stream)
self.k_buffer[layer_id - self.start_layer][loc] = cache_k
with self.device_module.stream(self.alt_stream):
self.v_buffer[layer_id - self.start_layer][loc] = cache_v
current_stream.wait_stream(self.alt_stream)
else:
self.k_buffer[layer_id - self.start_layer][loc] = cache_k
self.v_buffer[layer_id - self.start_layer][loc] = cache_v
_set_kv_buffer_impl(
cache_k,
cache_v,
self.k_buffer[layer_id - self.start_layer],
self.v_buffer[layer_id - self.start_layer],
loc,
row_dim=self.row_dim,
store_dtype=self.store_dtype,
device_module=self.device_module,
alt_stream=self.alt_stream,
same_kv_dim=self.same_kv_dim,
)
def move_kv_cache(self, tgt_loc: torch.Tensor, src_loc: torch.Tensor):
if envs.SGLANG_NATIVE_MOVE_KV_CACHE.get():