feat: Add FP4 (E2M1) KV Cache Support for MHA (#12612)
Signed-off-by: Ho-Ren (Jack) Chuang <horenchuang@bytedance.com> Co-authored-by: Yichen Wang <yichen.wang@bytedance.com>
This commit is contained in:
co-authored by
Yichen Wang
parent
67e6f1438d
commit
6d5e16fb1c
@@ -634,6 +634,47 @@ class MHATokenToKVPool(KVCache):
|
|||||||
if self.enable_custom_mem_pool
|
if self.enable_custom_mem_pool
|
||||||
else nullcontext()
|
else nullcontext()
|
||||||
):
|
):
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
m = self.size + self.page_size
|
||||||
|
n = self.head_num
|
||||||
|
k = self.head_dim
|
||||||
|
|
||||||
|
scale_block_size = 16
|
||||||
|
self.store_dtype = torch.uint8
|
||||||
|
self.k_buffer = [
|
||||||
|
torch.zeros(
|
||||||
|
(m, n, k // 2),
|
||||||
|
dtype=self.store_dtype,
|
||||||
|
device=self.device,
|
||||||
|
)
|
||||||
|
for _ in range(self.layer_num)
|
||||||
|
]
|
||||||
|
self.v_buffer = [
|
||||||
|
torch.zeros(
|
||||||
|
(m, n, k // 2),
|
||||||
|
dtype=self.store_dtype,
|
||||||
|
device=self.device,
|
||||||
|
)
|
||||||
|
for _ in range(self.layer_num)
|
||||||
|
]
|
||||||
|
|
||||||
|
self.k_scale_buffer = [
|
||||||
|
torch.zeros(
|
||||||
|
(m, (n * k) // scale_block_size),
|
||||||
|
dtype=self.store_dtype,
|
||||||
|
device=self.device,
|
||||||
|
)
|
||||||
|
for _ in range(self.layer_num)
|
||||||
|
]
|
||||||
|
self.v_scale_buffer = [
|
||||||
|
torch.zeros(
|
||||||
|
(m, (n * k) // scale_block_size),
|
||||||
|
dtype=self.store_dtype,
|
||||||
|
device=self.device,
|
||||||
|
)
|
||||||
|
for _ in range(self.layer_num)
|
||||||
|
]
|
||||||
|
else:
|
||||||
# [size, head_num, head_dim] for each layer
|
# [size, head_num, head_dim] for each layer
|
||||||
# The padded slot 0 is used for writing dummy outputs from padded tokens.
|
# The padded slot 0 is used for writing dummy outputs from padded tokens.
|
||||||
self.k_buffer = [
|
self.k_buffer = [
|
||||||
@@ -752,6 +793,21 @@ class MHATokenToKVPool(KVCache):
|
|||||||
def _get_key_buffer(self, layer_id: int):
|
def _get_key_buffer(self, layer_id: int):
|
||||||
# for internal use of referencing
|
# for internal use of referencing
|
||||||
if self.store_dtype != self.dtype:
|
if self.store_dtype != self.dtype:
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
cache_k_nope_fp4 = self.k_buffer[layer_id - self.start_layer].view(
|
||||||
|
torch.uint8
|
||||||
|
)
|
||||||
|
cache_k_nope_fp4_sf = self.k_scale_buffer[layer_id - self.start_layer]
|
||||||
|
|
||||||
|
from sglang.srt.layers.quantization.kvfp4_tensor import (
|
||||||
|
KVFP4QuantizeUtil,
|
||||||
|
)
|
||||||
|
|
||||||
|
cache_k_nope_fp4_dequant = KVFP4QuantizeUtil.batched_dequantize(
|
||||||
|
cache_k_nope_fp4, cache_k_nope_fp4_sf
|
||||||
|
)
|
||||||
|
return cache_k_nope_fp4_dequant
|
||||||
|
else:
|
||||||
return self.k_buffer[layer_id - self.start_layer].view(self.dtype)
|
return self.k_buffer[layer_id - self.start_layer].view(self.dtype)
|
||||||
return self.k_buffer[layer_id - self.start_layer]
|
return self.k_buffer[layer_id - self.start_layer]
|
||||||
|
|
||||||
@@ -766,6 +822,21 @@ class MHATokenToKVPool(KVCache):
|
|||||||
def _get_value_buffer(self, layer_id: int):
|
def _get_value_buffer(self, layer_id: int):
|
||||||
# for internal use of referencing
|
# for internal use of referencing
|
||||||
if self.store_dtype != self.dtype:
|
if self.store_dtype != self.dtype:
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
cache_v_nope_fp4 = self.v_buffer[layer_id - self.start_layer].view(
|
||||||
|
torch.uint8
|
||||||
|
)
|
||||||
|
cache_v_nope_fp4_sf = self.v_scale_buffer[layer_id - self.start_layer]
|
||||||
|
|
||||||
|
from sglang.srt.layers.quantization.kvfp4_tensor import (
|
||||||
|
KVFP4QuantizeUtil,
|
||||||
|
)
|
||||||
|
|
||||||
|
cache_v_nope_fp4_dequant = KVFP4QuantizeUtil.batched_dequantize(
|
||||||
|
cache_v_nope_fp4, cache_v_nope_fp4_sf
|
||||||
|
)
|
||||||
|
return cache_v_nope_fp4_dequant
|
||||||
|
else:
|
||||||
return self.v_buffer[layer_id - self.start_layer].view(self.dtype)
|
return self.v_buffer[layer_id - self.start_layer].view(self.dtype)
|
||||||
return self.v_buffer[layer_id - self.start_layer]
|
return self.v_buffer[layer_id - self.start_layer]
|
||||||
|
|
||||||
@@ -798,24 +869,44 @@ class MHATokenToKVPool(KVCache):
|
|||||||
cache_k.div_(k_scale)
|
cache_k.div_(k_scale)
|
||||||
if v_scale is not None:
|
if v_scale is not None:
|
||||||
cache_v.div_(v_scale)
|
cache_v.div_(v_scale)
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
from sglang.srt.layers.quantization.kvfp4_tensor import (
|
||||||
|
KVFP4QuantizeUtil,
|
||||||
|
)
|
||||||
|
|
||||||
|
cache_k, cache_k_fp4_sf = KVFP4QuantizeUtil.batched_quantize(cache_k)
|
||||||
|
cache_v, cache_v_fp4_sf = KVFP4QuantizeUtil.batched_quantize(cache_v)
|
||||||
|
else:
|
||||||
cache_k = cache_k.to(self.dtype)
|
cache_k = cache_k.to(self.dtype)
|
||||||
cache_v = cache_v.to(self.dtype)
|
cache_v = cache_v.to(self.dtype)
|
||||||
|
|
||||||
if self.store_dtype != self.dtype:
|
if self.store_dtype != self.dtype:
|
||||||
cache_k = cache_k.view(self.store_dtype)
|
cache_k = cache_k.view(self.store_dtype)
|
||||||
cache_v = cache_v.view(self.store_dtype)
|
cache_v = cache_v.view(self.store_dtype)
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
cache_k_fp4_sf = cache_k_fp4_sf.view(self.store_dtype)
|
||||||
|
cache_v_fp4_sf = cache_v_fp4_sf.view(self.store_dtype)
|
||||||
|
|
||||||
if get_is_capture_mode() and self.alt_stream is not None:
|
if get_is_capture_mode() and self.alt_stream is not None:
|
||||||
# Overlap the copy of K and V cache for small batch size
|
# Overlap the copy of K and V cache for small batch size
|
||||||
current_stream = self.device_module.current_stream()
|
current_stream = self.device_module.current_stream()
|
||||||
self.alt_stream.wait_stream(current_stream)
|
self.alt_stream.wait_stream(current_stream)
|
||||||
self.k_buffer[layer_id - self.start_layer][loc] = cache_k
|
self.k_buffer[layer_id - self.start_layer][loc] = cache_k
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
self.k_scale_buffer[layer_id - self.start_layer][loc] = cache_k_fp4_sf
|
||||||
with self.device_module.stream(self.alt_stream):
|
with self.device_module.stream(self.alt_stream):
|
||||||
self.v_buffer[layer_id - self.start_layer][loc] = cache_v
|
self.v_buffer[layer_id - self.start_layer][loc] = cache_v
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
self.v_scale_buffer[layer_id - self.start_layer][
|
||||||
|
loc
|
||||||
|
] = cache_v_fp4_sf
|
||||||
current_stream.wait_stream(self.alt_stream)
|
current_stream.wait_stream(self.alt_stream)
|
||||||
else:
|
else:
|
||||||
self.k_buffer[layer_id - self.start_layer][loc] = cache_k
|
self.k_buffer[layer_id - self.start_layer][loc] = cache_k
|
||||||
self.v_buffer[layer_id - self.start_layer][loc] = cache_v
|
self.v_buffer[layer_id - self.start_layer][loc] = cache_v
|
||||||
|
if is_float4_e2m1fn_x2(self.dtype):
|
||||||
|
self.k_scale_buffer[layer_id - self.start_layer][loc] = cache_k_fp4_sf
|
||||||
|
self.v_scale_buffer[layer_id - self.start_layer][loc] = cache_v_fp4_sf
|
||||||
|
|
||||||
def move_kv_cache(self, tgt_loc: torch.Tensor, src_loc: torch.Tensor):
|
def move_kv_cache(self, tgt_loc: torch.Tensor, src_loc: torch.Tensor):
|
||||||
N = tgt_loc.numel()
|
N = tgt_loc.numel()
|
||||||
|
|||||||
@@ -1325,6 +1325,24 @@ class ModelRunner:
|
|||||||
* 2
|
* 2
|
||||||
* torch._utils._element_size(self.kv_cache_dtype)
|
* torch._utils._element_size(self.kv_cache_dtype)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if is_float4_e2m1fn_x2(self.kv_cache_dtype):
|
||||||
|
# kv_scale_buffer
|
||||||
|
scale_block_size = 16
|
||||||
|
|
||||||
|
n = self.model_config.get_num_kv_heads(get_attention_tp_size())
|
||||||
|
k = self.model_config.head_dim
|
||||||
|
cell_size = (cell_size // 2) + (
|
||||||
|
(
|
||||||
|
n
|
||||||
|
* k
|
||||||
|
* num_layers
|
||||||
|
* 2
|
||||||
|
* torch._utils._element_size(self.kv_cache_dtype)
|
||||||
|
)
|
||||||
|
// scale_block_size
|
||||||
|
)
|
||||||
|
|
||||||
rest_memory = available_gpu_memory - total_gpu_memory * (
|
rest_memory = available_gpu_memory - total_gpu_memory * (
|
||||||
1 - self.mem_fraction_static
|
1 - self.mem_fraction_static
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user