Support CPU offload for mxfp8 KV cache (#35888)
This commit is contained in:
@@ -3547,13 +3547,73 @@ class MHATokenToKVPoolMXFP8(MHATokenToKVPool):
|
||||
self.k_scale_buffer[idx][tgt_loc] = self.k_scale_buffer[idx][src_loc]
|
||||
self.v_scale_buffer[idx][tgt_loc] = self.v_scale_buffer[idx][src_loc]
|
||||
|
||||
# These paths copy k/v buffers without the scale buffers; fail loudly
|
||||
# instead of silently corrupting dequantization.
|
||||
def _read_scales(self, idx, loc):
|
||||
"""Per-token UE8M0 K/V scales at ``loc``, inverse of ``_write_scales``."""
|
||||
if self.mxfp8_sf_interleaved:
|
||||
return (
|
||||
self._read_sf_interleaved(self.k_scale_buffer[idx], loc),
|
||||
self._read_sf_interleaved(self.v_scale_buffer[idx], loc),
|
||||
)
|
||||
return self.k_scale_buffer[idx][loc], self.v_scale_buffer[idx][loc]
|
||||
|
||||
def get_cpu_copy(self, indices, mamba_indices=None):
|
||||
raise NotImplementedError("CPU offloading is unsupported for MXFP8 KV cache.")
|
||||
# The scales travel with their fp8 payload; a restored slot dequantizes
|
||||
# against mismatched exponents without them.
|
||||
assert not self.use_hnd, (
|
||||
"CPU KV offload indexes by slot (NHD); HND KV cache "
|
||||
"(SGLANG_USE_HND_KVCACHE) is not supported with CPU offload yet."
|
||||
)
|
||||
current_platform.synchronize()
|
||||
kv_cache_cpu = []
|
||||
chunk_size = self.cpu_offloading_chunk_size
|
||||
for layer_id in range(self.layer_num):
|
||||
kv_cache_cpu.append([])
|
||||
for i in range(0, len(indices), chunk_size):
|
||||
chunk_indices = indices[i : i + chunk_size]
|
||||
k_scale, v_scale = self._read_scales(layer_id, chunk_indices)
|
||||
kv_cache_cpu[-1].append(
|
||||
[
|
||||
self.k_buffer[layer_id][chunk_indices].to(
|
||||
"cpu", non_blocking=True
|
||||
),
|
||||
self.v_buffer[layer_id][chunk_indices].to(
|
||||
"cpu", non_blocking=True
|
||||
),
|
||||
k_scale.to("cpu", non_blocking=True),
|
||||
v_scale.to("cpu", non_blocking=True),
|
||||
]
|
||||
)
|
||||
current_platform.synchronize()
|
||||
return kv_cache_cpu
|
||||
|
||||
def load_cpu_copy(self, kv_cache_cpu, indices, mamba_indices=None):
|
||||
raise NotImplementedError("CPU offloading is unsupported for MXFP8 KV cache.")
|
||||
assert not self.use_hnd, (
|
||||
"CPU KV offload indexes by slot (NHD); HND KV cache "
|
||||
"(SGLANG_USE_HND_KVCACHE) is not supported with CPU offload yet."
|
||||
)
|
||||
current_platform.synchronize()
|
||||
device = self.k_buffer[0].device
|
||||
chunk_size = self.cpu_offloading_chunk_size
|
||||
for layer_id in range(self.layer_num):
|
||||
for i in range(0, len(indices), chunk_size):
|
||||
chunk_indices = indices[i : i + chunk_size]
|
||||
k_cpu, v_cpu, k_scale_cpu, v_scale_cpu = kv_cache_cpu[layer_id][
|
||||
i // chunk_size
|
||||
]
|
||||
assert k_cpu.shape[0] == v_cpu.shape[0] == len(chunk_indices)
|
||||
self.k_buffer[layer_id][chunk_indices] = k_cpu.to(
|
||||
device, non_blocking=True
|
||||
)
|
||||
self.v_buffer[layer_id][chunk_indices] = v_cpu.to(
|
||||
device, non_blocking=True
|
||||
)
|
||||
self._write_scales(
|
||||
layer_id,
|
||||
chunk_indices,
|
||||
k_scale_cpu.to(device, non_blocking=True),
|
||||
v_scale_cpu.to(device, non_blocking=True),
|
||||
)
|
||||
current_platform.synchronize()
|
||||
|
||||
def get_kv_scale_buf_infos(self):
|
||||
"""(ptrs, lens, item_lens) for the UE8M0 scale buffers, k then v.
|
||||
|
||||
Reference in New Issue
Block a user