KernelBench hard · H100
KDA CUTLASS Grok 4.5
1.13%geomean peak fraction across shapes
harnessgrokagent session15mtotal wall16mcheck46sbenchmark8soutput tokens—gpu-lock wait4sgpu-lock held50sregimecompute
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
2×1024×8×128×128×640.217 ms1.3%0.12 TB/s · 6% of 2.0 TB/s HBM · also 10 TFLOPS (1% of compute)
2×2048×8×128×128×640.374 ms1.5%0.13 TB/s · 7% of 2.0 TB/s HBM · also 11 TFLOPS (2% of compute)
1×4096×8×128×128×640.448 ms1.3%0.11 TB/s · 6% of 2.0 TB/s HBM · also 10 TFLOPS (1% of compute)
1×2048×4×128×128×640.219 ms0.7%0.06 TB/s · 3% of 2.0 TB/s HBM · also 5 TFLOPS (1% of compute)
compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)
geomean(1.3% · 1.5% · 1.3% · 0.7%) = 1.1%
Kernel source (redacted)
"""Kimi Delta Attention (KDA) forward — chunk form, custom Triton kernels (SM90).
Implements the same math as reference.py (chunk-parallel KDA) without calling
any forbidden library entry points. Pipeline:
1. local cumsum of per-channel log-decay g within each chunk
2. intra-chunk: Aqk (lower-tri incl diag) + Akk via WY / tril solve
3. w = A @ (beta ⊙ exp(g) ⊙ k), u = A @ (beta ⊙ v)
4. inter-chunk gated-delta state recurrence → v_new, h
5. o = (q ⊙ exp(g)) @ h + Aqk @ v_new
Specialized for BT=64, K=V=128, H==HV (no GVA), no initial/final state.
"""
from __future__ import annotations
import torch
import torch.nn as nn
import triton
import triton.language as tl
# log2(e) so exp(x) = exp2(x * LOG2E) (matches FLA's RCP_LN2 cumsum path)
LOG2E = 1.4426950408889634
# ---------------------------------------------------------------------------
# Kernel 1: in-chunk cumsum of g, convert to log2 space (scale by LOG2E)
# g_in: [B, T, H, K] fp32 (raw log-decay, natural)
# g_out: [B, T, H, K] fp32 (cumsum * LOG2E so exp2(g_out) == exp(cumsum_nat))
# ---------------------------------------------------------------------------
@triton.jit
def _local_cumsum_kernel(
g_in,
g_out,
T,
H: tl.constexpr,
K: tl.constexpr,
BT: tl.constexpr,
BK: tl.constexpr,
SCALE: tl.constexpr,
):
i_bh = tl.program_id(0)
i_t = tl.program_id(1)
i_k = tl.program_id(2)
i_b = i_bh // H
i_h = i_bh % H
bos = i_b * T
offs_t = i_t * BT + tl.arange(0, BT)
offs_k = i_k * BK + tl.arange(0, BK)
mask_t = offs_t < T
mask_k = offs_k < K
mask = mask_t[:, None] & mask_k[None, :]
ptrs = g_in + ((bos + offs_t)[:, None] * H + i_h) * K + offs_k[None, :]
vals = tl.load(ptrs, mask=mask, other=0.0).to(tl.float32) * SCALE
# inclusive cumsum along the chunk time axis (axis 0)
out = tl.cumsum(vals, axis=0)
out_ptrs = g_out + ((bos + offs_t)[:, None] * H + i_h) * K + offs_k[None, :]
tl.store(out_ptrs, out, mask=mask)
# ---------------------------------------------------------------------------
# Kernel 2: diagonal sub-chunk Aqk / Akk (BC x BC), token-serial within subchunk
# Computes lower-tri Aqk (incl diag) and strict-lower Akk (row-scaled by beta).
# Akk written as raw L (not yet inverted) into Akkd fp32 buffer of width BC.
# ---------------------------------------------------------------------------
@triton.jit
def _intra_diag_kernel(
q,
k,
g,
beta,
Aqk,
Akkd,
scale,
T,
H: tl.constexpr,
K: tl.constexpr,
BT: tl.constexpr,
BC: tl.constexpr,
BK: tl.constexpr,
):
"""One program = one (batch, head, chunk, sub-chunk)."""
i_bh = tl.program_id(0)
i_tc = tl.program_id(1) # chunk index * NC + subchunk index
NC: tl.constexpr = BT // BC
i_t = i_tc // NC
i_s = i_tc % NC
i_b = i_bh // H
i_h = i_bh % H
bos = i_b * T
i_ti = i_t * BT + i_s * BC # absolute start of this sub-chunk
q += (bos * H + i_h) * K
k += (bos * H + i_h) * K
g += (bos * H + i_h) * K
beta += bos * H + i_h
Aqk += (bos * H + i_h) * BT
Akkd += (bos * H + i_h) * BC
o_i = tl.arange(0, BC)
o_k = tl.arange(0, BK)
m_k = o_k < K
# Midpoint offset for numerical stability (same trick as FLA safe path)
mid = BC // 2
b_gn = tl.load(g + (i_ti + mid) * H * K + o_k, mask=m_k, other=0.0)
# Load full sub-chunk blocks
p_q = tl.make_block_ptr(q, (T, K), (H * K, 1), (i_ti, 0), (BC, BK), (1, 0))
p_k = tl.make_block_ptr(k, (T, K), (H * K, 1), (i_ti, 0), (BC, BK), (1, 0))
p_g = tl.make_block_ptr(g, (T, K), (H * K, 1), (i_ti, 0), (BC, BK), (1, 0))
p_b = tl.make_block_ptr(beta, (T,), (H,), (i_ti,), (BC,), (0,))
b_q = tl.load(p_q, boundary_check=(0, 1)).to(tl.float32)
b_k = tl.load(p_k, boundary_check=(0, 1)).to(tl.float32)
b_g = tl.load(p_g, boundary_check=(0, 1)).to(tl.float32)
b_beta = tl.load(p_b, boundary_check=(0,)).to(tl.float32)
b_gm = b_g - b_gn[None, :]
b_gq = tl.math.exp2(b_gm)
b_gk = tl.math.exp2(-b_gm)
b_kgt = tl.trans(b_k * b_gk)
b_Aqk = tl.dot(b_q * b_gq, b_kgt) * scale
b_Akk = tl.dot(b_k * b_gq, b_kgt) * b_beta[:, None]
m_Aqk = o_i[:, None] >= o_i[None, :]
m_Akk = o_i[:, None] > o_i[None, :]
m_I = o_i[:, None] == o_i[None, :]
b_Aqk = tl.where(m_Aqk, b_Aqk, 0.0)
b_Akk = tl.where(m_Akk, b_Akk, 0.0)
p_Aqk = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_ti, i_s * BC), (BC, BC), (1, 0))
tl.store(p_Aqk, b_Aqk.to(Aqk.dtype.element_ty), boundary_check=(0, 1))
# Forward substitution on diagonal: invert (I - L) entirely in registers.
# Row i of -L is extracted via masked sum so we never reload from HBM.
b_Ai = -b_Akk
for i in range(2, BC):
b_a = -tl.sum(tl.where((o_i == i)[:, None], b_Akk, 0.0), 0)
b_a = tl.where(o_i < i, b_a, 0.0)
b_a += tl.sum(b_a[:, None] * b_Ai, 0)
b_Ai = tl.where((o_i == i)[:, None], b_a, b_Ai)
b_Ai += m_I
p_Akkd = tl.make_block_ptr(Akkd, (T, BC), (H * BC, 1), (i_ti, 0), (BC, BC), (1, 0))
tl.store(p_Akkd, b_Ai.to(tl.float32), boundary_check=(0, 1))
# ---------------------------------------------------------------------------
# Kernel 3: off-diagonal Aqk / Akk + merge 4x4 block inverse into full Akk
# ---------------------------------------------------------------------------
@triton.jit
def _inter_solve_kernel(
q,
k,
g,
beta,
Aqk,
Akkd,
Akk,
scale,
T,
H: tl.constexpr,
K: tl.constexpr,
BT: tl.constexpr,
BC: tl.constexpr,
BK: tl.constexpr,
):
i_t = tl.program_id(0)
i_bh = tl.program_id(1)
i_b = i_bh // H
i_h = i_bh % H
bos = i_b * T
i_tc0 = i_t * BT
i_tc1 = i_t * BT + BC
i_tc2 = i_t * BT + 2 * BC
i_tc3 = i_t * BT + 3 * BC
q += (bos * H + i_h) * K
k += (bos * H + i_h) * K
g += (bos * H + i_h) * K
beta += bos * H + i_h
Aqk += (bos * H + i_h) * BT
Akk += (bos * H + i_h) * BT
Akkd += (bos * H + i_h) * BC
o_i = tl.arange(0, BC)
b_Aqk10 = tl.zeros([BC, BC], dtype=tl.float32)
b_Akk10 = tl.zeros([BC, BC], dtype=tl.float32)
b_Aqk20 = tl.zeros([BC, BC], dtype=tl.float32)
b_Akk20 = tl.zeros([BC, BC], dtype=tl.float32)
b_Aqk21 = tl.zeros([BC, BC], dtype=tl.float32)
b_Akk21 = tl.zeros([BC, BC], dtype=tl.float32)
b_Aqk30 = tl.zeros([BC, BC], dtype=tl.float32)
b_Akk30 = tl.zeros([BC, BC], dtype=tl.float32)
b_Aqk31 = tl.zeros([BC, BC], dtype=tl.float32)
b_Akk31 = tl.zeros([BC, BC], dtype=tl.float32)
b_Aqk32 = tl.zeros([BC, BC], dtype=tl.float32)
b_Akk32 = tl.zeros([BC, BC], dtype=tl.float32)
for i_k in range(tl.cdiv(K, BK)):
o_k = i_k * BK + tl.arange(0, BK)
m_k = o_k < K
p_k0 = tl.make_block_ptr(k, (T, K), (H * K, 1), (i_tc0, i_k * BK), (BC, BK), (1, 0))
p_g0 = tl.make_block_ptr(g, (T, K), (H * K, 1), (i_tc0, i_k * BK), (BC, BK), (1, 0))
b_k0 = tl.load(p_k0, boundary_check=(0, 1)).to(tl.float32)
b_g0 = tl.load(p_g0, boundary_check=(0, 1)).to(tl.float32)
# block 1 vs 0
p_q1 = tl.make_block_ptr(q, (T, K), (H * K, 1), (i_tc1, i_k * BK), (BC, BK), (1, 0))
p_k1 = tl.make_block_ptr(k, (T, K), (H * K, 1), (i_tc1, i_k * BK), (BC, BK), (1, 0))
p_g1 = tl.make_block_ptr(g, (T, K), (H * K, 1), (i_tc1, i_k * BK), (BC, BK), (1, 0))
b_q1 = tl.load(p_q1, boundary_check=(0, 1)).to(tl.float32)
b_k1 = tl.load(p_k1, boundary_check=(0, 1)).to(tl.float32)
b_g1 = tl.load(p_g1, boundary_check=(0, 1)).to(tl.float32)
b_gn1 = tl.load(g + i_tc1 * H * K + o_k, mask=m_k, other=0.0).to(tl.float32)
b_gqn = tl.math.exp2(b_g1 - b_gn1[None, :])
b_kgt = tl.trans(b_k0 * tl.math.exp2(b_gn1[None, :] - b_g0))
b_Aqk10 += tl.dot(b_q1 * b_gqn, b_kgt)
b_Akk10 += tl.dot(b_k1 * b_gqn, b_kgt)
# block 2 vs 0,1
p_q2 = tl.make_block_ptr(q, (T, K), (H * K, 1), (i_tc2, i_k * BK), (BC, BK), (1, 0))
p_k2 = tl.make_block_ptr(k, (T, K), (H * K, 1), (i_tc2, i_k * BK), (BC, BK), (1, 0))
p_g2 = tl.make_block_ptr(g, (T, K), (H * K, 1), (i_tc2, i_k * BK), (BC, BK), (1, 0))
b_q2 = tl.load(p_q2, boundary_check=(0, 1)).to(tl.float32)
b_k2 = tl.load(p_k2, boundary_check=(0, 1)).to(tl.float32)
b_g2 = tl.load(p_g2, boundary_check=(0, 1)).to(tl.float32)
b_gn2 = tl.load(g + i_tc2 * H * K + o_k, mask=m_k, other=0.0).to(tl.float32)
b_gqn2 = tl.math.exp2(b_g2 - b_gn2[None, :])
b_qg2 = b_q2 * b_gqn2
b_kg2 = b_k2 * b_gqn2
b_kgt = tl.trans(b_k0 * tl.math.exp2(b_gn2[None, :] - b_g0))
b_Aqk20 += tl.dot(b_qg2, b_kgt)
b_Akk20 += tl.dot(b_kg2, b_kgt)
b_kgt = tl.trans(b_k1 * tl.math.exp2(b_gn2[None, :] - b_g1))
b_Aqk21 += tl.dot(b_qg2, b_kgt)
b_Akk21 += tl.dot(b_kg2, b_kgt)
# block 3 vs 0,1,2
p_q3 = tl.make_block_ptr(q, (T, K), (H * K, 1), (i_tc3, i_k * BK), (BC, BK), (1, 0))
p_k3 = tl.make_block_ptr(k, (T, K), (H * K, 1), (i_tc3, i_k * BK), (BC, BK), (1, 0))
p_g3 = tl.make_block_ptr(g, (T, K), (H * K, 1), (i_tc3, i_k * BK), (BC, BK), (1, 0))
b_q3 = tl.load(p_q3, boundary_check=(0, 1)).to(tl.float32)
b_k3 = tl.load(p_k3, boundary_check=(0, 1)).to(tl.float32)
b_g3 = tl.load(p_g3, boundary_check=(0, 1)).to(tl.float32)
b_gn3 = tl.load(g + i_tc3 * H * K + o_k, mask=m_k, other=0.0).to(tl.float32)
b_gqn3 = tl.math.exp2(b_g3 - b_gn3[None, :])
b_qg3 = b_q3 * b_gqn3
b_kg3 = b_k3 * b_gqn3
b_kgt = tl.trans(b_k0 * tl.math.exp2(b_gn3[None, :] - b_g0))
b_Aqk30 += tl.dot(b_qg3, b_kgt)
b_Akk30 += tl.dot(b_kg3, b_kgt)
b_kgt = tl.trans(b_k1 * tl.math.exp2(b_gn3[None, :] - b_g1))
b_Aqk31 += tl.dot(b_qg3, b_kgt)
b_Akk31 += tl.dot(b_kg3, b_kgt)
b_kgt = tl.trans(b_k2 * tl.math.exp2(b_gn3[None, :] - b_g2))
b_Aqk32 += tl.dot(b_qg3, b_kgt)
b_Akk32 += tl.dot(b_kg3, b_kgt)
# store off-diagonal Aqk (with scale) and apply beta to Akk off-diagonals
p_Aqk10 = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_tc1, 0), (BC, BC), (1, 0))
tl.store(p_Aqk10, (b_Aqk10 * scale).to(Aqk.dtype.element_ty), boundary_check=(0, 1))
b_b1 = tl.load(tl.make_block_ptr(beta, (T,), (H,), (i_tc1,), (BC,), (0,)), boundary_check=(0,)).to(tl.float32)
b_Akk10 = b_Akk10 * b_b1[:, None]
p_Aqk20 = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_tc2, 0), (BC, BC), (1, 0))
p_Aqk21 = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_tc2, BC), (BC, BC), (1, 0))
tl.store(p_Aqk20, (b_Aqk20 * scale).to(Aqk.dtype.element_ty), boundary_check=(0, 1))
tl.store(p_Aqk21, (b_Aqk21 * scale).to(Aqk.dtype.element_ty), boundary_check=(0, 1))
b_b2 = tl.load(tl.make_block_ptr(beta, (T,), (H,), (i_tc2,), (BC,), (0,)), boundary_check=(0,)).to(tl.float32)
b_Akk20 = b_Akk20 * b_b2[:, None]
b_Akk21 = b_Akk21 * b_b2[:, None]
p_Aqk30 = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_tc3, 0), (BC, BC), (1, 0))
p_Aqk31 = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_tc3, BC), (BC, BC), (1, 0))
p_Aqk32 = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_tc3, 2 * BC), (BC, BC), (1, 0))
tl.store(p_Aqk30, (b_Aqk30 * scale).to(Aqk.dtype.element_ty), boundary_check=(0, 1))
tl.store(p_Aqk31, (b_Aqk31 * scale).to(Aqk.dtype.element_ty), boundary_check=(0, 1))
tl.store(p_Aqk32, (b_Aqk32 * scale).to(Aqk.dtype.element_ty), boundary_check=(0, 1))
b_b3 = tl.load(tl.make_block_ptr(beta, (T,), (H,), (i_tc3,), (BC,), (0,)), boundary_check=(0,)).to(tl.float32)
b_Akk30 = b_Akk30 * b_b3[:, None]
b_Akk31 = b_Akk31 * b_b3[:, None]
b_Akk32 = b_Akk32 * b_b3[:, None]
# load diagonal inverses (already solved in _intra_diag_kernel)
b_Ai00 = tl.load(tl.make_block_ptr(Akkd, (T, BC), (H * BC, 1), (i_tc0, 0), (BC, BC), (1, 0)), boundary_check=(0, 1)).to(tl.float32)
b_Ai11 = tl.load(tl.make_block_ptr(Akkd, (T, BC), (H * BC, 1), (i_tc1, 0), (BC, BC), (1, 0)), boundary_check=(0, 1)).to(tl.float32)
b_Ai22 = tl.load(tl.make_block_ptr(Akkd, (T, BC), (H * BC, 1), (i_tc2, 0), (BC, BC), (1, 0)), boundary_check=(0, 1)).to(tl.float32)
b_Ai33 = tl.load(tl.make_block_ptr(Akkd, (T, BC), (H * BC, 1), (i_tc3, 0), (BC, BC), (1, 0)), boundary_check=(0, 1)).to(tl.float32)
# Block-triangular inverse merge:
# Ai10 = -Ai11 @ Akk10 @ Ai00
# Ai21 = -Ai22 @ Akk21 @ Ai11
# Ai20 = -Ai22 @ (Akk20 @ Ai00 + Akk21 @ Ai10)
# ...
b_Ai10 = -tl.dot(tl.dot(b_Ai11, b_Akk10), b_Ai00)
b_Ai21 = -tl.dot(tl.dot(b_Ai22, b_Akk21), b_Ai11)
b_Ai20 = -tl.dot(b_Ai22, tl.dot(b_Akk20, b_Ai00) + tl.dot(b_Akk21, b_Ai10))
b_Ai32 = -tl.dot(tl.dot(b_Ai33, b_Akk32), b_Ai22)
b_Ai31 = -tl.dot(b_Ai33, tl.dot(b_Akk31, b_Ai11) + tl.dot(b_Akk32, b_Ai21))
b_Ai30 = -tl.dot(
b_Ai33,
tl.dot(b_Akk30, b_Ai00) + tl.dot(b_Akk31, b_Ai10) + tl.dot(b_Akk32, b_Ai20),
)
# store full Akk inverse (without column beta — applied in w/u kernel)
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc0, 0), (BC, BC), (1, 0)), b_Ai00.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc1, 0), (BC, BC), (1, 0)), b_Ai10.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc1, BC), (BC, BC), (1, 0)), b_Ai11.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc2, 0), (BC, BC), (1, 0)), b_Ai20.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc2, BC), (BC, BC), (1, 0)), b_Ai21.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc2, 2 * BC), (BC, BC), (1, 0)), b_Ai22.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc3, 0), (BC, BC), (1, 0)), b_Ai30.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc3, BC), (BC, BC), (1, 0)), b_Ai31.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc3, 2 * BC), (BC, BC), (1, 0)), b_Ai32.to(Akk.dtype.element_ty), boundary_check=(0, 1))
tl.store(tl.make_block_ptr(Akk, (T, BT), (H * BT, 1), (i_tc3, 3 * BC), (BC, BC), (1, 0)), b_Ai33.to(Akk.dtype.element_ty), boundary_check=(0, 1))
# ---------------------------------------------------------------------------
# Kernel 4: w = A @ (beta * exp2(g) * k), u = A @ (beta * v), kg = k * exp2(g_last-g)
# ---------------------------------------------------------------------------
@triton.jit
def _wu_kernel(
q,
k,
v,
beta,
A,
g,
w,
u,
kg,
qg,
scale,
T,
H: tl.constexpr,
K: tl.constexpr,
V: tl.constexpr,
BT: tl.constexpr,
BK: tl.constexpr,
BV: tl.constexpr,
):
i_t = tl.program_id(0)
i_bh = tl.program_id(1)
i_b = i_bh // H
i_h = i_bh % H
bos = i_b * T
q += (bos * H + i_h) * K
k += (bos * H + i_h) * K
v += (bos * H + i_h) * V
w += (bos * H + i_h) * K
u += (bos * H + i_h) * V
kg += (bos * H + i_h) * K
qg += (bos * H + i_h) * K
g += (bos * H + i_h) * K
beta += bos * H + i_h
A += (bos * H + i_h) * BT
p_b = tl.make_block_ptr(beta, (T,), (H,), (i_t * BT,), (BT,), (0,))
b_b = tl.load(p_b, boundary_check=(0,)).to(tl.float32)
p_A = tl.make_block_ptr(A, (T, BT), (H * BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
b_A = tl.load(p_A, boundary_check=(0, 1)).to(tl.bfloat16)
last_idx = i_t * BT + BT - 1
for i_v in range(tl.cdiv(V, BV)):
p_v = tl.make_block_ptr(v, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
p_u = tl.make_block_ptr(u, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
b_v = tl.load(p_v, boundary_check=(0, 1)).to(tl.float32)
b_vb = (b_v * b_b[:, None]).to(tl.bfloat16)
b_u = tl.dot(b_A, b_vb)
tl.store(p_u, b_u.to(p_u.dtype.element_ty), boundary_check=(0, 1))
for i_k in range(tl.cdiv(K, BK)):
p_k = tl.make_block_ptr(k, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
p_q = tl.make_block_ptr(q, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
p_g = tl.make_block_ptr(g, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
p_w = tl.make_block_ptr(w, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
p_kg = tl.make_block_ptr(kg, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
p_qg = tl.make_block_ptr(qg, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
b_k = tl.load(p_k, boundary_check=(0, 1)).to(tl.float32)
b_q = tl.load(p_q, boundary_check=(0, 1)).to(tl.float32)
b_g = tl.load(p_g, boundary_check=(0, 1)).to(tl.float32)
o_k = i_k * BK + tl.arange(0, BK)
m_k = o_k < K
b_gn = tl.load(g + last_idx * H * K + o_k, mask=m_k, other=0.0).to(tl.float32)
b_eg = tl.math.exp2(b_g)
b_kb = (b_k * b_b[:, None] * b_eg).to(tl.bfloat16)
b_w = tl.dot(b_A, b_kb)
tl.store(p_w, b_w.to(p_w.dtype.element_ty), boundary_check=(0, 1))
b_kg = b_k * tl.math.exp2(b_gn[None, :] - b_g)
tl.store(p_kg, b_kg.to(p_kg.dtype.element_ty), boundary_check=(0, 1))
b_qg = (b_q * b_eg * scale).to(tl.bfloat16)
tl.store(p_qg, b_qg, boundary_check=(0, 1))
# ---------------------------------------------------------------------------
# Kernel 5: inter-chunk state recurrence
# v_new = u - w @ S
# store S at each chunk start into h
# S = S * exp2(g_last) + kg.T @ v_new
# ---------------------------------------------------------------------------
@triton.jit
def _fwd_h_kernel(
kg,
w,
u,
g,
v_new,
h,
T,
H: tl.constexpr,
K: tl.constexpr,
V: tl.constexpr,
BT: tl.constexpr,
BV: tl.constexpr,
):
i_v = tl.program_id(0)
i_bh = tl.program_id(1)
i_b = i_bh // H
i_h = i_bh % H
bos = i_b * T
NT = tl.cdiv(T, BT)
kg += (bos * H + i_h) * K
w += (bos * H + i_h) * K
u += (bos * H + i_h) * V
v_new += (bos * H + i_h) * V
g += (bos * H + i_h) * K
h += (i_b * NT * H + i_h) * K * V
b_h1 = tl.zeros([64, BV], dtype=tl.float32)
b_h2 = tl.zeros([64, BV], dtype=tl.float32)
for i_t in range(NT):
p_h1 = tl.make_block_ptr(h + i_t * H * K * V, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
p_h2 = tl.make_block_ptr(h + i_t * H * K * V, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
tl.store(p_h1, b_h1.to(p_h1.dtype.element_ty), boundary_check=(0, 1))
tl.store(p_h2, b_h2.to(p_h2.dtype.element_ty), boundary_check=(0, 1))
p_w1 = tl.make_block_ptr(w, (T, K), (H * K, 1), (i_t * BT, 0), (BT, 64), (1, 0))
p_w2 = tl.make_block_ptr(w, (T, K), (H * K, 1), (i_t * BT, 64), (BT, 64), (1, 0))
b_w1 = tl.load(p_w1, boundary_check=(0, 1))
b_w2 = tl.load(p_w2, boundary_check=(0, 1))
b_v = tl.dot(b_w1, b_h1.to(b_w1.dtype)) + tl.dot(b_w2, b_h2.to(b_w2.dtype))
p_u = tl.make_block_ptr(u, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
b_v = tl.load(p_u, boundary_check=(0, 1)) - b_v
p_vn = tl.make_block_ptr(v_new, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
tl.store(p_vn, b_v.to(p_vn.dtype.element_ty), boundary_check=(0, 1))
last_idx = i_t * BT + BT - 1
o_k1 = tl.arange(0, 64)
o_k2 = 64 + o_k1
b_gk1 = tl.load(g + last_idx * H * K + o_k1).to(tl.float32)
b_gk2 = tl.load(g + last_idx * H * K + o_k2).to(tl.float32)
b_h1 *= tl.math.exp2(b_gk1)[:, None]
b_h2 *= tl.math.exp2(b_gk2)[:, None]
b_v = b_v.to(kg.dtype.element_ty)
p_kg1 = tl.make_block_ptr(kg, (K, T), (1, H * K), (0, i_t * BT), (64, BT), (0, 1))
p_kg2 = tl.make_block_ptr(kg, (K, T), (1, H * K), (64, i_t * BT), (64, BT), (0, 1))
b_kg1 = tl.load(p_kg1, boundary_check=(0, 1))
b_kg2 = tl.load(p_kg2, boundary_check=(0, 1))
b_h1 += tl.dot(b_kg1, b_v)
b_h2 += tl.dot(b_kg2, b_v)
# ---------------------------------------------------------------------------
# Kernel 6: o = (q * exp2(g) * scale) @ h + Aqk @ v_new
# ---------------------------------------------------------------------------
@triton.jit
def _fwd_o_kernel(
qg,
v_new,
A,
h,
o,
T,
H: tl.constexpr,
K: tl.constexpr,
V: tl.constexpr,
BT: tl.constexpr,
BV: tl.constexpr,
):
i_v = tl.program_id(0)
i_t = tl.program_id(1)
i_bh = tl.program_id(2)
i_b = i_bh // H
i_h = i_bh % H
bos = i_b * T
NT = tl.cdiv(T, BT)
qg += (bos * H + i_h) * K
v_new += (bos * H + i_h) * V
A += (bos * H + i_h) * BT
o += (bos * H + i_h) * V
h += (i_b * NT * H + i_h + i_t * H) * K * V
p_A = tl.make_block_ptr(A, (T, BT), (H * BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
b_A = tl.load(p_A, boundary_check=(0, 1))
p_v = tl.make_block_ptr(v_new, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
b_v = tl.load(p_v, boundary_check=(0, 1))
b_o = tl.dot(b_A, b_v)
p_qg1 = tl.make_block_ptr(qg, (T, K), (H * K, 1), (i_t * BT, 0), (BT, 64), (1, 0))
p_qg2 = tl.make_block_ptr(qg, (T, K), (H * K, 1), (i_t * BT, 64), (BT, 64), (1, 0))
b_qg1 = tl.load(p_qg1, boundary_check=(0, 1))
b_qg2 = tl.load(p_qg2, boundary_check=(0, 1))
p_h1 = tl.make_block_ptr(h, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
p_h2 = tl.make_block_ptr(h, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
b_h1 = tl.load(p_h1, boundary_check=(0, 1))
b_h2 = tl.load(p_h2, boundary_check=(0, 1))
b_o += tl.dot(b_qg1, b_h1.to(tl.bfloat16))
b_o += tl.dot(b_qg2, b_h2.to(tl.bfloat16))
p_o = tl.make_block_ptr(o, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1))
# ---------------------------------------------------------------------------
# Host glue — buffer cache to avoid re-allocating intermediates every call
# ---------------------------------------------------------------------------
_BUF_CACHE: dict = {}
def _bufs(B, T, H, K, V, BT, BC, NT, device, dtype):
key = (B, T, H, K, V, BT, BC, NT, device, dtype, 3)
hit = _BUF_CACHE.get(key)
if hit is not None:
return hit
# Aqk/Akk upper triangle must stay zero; kernels only rewrite lower/diag.
bufs = {
"g_cs": torch.empty(B, T, H, K, device=device, dtype=torch.float32),
"Aqk": torch.zeros(B, T, H, BT, device=device, dtype=dtype),
"Akkd": torch.empty(B, T, H, BC, device=device, dtype=torch.float32),
"Akk": torch.zeros(B, T, H, BT, device=device, dtype=dtype),
"w": torch.empty(B, T, H, K, device=device, dtype=dtype),
"u": torch.empty(B, T, H, V, device=device, dtype=dtype),
"kg": torch.empty(B, T, H, K, device=device, dtype=dtype),
"qg": torch.empty(B, T, H, K, device=device, dtype=dtype),
"v_new": torch.empty(B, T, H, V, device=device, dtype=dtype),
"h": torch.empty(B, NT, H, K, V, device=device, dtype=dtype),
}
_BUF_CACHE[key] = bufs
return bufs
def _kimi_delta_attn_fwd(
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
g: torch.Tensor,
beta: torch.Tensor,
scale: float,
chunk_size: int = 64,
) -> torch.Tensor:
B, T, H, K = q.shape
V = v.shape[-1]
BT = chunk_size
assert T % BT == 0
assert K == 128 and V == 128 and BT == 64, "specialized for K=V=128, BT=64"
NT = T // BT
BC = 16
NC = BT // BC
BK = 64
BV = 64
device = q.device
dtype = v.dtype
b = _bufs(B, T, H, K, V, BT, BC, NT, device, dtype)
# 1. local cumsum of g → log2 space
_local_cumsum_kernel[(B * H, NT, triton.cdiv(K, BK))](
g, b["g_cs"], T, H=H, K=K, BT=BT, BK=BK, SCALE=LOG2E, num_warps=4,
)
# 2. diagonal sub-chunk Aqk/Akk + local solve
_intra_diag_kernel[(B * H, NT * NC)](
q, k, b["g_cs"], beta, b["Aqk"], b["Akkd"], scale,
T, H=H, K=K, BT=BT, BC=BC, BK=128,
num_warps=4,
)
# 3. off-diagonal + merge inverse
_inter_solve_kernel[(NT, B * H)](
q, k, b["g_cs"], beta, b["Aqk"], b["Akkd"], b["Akk"], scale,
T, H=H, K=K, BT=BT, BC=BC, BK=BK,
num_warps=4,
)
# 4. w, u, kg, qg
_wu_kernel[(NT, B * H)](
q, k, v, beta, b["Akk"], b["g_cs"], b["w"], b["u"], b["kg"], b["qg"], scale,
T, H=H, K=K, V=V, BT=BT, BK=BK, BV=BV,
num_warps=4, num_stages=2,
)
# 5. inter-chunk state
_fwd_h_kernel[(triton.cdiv(V, BV), B * H)](
b["kg"], b["w"], b["u"], b["g_cs"], b["v_new"], b["h"],
T, H=H, K=K, V=V, BT=BT, BV=BV,
num_warps=4, num_stages=2,
)
# 6. output
o = torch.empty(B, T, H, V, device=device, dtype=dtype)
_fwd_o_kernel[(triton.cdiv(V, BV), NT, B * H)](
b["qg"], b["v_new"], b["Aqk"], b["h"], o,
T, H=H, K=K, V=V, BT=BT, BV=BV,
num_warps=4, num_stages=2,
)
return o
class Model(nn.Module):
"""KDA forward (chunk form). No learned parameters; all inputs are activations."""
def __init__(self, B: int, T: int, H: int, K: int, V: int, chunk_size: int = 64):
super().__init__()
self.B, self.T, self.H, self.K, self.V = B, T, H, K, V
self.chunk_size = chunk_size
self.scale = float(K) ** -0.5
self.register_buffer("_dummy", torch.zeros(1), persistent=False)
def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
g: torch.Tensor,
beta: torch.Tensor,
) -> torch.Tensor:
return _kimi_delta_attn_fwd(q, k, v, g, beta, scale=self.scale, chunk_size=self.chunk_size)
# Module-level shape shims (overridden by check.py / benchmark.py per shape).
B = 2
T = 1024
H = 8
K = 128
V = 128
CHUNK_SIZE = 64
def get_inputs():
torch.manual_seed(0)
q = torch.randn(B, T, H, K, dtype=torch.bfloat16) * 0.1
k = torch.randn(B, T, H, K, dtype=torch.bfloat16) * 0.1
v = torch.randn(B, T, H, V, dtype=torch.bfloat16) * 0.1
g = (torch.randn(B, T, H, K, dtype=torch.float32) * 0.1 - 0.05)
beta = torch.sigmoid(torch.randn(B, T, H, dtype=torch.bfloat16))
return [q, k, v, g, beta]
def get_init_inputs():
return [B, T, H, K, V, CHUNK_SIZE]
20260709_010052_grok_grok-4.5_02_kda_cutlass