[HiCache] refactor: hicache normalization flow and compatibility checks (#19669)

This commit is contained in:
shuwenn
2026-03-21 09:38:44 +08:00
committed by GitHub
parent 9419453713
commit 6c91590e1b
4 changed files with 184 additions and 46 deletions

View File

@@ -728,6 +728,10 @@ class HiCacheController:
return host_indices, device_indices.index_select(0, idx)
elif self.mem_pool_host.layout == "page_first_direct":
return host_indices, device_indices.cpu()
else:
raise ValueError(
f"Unsupported layout {self.mem_pool_host.layout!r} for io backend 'direct'"
)
elif self.io_backend == "kernel_ascend":
return host_indices, device_indices.cpu()
else:

View File

@@ -58,13 +58,6 @@ class HiRadixCache(RadixCache):
def __init__(self, params: CacheInitParams, server_args: ServerArgs):
self._enable_metrics_flag = params.enable_metrics
if server_args.hicache_io_backend == "direct":
# FIXME: move this logic into server_args parsing
if server_args.hicache_mem_layout == "page_first":
server_args.hicache_mem_layout = "page_first_direct"
logger.warning(
"Page first layout is not supported with direct IO backend, switching to page first direct layout"
)
if not server_args.disable_hicache_numa_detect:
bind_to_closest_numa_node_cuda()

View File

@@ -2786,56 +2786,103 @@ class ServerArgs:
)
def _handle_hicache(self):
"""Normalize hicache-related knobs into a valid runtime configuration.
Resolution order:
1) Layout <-> I/O compatibility for direct conflicts.
2) Storage <-> layout compatibility (may rewrite layout).
3) I/O <-> decode-attention compatibility (may rewrite I/O or decode backend).
4) Re-run step (1) if step (3) changed I/O backend.
"""
# Skip all normalization when neither hicache nor decode-offload path is active.
if not (
self.enable_hierarchical_cache
or self.disaggregation_decode_enable_offload_kvcache
):
return
# Step 1: Initial layout-io compatibility normalization.
self._resolve_layout_io_compatibility()
# Step 2: Storage-layout normalization without changing io backend.
self._resolve_storage_layout_compatibility()
# Step 3: IO-decode backend compatibility (may change io backend).
io_changed = self._resolve_io_decode_attention_compatibility()
# Step 4: Re-normalize layout after io backend changes.
if io_changed:
self._resolve_layout_io_compatibility()
def _resolve_layout_io_compatibility(self):
if (
self.hicache_mem_layout == "page_first_direct"
and self.hicache_io_backend == "kernel"
):
self.hicache_io_backend = "direct"
logger.warning(
"Kernel io backend does not support page first direct layout"
"Kernel io backend does not support page first direct layout, switching to direct io backend"
)
if (
self.enable_hierarchical_cache
or self.disaggregation_decode_enable_offload_kvcache
) and self.hicache_io_backend == "kernel":
# fix for the compatibility issue with FlashAttention3 decoding and HiCache kernel backend
# Only override when the *effective* decode backend would be FA3.
# Otherwise, respect the user's chosen attention backend (e.g., aiter on ROCm).
effective_decode_backend = (
self.decode_attention_backend
if self.decode_attention_backend is not None
else self.attention_backend
self.hicache_mem_layout == "page_first"
and self.hicache_io_backend == "direct"
):
self.hicache_mem_layout = "page_first_direct"
logger.warning(
"Page first layout is not supported with direct IO backend, switching to page first direct layout"
)
if effective_decode_backend == "fa3":
if self.decode_attention_backend is None:
# If decode backend wasn't explicitly set, pick a safe default that works with HiCache kernel IO.
if not self.use_mla_backend():
self.decode_attention_backend = (
"flashinfer" if is_flashinfer_available() else "triton"
)
else:
self.decode_attention_backend = (
"flashinfer" if is_sm100_supported() else "triton"
)
else:
# If user explicitly requested FA3 decode, fall back to direct IO.
self.hicache_io_backend = "direct"
logger.warning(
"FlashAttention3 decode backend is not compatible with hierarchical cache. "
"Setting hicache_io_backend to vanilla I/O, which may lead to suboptimal performance with small page sizes."
)
if self.hicache_storage_backend == "mooncake":
if self.hicache_mem_layout == "layer_first":
if self.hicache_io_backend == "direct":
self.hicache_mem_layout = "page_first_direct"
elif self.hicache_io_backend == "kernel":
self.hicache_mem_layout = "page_first"
logger.warning(
f"Mooncake storage backend does not support layer_first layout, "
f"switching to {self.hicache_mem_layout} layout for {self.hicache_io_backend} io backend"
)
def _resolve_storage_layout_compatibility(self):
if (
self.hicache_storage_backend != "mooncake"
or self.hicache_mem_layout != "layer_first"
):
return
if self.hicache_io_backend == "direct":
new_layout = "page_first_direct"
elif self.hicache_io_backend == "kernel":
new_layout = "page_first"
else:
# Keep current behavior for unknown backends (e.g., kernel_ascend).
new_layout = self.hicache_mem_layout
self.hicache_mem_layout = new_layout
logger.warning(
f"Mooncake storage backend does not support layer_first layout, "
f"switching to {new_layout} layout for {self.hicache_io_backend} io backend"
)
def _resolve_io_decode_attention_compatibility(self) -> bool:
if self.hicache_io_backend != "kernel":
return False
# Only patch settings when the effective decode backend is FA3.
effective_decode_backend = (
self.decode_attention_backend or self.attention_backend
)
if effective_decode_backend != "fa3":
return False
if self.decode_attention_backend is not None:
self.hicache_io_backend = "direct"
logger.warning(
"FlashAttention3 decode backend is not compatible with hierarchical cache. "
"Setting hicache_io_backend to vanilla I/O, which may lead to suboptimal performance with small page sizes."
)
return True
# If decode backend is implicit, pick a safe backend without changing io backend.
if not self.use_mla_backend():
self.decode_attention_backend = (
"flashinfer" if is_flashinfer_available() else "triton"
)
else:
self.decode_attention_backend = (
"flashinfer" if is_sm100_supported() else "triton"
)
return False
def _handle_speculative_decoding(self):
if (