Skip to content

Commit e39e058

Browse files
committed
[KDA] Add fused BT=16 inference kernels for KDA prefill
1 parent 98e4e06 commit e39e058

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

tests/ops/test_kda.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,3 +1296,175 @@ def test_flashkda_chunk_varlen(H, D, cu_seqlens, monkeypatch):
12961296
)
12971297
assert_close("o", ref_o, tri_o, _FLASHKDA_RTOL)
12981298
assert_close("ht", ref_ht, tri_ht.to(ref_ht.dtype), _FLASHKDA_RTOL)
1299+
1300+
1301+
@pytest.mark.parametrize(
1302+
("B", "T", "H", "HV", "D", "scale", "has_dt_bias", "use_gate_in_kernel", "dtype"),
1303+
[
1304+
pytest.param(
1305+
*test,
1306+
id="B{}-T{}-H{}-HV{}-D{}-scale{}-has_dt_bias{}-use_gate{}-{}".format(*test),
1307+
)
1308+
for test in [
1309+
(4, 1024, 4, 4, 128, 0.1, True, True, torch.bfloat16),
1310+
(2, 2048, 4, 8, 64, 0.1, True, True, torch.bfloat16),
1311+
(1, 8192, 4, 4, 128, 0.1, False, True, torch.bfloat16),
1312+
(4, 1024, 4, 4, 128, 0.1, True, False, torch.bfloat16),
1313+
(2, 2048, 4, 8, 64, 0.1, True, False, torch.float16),
1314+
]
1315+
],
1316+
)
1317+
@torch.inference_mode()
1318+
def test_chunk_fwd_inference(
1319+
B: int,
1320+
T: int,
1321+
H: int,
1322+
HV: int,
1323+
D: int,
1324+
scale: float,
1325+
has_dt_bias: bool,
1326+
use_gate_in_kernel: bool,
1327+
dtype: torch.dtype,
1328+
):
1329+
torch.manual_seed(42)
1330+
1331+
q = torch.rand(B, T, H, D, dtype=dtype, device=device)
1332+
k = torch.rand(B, T, H, D, dtype=dtype, device=device)
1333+
v = torch.rand(B, T, HV, D, dtype=dtype, device=device)
1334+
g = torch.randn(B, T, HV, D, dtype=dtype, device=device)
1335+
beta_raw = torch.randn(B, T, HV, dtype=dtype, device=device)
1336+
A_log = torch.log(torch.empty(HV, dtype=torch.float32, device=device).uniform_(1, 16))
1337+
dt_bias = torch.randn(HV * D, dtype=torch.float32, device=device) if has_dt_bias else None
1338+
h0 = torch.randn(B, HV, D, D, dtype=torch.float32, device=device)
1339+
1340+
if use_gate_in_kernel:
1341+
g_activated = naive_kda_lowerbound_gate(
1342+
g.clone().float(), A_log.clone(),
1343+
dt_bias.clone() if dt_bias is not None else torch.zeros(HV * D, device=device),
1344+
lower_bound=-5.0
1345+
)
1346+
else:
1347+
g_activated = F.logsigmoid(g.clone().float())
1348+
1349+
q_norm = F.normalize(q.float(), p=2, dim=-1)
1350+
k_norm = F.normalize(k.float(), p=2, dim=-1)
1351+
1352+
ref, ref_ht = naive_recurrent_kda(
1353+
q=q_norm,
1354+
k=k_norm,
1355+
v=v.clone().float(),
1356+
g=g_activated,
1357+
beta=beta_raw.clone().float().sigmoid(),
1358+
scale=scale,
1359+
initial_state=h0.clone(),
1360+
output_final_state=True,
1361+
)
1362+
1363+
if use_gate_in_kernel:
1364+
tri, tri_ht = chunk_kda(
1365+
q=q, k=k, v=v, g=g, beta=beta_raw,
1366+
scale=scale,
1367+
initial_state=h0.clone(),
1368+
output_final_state=True,
1369+
use_qk_l2norm_in_kernel=True,
1370+
use_gate_in_kernel=True,
1371+
use_beta_sigmoid_in_kernel=True,
1372+
safe_gate=True,
1373+
lower_bound=-5.0,
1374+
A_log=A_log,
1375+
dt_bias=dt_bias,
1376+
)
1377+
else:
1378+
tri, tri_ht = chunk_kda(
1379+
q=q, k=k, v=v, g=g_activated.to(dtype), beta=beta_raw.sigmoid(),
1380+
scale=scale,
1381+
initial_state=h0.clone(),
1382+
output_final_state=True,
1383+
use_qk_l2norm_in_kernel=True,
1384+
use_gate_in_kernel=False,
1385+
use_beta_sigmoid_in_kernel=False,
1386+
)
1387+
assert_close("o", ref, tri, 0.005)
1388+
assert_close("ht", ref_ht, tri_ht, 0.005)
1389+
1390+
1391+
@pytest.mark.parametrize(
1392+
("H", "HV", "D", "scale", "cu_seqlens", "has_dt_bias", "dtype"),
1393+
[
1394+
pytest.param(
1395+
*test,
1396+
id="H{}-HV{}-D{}-scale{}-cu_seqlens-has_dt_bias{}-{}".format(*test),
1397+
)
1398+
for test in [
1399+
(4, 4, 128, 0.1, [0, 256, 500, 1000], True, torch.bfloat16),
1400+
(4, 8, 128, 0.1, [0, 100, 300, 1200, 2000], True, torch.float16),
1401+
(4, 4, 64, 0.1, [0, 101, 303, 1205, 3007, 4096], False, torch.bfloat16),
1402+
]
1403+
],
1404+
)
1405+
@torch.inference_mode()
1406+
def test_chunk_fwd_inference_varlen(
1407+
H: int,
1408+
HV: int,
1409+
D: int,
1410+
scale: float,
1411+
cu_seqlens: list[int],
1412+
has_dt_bias: bool,
1413+
dtype: torch.dtype,
1414+
):
1415+
torch.manual_seed(42)
1416+
1417+
cu_seqlens_t = torch.LongTensor(cu_seqlens).to(device)
1418+
T = cu_seqlens[-1]
1419+
N = len(cu_seqlens) - 1
1420+
1421+
q = torch.randn(1, T, H, D, dtype=dtype, device=device)
1422+
k = torch.randn(1, T, H, D, dtype=dtype, device=device)
1423+
v = torch.randn(1, T, HV, D, dtype=dtype, device=device)
1424+
g = torch.randn(1, T, HV, D, dtype=dtype, device=device)
1425+
beta_raw = torch.randn(1, T, HV, dtype=dtype, device=device)
1426+
A_log = torch.log(torch.empty(HV, dtype=torch.float32, device=device).uniform_(1, 16))
1427+
dt_bias = torch.randn(HV * D, dtype=torch.float32, device=device) if has_dt_bias else None
1428+
h0 = torch.randn(N, HV, D, D, dtype=torch.float32, device=device)
1429+
1430+
g_activated = naive_kda_lowerbound_gate(
1431+
g.clone().float(), A_log.clone(),
1432+
dt_bias.clone() if dt_bias is not None else torch.zeros(HV * D, device=device),
1433+
lower_bound=-5.0
1434+
)
1435+
q_norm = F.normalize(q.float(), p=2, dim=-1)
1436+
k_norm = F.normalize(k.float(), p=2, dim=-1)
1437+
1438+
ref_list, ref_ht_list = [], []
1439+
for i in range(N):
1440+
ref_i, ref_ht_i = naive_recurrent_kda(
1441+
q=q_norm[:, cu_seqlens[i]:cu_seqlens[i + 1]],
1442+
k=k_norm[:, cu_seqlens[i]:cu_seqlens[i + 1]],
1443+
v=v[:, cu_seqlens[i]:cu_seqlens[i + 1]].clone().float(),
1444+
g=g_activated[:, cu_seqlens[i]:cu_seqlens[i + 1]],
1445+
beta=beta_raw[:, cu_seqlens[i]:cu_seqlens[i + 1]].clone().float().sigmoid(),
1446+
scale=scale,
1447+
initial_state=h0[i:i + 1].clone(),
1448+
output_final_state=True,
1449+
)
1450+
ref_list.append(ref_i)
1451+
ref_ht_list.append(ref_ht_i)
1452+
ref = torch.cat(ref_list, 1)
1453+
ref_ht = torch.cat(ref_ht_list, 0)
1454+
1455+
tri, tri_ht = chunk_kda(
1456+
q=q, k=k, v=v, g=g, beta=beta_raw,
1457+
scale=scale,
1458+
initial_state=h0.clone(),
1459+
output_final_state=True,
1460+
cu_seqlens=cu_seqlens_t,
1461+
use_qk_l2norm_in_kernel=True,
1462+
use_gate_in_kernel=True,
1463+
use_beta_sigmoid_in_kernel=True,
1464+
safe_gate=True,
1465+
lower_bound=-5.0,
1466+
A_log=A_log,
1467+
dt_bias=dt_bias,
1468+
)
1469+
assert_close("o", ref, tri, 0.005)
1470+
assert_close("ht", ref_ht, tri_ht, 0.005)

0 commit comments

Comments
 (0)