Kai-2.0-Base / luau_kernels.py
khtsly's picture
Upload folder using huggingface_hub
f0e968d verified
Raw History Blame Contribute Delete
50.4 kB
"""Triton kernels for ``luau_infer`` (fused decoding of the LuauCoder HF port).
Every kernel works on *rows*: one row per token of the call (``B`` rows when
decoding, ``B * T`` for a prefill or a speculative verify), each with its own
document position ``pos`` (RoPE, conv / value-shift masks), cache ``slot``
(where its K/V go) and ``start`` (the first valid key slot of its sequence:
left padding). Nothing reads a Python scalar that changes between steps, so a
decode step replays as a CUDA graph.
* ``mix_norm`` Block AttnRes depth mix (a softmax over the sources, one tile)
+ RMSNorm, or the embedding gather + RMSNorm (layer 0).
* ``gemv`` skinny ``x @ W.T`` (bf16 or rowwise-FP8 weights), epilogues:
store, residual add, SwiGLU (gate|up rows), softcap logits.
* ``qk_decode`` CCGQA conv + QK-mean + QK-norm + gains + RoPE + value shift,
writes K/V to the cache and the conv latents to their ring.
* ``attention`` GQA flash-decoding: each program reads a span of one KV
head's keys once for all its query heads and all the
sequence's rows (a speculative verify); the last span
program merges and finishes DiffAttn V2 + XSA (+ gate).
* ``qk_prefill`` ``qk_decode`` for prompts, in token tiles.
* ``iha_mix`` IHA: pseudo-head mixes of the latent q / k / v, norm, temperature,
RoPE at virtual positions, interleaved pseudo-K/V cache write;
``attention`` then runs over pseudo-rows with a sliding window and
its finish collapses them (+ Gram-Schmidt XSA, gate).
* ``head_gate`` the gated byte head's ``[h, sigmoid(u.h + b) P h]`` input.
* ``sample`` repetition penalty / temperature / top-k / top-p / min-p + draw
(or greedy), one row per program.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
import torch
try: # (remote code: transformers only requires imports outside try blocks)
import triton
import triton.language as tl
except ImportError as exc: # pragma: no cover
raise ImportError("the fused LuauCoder decoding kernels need triton") from exc
from .luau_gemv_tuned import TABLE as _TABLE
LOG2E = 1.4426950408889634
# programmatic dependent launch (SM90+); LUAU_INFER_PDL=0 turns it off
PDL = (torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 9
and os.environ.get("LUAU_INFER_PDL", "1") != "0")
# L2 prefetch of the next GEMV's weights (SM90+); LUAU_INFER_PREFETCH=0 turns it off
BULK = torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 9
PREFETCH = BULK and os.environ.get("LUAU_INFER_PREFETCH", "1") != "0"
def _pf(pf) -> dict:
"""Launch kwargs of an optional prefetch target ``pf`` (a tensor)."""
if pf is None or not PREFETCH:
return dict(pf_ptr=None, pf_bytes=0, PF=False, BULK=False)
return dict(pf_ptr=pf, pf_bytes=pf.numel() * pf.element_size(), PF=True, BULK=BULK)
@triton.jit
def _pdl(ON: tl.constexpr):
"""Wait for the previous kernel's writes, then let the next kernel launch."""
if ON:
tl.extra.cuda.gdc_wait()
tl.extra.cuda.gdc_launch_dependents()
@triton.jit
def _prefetch(ptr, nbytes, PF: tl.constexpr, BULK: tl.constexpr):
"""Fire-and-forget L2 prefetch of ``nbytes`` at ``ptr``, split over the kernel's programs."""
if PF:
pid = tl.program_id(0) + tl.num_programs(0) * (tl.program_id(1) + tl.num_programs(1) * tl.program_id(2))
npg = tl.num_programs(0) * tl.num_programs(1) * tl.num_programs(2)
CH: tl.constexpr = 4096 if BULK else 128
n = tl.cdiv(nbytes, CH)
per = tl.cdiv(n, npg)
lo = pid * per
hi = tl.minimum(n, lo + per)
base = ptr.to(tl.pointer_type(tl.int8))
for i0 in range(lo, hi, 256):
i = i0 + tl.arange(0, 256)
m = (i < hi).to(tl.int32)
a = base + i.to(tl.int64) * CH
if BULK:
sz = tl.minimum(nbytes - i.to(tl.int64) * CH, CH).to(tl.int32)
tl.inline_asm_elementwise(
"{ .reg .pred p; setp.ne.b32 p, $3, 0; @p cp.async.bulk.prefetch.L2.global [$1], $2; "
"mov.u32 $0, 0; }", "=r,l,r,r", [a, sz, m], dtype=tl.int32, is_pure=False, pack=1)
else:
tl.inline_asm_elementwise(
"{ .reg .pred p; setp.ne.b32 p, $2, 0; @p prefetch.global.L2 [$1]; mov.u32 $0, 0; }",
"=r,l,r", [a, m], dtype=tl.int32, is_pure=False, pack=1)
@triton.jit
def _mix_norm_kernel(s_ptr, s_row, s_ent, ids_ptr, emb_ptr, w_ptr, g_ptr, out_ptr, o_row, eps, D,
NE: tl.constexpr, NE_PAD: tl.constexpr, MODE: tl.constexpr, BLOCK: tl.constexpr,
ADD: tl.constexpr = 0, PART: tl.constexpr = 1, PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
"""MODE 0: h = S[r, 0]; 1: embedding gather; 2: AttnRes mix; 3: sum of entries. out = rmsnorm(h) * g."""
r = tl.program_id(0).to(tl.int64)
offs = tl.arange(0, BLOCK)
msk = offs < D
g = tl.load(g_ptr + offs, mask=msk, other=0.0).to(tl.float32)
if MODE == 2:
w = tl.load(w_ptr + offs, mask=msk, other=0.0)
_prefetch(pf_ptr, pf_bytes, PF, BULK)
_pdl(PDL)
if MODE == 1:
tok = tl.load(ids_ptr + r).to(tl.int64)
hb = tl.load(emb_ptr + tok * D + offs, mask=msk, other=0.0)
tl.store(s_ptr + r * s_row + offs, hb, mask=msk)
h = hb.to(tl.float32)
elif MODE == 0:
h = tl.load(s_ptr + r * s_row + offs, mask=msk, other=0.0).to(tl.float32)
else:
j = tl.arange(0, NE_PAD)
jm = j < NE
v = tl.load(s_ptr + r * s_row + j[:, None] * s_ent + offs[None, :], mask=jm[:, None] & msk[None, :],
other=0.0).to(tl.float32)
if MODE == 3:
h = tl.sum(v, 0)
else:
sc = tl.sum(v * w[None, :], 1) * tl.rsqrt(tl.sum(v * v, 1) / D + eps)
sc = tl.where(j < NE - 1 + PART, sc, float("-inf"))
e = tl.exp(sc - tl.max(sc, 0))
p = e / tl.sum(e, 0)
h = tl.sum((p + ADD)[:, None] * v, 0)
h = h.to(out_ptr.dtype.element_ty).to(tl.float32) # the mix output is bf16
y = h * tl.rsqrt(tl.sum(h * h, 0) / D + eps) * g
tl.store(out_ptr + r * o_row + offs, y.to(out_ptr.dtype.element_ty), mask=msk)
def mix_norm(S: torch.Tensor, n_ent: int, mode: int, out: torch.Tensor, g: torch.Tensor, eps: float,
w: torch.Tensor | None = None, ids: torch.Tensor | None = None,
emb: torch.Tensor | None = None, add: bool = False, partial: bool = True, pf=None) -> None:
"""``S`` ``(R, NS, D)`` sources (row-major per row), ``out`` ``(R, D)``."""
R, _, D = S.shape
block = triton.next_power_of_2(D)
ne_pad = triton.next_power_of_2(n_ent) if mode >= 2 else 1
warps = max(4, min(16, ne_pad * block // 1024))
_mix_norm_kernel[(R,)](S, S.stride(0), S.stride(1), ids if ids is not None else S,
emb if emb is not None else S, w if w is not None else g, g, out,
out.stride(0), eps, D, NE=n_ent, NE_PAD=ne_pad, MODE=mode, BLOCK=block,
ADD=int(add), PART=int(partial), num_warps=warps, PDL=PDL, launch_pdl=PDL, **_pf(pf))
_TUNED_PATH = Path(__file__).with_name("luau_gemv_tuned.py")
_TUNED: dict | None = None
def _tuned() -> dict:
"""Pinned GEMV configs for this GPU (``benchmark/tune_gemv.py``), else {}."""
global _TUNED
if _TUNED is None:
name = torch.cuda.get_device_name()
p = torch.cuda.get_device_properties(0)
arch = [p.major, p.minor, p.multi_processor_count]
hit = _TABLE.get(name) or next((e for e in _TABLE.values() if e.get("arch") == arch), None)
_TUNED = dict(hit["gemv"]) if hit else {}
return _TUNED
def gemv_key(m_pad: int, n: int, k: int, fp8: bool, epi: int) -> str:
return f"{m_pad},{n},{k},{int(fp8)},{int(epi == 2)}"
def gemv_config(m_pad: int, n: int, k: int, fp8: bool, epi: int) -> tuple[int, int, int, int, int]:
"""``(BLOCK_N, BLOCK_K, num_warps, num_stages, DOT)``: pinned, else a default."""
hit = _tuned().get(gemv_key(m_pad, n, k, fp8, epi))
if hit:
return tuple(hit) + (0,) * (5 - len(hit))
if m_pad >= 4:
return 32, 128, 4, 3, 1
bk = 512 if fp8 else 256
bn = max(1, min(16, 2048 // (bk * m_pad) * 4))
return bn, bk, 4, 2, 0
@triton.jit
def _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn, nm, K, M_PAD: tl.constexpr, BLOCK_N: tl.constexpr,
BLOCK_K: tl.constexpr, DOT: tl.constexpr):
if DOT:
acc = tl.zeros([M_PAD, BLOCK_N], dtype=tl.float32)
for k0 in range(0, K, BLOCK_K):
rk = k0 + tl.arange(0, BLOCK_K)
km = rk < K
x = tl.load(x_ptr + rm[:, None] * x_row + rk[None, :], mask=mm[:, None] & km[None, :], other=0.0)
w = tl.load(w_ptr + rn[:, None] * w_row + rk[None, :], mask=nm[:, None] & km[None, :], other=0.0)
acc = tl.dot(x.to(tl.bfloat16), tl.trans(w.to(tl.bfloat16)), acc)
return acc
acc3 = tl.zeros([M_PAD, BLOCK_N, BLOCK_K], dtype=tl.float32)
for k0 in range(0, K, BLOCK_K):
rk = k0 + tl.arange(0, BLOCK_K)
km = rk < K
x = tl.load(x_ptr + rm[:, None] * x_row + rk[None, :], mask=mm[:, None] & km[None, :], other=0.0)
w = tl.load(w_ptr + rn[:, None] * w_row + rk[None, :], mask=nm[:, None] & km[None, :], other=0.0)
acc3 += x.to(tl.float32)[:, None, :] * w.to(tl.float32)[None, :, :]
return tl.sum(acc3, 2)
@triton.jit
def _gemv_kernel(x_ptr, x_row, w_ptr, w_row, ws_ptr, y_ptr, y_row, M, N, K, up_off, cap,
M_PAD: tl.constexpr, EPI: tl.constexpr, FP8: tl.constexpr,
BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr, DOT: tl.constexpr = False,
PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
"""y[m, n] = x[m] . W[n]. EPI 0: store; 1: residual add; 2: SwiGLU; 3: softcapped fp32 logits."""
_prefetch(pf_ptr, pf_bytes, PF, BULK)
_pdl(PDL)
pid = tl.program_id(0)
rn = pid * BLOCK_N + tl.arange(0, BLOCK_N)
nm = rn < N
rm = tl.arange(0, M_PAD)
mm = rm < M
acc = _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn, nm, K, M_PAD, BLOCK_N, BLOCK_K, DOT)
if FP8:
acc = acc * tl.load(ws_ptr + rn, mask=nm, other=0.0)[None, :]
ym = mm[:, None] & nm[None, :]
yp = y_ptr + rm[:, None] * y_row + rn[None, :]
if EPI == 2:
up = _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn + up_off, nm, K, M_PAD, BLOCK_N, BLOCK_K, DOT)
if FP8:
up = up * tl.load(ws_ptr + up_off + rn, mask=nm, other=0.0)[None, :]
gate = acc.to(tl.bfloat16).to(tl.float32)
up = up.to(tl.bfloat16).to(tl.float32)
tl.store(yp, (gate * tl.sigmoid(gate) * up).to(y_ptr.dtype.element_ty), mask=ym)
elif EPI == 1:
res = tl.load(yp, mask=ym, other=0.0).to(tl.float32)
tl.store(yp, (acc.to(tl.bfloat16).to(tl.float32) + res).to(y_ptr.dtype.element_ty), mask=ym)
elif EPI == 3:
z = acc.to(tl.bfloat16).to(tl.float32)
if cap > 0:
t = z / cap
e = tl.exp(-2.0 * tl.abs(t))
th = (1.0 - e) / (1.0 + e)
z = cap * tl.where(t < 0, -th, th)
tl.store(yp, z, mask=ym)
else:
tl.store(yp, acc.to(y_ptr.dtype.element_ty), mask=ym)
def gemv(x: torch.Tensor, w: torch.Tensor, y: torch.Tensor, epi: int = 0, scale: torch.Tensor | None = None,
n: int | None = None, up_off: int = 0, cap: float = 0.0, config: tuple | None = None, pf=None) -> None:
"""``x`` ``(M, K)`` (row stride free), ``w`` ``(N_w, K)``, ``y`` ``(M, n)``; ``n`` output
columns (SwiGLU: the hidden size, ``up_off`` = its up rows' offset)."""
M, K = x.shape
N = n if n is not None else w.shape[0]
m_pad = max(1, triton.next_power_of_2(M))
fp8 = scale is not None
cfg = tuple(config or gemv_config(m_pad, N, K, fp8, epi))
bn, bk, warps, stages, dot = cfg + (0,) * (5 - len(cfg))
_gemv_kernel[(triton.cdiv(N, bn),)](x, x.stride(0), w, w.stride(0), scale if fp8 else w, y, y.stride(0),
M, N, K, up_off, float(cap), M_PAD=max(16, m_pad) if dot else m_pad,
EPI=epi, FP8=fp8, BLOCK_N=bn, BLOCK_K=bk, DOT=bool(dot),
num_warps=warps, num_stages=stages, PDL=PDL, launch_pdl=PDL, **_pf(pf))
# CCGQA conv-latent / value-shift ring slots; a verify of up to RING - 3 drafts never overwrites
RING: tl.constexpr = tl.constexpr(32)
@triton.jit
def _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, cidx, LAG: tl.constexpr):
"""The pre-conv latent of the row's token ``LAG`` back (zero across a document start)."""
if LAG == 0:
v = tl.load(qkv_ptr + r * qkv_row + col + cidx).to(tl.float32)
elif t >= LAG:
v = tl.load(qkv_ptr + (r - LAG) * qkv_row + col + cidx, mask=pos >= LAG, other=0.0).to(tl.float32)
else:
v = tl.load(lat_ptr + b * lat_b + ((slot - LAG) % RING) * lat_l + col + cidx, mask=pos >= LAG,
other=0.0).to(tl.float32)
return tl.where(pos >= LAG, v, 0.0)
@triton.jit
def _conv(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cs_ptr, csb_ptr, ch_ptr, chb_ptr, h, r, t, b, slot, pos, col,
HD: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr):
"""Virtual head ``h``'s causal conv."""
d = tl.arange(0, HD)
b1 = tl.load(csb_ptr + h * HD + d).to(tl.float32)
acc = tl.load(chb_ptr + h * HD + d).to(tl.float32)
for j in tl.static_range(KC):
u = b1
for i in tl.static_range(KS):
a = tl.load(cs_ptr + (h * HD + d) * KS + i).to(tl.float32)
u += a * _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, d, i + j)
w2 = tl.load(ch_ptr + ((h * KC + j) * HD + d[:, None]) * HD + d[None, :]).to(tl.float32)
acc += tl.sum(w2 * u[:, None], 0)
return acc
@triton.jit
def _conv_pre(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, b1, acc, a0, a1, w0, w1, r, t, b, slot, pos, col,
HD: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr):
"""``_conv`` (``KS``, ``KC`` <= 2) on taps already in registers (loaded before the PDL wait)."""
d = tl.arange(0, HD)
for j in tl.static_range(KC):
u = b1
for i in tl.static_range(KS):
a = a0 if i == 0 else a1
u += a * _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, d, i + j)
w2 = w0 if j == 0 else w1
acc += tl.sum(w2 * u[:, None], 0)
return acc
@triton.jit
def _qk_decode_kernel(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, vh_ptr, vh_b, vh_l,
pos_ptr, slot_ptr, cos_ptr, sin_ptr, cs_ptr, csb_ptr, ch_ptr, chb_ptr, qg_ptr, kg_ptr,
q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h, T, eps,
NQ: tl.constexpr, NKV: tl.constexpr, NXQ: tl.constexpr, HD: tl.constexpr,
V_OFF: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr, CCGQA: tl.constexpr,
QK_MEAN: tl.constexpr, VSHIFT: tl.constexpr, ROPE: tl.constexpr,
PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
"""One (row, virtual head): queries go to ``q``, keys / values to the caches."""
h = tl.program_id(1)
PRE: tl.constexpr = CCGQA and KS <= 2 and KC <= 2
if PRE:
dd = tl.arange(0, HD)
cb1 = tl.load(csb_ptr + h * HD + dd).to(tl.float32)
cacc = tl.load(chb_ptr + h * HD + dd).to(tl.float32)
ca0 = tl.load(cs_ptr + (h * HD + dd) * KS).to(tl.float32)
ca1 = tl.load(cs_ptr + (h * HD + dd) * KS + (KS - 1)).to(tl.float32)
cw0 = tl.load(ch_ptr + ((h * KC) * HD + dd[:, None]) * HD + dd[None, :]).to(tl.float32)
cw1 = tl.load(ch_ptr + ((h * KC + (KC - 1)) * HD + dd[:, None]) * HD + dd[None, :]).to(tl.float32)
r = tl.program_id(0).to(tl.int64)
b = r // T
t = r % T
pos = tl.load(pos_ptr + r).to(tl.int64)
slot = tl.load(slot_ptr + r).to(tl.int64)
_prefetch(pf_ptr, pf_bytes, PF, BULK)
_pdl(PDL)
HALF: tl.constexpr = HD // 2
G: tl.constexpr = NQ // NKV
d = tl.arange(0, HD)
is_k = h >= NQ
g = h - NQ
ph = tl.where(is_k, NXQ + g, h % NXQ)
col = ph * HD
base = qkv_ptr + r * qkv_row
keep = t + RING >= T # only a call's last RING tokens go to the rings (no write races)
if CCGQA:
if PRE:
x = _conv_pre(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cb1, cacc, ca0, ca1, cw0, cw1, r, t, b, slot,
pos, col, HD, KS, KC)
else:
x = _conv(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cs_ptr, csb_ptr, ch_ptr, chb_ptr, h, r, t, b, slot,
pos, col, HD, KS, KC)
if QK_MEAN:
if is_k:
qm = tl.zeros([HD], dtype=tl.float32)
for jh in tl.static_range(G):
qm += tl.load(base + ((g * G + jh) % NXQ) * HD + d).to(tl.float32)
x += 0.5 * (qm / G + tl.load(base + col + d).to(tl.float32))
else:
x += 0.5 * (tl.load(base + col + d).to(tl.float32)
+ tl.load(base + (NXQ + h // G) * HD + d).to(tl.float32))
else:
x = tl.load(base + col + d).to(tl.float32)
gain = tl.load(tl.where(is_k, kg_ptr + g * HD, qg_ptr) + d)
x = x * tl.rsqrt(tl.sum(x * x, 0) / HD + eps) * gain
if not CCGQA: # plain QK-norm rounds to bf16 before RoPE
x = x.to(tl.bfloat16).to(tl.float32)
lo, hi = tl.split(tl.permute(tl.reshape(x, [2, HALF]), (1, 0)))
if ROPE:
i2 = tl.arange(0, HALF)
c = tl.load(cos_ptr + pos * HALF + i2)
s = tl.load(sin_ptr + pos * HALF + i2)
lo, hi = lo * c - hi * s, hi * c + lo * s
lo_i = tl.arange(0, HALF)
if is_k:
dst = kc_ptr + b * kc_b + g * kc_h + slot * HD
else:
dst = q_ptr + r * q_row + h * HD
tl.store(dst + lo_i, lo.to(q_ptr.dtype.element_ty))
tl.store(dst + HALF + lo_i, hi.to(q_ptr.dtype.element_ty))
if is_k:
ch = g * HD + d
own = tl.load(base + V_OFF + ch)
v = own
if VSHIFT:
VH: tl.constexpr = NKV * HD // 2
upper = ch >= VH
if t >= 1:
prev = tl.load(base - qkv_row + V_OFF + ch, mask=upper, other=0.0)
else:
prev = tl.load(vh_ptr + b * vh_b + ((slot - 1) % RING) * vh_l + ch - VH,
mask=upper & (slot >= 1), other=0.0)
v = tl.where(upper, tl.where(pos > 0, prev, 0.0), own)
tl.store(vh_ptr + b * vh_b + (slot % RING) * vh_l + ch - VH, own, mask=upper & keep)
tl.store(vc_ptr + b * kc_b + g * kc_h + slot * HD + d, v.to(vc_ptr.dtype.element_ty))
if CCGQA:
if ((h < NXQ) | is_k) & keep:
tl.store(lat_ptr + b * lat_b + (slot % RING) * lat_l + col + d, tl.load(base + col + d))
def qk_decode(qkv, lat, vh, pos, slot, cos, sin, conv, qg, kg, q, kc, vc, T: int, eps: float, *, nq, nkv,
nxq, hd, v_off, ks, kc_taps, ccgqa, qk_mean, vshift, rope, pf=None) -> None:
R = qkv.shape[0]
cs, csb, ch, chb = conv if conv is not None else (qkv,) * 4
_qk_decode_kernel[(R, nq + nkv)](
qkv, qkv.stride(0), lat if lat is not None else qkv, lat.stride(0) if lat is not None else 0,
lat.stride(1) if lat is not None else 0, vh if vh is not None else qkv,
vh.stride(0) if vh is not None else 0, vh.stride(1) if vh is not None else 0,
pos, slot, cos, sin, cs, csb, ch, chb, qg, kg, q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1),
T, eps, NQ=nq, NKV=nkv, NXQ=nxq, HD=hd, V_OFF=v_off, KS=ks, KC=kc_taps, CCGQA=ccgqa,
QK_MEAN=qk_mean, VSHIFT=vshift, ROPE=rope, num_warps=8, PDL=PDL, launch_pdl=PDL, **_pf(pf))
@triton.jit
def _tile_lat(qkv_ptr, qkv_row, r0, t, tm, pos, col, d, T, LAG: tl.constexpr):
"""``[BT, HD]`` pre-conv latents ``LAG`` tokens back of a prompt tile."""
ok = tm & (pos >= LAG) & (t >= LAG)
return tl.load(qkv_ptr + (r0 + t - LAG)[:, None] * qkv_row + col + d[None, :], mask=ok[:, None],
other=0.0).to(tl.float32)
@triton.jit
def _qk_prefill_kernel(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, vh_ptr, vh_b, vh_l, pos_ptr, cos_ptr, sin_ptr,
cs_ptr, csb_ptr, ch_ptr, chb_ptr, qg_ptr, kg_ptr, q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h,
T, eps, NQ: tl.constexpr, NKV: tl.constexpr, NXQ: tl.constexpr, HD: tl.constexpr,
V_OFF: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr, QK_MEAN: tl.constexpr,
VSHIFT: tl.constexpr, ROPE: tl.constexpr, BT: tl.constexpr):
"""``qk_decode`` for a prompt, ``BT`` tokens of one virtual head per program (taps on tensor cores)."""
tt = tl.program_id(0)
h = tl.program_id(1)
b = tl.program_id(2).to(tl.int64)
HALF: tl.constexpr = HD // 2
G: tl.constexpr = NQ // NKV
t = tt * BT + tl.arange(0, BT)
tm = t < T
r0 = b * T
rows = r0 + t
pos = tl.load(pos_ptr + rows, mask=tm, other=0).to(tl.int64)
d = tl.arange(0, HD)
is_k = h >= NQ
g = h - NQ
ph = tl.where(is_k, NXQ + g, h % NXQ)
col = ph * HD
b1 = tl.load(csb_ptr + h * HD + d).to(tl.float32)
x = tl.zeros([BT, HD], dtype=tl.float32) + tl.load(chb_ptr + h * HD + d).to(tl.float32)[None, :]
for j in tl.static_range(KC):
u = tl.zeros([BT, HD], dtype=tl.float32) + b1[None, :]
for i in tl.static_range(KS):
a = tl.load(cs_ptr + (h * HD + d) * KS + i).to(tl.float32)
u += a[None, :] * _tile_lat(qkv_ptr, qkv_row, r0, t, tm, pos, col, d, T, i + j)
w2 = tl.load(ch_ptr + ((h * KC + j) * HD + d[:, None]) * HD + d[None, :]).to(tl.bfloat16)
u_hi = u.to(tl.bfloat16)
x = tl.dot(u_hi, w2, x)
x = tl.dot((u - u_hi.to(tl.float32)).to(tl.bfloat16), w2, x)
base = qkv_ptr + rows[:, None] * qkv_row
if QK_MEAN:
if is_k:
qm = tl.zeros([BT, HD], dtype=tl.float32)
for jh in tl.static_range(G):
qm += tl.load(base + ((g * G + jh) % NXQ) * HD + d[None, :], mask=tm[:, None], other=0.0).to(
tl.float32)
x += 0.5 * (qm / G + tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0).to(tl.float32))
else:
x += 0.5 * (tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0).to(tl.float32)
+ tl.load(base + (NXQ + h // G) * HD + d[None, :], mask=tm[:, None], other=0.0).to(
tl.float32))
gain = tl.load(tl.where(is_k, kg_ptr + g * HD, qg_ptr) + d)
x = x * tl.rsqrt(tl.sum(x * x, 1) / HD + eps)[:, None] * gain[None, :]
lo, hi = tl.split(tl.permute(tl.reshape(x, [BT, 2, HALF]), (0, 2, 1)))
i2 = tl.arange(0, HALF)
if ROPE:
c = tl.load(cos_ptr + pos[:, None] * HALF + i2[None, :])
sn = tl.load(sin_ptr + pos[:, None] * HALF + i2[None, :])
lo, hi = lo * c - hi * sn, hi * c + lo * sn
if is_k:
dst = kc_ptr + b * kc_b + g * kc_h + t[:, None] * HD
else:
dst = q_ptr + rows[:, None] * q_row + h * HD
tl.store(dst + i2[None, :], lo.to(q_ptr.dtype.element_ty), mask=tm[:, None])
tl.store(dst + HALF + i2[None, :], hi.to(q_ptr.dtype.element_ty), mask=tm[:, None])
keep = tm & (t + RING >= T)
if is_k:
ch = g * HD + d
own = tl.load(base + V_OFF + ch[None, :], mask=tm[:, None], other=0.0)
v = own
if VSHIFT:
VH: tl.constexpr = NKV * HD // 2
upper = (ch >= VH)[None, :]
prev = tl.load(base - qkv_row + V_OFF + ch[None, :], mask=tm[:, None] & upper & (t >= 1)[:, None],
other=0.0)
v = tl.where(upper, tl.where((pos > 0)[:, None], prev, 0.0), own)
tl.store(vh_ptr + b * vh_b + (t % RING)[:, None] * vh_l + ch[None, :] - VH, own,
mask=keep[:, None] & upper)
tl.store(vc_ptr + b * kc_b + g * kc_h + t[:, None] * HD + d[None, :], v.to(vc_ptr.dtype.element_ty),
mask=tm[:, None])
if (h < NXQ) | is_k:
tl.store(lat_ptr + b * lat_b + (t % RING)[:, None] * lat_l + col + d[None, :],
tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0), mask=keep[:, None])
def qk_prefill(qkv, lat, vh, pos, cos, sin, conv, qg, kg, q, kc, vc, T: int, eps: float, *, nq, nkv, nxq, hd,
v_off, ks, kc_taps, qk_mean, vshift, rope) -> None:
"""CCGQA ``qk_decode`` for ``B`` prompts of ``T`` tokens starting at slot 0."""
B = qkv.shape[0] // T
cs, csb, ch, chb = conv
bt = 32 if hd <= 128 else 16 # 64-token tiles spill
_qk_prefill_kernel[(triton.cdiv(T, bt), nq + nkv, B)](
qkv, qkv.stride(0), lat, lat.stride(0), lat.stride(1), vh if vh is not None else qkv,
vh.stride(0) if vh is not None else 0, vh.stride(1) if vh is not None else 0, pos, cos, sin,
cs, csb, ch, chb, qg, kg, q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1), T, eps, NQ=nq, NKV=nkv,
NXQ=nxq, HD=hd, V_OFF=v_off, KS=ks, KC=kc_taps, QK_MEAN=qk_mean, VSHIFT=vshift, ROPE=rope, BT=bt,
num_warps=4)
# grid (span, KV head, sequence); the last span program merges the partials and finishes the rows
@triton.jit
def _spans(start, slot, NSPL: tl.constexpr, BL: tl.constexpr, MINB: tl.constexpr = 1):
"""``(span, n_spans)`` for keys ``[start, slot]``, sized on the device."""
n = slot + 1 - start
span = tl.maximum(tl.cdiv(tl.cdiv(n, NSPL), BL), MINB) * BL
return span, tl.where(n > 0, tl.cdiv(n, span), 0)
@triton.jit
def _merge(ml_ptr, acc_ptr, base0, n_sp, hrow, HD: tl.constexpr, G_PAD: tl.constexpr, GH_PAD: tl.constexpr,
NSPL: tl.constexpr, CH: tl.constexpr):
"""Merge the span partials of the query heads ``hrow`` -> bf16-rounded output."""
d = tl.arange(0, HD)
si = tl.arange(0, NSPL)
m = tl.load(ml_ptr + (base0 + si)[:, None] * 2 * G_PAD + hrow[None, :], mask=(si < n_sp)[:, None],
other=float("-inf"))
l = tl.load(ml_ptr + (base0 + si)[:, None] * 2 * G_PAD + G_PAD + hrow[None, :], mask=(si < n_sp)[:, None],
other=0.0)
mg = tl.max(m, 0)
mg = tl.where(mg == float("-inf"), 0.0, mg)
lsum = tl.sum(tl.exp2(m - mg[None, :]) * l, 0)
o = tl.zeros([GH_PAD, HD], dtype=tl.float32)
for c0 in range(0, n_sp, CH):
cs = c0 + tl.arange(0, CH)
cm = cs < n_sp
mc = tl.load(ml_ptr + (base0 + cs)[:, None] * 2 * G_PAD + hrow[None, :], mask=cm[:, None],
other=float("-inf"))
a = tl.load(acc_ptr + ((base0 + cs)[:, None, None] * G_PAD + hrow[None, :, None]) * HD
+ d[None, None, :], mask=cm[:, None, None], other=0.0)
o += tl.sum(tl.exp2(mc - mg[None, :])[:, :, None] * a, 0)
o = o / tl.maximum(lsum, 1e-30)[:, None]
return o.to(tl.bfloat16).to(tl.float32)
@triton.jit
def _finish(ml_ptr, acc_ptr, r, g, b, n_sp, slot, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row,
lam_off, gate_off, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr,
HD: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr, GATE: tl.constexpr):
"""Merge row ``r``'s spans for KV head ``g`` and finish DiffAttn V2 + XSA (+ gate) into ``y``."""
base0 = (r * NKV + g) * NSPL
d = tl.arange(0, HD)
if DIFF:
GH: tl.constexpr = G // 2
GH_PAD: tl.constexpr = G_PAD // 2
i = tl.arange(0, GH_PAD)
im = i < GH
y0 = _merge(ml_ptr, acc_ptr, base0, n_sp, 2 * i, HD, G_PAD, GH_PAD, NSPL, CH)
y1 = _merge(ml_ptr, acc_ptr, base0, n_sp, 2 * i + 1, HD, G_PAD, GH_PAD, NSPL, CH)
lam = tl.load(qkv_ptr + r * qkv_row + lam_off + g * GH + i, mask=im, other=0.0).to(tl.float32)
y = y0 - tl.sigmoid(lam)[:, None] * y1
else:
GH: tl.constexpr = G
GH_PAD: tl.constexpr = G_PAD
i = tl.arange(0, GH_PAD)
im = i < GH
y = _merge(ml_ptr, acc_ptr, base0, n_sp, i, HD, G_PAD, GH_PAD, NSPL, CH)
if XSA:
v = tl.load(vc_ptr + b * kc_b + g * kc_h + slot * HD + d).to(tl.float32)
vn = v / tl.maximum(tl.sqrt(tl.sum(v * v, 0)), 1e-12)
y = y - tl.sum(y * vn[None, :], 1)[:, None] * vn[None, :]
oh = g * GH + i
if GATE == 1: # per channel
gt = tl.load(qkv_ptr + r * qkv_row + gate_off + oh[:, None] * HD + d[None, :], mask=im[:, None],
other=0.0).to(tl.float32)
y = y * tl.sigmoid(gt)
elif GATE == 2: # per head
gt = tl.load(qkv_ptr + r * qkv_row + gate_off + oh, mask=im, other=0.0).to(tl.float32)
y = y * tl.sigmoid(gt)[:, None]
tl.store(y_ptr + r * y_row + oh[:, None] * HD + d[None, :], y.to(y_ptr.dtype.element_ty), mask=im[:, None])
@triton.jit
def _fence():
"""``fence.acq_rel.gpu`` in every thread (CUDA's ``__threadfence``)."""
tl.inline_asm_elementwise("fence.acq_rel.gpu; mov.u32 $0, 0;", "=r", [], dtype=tl.int32, is_pure=False,
pack=1)
@triton.jit
def _seq_keys(start_ptr, slot_ptr, r0, T, win, WIN: tl.constexpr):
"""A sequence's key range ``[start, last]``."""
start = tl.load(start_ptr + r0)
if WIN:
start = tl.maximum(start, tl.load(slot_ptr + r0) - win + 1)
return start, tl.load(slot_ptr + r0 + T - 1)
@triton.jit
def _unit(x, tol):
"""``x / |x|``; 0 where ``|x| <= tol`` (the port's ``_unit``)."""
n = tl.sqrt(tl.sum(x * x, 0))
return tl.where(n > tol, x / tl.maximum(n, tol), 0.0)
@triton.jit
def _iha_finish(ml_ptr, acc_ptr, rt, g, b, n_sp, vs0, r_ptr, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr,
y_row, gate_off, P: tl.constexpr, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr,
G_PAD: tl.constexpr, HD: tl.constexpr, CH: tl.constexpr, XSA: tl.constexpr, GATE: tl.constexpr):
"""IHA finish: merge, collapse with ``R``, Gram-Schmidt XSA and the gate for one token row."""
d = tl.arange(0, HD)
i = tl.arange(0, G_PAD)
im = i < G
oh = g * G + i
y = tl.zeros([G_PAD, HD], dtype=tl.float32)
for p in tl.static_range(P):
yp = _merge(ml_ptr, acc_ptr, ((rt * P + p) * NKV + g) * NSPL, n_sp, i, HD, G_PAD, G_PAD, NSPL, CH)
rr = tl.load(r_ptr + oh * P + p, mask=im, other=0.0).to(tl.float32)
y += rr[:, None] * yp
y = y.to(tl.bfloat16).to(tl.float32)
if XSA:
vb = vc_ptr + b * kc_b + g * kc_h + vs0 * HD
e0 = tl.zeros([HD], dtype=tl.float32)
e1 = tl.zeros([HD], dtype=tl.float32)
e2 = tl.zeros([HD], dtype=tl.float32)
for p in tl.static_range(P):
w = _unit(tl.load(vb + p * HD + d).to(tl.float32), 1e-12)
w = w - tl.sum(w * e0, 0) * e0
w = w - tl.sum(w * e1, 0) * e1
w = w - tl.sum(w * e2, 0) * e2
e = _unit(w, 1e-2)
y = y - tl.sum(y * e[None, :], 1)[:, None] * e[None, :]
if p == 0:
e0 = e
elif p == 1:
e1 = e
elif p == 2:
e2 = e
if GATE == 1:
gt = tl.load(qkv_ptr + rt * qkv_row + gate_off + oh[:, None] * HD + d[None, :], mask=im[:, None],
other=0.0).to(tl.float32)
y = y * tl.sigmoid(gt)
elif GATE == 2:
gt = tl.load(qkv_ptr + rt * qkv_row + gate_off + oh, mask=im, other=0.0).to(tl.float32)
y = y * tl.sigmoid(gt)[:, None]
tl.store(y_ptr + rt * y_row + oh[:, None] * HD + d[None, :], y.to(y_ptr.dtype.element_ty), mask=im[:, None])
@triton.jit
def _attn_kernel(q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h, slot_ptr, start_ptr, ml_ptr, acc_ptr, cnt_ptr,
qkv_ptr, qkv_row, y_ptr, y_row, T, qk_scale, lam_off, gate_off, win, r_ptr,
NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr, QR: tl.constexpr,
HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr,
GATE: tl.constexpr, FINISH: tl.constexpr, WIN: tl.constexpr = False, IHA: tl.constexpr = 0,
MINB: tl.constexpr = 1, PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
"""Flash-decoding attention; ``FINISH``: the last span program finishes the row."""
sp = tl.program_id(0)
g = tl.program_id(1)
b = tl.program_id(2).to(tl.int64)
r0 = b * T
start, last = _seq_keys(start_ptr, slot_ptr, r0, T, win, WIN)
span, n_sp = _spans(start, last, NSPL, BL, MINB)
lo = start + sp * span
hi = tl.minimum(last + 1, lo + span)
qi = tl.arange(0, QR)
t = qi // G
j = qi % G
qm = t < T
lim = tl.load(slot_ptr + r0 + t, mask=qm, other=-1)
_prefetch(pf_ptr, pf_bytes, PF, BULK)
_pdl(PDL)
if lo < hi:
d = tl.arange(0, HD)
q = tl.load(q_ptr + (r0 + t)[:, None] * q_row + (g * G + j)[:, None] * HD + d[None, :],
mask=qm[:, None], other=0.0)
m_i = tl.full([QR], float("-inf"), tl.float32)
l_i = tl.zeros([QR], dtype=tl.float32)
acc = tl.zeros([QR, HD], dtype=tl.float32)
kbase = b * kc_b + g * kc_h
for k0 in range(lo, hi, BL):
kk = k0 + tl.arange(0, BL)
km = kk < hi
k = tl.load(kc_ptr + kbase + kk[:, None] * HD + d[None, :], mask=km[:, None], other=0.0)
s = tl.dot(q, tl.trans(k)) * qk_scale # base-2 logits
ok = km[None, :] & (kk[None, :] <= lim[:, None])
if WIN:
ok = ok & (kk[None, :] > lim[:, None] - win)
s = tl.where(ok, s, float("-inf"))
m_new = tl.maximum(m_i, tl.max(s, 1))
m_ok = tl.where(m_new == float("-inf"), 0.0, m_new) # a row with no key yet
alpha = tl.exp2(m_i - m_ok)
p = tl.exp2(s - m_ok[:, None])
l_i = l_i * alpha + tl.sum(p, 1)
v = tl.load(vc_ptr + kbase + kk[:, None] * HD + d[None, :], mask=km[:, None], other=0.0)
acc = acc * alpha[:, None] + tl.dot(p.to(v.dtype), v)
m_i = m_new
base = ((r0 + t) * NKV + g) * NSPL + sp
tl.store(ml_ptr + base * 2 * G_PAD + j, m_i, mask=qm)
tl.store(ml_ptr + base * 2 * G_PAD + G_PAD + j, l_i, mask=qm)
tl.store(acc_ptr + (base * G_PAD + j)[:, None] * HD + d[None, :], acc, mask=qm[:, None])
if FINISH:
_fence()
tl.debug_barrier()
old = tl.atomic_add(cnt_ptr + b * NKV + g, 1, sem="acq_rel", scope="gpu")
if old == n_sp - 1:
_fence()
tl.atomic_xchg(cnt_ptr + b * NKV + g, 0, sem="relaxed", scope="gpu")
if IHA:
_iha_finish(ml_ptr, acc_ptr, b, g, b, n_sp, tl.load(slot_ptr + r0), r_ptr, qkv_ptr, qkv_row,
vc_ptr, kc_b, kc_h, y_ptr, y_row, gate_off, IHA, NKV, NSPL, G, G_PAD, HD, CH,
XSA, GATE)
else:
_finish(ml_ptr, acc_ptr, r0, g, b, n_sp, last, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr,
y_row, lam_off, gate_off, NKV, NSPL, G, G_PAD, HD, CH, DIFF, XSA, GATE)
@triton.jit
def _finish_kernel(ml_ptr, slot_ptr, start_ptr, acc_ptr, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row, T,
lam_off, gate_off, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr,
HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr,
GATE: tl.constexpr, MINB: tl.constexpr = 1, PDL: tl.constexpr = False):
"""The finish of a multi-row (verify / short prompt) attention, a program per (row, KV head)."""
_pdl(PDL)
r = tl.program_id(0).to(tl.int64)
g = tl.program_id(1)
b = r // T
_, n_sp = _spans(tl.load(start_ptr + b * T), tl.load(slot_ptr + b * T + T - 1), NSPL, BL, MINB)
_finish(ml_ptr, acc_ptr, r, g, b, n_sp, tl.load(slot_ptr + r), qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr,
y_row, lam_off, gate_off, NKV, NSPL, G, G_PAD, HD, CH, DIFF, XSA, GATE)
@triton.jit
def _iha_finish_kernel(ml_ptr, slot_ptr, start_ptr, acc_ptr, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row,
Tt, win, r_ptr, gate_off, P: tl.constexpr, NKV: tl.constexpr, NSPL: tl.constexpr,
G: tl.constexpr, G_PAD: tl.constexpr, HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr,
XSA: tl.constexpr, GATE: tl.constexpr, WIN: tl.constexpr, MINB: tl.constexpr = 1,
PDL: tl.constexpr = False):
"""IHA's finish of a multi-token call, a program per (token row, KV head)."""
_pdl(PDL)
rt = tl.program_id(0).to(tl.int64)
g = tl.program_id(1)
b = rt // Tt
start, last = _seq_keys(start_ptr, slot_ptr, b * Tt * P, Tt * P, win, WIN)
_, n_sp = _spans(start, last, NSPL, BL, MINB)
_iha_finish(ml_ptr, acc_ptr, rt, g, b, n_sp, tl.load(slot_ptr + rt * P), r_ptr, qkv_ptr, qkv_row, vc_ptr,
kc_b, kc_h, y_ptr, y_row, gate_off, P, NKV, NSPL, G, G_PAD, HD, CH, XSA, GATE)
# tests set 32 / False so a 1-row step and a verify match bitwise
QR_MIN = 16
FUSED_FINISH = True
# smallest span, in 64-key blocks
ATTN_MINB = 1
def attention(q, kc, vc, slot, start, qkv, y, ml, acc, cnt, T: int, *, nq, nkv, hd, nspl, lam_off,
gate_off, diff, xsa, gate, win: int = 0, iha_p: int = 0, iha_r=None, pf=None, bl: int = 64,
warps: int | None = None) -> None:
"""Flash-decoding attention for ``R = B * T`` rows (``win``: sliding window; ``iha_p``: pseudo-rows)."""
if iha_p:
assert not diff, "IHA with DiffAttn V2: no fused finish"
T = T * iha_p
R = q.shape[0]
G = nq // nkv
g_pad = max(16, triton.next_power_of_2(G))
qr = max(QR_MIN, triton.next_power_of_2(T * G))
ch = 8 if g_pad <= 16 else 4
_attn_kernel[(nspl, nkv, R // T)](q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1), slot, start, ml, acc,
cnt, qkv, qkv.stride(0), y, y.stride(0), T, hd ** -0.5 * LOG2E, lam_off,
gate_off, int(win), iha_r if iha_p else q,
NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad, QR=qr, HD=hd, BL=bl,
CH=ch, DIFF=diff, XSA=xsa, GATE=gate,
FINISH=T == max(iha_p, 1) and FUSED_FINISH, WIN=win > 0, IHA=iha_p,
MINB=ATTN_MINB,
num_warps=warps or (4 if qr <= 32 else 8), num_stages=2, PDL=PDL, launch_pdl=PDL,
**_pf(pf))
if iha_p:
if T > iha_p or not FUSED_FINISH:
_iha_finish_kernel[(R // iha_p, nkv)](ml, slot, start, acc, qkv, qkv.stride(0), vc, kc.stride(0),
kc.stride(1), y, y.stride(0), T // iha_p, int(win), iha_r,
gate_off, P=iha_p, NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad,
HD=hd, BL=bl, CH=ch, XSA=xsa, GATE=gate, WIN=win > 0, MINB=ATTN_MINB,
num_warps=4, PDL=PDL, launch_pdl=PDL)
return
if T > 1 or not FUSED_FINISH:
_finish_kernel[(R, nkv)](ml, slot, start, acc, qkv, qkv.stride(0), vc, kc.stride(0), kc.stride(1), y,
y.stride(0), T, lam_off, gate_off, NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad, HD=hd,
BL=bl, CH=ch, DIFF=diff, XSA=xsa, GATE=gate, MINB=ATTN_MINB, num_warps=4, PDL=PDL,
launch_pdl=PDL)
@triton.jit
def _iha_mix_kernel(q_ptr, q_row, kl_ptr, vl_ptr, kl_b, kl_h, aq_ptr, ak_ptr, av_ptr, qg_ptr, kg_ptr, temp_ptr,
pos_ptr, slot_ptr, cos_ptr, sin_ptr, qv_ptr, qv_row, kc_ptr, vc_ptr, kc_b, kc_h, T, eps,
P: tl.constexpr, NQ: tl.constexpr, NQ_PAD: tl.constexpr, NKV: tl.constexpr, HD: tl.constexpr,
ROPE: tl.constexpr, PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
"""One (row, pseudo-head p): IHA mixes, norm, temperature and RoPE, then cache / ``qv`` writes."""
r = tl.program_id(0).to(tl.int64)
p = tl.program_id(1)
b = r // T
HALF: tl.constexpr = HD // 2
rslot = tl.load(slot_ptr + r).to(tl.int64)
pos = tl.load(pos_ptr + r).to(tl.int64) * P + p
slot = rslot * P + p
d = tl.arange(0, HD)
i2 = tl.arange(0, HALF)
h = tl.arange(0, NQ_PAD)
hm = h < NQ
at = tl.load(aq_ptr + (h[None, :] * NQ + h[:, None]) * P + p, mask=hm[:, None] & hm[None, :], other=0.0)
qgain = tl.load(qg_ptr + d)
kgain = tl.load(kg_ptr + d)
if ROPE:
c = tl.load(cos_ptr + pos * HALF + i2)
s = tl.load(sin_ptr + pos * HALF + i2)
_prefetch(pf_ptr, pf_bytes, PF, BULK)
_pdl(PDL)
# queries: one tensor-core tile
q = tl.load(q_ptr + r * q_row + h[:, None] * HD + d[None, :], mask=hm[:, None], other=0.0)
x = tl.dot(at.to(tl.bfloat16), q.to(tl.bfloat16)).to(tl.bfloat16).to(tl.float32) # at[h, m]
x = x * tl.rsqrt(tl.sum(x * x, 1) / HD + eps)[:, None] * qgain[None, :]
x = x.to(tl.bfloat16).to(tl.float32)
lo, hi = tl.split(tl.permute(tl.reshape(x, [NQ_PAD, 2, HALF]), (0, 2, 1)))
if ROPE:
lo, hi = lo * c[None, :] - hi * s[None, :], hi * c[None, :] + lo * s[None, :]
dst = qv_ptr + (r * P + p) * qv_row + h[:, None] * HD + i2[None, :]
tl.store(dst, lo.to(qv_ptr.dtype.element_ty), mask=hm[:, None])
tl.store(dst + HALF, hi.to(qv_ptr.dtype.element_ty), mask=hm[:, None])
# keys / values: CUDA-core sums
src = b * kl_b + rslot * HD
for g in tl.static_range(NKV):
k = tl.zeros([HD], dtype=tl.float32)
v = tl.zeros([HD], dtype=tl.float32)
for m in tl.static_range(NKV):
ak = tl.load(ak_ptr + (m * NKV + g) * P + p).to(tl.float32)
av = tl.load(av_ptr + (m * NKV + g) * P + p).to(tl.float32)
k += ak * tl.load(kl_ptr + src + m * kl_h + d).to(tl.float32)
v += av * tl.load(vl_ptr + src + m * kl_h + d).to(tl.float32)
k = k.to(tl.bfloat16).to(tl.float32)
k = (k * tl.rsqrt(tl.sum(k * k, 0) / HD + eps) * kgain).to(tl.bfloat16).to(tl.float32)
k = (k * tl.load(temp_ptr + g)).to(tl.bfloat16).to(tl.float32)
klo, khi = tl.split(tl.permute(tl.reshape(k, [2, HALF]), (1, 0)))
if ROPE:
klo, khi = klo * c - khi * s, khi * c + klo * s
kd = kc_ptr + b * kc_b + g * kc_h + slot * HD
tl.store(kd + i2, klo.to(kc_ptr.dtype.element_ty))
tl.store(kd + HALF + i2, khi.to(kc_ptr.dtype.element_ty))
tl.store(vc_ptr + b * kc_b + g * kc_h + slot * HD + d, v.to(vc_ptr.dtype.element_ty))
def iha_mix(q, kl, vl, aq, ak, av, qg, kg, temp, pos, slot, cos, sin, qv, kc, vc, T: int, eps: float, *,
P: int, nq: int, nkv: int, hd: int, rope: bool, pf=None) -> None:
"""``q`` ``(R, nq, hd)`` latent queries, ``kl`` / ``vl`` ``(B, nkv, L, hd)`` latent keys / values at the
rows' slots, mixes ``(H, H, P)`` bf16, ``qv`` ``(R * P, nq, hd)``, caches ``(B, nkv, L * P, hd)``."""
R = q.shape[0]
_iha_mix_kernel[(R, P)](q, q.stride(0), kl, vl, kl.stride(0), kl.stride(1), aq, ak, av, qg, kg, temp, pos,
slot, cos, sin, qv, qv.stride(0), kc, vc, kc.stride(0), kc.stride(1), T, eps,
P=P, NQ=nq, NQ_PAD=max(16, triton.next_power_of_2(nq)), NKV=nkv, HD=hd, ROPE=rope,
num_warps=4, PDL=PDL, launch_pdl=PDL, **_pf(pf))
@triton.jit
def _head_gate_kernel(y_ptr, y_row, b_ptr, h_ptr, h_row, D, RK: tl.constexpr, BLOCK: tl.constexpr,
PDL: tl.constexpr = False):
"""Gated byte head input ``[h, sigmoid(u . h + b) * P h]``."""
_pdl(PDL)
r = tl.program_id(0).to(tl.int64)
j = tl.arange(0, BLOCK)
jm = j < RK
a = tl.load(y_ptr + r * y_row + RK).to(tl.float32) + tl.load(b_ptr).to(tl.float32)
gt = tl.sigmoid(a.to(tl.bfloat16).to(tl.float32)).to(tl.bfloat16).to(tl.float32)
yp = tl.load(y_ptr + r * y_row + j, mask=jm, other=0.0).to(tl.float32)
tl.store(h_ptr + r * h_row + D + j, (gt * yp).to(h_ptr.dtype.element_ty), mask=jm)
def head_gate(y, bias, h, D: int, rank: int) -> None:
_head_gate_kernel[(y.shape[0],)](y, y.stride(0), bias, h, h.stride(0), D, RK=rank,
BLOCK=triton.next_power_of_2(rank), num_warps=4, PDL=PDL, launch_pdl=PDL)
@triton.jit
def _sample_kernel(x_ptr, x_row, V, inv_temp, top_k, top_p, min_p, seed_ptr, excl_ptr, y_ptr, pd_ptr, u_ptr,
pres_ptr, pres_row, ids_ptr, penalty, T,
TOP_K: tl.constexpr, TOP_P: tl.constexpr, MIN_P: tl.constexpr, EXCL: tl.constexpr,
REP: tl.constexpr, GREEDY: tl.constexpr, T_MAX: tl.constexpr, BLOCK: tl.constexpr,
ITERS: tl.constexpr):
"""One row: repetition penalty, temperature, top-k, top-p, min-p (thresholds by bisection), then a
draw."""
r = tl.program_id(0).to(tl.int64)
offs = tl.arange(0, BLOCK)
msk = offs < V
x = tl.load(x_ptr + r * x_row + offs, mask=msk, other=float("-inf"))
if REP:
b = r // T
t = r % T
seen = tl.load(pres_ptr + b * pres_row + offs, mask=msk, other=0) != 0
for i in tl.static_range(1, T_MAX):
if i <= t:
seen = seen | (offs == tl.load(ids_ptr + b * T + i))
x = tl.where(seen, tl.where(x < 0, x * penalty, x / penalty), x)
if GREEDY:
tl.store(y_ptr + r, tl.argmax(x, 0).to(tl.int64))
else:
x = x * inv_temp
if TOP_K:
lo = tl.min(tl.where(msk, x, float("inf")), 0)
hi = tl.max(x, 0)
for _ in range(ITERS): # count(x >= lo) >= k > count(x >= hi)
mid = lo + (hi - lo) * 0.5
ok = tl.sum((x >= mid).to(tl.int32), 0) >= top_k
lo = tl.where(ok, mid, lo)
hi = tl.where(ok, hi, mid)
kth = tl.min(tl.where(x >= lo, x, float("inf")), 0)
x = tl.where(x >= kth, x, float("-inf"))
m = tl.max(x, 0)
p = tl.exp(x - m) # max 1
z = tl.sum(p, 0)
if TOP_P:
lo = tl.zeros([], dtype=tl.float32)
hi = tl.full([], 1.0, tl.float32) # mass(p >= lo) >= top_p * z > mass(p >= hi) (or hi = 1)
goal = top_p * z
for _ in range(ITERS):
mid = lo + (hi - lo) * 0.5
ok = tl.sum(tl.where(p >= mid, p, 0.0), 0) >= goal
lo = tl.where(ok, mid, lo)
hi = tl.where(ok, hi, mid)
tau = tl.min(tl.where(p >= lo, p, float("inf")), 0)
p = tl.where(p >= tau, p, 0.0)
if MIN_P:
p = tl.where(p >= min_p, p, 0.0) # max is 1: min_p * max = min_p
seed = tl.load(seed_ptr)
if EXCL:
e = tl.load(excl_ptr + r)
pd = tl.sum(tl.where(offs == e, p, 0.0), 0) / tl.sum(p, 0)
tl.store(pd_ptr + r, pd)
tl.store(u_ptr + r, tl.rand(seed + 7919, r.to(tl.int32)))
p = tl.where(offs == e, 0.0, p)
cdf = tl.cumsum(p, 0)
u = tl.rand(seed, r.to(tl.int32)) * tl.max(cdf, 0)
tok = tl.min(tl.where((cdf > u) & (p > 0), offs, BLOCK), 0)
tl.store(y_ptr + r, tl.where(tok < V, tok, tl.argmax(p, 0)).to(tl.int64))
def sample(x: torch.Tensor, seed: torch.Tensor, y: torch.Tensor, *, temperature: float, top_k: int, top_p: float,
min_p: float, excl: torch.Tensor | None = None, pd: torch.Tensor | None = None,
u: torch.Tensor | None = None, greedy: bool = False, penalty: float = 1.0,
pres: torch.Tensor | None = None, ids: torch.Tensor | None = None, T: int = 1) -> None:
"""Draw ``y`` ``(R,)`` from fp32 logits ``x`` ``(R, V)``."""
R, V = x.shape
block = triton.next_power_of_2(V)
rep = penalty != 1.0
_sample_kernel[(R,)](x, x.stride(0), V, 1.0 / temperature, int(top_k), float(top_p), float(min_p), seed,
excl if excl is not None else y, y, pd if pd is not None else x,
u if u is not None else x, pres.view(torch.uint8) if rep else y,
pres.stride(0) if rep else 0, ids if rep else y, float(penalty), T,
TOP_K=bool(top_k) and top_k < V, TOP_P=top_p < 1.0, MIN_P=bool(min_p),
EXCL=excl is not None, REP=rep, GREEDY=greedy, T_MAX=T, BLOCK=block, ITERS=40,
num_warps=32 if block >= 16384 else 8)
kernels = sys.modules[__name__] # ``from .luau_kernels import kernels as K``: transformers'
# remote code only follows ``from .module import ...`` lines