[KDA] Fix mixed exponent bases in Triton chunk prefill (#31904)
Co-authored-by: luoyuan.luo <luoyuan.luo@antgroup.com>
This commit is contained in:
@@ -13,7 +13,7 @@ from sglang.kernels.ops.attention.fla.index import (
|
||||
prepare_chunk_indices,
|
||||
prepare_chunk_offsets,
|
||||
)
|
||||
from sglang.kernels.ops.attention.fla.op import exp, safe_exp
|
||||
from sglang.kernels.ops.attention.fla.op import exp, exp2, safe_exp
|
||||
from sglang.kernels.ops.attention.fla.utils import (
|
||||
autotune_cache_kwargs,
|
||||
is_nvidia_hopper,
|
||||
@@ -76,6 +76,7 @@ def chunk_gated_delta_rule_fwd_kernel_h_blockdim64(
|
||||
SAVE_NEW_VALUE: tl.constexpr,
|
||||
IS_VARLEN: tl.constexpr,
|
||||
NT_BUCKET: tl.constexpr,
|
||||
USE_EXP2: tl.constexpr,
|
||||
):
|
||||
i_v, i_nh = tl.program_id(0), tl.program_id(1)
|
||||
i_n, i_h = i_nh // H, i_nh % H
|
||||
@@ -220,7 +221,10 @@ def chunk_gated_delta_rule_fwd_kernel_h_blockdim64(
|
||||
mask=(o_k1 < K),
|
||||
other=0.0,
|
||||
)
|
||||
b_h1 *= exp(b_gk_last1)[None, :]
|
||||
if USE_EXP2:
|
||||
b_h1 *= exp2(b_gk_last1)[None, :]
|
||||
else:
|
||||
b_h1 *= exp(b_gk_last1)[None, :]
|
||||
if K > 64:
|
||||
o_k2 = 64 + o_k1
|
||||
b_gk_last2 = tl.load(
|
||||
@@ -228,7 +232,10 @@ def chunk_gated_delta_rule_fwd_kernel_h_blockdim64(
|
||||
mask=(o_k2 < K),
|
||||
other=0.0,
|
||||
)
|
||||
b_h2 *= exp(b_gk_last2)[None, :]
|
||||
if USE_EXP2:
|
||||
b_h2 *= exp2(b_gk_last2)[None, :]
|
||||
else:
|
||||
b_h2 *= exp(b_gk_last2)[None, :]
|
||||
if K > 128:
|
||||
o_k3 = 128 + o_k1
|
||||
b_gk_last3 = tl.load(
|
||||
@@ -236,7 +243,10 @@ def chunk_gated_delta_rule_fwd_kernel_h_blockdim64(
|
||||
mask=(o_k3 < K),
|
||||
other=0.0,
|
||||
)
|
||||
b_h3 *= exp(b_gk_last3)[None, :]
|
||||
if USE_EXP2:
|
||||
b_h3 *= exp2(b_gk_last3)[None, :]
|
||||
else:
|
||||
b_h3 *= exp(b_gk_last3)[None, :]
|
||||
if K > 192:
|
||||
o_k4 = 192 + o_k1
|
||||
b_gk_last4 = tl.load(
|
||||
@@ -244,7 +254,10 @@ def chunk_gated_delta_rule_fwd_kernel_h_blockdim64(
|
||||
mask=(o_k4 < K),
|
||||
other=0.0,
|
||||
)
|
||||
b_h4 *= exp(b_gk_last4)[None, :]
|
||||
if USE_EXP2:
|
||||
b_h4 *= exp2(b_gk_last4)[None, :]
|
||||
else:
|
||||
b_h4 *= exp(b_gk_last4)[None, :]
|
||||
b_v = b_v.to(k.dtype.element_ty)
|
||||
|
||||
p_k = tl.make_block_ptr(
|
||||
@@ -303,7 +316,11 @@ def chunk_gated_delta_rule_fwd_h(
|
||||
save_new_value: bool = True,
|
||||
cu_seqlens: Optional[torch.LongTensor] = None,
|
||||
chunk_indices: Optional[torch.LongTensor] = None,
|
||||
use_exp2: bool = False,
|
||||
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
||||
assert not (
|
||||
use_exp2 and g is not None
|
||||
), "use_exp2 covers only the per-channel gk path; scalar g stays natural-exp"
|
||||
B, T, Hg, K, V = *k.shape, u.shape[-1]
|
||||
H = u.shape[-2]
|
||||
BT = CHUNK_SIZE
|
||||
@@ -353,5 +370,6 @@ def chunk_gated_delta_rule_fwd_h(
|
||||
SAVE_NEW_VALUE=v_new is not None,
|
||||
IS_VARLEN=cu_seqlens is not None,
|
||||
NT_BUCKET=(0 if NT <= 32 else (1 if NT <= 128 else 2)),
|
||||
USE_EXP2=use_exp2,
|
||||
)
|
||||
return h, v_new
|
||||
|
||||
@@ -11,7 +11,7 @@ from sglang.kernels.ops.attention.fla.chunk_intra_token_parallel import (
|
||||
from sglang.kernels.ops.attention.fla.index import (
|
||||
prepare_chunk_indices,
|
||||
)
|
||||
from sglang.kernels.ops.attention.fla.op import exp, exp2, gather
|
||||
from sglang.kernels.ops.attention.fla.op import exp2, gather
|
||||
from sglang.kernels.ops.attention.fla.utils import (
|
||||
autotune_cache_kwargs,
|
||||
is_gather_supported,
|
||||
@@ -632,7 +632,7 @@ def chunk_kda_fwd_kernel_inter_solve_fused(
|
||||
tl.store(p_u2, b_u2.to(p_u2.dtype.element_ty), boundary_check=(0, 1))
|
||||
tl.store(p_u3, b_u3.to(p_u3.dtype.element_ty), boundary_check=(0, 1))
|
||||
|
||||
# ---- w = A_inv @ (k * beta * exp(gk)), kg = k * exp(gn - gk) ----
|
||||
# ---- w = A_inv @ (k * beta * exp2(gk)), kg = k * exp2(gn - gk) ----
|
||||
w_base = w_out + (bos * H + i_h) * K
|
||||
kg_base = kg_out + (bos * H + i_h) * K
|
||||
last_idx = min(i_t * BT + BT, T) - 1
|
||||
@@ -680,10 +680,10 @@ def chunk_kda_fwd_kernel_inter_solve_fused(
|
||||
b_gk2r = tl.load(p_gk2, boundary_check=(0, 1)).to(tl.float32)
|
||||
b_gk3r = tl.load(p_gk3, boundary_check=(0, 1)).to(tl.float32)
|
||||
|
||||
b_kb0 = (b_k0r * b_b0[:, None] * exp(b_gk0r)).to(b_k0r.dtype)
|
||||
b_kb1 = (b_k1r * b_b1r[:, None] * exp(b_gk1r)).to(b_k1r.dtype)
|
||||
b_kb2 = (b_k2r * b_b2r[:, None] * exp(b_gk2r)).to(b_k2r.dtype)
|
||||
b_kb3 = (b_k3r * b_b3r[:, None] * exp(b_gk3r)).to(b_k3r.dtype)
|
||||
b_kb0 = (b_k0r * b_b0[:, None] * exp2(b_gk0r)).to(b_k0r.dtype)
|
||||
b_kb1 = (b_k1r * b_b1r[:, None] * exp2(b_gk1r)).to(b_k1r.dtype)
|
||||
b_kb2 = (b_k2r * b_b2r[:, None] * exp2(b_gk2r)).to(b_k2r.dtype)
|
||||
b_kb3 = (b_k3r * b_b3r[:, None] * exp2(b_gk3r)).to(b_k3r.dtype)
|
||||
|
||||
b_w0 = tl.dot(b_Ai00_h, b_kb0)
|
||||
b_w1 = tl.dot(b_Ai10_h, b_kb0) + tl.dot(b_Ai11_h, b_kb1)
|
||||
@@ -716,10 +716,10 @@ def chunk_kda_fwd_kernel_inter_solve_fused(
|
||||
tl.store(p_w2, b_w2.to(p_w2.dtype.element_ty), boundary_check=(0, 1))
|
||||
tl.store(p_w3, b_w3.to(p_w3.dtype.element_ty), boundary_check=(0, 1))
|
||||
|
||||
b_kg0 = b_k0r * exp(b_gn[None, :] - b_gk0r)
|
||||
b_kg1 = b_k1r * exp(b_gn[None, :] - b_gk1r)
|
||||
b_kg2 = b_k2r * exp(b_gn[None, :] - b_gk2r)
|
||||
b_kg3 = b_k3r * exp(b_gn[None, :] - b_gk3r)
|
||||
b_kg0 = b_k0r * exp2(b_gn[None, :] - b_gk0r)
|
||||
b_kg1 = b_k1r * exp2(b_gn[None, :] - b_gk1r)
|
||||
b_kg2 = b_k2r * exp2(b_gn[None, :] - b_gk2r)
|
||||
b_kg3 = b_k3r * exp2(b_gn[None, :] - b_gk3r)
|
||||
|
||||
p_kg0 = tl.make_block_ptr(
|
||||
kg_base, (T, K), (H * K, 1), (i_tc0, i_k * BK), (BC, BK), (1, 0)
|
||||
@@ -918,7 +918,6 @@ def chunk_kda_fwd_intra(
|
||||
chunk_size: int = 64,
|
||||
chunk_indices: torch.LongTensor | None = None,
|
||||
safe_gate: bool = False,
|
||||
disable_recompute: bool = False,
|
||||
fuse_recompute: bool = False,
|
||||
fuse_diagonal: bool = False,
|
||||
):
|
||||
@@ -1042,14 +1041,13 @@ def chunk_kda_fwd_intra(
|
||||
recompute_w_u_fwd as kda_recompute_w_u_fwd,
|
||||
)
|
||||
|
||||
w, u, qg, kg = kda_recompute_w_u_fwd(
|
||||
w, u, kg = kda_recompute_w_u_fwd(
|
||||
k=k,
|
||||
v=v,
|
||||
beta=beta,
|
||||
A=Akk,
|
||||
q=q if disable_recompute else None,
|
||||
gk=gk,
|
||||
cu_seqlens=cu_seqlens,
|
||||
chunk_indices=chunk_indices,
|
||||
)
|
||||
return w, u, qg, kg, Aqk, Akk
|
||||
return w, u, None, kg, Aqk, Akk
|
||||
|
||||
@@ -23,7 +23,7 @@ from sglang.kernels.ops.attention.fla.index import (
|
||||
prepare_chunk_indices,
|
||||
)
|
||||
from sglang.kernels.ops.attention.fla.l2norm import l2norm_fwd
|
||||
from sglang.kernels.ops.attention.fla.op import exp, log
|
||||
from sglang.kernels.ops.attention.fla.op import exp, exp2, log
|
||||
from sglang.kernels.ops.attention.fla.utils import (
|
||||
check_shared_mem,
|
||||
is_intel,
|
||||
@@ -37,6 +37,10 @@ if is_intel:
|
||||
|
||||
BS_LIST = [32, 64] if check_shared_mem() else [16, 32]
|
||||
|
||||
# Convert natural-log gates to log2 space before the exp2-based chunk kernels.
|
||||
# log2(e) rounded to fp32, matching flash-linear-attention.
|
||||
RCP_LN2 = 1.4426950216293335
|
||||
|
||||
|
||||
def cdiv(a: int, b: int) -> int:
|
||||
"""Ceiling division."""
|
||||
@@ -223,6 +227,7 @@ def chunk_kda_scaled_dot_kkt_fwd_kernel_intra_sub_inter(
|
||||
A,
|
||||
Aqk,
|
||||
scale,
|
||||
gk_scale,
|
||||
cu_seqlens,
|
||||
chunk_indices,
|
||||
T,
|
||||
@@ -288,19 +293,22 @@ def chunk_kda_scaled_dot_kkt_fwd_kernel_intra_sub_inter(
|
||||
o_k = i_k * BK + tl.arange(0, BK)
|
||||
m_k = o_k < K
|
||||
# [BK,]
|
||||
b_gn = tl.load(g + (i_t * BT + i_i * BC) * H * K + o_k, mask=m_k, other=0)
|
||||
b_gn = (
|
||||
tl.load(g + (i_t * BT + i_i * BC) * H * K + o_k, mask=m_k, other=0)
|
||||
* gk_scale
|
||||
)
|
||||
# [BC, BK]
|
||||
b_g = tl.load(p_g, boundary_check=(0, 1))
|
||||
b_k = tl.load(p_k, boundary_check=(0, 1)) * exp(b_g - b_gn[None, :])
|
||||
b_g = tl.load(p_g, boundary_check=(0, 1)) * gk_scale
|
||||
b_k = tl.load(p_k, boundary_check=(0, 1)) * exp2(b_g - b_gn[None, :])
|
||||
# [BK, BC]
|
||||
b_gk = tl.load(p_gk, boundary_check=(0, 1))
|
||||
b_gk = tl.load(p_gk, boundary_check=(0, 1)) * gk_scale
|
||||
b_kt = tl.load(b_kt, boundary_check=(0, 1))
|
||||
# [BC, BC]
|
||||
b_ktg = b_kt * exp(b_gn[:, None] - b_gk)
|
||||
b_ktg = b_kt * exp2(b_gn[:, None] - b_gk)
|
||||
b_A += tl.dot(b_k, b_ktg)
|
||||
|
||||
b_q = tl.load(p_q, boundary_check=(0, 1))
|
||||
b_qg = b_q * exp(b_g - b_gn[None, :]) * scale
|
||||
b_qg = b_q * exp2(b_g - b_gn[None, :]) * scale
|
||||
b_Aqk += tl.dot(b_qg, b_ktg)
|
||||
|
||||
b_A *= b_b[:, None]
|
||||
@@ -328,6 +336,7 @@ def chunk_kda_scaled_dot_kkt_fwd_kernel_intra_sub_intra(
|
||||
A,
|
||||
Aqk,
|
||||
scale,
|
||||
gk_scale,
|
||||
cu_seqlens,
|
||||
chunk_indices,
|
||||
T,
|
||||
@@ -388,7 +397,7 @@ def chunk_kda_scaled_dot_kkt_fwd_kernel_intra_sub_intra(
|
||||
)
|
||||
b_q = tl.load(p_q, boundary_check=(0, 1))
|
||||
b_k = tl.load(p_k, boundary_check=(0, 1))
|
||||
b_g = tl.load(p_g, boundary_check=(0, 1))
|
||||
b_g = tl.load(p_g, boundary_check=(0, 1)) * gk_scale
|
||||
|
||||
p_b = beta + (bos + i_t * BT + i_i * BC + o_i) * H + i_h
|
||||
b_k = b_k * tl.load(p_b, mask=m_A, other=0)[:, None]
|
||||
@@ -398,8 +407,8 @@ def chunk_kda_scaled_dot_kkt_fwd_kernel_intra_sub_intra(
|
||||
|
||||
for j in range(0, min(BC, T - i_t * BT - i_i * BC)):
|
||||
b_kt = tl.load(p_kt, mask=m_k, other=0).to(tl.float32)
|
||||
b_gk = tl.load(p_gk, mask=m_k, other=0).to(tl.float32)
|
||||
b_ktg = b_kt[None, :] * exp(b_g - b_gk[None, :])
|
||||
b_gk = tl.load(p_gk, mask=m_k, other=0).to(tl.float32) * gk_scale
|
||||
b_ktg = b_kt[None, :] * exp2(b_g - b_gk[None, :])
|
||||
b_A = tl.sum(b_k * b_ktg, 1)
|
||||
b_A = tl.where(o_i > j, b_A, 0.0)
|
||||
b_Aqk = tl.sum(b_q * b_ktg, 1)
|
||||
@@ -416,6 +425,7 @@ def chunk_kda_scaled_dot_kkt_fwd(
|
||||
gk: torch.Tensor | None = None,
|
||||
beta: torch.Tensor | None = None,
|
||||
scale: float | None = None,
|
||||
gk_scale: float = 1.0,
|
||||
cu_seqlens: torch.LongTensor | None = None,
|
||||
chunk_size: int = 64,
|
||||
output_dtype: torch.dtype = torch.float32,
|
||||
@@ -429,7 +439,13 @@ def chunk_kda_scaled_dot_kkt_fwd(
|
||||
beta (torch.Tensor):
|
||||
The beta tensor of shape `[B, T, H]`.
|
||||
gk (torch.Tensor):
|
||||
The cumulative sum of the gate tensor of shape `[B, T, H, K]` applied to the key tensor. Default: `None`.
|
||||
The cumulative sum of the gate tensor of shape `[B, T, H, K]` applied to the key tensor,
|
||||
in log2 space (the kernels apply `exp2`).
|
||||
Default: `None`.
|
||||
gk_scale (float):
|
||||
Scale multiplied onto `gk` as it is loaded in the kernels. Pass a natural-log
|
||||
cumsum with `gk_scale=RCP_LN2` to convert to log2 space in-kernel without
|
||||
materializing a scaled copy of `gk`. Default: `1.0`.
|
||||
cu_seqlens (torch.LongTensor):
|
||||
The cumulative sequence lengths of the input tensor.
|
||||
Default: None
|
||||
@@ -463,6 +479,7 @@ def chunk_kda_scaled_dot_kkt_fwd(
|
||||
A=A,
|
||||
Aqk=Aqk,
|
||||
scale=scale,
|
||||
gk_scale=gk_scale,
|
||||
cu_seqlens=cu_seqlens,
|
||||
chunk_indices=chunk_indices,
|
||||
T=T,
|
||||
@@ -483,6 +500,7 @@ def chunk_kda_scaled_dot_kkt_fwd(
|
||||
A=A,
|
||||
Aqk=Aqk,
|
||||
scale=scale,
|
||||
gk_scale=gk_scale,
|
||||
cu_seqlens=cu_seqlens,
|
||||
chunk_indices=chunk_indices,
|
||||
T=T,
|
||||
@@ -508,9 +526,7 @@ def chunk_kda_scaled_dot_kkt_fwd(
|
||||
)
|
||||
@triton.jit(do_not_specialize=["T"])
|
||||
def recompute_w_u_fwd_kernel(
|
||||
q,
|
||||
k,
|
||||
qg,
|
||||
kg,
|
||||
v,
|
||||
beta,
|
||||
@@ -527,7 +543,6 @@ def recompute_w_u_fwd_kernel(
|
||||
BT: tl.constexpr,
|
||||
BK: tl.constexpr,
|
||||
BV: tl.constexpr,
|
||||
STORE_QG: tl.constexpr,
|
||||
STORE_KG: tl.constexpr,
|
||||
IS_VARLEN: tl.constexpr,
|
||||
DOT_PRECISION: tl.constexpr,
|
||||
@@ -605,27 +620,7 @@ def recompute_w_u_fwd_kernel(
|
||||
(1, 0),
|
||||
)
|
||||
b_gk = tl.load(p_gk, boundary_check=(0, 1))
|
||||
b_kb *= exp(b_gk)
|
||||
if STORE_QG:
|
||||
p_q = tl.make_block_ptr(
|
||||
q + (bos * H + i_h) * K,
|
||||
(T, K),
|
||||
(H * K, 1),
|
||||
(i_t * BT, i_k * BK),
|
||||
(BT, BK),
|
||||
(1, 0),
|
||||
)
|
||||
p_qg = tl.make_block_ptr(
|
||||
qg + (bos * H + i_h) * K,
|
||||
(T, K),
|
||||
(H * K, 1),
|
||||
(i_t * BT, i_k * BK),
|
||||
(BT, BK),
|
||||
(1, 0),
|
||||
)
|
||||
b_q = tl.load(p_q, boundary_check=(0, 1))
|
||||
b_qg = b_q * exp(b_gk)
|
||||
tl.store(p_qg, b_qg.to(p_qg.dtype.element_ty), boundary_check=(0, 1))
|
||||
b_kb *= exp2(b_gk)
|
||||
if STORE_KG:
|
||||
last_idx = min(i_t * BT + BT, T) - 1
|
||||
|
||||
@@ -634,7 +629,7 @@ def recompute_w_u_fwd_kernel(
|
||||
b_gn = tl.load(
|
||||
gk + ((bos + last_idx) * H + i_h) * K + o_k, mask=m_k, other=0.0
|
||||
)
|
||||
b_kg = b_k * exp(b_gn - b_gk)
|
||||
b_kg = b_k * exp2(b_gn - b_gk)
|
||||
|
||||
p_kg = tl.make_block_ptr(
|
||||
kg + (bos * H + i_h) * K,
|
||||
@@ -655,11 +650,10 @@ def recompute_w_u_fwd(
|
||||
v: torch.Tensor,
|
||||
beta: torch.Tensor,
|
||||
A: torch.Tensor,
|
||||
q: torch.Tensor | None = None,
|
||||
gk: torch.Tensor | None = None,
|
||||
cu_seqlens: torch.LongTensor | None = None,
|
||||
chunk_indices: torch.LongTensor | None = None,
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor | None]:
|
||||
B, T, H, K, V = *k.shape, v.shape[-1]
|
||||
BT = A.shape[-1]
|
||||
|
||||
@@ -671,9 +665,7 @@ def recompute_w_u_fwd(
|
||||
u = torch.empty_like(v)
|
||||
kg = torch.empty_like(k) if gk is not None else None
|
||||
recompute_w_u_fwd_kernel[(NT, B * H)](
|
||||
q=q,
|
||||
k=k,
|
||||
qg=None,
|
||||
kg=kg,
|
||||
v=v,
|
||||
beta=beta,
|
||||
@@ -688,12 +680,11 @@ def recompute_w_u_fwd(
|
||||
K=K,
|
||||
V=V,
|
||||
BT=BT,
|
||||
STORE_QG=False,
|
||||
STORE_KG=kg is not None,
|
||||
IS_VARLEN=cu_seqlens is not None,
|
||||
DOT_PRECISION="tf32",
|
||||
)
|
||||
return w, u, None, kg
|
||||
return w, u, kg
|
||||
|
||||
|
||||
@triton.autotune(
|
||||
@@ -780,7 +771,7 @@ def chunk_gla_fwd_kernel_o(
|
||||
# [BT, BK]
|
||||
b_g = tl.load(p_g, boundary_check=(0, 1))
|
||||
# [BT, BK]
|
||||
b_qg = (b_q * exp(b_g)).to(b_q.dtype)
|
||||
b_qg = (b_q * exp2(b_g)).to(b_q.dtype)
|
||||
# [BK, BV]
|
||||
b_h = tl.load(p_h, boundary_check=(0, 1))
|
||||
# works but dkw, owing to divine benevolence
|
||||
@@ -1060,6 +1051,7 @@ def chunk_kda_fwd(
|
||||
g,
|
||||
A_log=A_log,
|
||||
chunk_size=chunk_size,
|
||||
scale=RCP_LN2,
|
||||
dt_bias=dt_bias,
|
||||
cu_seqlens=cu_seqlens,
|
||||
chunk_indices=chunk_indices,
|
||||
@@ -1070,6 +1062,7 @@ def chunk_kda_fwd(
|
||||
g = chunk_local_cumsum(
|
||||
g,
|
||||
chunk_size=chunk_size,
|
||||
scale=RCP_LN2,
|
||||
cu_seqlens=cu_seqlens,
|
||||
chunk_indices=chunk_indices,
|
||||
)
|
||||
@@ -1114,6 +1107,7 @@ def chunk_kda_fwd(
|
||||
initial_state_indices=initial_state_indices,
|
||||
cu_seqlens=cu_seqlens,
|
||||
chunk_indices=chunk_indices,
|
||||
use_exp2=True,
|
||||
)
|
||||
del w, u, kg
|
||||
|
||||
|
||||
@@ -173,13 +173,20 @@ def chunk_kda_cutedsl(
|
||||
# injected through the cutedsl KKT/Aqk MMAs as an identity-right-operand pass:
|
||||
# with kL'=M (M in the first 64 K-slots) and kR'=onehot(chunk-pos), the MMA
|
||||
# kL'@kR'.T == M, so kkt_inv_uw/kernel_o see the correct matrix without overflow.
|
||||
from sglang.kernels.ops.attention.fla.kda import chunk_kda_scaled_dot_kkt_fwd
|
||||
from sglang.kernels.ops.attention.fla.kda import (
|
||||
RCP_LN2,
|
||||
chunk_kda_scaled_dot_kkt_fwd,
|
||||
)
|
||||
|
||||
ones_beta = q.new_ones(1, T, Hv, dtype=torch.float32)
|
||||
# The FLA kkt kernels consume log2-space gate cumsums (exp2-based); g_cu must
|
||||
# stay natural-log for the cutedsl kernels below, so let the kernels apply
|
||||
# gk_scale=RCP_LN2 at load time instead of materializing a scaled copy.
|
||||
M_kk, M_qk = chunk_kda_scaled_dot_kkt_fwd(
|
||||
q.unsqueeze(0).contiguous(),
|
||||
k.unsqueeze(0).contiguous(),
|
||||
gk=g_cu.unsqueeze(0),
|
||||
gk_scale=RCP_LN2,
|
||||
beta=ones_beta,
|
||||
scale=float(scale),
|
||||
cu_seqlens=cu_seqlens,
|
||||
|
||||
@@ -8,7 +8,12 @@ from sglang.kernels.ops.attention.fla.index import (
|
||||
prepare_chunk_indices,
|
||||
prepare_chunk_offsets,
|
||||
)
|
||||
from sglang.kernels.ops.attention.fla.op import exp, make_tensor_descriptor, safe_exp
|
||||
from sglang.kernels.ops.attention.fla.op import (
|
||||
exp,
|
||||
exp2,
|
||||
make_tensor_descriptor,
|
||||
safe_exp,
|
||||
)
|
||||
from sglang.kernels.ops.attention.fla.utils import (
|
||||
autotune_cache_kwargs,
|
||||
)
|
||||
@@ -52,6 +57,7 @@ def chunk_gated_delta_rule_fwd_kernel_h_blockdim64_k_loop(
|
||||
SAVE_NEW_VALUE: tl.constexpr,
|
||||
IS_VARLEN: tl.constexpr,
|
||||
NT_BUCKET: tl.constexpr, # this arg is kept to align with the triton kernel for CUDA
|
||||
USE_EXP2: tl.constexpr,
|
||||
):
|
||||
i_v, i_nh = tl.program_id(0), tl.program_id(1)
|
||||
i_n, i_h = i_nh // H, i_nh % H
|
||||
@@ -199,7 +205,10 @@ def chunk_gated_delta_rule_fwd_kernel_h_blockdim64_k_loop(
|
||||
mask=(o_k1 < K),
|
||||
other=0.0,
|
||||
)
|
||||
b_h *= tl.expand_dims(exp(b_gk_last1), 0)
|
||||
if USE_EXP2:
|
||||
b_h *= tl.expand_dims(exp2(b_gk_last1), 0)
|
||||
else:
|
||||
b_h *= tl.expand_dims(exp(b_gk_last1), 0)
|
||||
|
||||
# Delta update: h += k^T @ v
|
||||
b_k = tl.trans(k_desc.load([i_t * BT, k_blk]))
|
||||
@@ -224,7 +233,11 @@ def chunk_gated_delta_rule_fwd_h(
|
||||
save_new_value: bool = True,
|
||||
cu_seqlens: Optional[torch.LongTensor] = None,
|
||||
chunk_indices: Optional[torch.LongTensor] = None,
|
||||
use_exp2: bool = False,
|
||||
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
||||
assert not (
|
||||
use_exp2 and g is not None
|
||||
), "use_exp2 covers only the per-channel gk path; scalar g stays natural-exp"
|
||||
B, T, Hg, K, V = *k.shape, u.shape[-1]
|
||||
H = u.shape[-2]
|
||||
BT = CHUNK_SIZE
|
||||
@@ -276,5 +289,6 @@ def chunk_gated_delta_rule_fwd_h(
|
||||
SAVE_NEW_VALUE=v_new is not None,
|
||||
IS_VARLEN=cu_seqlens is not None,
|
||||
NT_BUCKET=(0 if NT <= 32 else (1 if NT <= 128 else 2)),
|
||||
USE_EXP2=use_exp2,
|
||||
)
|
||||
return h, v_new
|
||||
|
||||
Reference in New Issue
Block a user