Sources cloned and tree'd (no --depth):
- jd-opensource/xllm: ILU kernels, CUDA kernels, MoE kernels
- NVIDIA/cccl: CUB tuning/dispatch headers (block-level primitives)
- fla-org/flash-linear-attention: Triton GDN kernels
- NVIDIA/cutlass: grouped GEMM reference (read, not copied)
- Dao-AILab/flash-attention: attention kernel reference (SM80+, read only)
New CUDA kernels (from xllm, SM-agnostic, portable to BI-V100):
ex_engine/xllm_kernels/cuda/activation.cu (188 lines) — silu_and_mul, gelu
ex_engine/xllm_kernels/cuda/norm.cu (600 lines) — rms_norm, fused_add_rms_norm
ex_engine/xllm_kernels/cuda/rope.cu (258 lines) — rotary_embedding
ex_engine/xllm_kernels/cuda/block_copy.cu (209 lines) — copy_blocks, swap_blocks
ex_engine/xllm_kernels/cuda/reshape_paged_cache.cu (101 lines) — KV cache ops
ex_engine/xllm_kernels/cuda/headers/ (5 headers for compilation)
ILU bridge kernel sources (from xllm, verified SAME as upstream):
ex_engine/xllm_kernels/ilu/ (10 files, 925 lines total)
— activation.cpp, attention.cpp, fused_moe.cpp, group_gemm.cpp,
matmul.cpp, norm.cpp, rope.cpp, ilu_ops_api.h, ixformer.h, utils.h
FLA Triton GDN kernels (for GatedDeltaNet without SM90+ FlashQLA):
ex_engine/fla_kernels/gated_delta_rule/ (7 files, 2370 lines)
— chunk_fwd.py (428), chunk.py (487), wy_fast.py (409),
fused_recurrent.py (392), naive.py (161), gate.py (380)
CCCL sync (12 tuning + 14 dispatch headers updated from NVIDIA/cccl):
cccl_upstream/cub/cub/device/dispatch/tuning/ — 12 changed files synced
cccl_upstream/cub/cub/device/dispatch/ — 14 changed dispatch files synced
Compilation targets for real machine (ivcore10):
1. CUDA kernels: --cuda-gpu-arch=ivcore10 via corex clang/16
2. ILU bridges: torch.utils.cpp_extension linking ixformer .so
3. FLA kernels: Triton JIT (if Triton works on BI-V100)
162 lines
5.2 KiB
Python
162 lines
5.2 KiB
Python
# Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
|
|
#
|
|
# This source code is licensed under the MIT license found in the
|
|
# LICENSE file in the root directory of this source tree.
|
|
# For a list of all contributors, visit:
|
|
# https://github.com/fla-org/flash-linear-attention/graphs/contributors
|
|
|
|
import torch
|
|
import torch.nn.functional as F
|
|
from einops import rearrange
|
|
|
|
|
|
def naive_recurrent_gated_delta_rule(
|
|
q: torch.Tensor,
|
|
k: torch.Tensor,
|
|
v: torch.Tensor,
|
|
beta: torch.Tensor,
|
|
g: torch.Tensor,
|
|
scale: float = None,
|
|
initial_state: torch.Tensor = None,
|
|
output_final_state: bool = False,
|
|
):
|
|
"""
|
|
Reference PyTorch implementation of recurrent gated delta rule.
|
|
|
|
Args:
|
|
q: [B, T, H, K]
|
|
k: [B, T, H, K]
|
|
v: [B, T, H, V]
|
|
beta: [B, T, H]
|
|
g: [B, T, H]
|
|
scale: float, optional
|
|
initial_state: [B, H, K, V], optional
|
|
output_final_state: bool
|
|
|
|
Returns:
|
|
o: [B, T, H, V]
|
|
final_state: [B, H, K, V] if output_final_state else None
|
|
"""
|
|
q, k, v, beta, g = map(lambda x: x.transpose(1, 2).contiguous().to(torch.float32), [q, k, v, beta, g])
|
|
B, H, T, K, V = *k.shape, v.shape[-1]
|
|
o = torch.zeros(B, H, T, V).to(v)
|
|
h = torch.zeros(B, H, K, V).to(v)
|
|
if initial_state is not None:
|
|
h = initial_state.to(torch.float32)
|
|
if scale is None:
|
|
scale = 1 / (q.shape[-1] ** 0.5)
|
|
q = q * scale
|
|
|
|
for i in range(T):
|
|
b_q = q[:, :, i]
|
|
b_k = k[:, :, i]
|
|
b_v = v[:, :, i].clone()
|
|
h = h.clone() * g[:, :, i].exp()[..., None, None]
|
|
b_beta = beta[:, :, i]
|
|
b_v = b_v - (h.clone() * b_k[..., None]).sum(-2)
|
|
b_v = b_v * b_beta[..., None]
|
|
h = h.clone() + b_k.unsqueeze(-1) * b_v.unsqueeze(-2)
|
|
o[:, :, i] = torch.einsum('bhd,bhdm->bhm', b_q, h)
|
|
|
|
if not output_final_state:
|
|
h = None
|
|
o = o.transpose(1, 2).contiguous()
|
|
return o, h
|
|
|
|
|
|
def naive_chunk_gated_delta_rule(
|
|
q: torch.Tensor,
|
|
k: torch.Tensor,
|
|
v: torch.Tensor,
|
|
g: torch.Tensor,
|
|
beta: torch.Tensor,
|
|
chunk_size: int = 64,
|
|
scale: float = None,
|
|
initial_state: torch.Tensor = None,
|
|
output_final_state: bool = False,
|
|
):
|
|
"""
|
|
Reference PyTorch implementation of chunk gated delta rule.
|
|
|
|
Args:
|
|
q: [B, T, H, K]
|
|
k: [B, T, H, K]
|
|
v: [B, T, H, V]
|
|
g: [B, T, H]
|
|
beta: [B, T, H]
|
|
chunk_size: int
|
|
scale: float, optional
|
|
initial_state: [B, H, K, V], optional
|
|
output_final_state: bool
|
|
|
|
Returns:
|
|
o: [B, T, H, V]
|
|
final_state: [B, H, K, V] if output_final_state else None
|
|
"""
|
|
BT = chunk_size
|
|
if scale is None:
|
|
scale = 1 / (q.shape[-1] ** 0.5)
|
|
|
|
q, k, v, beta, g = map(lambda x: x.transpose(1, 2).contiguous().to(torch.float32), [q, k, v, beta, g])
|
|
|
|
T = q.shape[-2]
|
|
pad_len = (BT - (T % BT)) % BT
|
|
if pad_len > 0:
|
|
q = F.pad(q, (0, 0, 0, pad_len))
|
|
k = F.pad(k, (0, 0, 0, pad_len))
|
|
v = F.pad(v, (0, 0, 0, pad_len))
|
|
beta = F.pad(beta, (0, pad_len))
|
|
g = F.pad(g, (0, pad_len))
|
|
|
|
q, k, v, beta, g = map(lambda x: x.to(torch.float32), [q, k, v, beta, g])
|
|
decay = g
|
|
chunk_size = BT
|
|
b, h, l, d_k = q.shape
|
|
d_v = v.shape[-1]
|
|
q = q * scale
|
|
v = v * beta[..., None]
|
|
k_beta = k * beta[..., None]
|
|
assert l % chunk_size == 0
|
|
|
|
# note that diagonal is masked.
|
|
mask = torch.triu(torch.ones(chunk_size, chunk_size, dtype=torch.bool, device=q.device), diagonal=0)
|
|
q, k, v, k_beta, decay = map(
|
|
lambda x: rearrange(x, 'b h (n c) d -> b h n c d', c=chunk_size),
|
|
[q, k, v, k_beta, decay.unsqueeze(-1)],
|
|
)
|
|
decay = decay.squeeze(-1).cumsum(-1)
|
|
decay_exp = decay.exp()[..., None]
|
|
L_mask = ((decay.unsqueeze(-1) - decay.unsqueeze(-2)).tril().exp().float()).tril()
|
|
attn = -((k_beta @ k.transpose(-1, -2)) * L_mask).masked_fill(mask, 0)
|
|
for i in range(1, chunk_size):
|
|
attn[..., i, :i] = attn[..., i, :i].clone() + (attn[..., i, :i, None].clone() * attn[..., :i, :i].clone()).sum(-2)
|
|
attn = attn + torch.eye(chunk_size, dtype=torch.float, device=q.device)
|
|
attn = attn
|
|
k_cumsum = attn @ v
|
|
k_cumdecay = attn @ (k_beta * decay_exp)
|
|
v = k_cumsum
|
|
|
|
S = k.new_zeros(b, h, d_k, d_v)
|
|
if initial_state is not None:
|
|
S = initial_state.to(torch.float32)
|
|
|
|
o = torch.zeros_like(v)
|
|
mask = torch.triu(torch.ones(chunk_size, chunk_size, dtype=torch.bool, device=q.device), diagonal=1)
|
|
for i in range(0, l // chunk_size):
|
|
q_i, k_i, v_i = q[:, :, i], k[:, :, i], v[:, :, i]
|
|
attn = (q_i @ k_i.transpose(-1, -2) * L_mask[:, :, i]).masked_fill_(mask, 0)
|
|
v_prime = (k_cumdecay[:, :, i]) @ S
|
|
v_new = v_i - v_prime
|
|
o_inter = (q_i * decay[:, :, i, :, None].exp()) @ S
|
|
o[:, :, i] = o_inter + attn @ v_new
|
|
S = S * decay[:, :, i, -1, None, None].exp() + (k_i * (decay[:, :, i, -1, None] - decay[:, :, i]).exp()
|
|
[..., None]).transpose(-1, -2) @ v_new
|
|
if not output_final_state:
|
|
S = None
|
|
|
|
# unpad
|
|
o = rearrange(o, 'b h n c d -> b h (n c) d')
|
|
o = o[:, :, :T]
|
|
o = o.transpose(1, 2)
|
|
return o, S
|