Skip to content

Commit e345820

Browse files
authored
[kunlunxin] fix outer,mv,scatter,batch_norm; add reshape_and_cache (#723)
* [kunlunxin] fix outer,mv,scatter,batch_norm; add reshape_and_cache * [kunlunxin] add concat_and_cache_mla
1 parent 58a1c4b commit e345820

9 files changed

Lines changed: 120 additions & 107 deletions

File tree

src/flag_gems/runtime/backend/_kunlunxin/fused/concat_and_cache_mla.py

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -40,53 +40,51 @@ def concat_and_cache_mla_kernel(
4040
slot_idx = tl.load(slot_mapping_ptr + token_idx)
4141

4242
# Skip padded tokens
43-
if slot_idx < 0:
44-
return
45-
46-
# Calculate cache position
47-
block_id = slot_idx // block_size
48-
block_offset = slot_idx % block_size
49-
cache_base = block_id * block_stride + block_offset * entry_stride
50-
51-
# Preload scale if needed
52-
if kv_dtype != FP8_KV_CACHE_DATA_TYPE_AUTO:
53-
scale_val = tl.load(scale_ptr)
54-
55-
# Process kv_c section
56-
for i in range(0, kv_lora_rank, BLOCK_SIZE):
57-
idx = i + tl.arange(0, BLOCK_SIZE)
58-
mask = idx < kv_lora_rank
59-
60-
src_ptr = kv_c_ptr + token_idx * kv_c_stride + idx
61-
dst_ptr = kv_cache_ptr + cache_base + idx
62-
63-
val = tl.load(src_ptr, mask=mask, other=0)
64-
65-
if kv_dtype != FP8_KV_CACHE_DATA_TYPE_AUTO:
66-
if kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E4M3:
67-
val = (val / scale_val).to(tl.float8e4nv)
68-
elif kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E5M2:
69-
val = (val / scale_val).to(tl.float8e5)
70-
val = val.to(tl.uint8, bitcast=True)
71-
tl.store(dst_ptr, val, mask=mask)
72-
73-
# Process k_pe section
74-
for j in range(0, pe_dim, BLOCK_SIZE):
75-
idx = j + tl.arange(0, BLOCK_SIZE)
76-
mask = idx < pe_dim
77-
78-
src_ptr = k_pe_ptr + token_idx * k_pe_stride + idx
79-
dst_ptr = kv_cache_ptr + cache_base + kv_lora_rank + idx
80-
81-
val = tl.load(src_ptr, mask=mask, other=0)
43+
if slot_idx >= 0:
44+
# Calculate cache position
45+
block_id = slot_idx // block_size
46+
block_offset = slot_idx % block_size
47+
cache_base = block_id * block_stride + block_offset * entry_stride
8248

49+
# Preload scale if needed
8350
if kv_dtype != FP8_KV_CACHE_DATA_TYPE_AUTO:
84-
if kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E4M3:
85-
val = (val / scale_val).to(tl.float8e4nv)
86-
elif kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E5M2:
87-
val = (val / scale_val).to(tl.float8e5)
88-
val = val.to(tl.uint8, bitcast=True)
89-
tl.store(dst_ptr, val, mask=mask)
51+
scale_val = tl.load(scale_ptr)
52+
53+
# Process kv_c section
54+
for i in range(0, kv_lora_rank, BLOCK_SIZE):
55+
idx = i + tl.arange(0, BLOCK_SIZE)
56+
mask = idx < kv_lora_rank
57+
58+
src_ptr = kv_c_ptr + token_idx * kv_c_stride + idx
59+
dst_ptr = kv_cache_ptr + cache_base + idx
60+
61+
val = tl.load(src_ptr, mask=mask, other=0)
62+
63+
if kv_dtype != FP8_KV_CACHE_DATA_TYPE_AUTO:
64+
if kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E4M3:
65+
val = (val / scale_val).to(tl.float8e4nv)
66+
elif kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E5M2:
67+
val = (val / scale_val).to(tl.float8e5)
68+
val = val.to(tl.uint8, bitcast=True)
69+
tl.store(dst_ptr, val, mask=mask)
70+
71+
# Process k_pe section
72+
for j in range(0, pe_dim, BLOCK_SIZE):
73+
idx = j + tl.arange(0, BLOCK_SIZE)
74+
mask = idx < pe_dim
75+
76+
src_ptr = k_pe_ptr + token_idx * k_pe_stride + idx
77+
dst_ptr = kv_cache_ptr + cache_base + kv_lora_rank + idx
78+
79+
val = tl.load(src_ptr, mask=mask, other=0)
80+
81+
if kv_dtype != FP8_KV_CACHE_DATA_TYPE_AUTO:
82+
if kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E4M3:
83+
val = (val / scale_val).to(tl.float8e4nv)
84+
elif kv_dtype == FP8_KV_CACHE_DATA_TYPE_FP8E5M2:
85+
val = (val / scale_val).to(tl.float8e5)
86+
val = val.to(tl.uint8, bitcast=True)
87+
tl.store(dst_ptr, val, mask=mask)
9088

9189

9290
class ConcatAndCacheMla(torch.autograd.Function):

src/flag_gems/runtime/backend/_kunlunxin/fused/outer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import torch
44

5-
from ..ops import mul, mv
5+
from ..ops import mul, mv_cluster
66

77
logger = logging.getLogger(__name__)
88

@@ -27,8 +27,8 @@ def backward(ctx, out_grad):
2727

2828
inp, weight = ctx.saved_tensors
2929

30-
inp_grad = mv(out_grad, weight)
31-
weight_grad = mv(out_grad.t().contiguous(), inp)
30+
inp_grad = mv_cluster(out_grad, weight)
31+
weight_grad = mv_cluster(out_grad.t().contiguous(), inp)
3232

3333
return inp_grad, weight_grad
3434

src/flag_gems/runtime/backend/_kunlunxin/fused/reshape_and_cache.py

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,41 +29,39 @@ def reshape_and_cache_kernel(
2929
):
3030
token_idx = tl.program_id(0)
3131
slot_idx = tl.load(slot_mapping + token_idx)
32-
if slot_idx < 0:
33-
return
32+
if slot_idx >= 0:
33+
block_idx = slot_idx // block_size
34+
block_offset = slot_idx % block_size
35+
i = tl.arange(0, triton.next_power_of_2(n))
36+
mask = i < n
3437

35-
block_idx = slot_idx // block_size
36-
block_offset = slot_idx % block_size
37-
i = tl.arange(0, triton.next_power_of_2(n))
38-
mask = i < n
38+
src_key_idx = token_idx * key_stride + i
39+
src_value_idx = token_idx * value_stride + i
40+
head_idx = i // head_size
41+
head_offset = i % head_size
42+
x_idx = head_offset // x
43+
x_offset = head_offset % x
3944

40-
src_key_idx = token_idx * key_stride + i
41-
src_value_idx = token_idx * value_stride + i
42-
head_idx = i // head_size
43-
head_offset = i % head_size
44-
x_idx = head_offset // x
45-
x_offset = head_offset % x
46-
47-
tgt_key_idx = (
48-
block_idx * num_heads * (head_size // x) * block_size * x
49-
+ head_idx * (head_size // x) * block_size * x
50-
+ x_idx * block_size * x
51-
+ block_offset * x
52-
+ x_offset
53-
)
54-
tgt_value_idx = (
55-
block_idx * num_heads * head_size * block_size
56-
+ head_idx * head_size * block_size
57-
+ head_offset * block_size
58-
+ block_offset
59-
)
45+
tgt_key_idx = (
46+
block_idx * num_heads * (head_size // x) * block_size * x
47+
+ head_idx * (head_size // x) * block_size * x
48+
+ x_idx * block_size * x
49+
+ block_offset * x
50+
+ x_offset
51+
)
52+
tgt_value_idx = (
53+
block_idx * num_heads * head_size * block_size
54+
+ head_idx * head_size * block_size
55+
+ head_offset * block_size
56+
+ block_offset
57+
)
6058

61-
tgt_key = tl.load(key + src_key_idx, mask=mask)
62-
tgt_value = tl.load(value + src_value_idx, mask=mask)
59+
tgt_key = tl.load(key + src_key_idx, mask=mask)
60+
tgt_value = tl.load(value + src_value_idx, mask=mask)
6361

64-
# TODO: support fp8 dtype
65-
tl.store(key_cache + tgt_key_idx, tgt_key, mask=mask)
66-
tl.store(value_cache + tgt_value_idx, tgt_value, mask=mask)
62+
# TODO: support fp8 dtype
63+
tl.store(key_cache + tgt_key_idx, tgt_key, mask=mask)
64+
tl.store(value_cache + tgt_value_idx, tgt_value, mask=mask)
6765

6866

6967
def reshape_and_cache(

src/flag_gems/runtime/backend/_kunlunxin/ops/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
from .mse_loss import mse_loss
105105
from .mul import mul, mul_
106106
from .multinomial import multinomial
107-
from .mv import mv
107+
from .mv import mv, mv_cluster
108108
from .nan_to_num import nan_to_num
109109
from .ne import ne, ne_scalar
110110
from .neg import neg, neg_
@@ -311,6 +311,7 @@
311311
"normal_tensor_tensor",
312312
"uniform_",
313313
"mv",
314+
"mv_cluster",
314315
"nan_to_num",
315316
"ne",
316317
"ne_scalar",

src/flag_gems/runtime/backend/_kunlunxin/ops/batch_norm.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ def make_3d_for_bn(input: Tensor) -> Tensor:
3737

3838

3939
@libentry()
40-
@triton.autotune(
41-
configs=runtime.get_tuned_config("batch_norm"),
42-
key=["batch_dim", "spatial_dim"],
43-
restore_value=["running_mean_pointer", "running_var_pointer"],
44-
)
40+
# @triton.autotune(
41+
# configs=runtime.get_tuned_config("batch_norm"),
42+
# key=["batch_dim", "spatial_dim"],
43+
# restore_value=["running_mean_pointer", "running_var_pointer"],
44+
# )
4545
@triton.heuristics(runtime.get_heuristic_config("batch_norm"))
4646
@triton.jit
4747
def batch_norm_forward_kernel(
@@ -186,10 +186,10 @@ def batch_norm_heur_block_n(args):
186186

187187

188188
@libentry()
189-
@triton.autotune(
190-
configs=runtime.get_tuned_config("batch_norm"),
191-
key=["batch_dim", "spatial_dim"],
192-
)
189+
# @triton.autotune(
190+
# configs=runtime.get_tuned_config("batch_norm"),
191+
# key=["batch_dim", "spatial_dim"],
192+
# )
193193
@triton.heuristics(
194194
values={
195195
"BLOCK_M": batch_norm_heur_block_m,

src/flag_gems/runtime/backend/_kunlunxin/ops/mv.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,25 @@ def mv(inp, vec):
9898
out = out.squeeze()
9999
del os.environ["XMLIR_MATMUL_FAST_MODE"]
100100
return out
101+
102+
103+
def mv_cluster(inp, vec):
104+
logger.debug("GEMS MV")
105+
assert inp.shape[1] == vec.shape[0], "incompatible dimensions"
106+
N, M = inp.shape
107+
out = torch.empty((N,), device=inp.device, dtype=inp.dtype)
108+
grid = lambda META: (triton.cdiv(N, META["BLOCK_N"]),)
109+
with torch_device_fn.device(inp.device):
110+
mv_kernel[grid](
111+
inp,
112+
vec,
113+
out,
114+
N,
115+
M,
116+
inp.stride(0),
117+
inp.stride(1),
118+
vec.stride(0),
119+
out.stride(0),
120+
buffer_size_limit=256,
121+
)
122+
return out

src/flag_gems/runtime/backend/_kunlunxin/ops/scatter.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import torch
77

88
from flag_gems.utils.code_cache import code_cache_dir
9-
from flag_gems.utils.code_utils import IndentedBuffer
9+
from flag_gems.utils.code_utils import IndentedBuffer, write_atomic
1010
from flag_gems.utils.shape_utils import (
1111
MemOverlap,
1212
has_internal_overlapping,
@@ -23,6 +23,7 @@ def generate_imports(code: IndentedBuffer) -> IndentedBuffer:
2323
code.newline()
2424
code.writeline("from flag_gems.utils import libentry")
2525
code.writeline("from flag_gems import runtime")
26+
code.writeline("import flag_gems")
2627
# code.writeline("from flag_gems.utils import triton_lang_extension as tle")
2728
code.newline()
2829
code.newline()
@@ -159,17 +160,13 @@ def generate_scatter_kernel(
159160
code.writeline("if IS_ADD: ")
160161
with code.indent():
161162
code.writeline(
162-
"cur_inp = tl.load(inp + inp_offsets, mask=mask, other=0)"
163+
"tl.atomic_add(out + inp_offsets, cur_src, mask=mask, sem='relaxed')"
163164
)
164-
code.writeline("res = cur_inp + cur_src")
165-
code.writeline("tl.store(out + inp_offsets, res, mask=mask)")
166165
code.writeline("elif IS_MUL: ")
167166
with code.indent():
168167
code.writeline(
169-
"cur_inp = tl.load(inp + inp_offsets, mask=mask, other=0)"
168+
"tl.atomic_mul(out + inp_offsets, cur_src, mask=mask, sem='relaxed')"
170169
)
171-
code.writeline("res = cur_inp * cur_src")
172-
code.writeline("tl.store(out + inp_offsets, res, mask=mask)")
173170

174171
code.writeline("else: ")
175172
with code.indent():
@@ -252,6 +249,7 @@ def generate_destination_passing_wrapper(
252249
code.writeline("IS_ADD,")
253250
code.writeline("IS_MUL,")
254251
code.writeline("INT32_OFFSET=int32_offset,")
252+
# code.writeline("buffer_size_limit=512,")
255253
# code.writeline("isCloseUnrollControl=True,")
256254

257255
code.writeline(")")
@@ -294,15 +292,14 @@ def __call__(self, *args, **kwargs):
294292
code,
295293
)
296294

297-
file_name = f"scatter_rank_{key}_pid_{self.pid}.py"
298-
299-
with open(code_cache_dir() / file_name, "wt", encoding="utf-8") as f:
300-
f.write(code.getvalue())
295+
file_name = f"scatter_rank_{key}.py"
296+
file_path = code_cache_dir() / file_name
297+
write_atomic(file_path, code.getvalue())
301298

302299
# load
303300
spec = importlib.util.spec_from_file_location(
304-
f"_gen_module_rank_{key}_pid_{self.pid}",
305-
f.name,
301+
f"_gen_module_rank_{key}",
302+
file_path,
306303
)
307304

308305
m = importlib.util.module_from_spec(spec)
@@ -319,7 +316,6 @@ def arg_key(self, *args):
319316

320317

321318
_scatter_func = ScatterFunction()
322-
_scatter_inplace_func = ScatterFunction()
323319

324320

325321
def scatter(inp, dim, index, src, reduce=None):
@@ -378,7 +374,7 @@ def scatter_(inp, dim, index, src, reduce=None):
378374

379375
int32_size_dim = lambda x: x.stride(dim) * x.size(dim) < 2**32
380376
use_int32_offset = all(map(int32_size_dim, (inp, index, src)))
381-
_scatter_inplace_func(
377+
_scatter_func(
382378
src_restrided,
383379
index,
384380
inp_restrided,

tests/test_attention_ops.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ def convert_fp8(
127127

128128
@pytest.mark.skipif(flag_gems.vendor_name == "hygon", reason="RuntimeError")
129129
@pytest.mark.skipif(flag_gems.device == "musa", reason="RuntimeError")
130-
@pytest.mark.skipif(flag_gems.vendor_name == "kunlunxin", reason="RESULT TODOFIX")
131130
@pytest.mark.concat_and_cache_mla
132131
@pytest.mark.parametrize("kv_lora_rank", KV_LORA_RANKS)
133132
@pytest.mark.parametrize("qk_rope_head_dim", QK_ROPE_HEAD_DIMS)

tests/test_quant.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
CUDA_DEVICES = [f"cuda:{i}" for i in range(1 if torch.cuda.device_count() == 1 else 2)]
3636

3737
# We assume fp8 is always enabled for testing.
38-
KV_CACHE_DTYPE = ["auto", "fp8"]
38+
KV_CACHE_DTYPE = ["auto", "fp8"] if flag_gems.vendor_name != "kunlunxin" else ["auto"]
3939

4040

4141
def _create_mla_cache(
@@ -65,7 +65,6 @@ def convert_fp8(
6565

6666
@pytest.mark.skipif(flag_gems.vendor_name == "hygon", reason="RuntimeError")
6767
@pytest.mark.skipif(flag_gems.device == "musa", reason="RuntimeError")
68-
@pytest.mark.skipif(flag_gems.vendor_name == "kunlunxin", reason="RESULT TODOFIX")
6968
@pytest.mark.concat_and_cache_mla
7069
@pytest.mark.parametrize("kv_lora_rank", KV_LORA_RANKS)
7170
@pytest.mark.parametrize("qk_rope_head_dim", QK_ROPE_HEAD_DIMS)

0 commit comments

Comments
 (0)