Skip to content

Commit d918d73

Browse files
authored
[KMCompiler] Add fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert (#3327)
1. RMSNorm on Q 2. ROPE for Q, KV 3. quantize KV nope part to fp8(E4M3) 4. organize each block in KV_Cache like as: token quantization part: block_size * 448 bytes (fp8, E4M3) token rope part: block_size * 128 bytes (bf16) scale part: block_size * 8 bytes (uint8, 7 scale, 1 pad)
1 parent 5d92137 commit d918d73

4 files changed

Lines changed: 964 additions & 0 deletions
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import dataclasses
2+
import random
3+
4+
import pytest
5+
import torch
6+
7+
import flag_gems
8+
from flag_gems.utils.device_info import get_device_capability
9+
10+
from . import base
11+
12+
13+
def is_support_fp8e4nv():
14+
major, minor = get_device_capability()
15+
return major * 10 + minor >= 89
16+
17+
18+
VLLM_REF_AVAILABLE = hasattr(
19+
torch.ops._C, "fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert"
20+
)
21+
HEAD_DIM = 512
22+
ROPE_DIM = 64
23+
HEAD_BYTES = 584
24+
25+
26+
@dataclasses.dataclass
27+
class TestParam:
28+
# Instruct pytest to ignore this class
29+
__test__ = False
30+
31+
num_tokens: int
32+
num_heads: int
33+
num_tokens_insert: int
34+
block_size: int
35+
max_pos: int
36+
eps: float
37+
dtype: torch.dtype = torch.bfloat16
38+
device: torch.device = flag_gems.device
39+
40+
41+
_random_counter = 0
42+
43+
44+
class FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark(base.Benchmark):
45+
def __init__(self):
46+
super().__init__(
47+
"fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert",
48+
torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert,
49+
[torch.bfloat16],
50+
)
51+
self.set_gems(flag_gems.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert)
52+
53+
def set_shapes(self, shape_file_path=None):
54+
self.shapes = []
55+
56+
def get_input_iter(self, dtype):
57+
_ = dtype
58+
for (
59+
param
60+
) in (
61+
FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark.get_performance_test_params()
62+
):
63+
yield from FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark.make_input(
64+
param
65+
)
66+
67+
@staticmethod
68+
def get_performance_test_params():
69+
cases = [
70+
TestParam(
71+
num_tokens,
72+
num_heads,
73+
num_tokens_insert=num_tokens,
74+
block_size=64,
75+
max_pos=4096,
76+
eps=1e-6,
77+
)
78+
for num_tokens in [
79+
1,
80+
4,
81+
17,
82+
64,
83+
1024,
84+
2048,
85+
8192,
86+
32768,
87+
65536,
88+
98304,
89+
131072,
90+
]
91+
for num_heads in [64, 128]
92+
]
93+
return cases
94+
95+
@staticmethod
96+
def init_seed(seed):
97+
random.seed(seed)
98+
torch.manual_seed(seed)
99+
100+
@staticmethod
101+
def make_cos_sin_cache(max_pos: int, rope_dim: int, dtype, device):
102+
if max_pos <= 8192:
103+
base = 10000.0
104+
elif max_pos <= 32768:
105+
base = 20000.0
106+
elif max_pos <= 65536:
107+
base = 40000.0
108+
elif max_pos <= 98304:
109+
base = 60000.0
110+
else:
111+
base = 100000.0
112+
113+
inv_freq = 1.0 / (
114+
base
115+
** (
116+
torch.arange(0, rope_dim, 2, dtype=torch.float32, device=device)
117+
/ rope_dim
118+
)
119+
)
120+
t = torch.arange(max_pos, dtype=torch.float32, device=device)
121+
freqs = torch.einsum("i,j -> ij", t, inv_freq) # [max_pos, rope_dim/2]
122+
cache = torch.cat((freqs.cos(), freqs.sin()), dim=-1) # [max_pos, rope_dim]
123+
return cache.to(dtype)
124+
125+
@staticmethod
126+
def make_input(param: TestParam):
127+
num_tokens = param.num_tokens
128+
num_heads = param.num_heads
129+
num_tokens_insert = param.num_tokens_insert
130+
block_size = param.block_size
131+
max_pos = max(param.max_pos, num_tokens)
132+
eps = param.eps
133+
dtype = param.dtype
134+
device = param.device
135+
136+
global _random_counter
137+
FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark.init_seed(_random_counter)
138+
_random_counter = _random_counter + 1
139+
140+
q = torch.randn(num_tokens, num_heads, HEAD_DIM, dtype=dtype, device=device)
141+
kv = torch.randn(num_tokens, HEAD_DIM, dtype=dtype, device=device)
142+
positions = torch.arange(num_tokens, dtype=torch.int64, device=device)
143+
cos_sin_cache = (
144+
FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark.make_cos_sin_cache(
145+
max_pos, ROPE_DIM, torch.float32, device
146+
)
147+
)
148+
149+
num_blocks = (num_tokens + block_size - 1) // block_size + 1
150+
slot_mapping = torch.arange(num_tokens_insert, dtype=torch.int64, device=device)
151+
k_cache = torch.zeros(
152+
num_blocks, block_size * HEAD_BYTES, dtype=torch.uint8, device=device
153+
)
154+
yield (q, kv, k_cache, slot_mapping, positions, cos_sin_cache, eps, block_size)
155+
156+
157+
@pytest.mark.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert
158+
@pytest.mark.skipif(
159+
not VLLM_REF_AVAILABLE, reason="The referenced vLLM implementation is not installed"
160+
)
161+
@pytest.mark.skipif(
162+
not is_support_fp8e4nv(), reason="Do not support fp8e4nv when capability < 89"
163+
)
164+
def test_fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert():
165+
bench = FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark()
166+
bench.run()

src/flag_gems/fused/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
from flag_gems.fused.flash_mla import flash_mla
1212
from flag_gems.fused.flashmla_sparse import flash_mla_sparse_fwd
1313
from flag_gems.fused.fused_add_rms_norm import fused_add_rms_norm
14+
from flag_gems.fused.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert import (
15+
fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert,
16+
)
1417
from flag_gems.fused.fused_moe import (
1518
dispatch_fused_moe_kernel,
1619
fused_experts_impl,
@@ -70,6 +73,7 @@
7073
"flash_mla",
7174
"flash_mla_sparse_fwd",
7275
"fused_add_rms_norm",
76+
"fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert",
7377
"fused_experts_impl",
7478
"fused_recurrent_gated_delta_rule_fwd",
7579
"geglu",

0 commit comments

Comments
 (0)