[Kernel] Fuse KV-cache writes for asymmetric K/V (head_dim != v_head_dim) (#32813)

This commit is contained in:
Liangsheng Yin
2026-07-30 00:26:10 -07:00
committed by GitHub
parent 2625fdfe6b
commit c192145830
6 changed files with 511 additions and 62 deletions
+21 -7
View File
@@ -149,21 +149,26 @@ def _set_kv_buffer_impl(
device_module: Any,
size_limit: int,
alt_stream: Optional[torch.cuda.Stream] = None,
same_kv_dim: bool = True,
v_row_dim: Optional[int] = None, # head_num * v_head_dim; defaults to row_dim
) -> None:
v_row_dim = row_dim if v_row_dim is None else v_row_dim
row_bytes = row_dim * store_dtype.itemsize
if (_is_cuda or _is_hip) and same_kv_dim and can_use_store_cache(row_bytes):
v_row_bytes = v_row_dim * store_dtype.itemsize
if (_is_cuda or _is_hip) and can_use_store_cache(row_bytes, v_row_bytes):
return store_cache(
k.view(-1, row_dim),
v.view(-1, row_dim),
v.view(-1, v_row_dim),
k_cache.view(-1, row_dim),
v_cache.view(-1, row_dim),
v_cache.view(-1, v_row_dim),
indices,
row_bytes=row_bytes,
v_row_bytes=v_row_bytes,
size_limit=size_limit,
)
if _is_cpu and _cpu_has_amx_support:
# store_cache_cpu takes a single row_dim for both K and V, so it only serves
# equal-width rows; asymmetric KV falls through to the naive path below.
if _is_cpu and _cpu_has_amx_support and v_row_dim == row_dim:
return torch.ops.sgl_kernel.store_cache_cpu(
k,
v,
@@ -1819,7 +1824,7 @@ class MHATokenToKVPool(KVCache):
# 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
self.v_row_dim = self.head_num * self.v_head_dim
def _init_kv_copy_and_warmup(self):
# Zero-layer pool (e.g. all-SWA model's full sub-pool) has no buffers.
@@ -2386,7 +2391,7 @@ class MHATokenToKVPool(KVCache):
# dummy tokens write there); valid index range is [0, size + page_size).
size_limit=self.size + self.page_size,
alt_stream=self.alt_stream,
same_kv_dim=self.same_kv_dim,
v_row_dim=self.v_row_dim,
)
def _quantized_scales(self, global_layer_id: int, k_scale, v_scale):
@@ -2712,6 +2717,15 @@ class MHATokenToKVPool(KVCache):
)
return
# The tiled kernel takes one ROW_BYTES for both tensors, so an asymmetric V
# row would be written at K's width and bleed into the next slot. Only this
# path needs the gate; the non-CUDA branch above handles both widths.
if self.v_row_dim != self.row_dim:
raise NotImplementedError(
"prefix-valid commit requires equal-width K/V rows, got "
f"head_dim={self.head_dim} v_head_dim={self.v_head_dim}."
)
_set_kv_buffer_prefix_valid_impl(
cache_k,
cache_v,