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)
345 lines
9.2 KiB
Python
345 lines
9.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
|
|
import triton
|
|
import triton.language as tl
|
|
|
|
from fla.ops.backends import dispatch
|
|
from fla.ops.utils.cache import fla_cache_autotune
|
|
from fla.ops.utils.index import prepare_chunk_indices
|
|
from fla.ops.utils.op import exp
|
|
from fla.ops.utils.softplus import softplus
|
|
from fla.utils import autocast_custom_bwd, autocast_custom_fwd, autotune_cache_kwargs, input_guard
|
|
|
|
|
|
def naive_gdn_gate(
|
|
g: torch.Tensor,
|
|
A_log: torch.Tensor,
|
|
dt_bias: torch.Tensor | None = None,
|
|
output_dtype: torch.dtype = torch.float32,
|
|
) -> torch.Tensor:
|
|
"""
|
|
Torch reference implementation for GDN gate computation.
|
|
|
|
Computes: ``g = -A_log.exp() * softplus(g + dt_bias)``
|
|
|
|
Args:
|
|
g (torch.Tensor):
|
|
Input tensor of shape `[..., HV]`.
|
|
A_log (torch.Tensor):
|
|
Decay parameter tensor with `HV` elements.
|
|
dt_bias (torch.Tensor | None):
|
|
Optional bias tensor added to `g` before activation, shape `[HV]`.
|
|
|
|
Returns:
|
|
Output tensor of shape `[..., HV]`.
|
|
"""
|
|
g = g.float()
|
|
if dt_bias is not None:
|
|
g = g + dt_bias.float()
|
|
return (-A_log.float().exp() * F.softplus(g)).to(output_dtype)
|
|
|
|
|
|
@triton.heuristics({
|
|
'HAS_BIAS': lambda args: args['dt_bias'] is not None,
|
|
'HAS_SCALE': lambda args: args['scale'] is not None,
|
|
'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
|
|
})
|
|
@fla_cache_autotune(
|
|
configs=[
|
|
triton.Config({}, num_warps=num_warps)
|
|
for num_warps in [1, 2, 4, 8]
|
|
],
|
|
key=['H', 'BT', 'IS_VARLEN', 'REVERSE'],
|
|
**autotune_cache_kwargs,
|
|
)
|
|
@triton.jit(do_not_specialize=['T'])
|
|
def gdn_gate_chunk_cumsum_scalar_kernel(
|
|
g,
|
|
A_log,
|
|
dt_bias,
|
|
o,
|
|
scale,
|
|
cu_seqlens,
|
|
chunk_indices,
|
|
T,
|
|
H: tl.constexpr,
|
|
BT: tl.constexpr,
|
|
REVERSE: tl.constexpr,
|
|
HAS_BIAS: tl.constexpr,
|
|
HAS_SCALE: tl.constexpr,
|
|
IS_VARLEN: tl.constexpr,
|
|
):
|
|
i_t, i_bh = tl.program_id(0).to(tl.int64), tl.program_id(1).to(tl.int64)
|
|
i_b, i_h = i_bh // H, i_bh % H
|
|
|
|
if IS_VARLEN:
|
|
i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(chunk_indices + i_t * 2 + 1).to(tl.int64)
|
|
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(cu_seqlens + i_n + 1).to(tl.int64)
|
|
T = eos - bos
|
|
else:
|
|
bos, eos = i_b * T, i_b * T + T
|
|
|
|
o_t = i_t * BT + tl.arange(0, BT)
|
|
m_t = o_t < T
|
|
p_g = g + bos * H + i_h + o_t * H
|
|
p_o = o + bos * H + i_h + o_t * H
|
|
|
|
b_g = tl.load(p_g, mask=m_t, other=0.0).to(tl.float32)
|
|
if HAS_BIAS:
|
|
b_g = b_g + tl.load(dt_bias + i_h).to(tl.float32)
|
|
b_A = tl.load(A_log + i_h).to(tl.float32)
|
|
b_gate = -exp(b_A) * softplus(b_g)
|
|
|
|
b_o = tl.cumsum(b_gate, axis=0)
|
|
if REVERSE:
|
|
b_z = tl.sum(b_gate, axis=0)
|
|
b_o = -b_o + b_z[None] + b_gate
|
|
if HAS_SCALE:
|
|
b_o *= scale
|
|
tl.store(p_o, b_o.to(p_o.dtype.element_ty), mask=m_t)
|
|
|
|
|
|
@triton.heuristics({
|
|
'HAS_BIAS': lambda args: args['dt_bias'] is not None,
|
|
})
|
|
@fla_cache_autotune(
|
|
configs=[
|
|
triton.Config({}, num_warps=num_warps)
|
|
for num_warps in [1, 2, 4, 8]
|
|
],
|
|
key=['H', 'BT'],
|
|
**autotune_cache_kwargs,
|
|
)
|
|
@triton.jit(do_not_specialize=['T'])
|
|
def gdn_gate_bwd_kernel(
|
|
g,
|
|
A_log,
|
|
dt_bias,
|
|
dyg,
|
|
dg,
|
|
dA,
|
|
T,
|
|
H: tl.constexpr,
|
|
BT: tl.constexpr,
|
|
HAS_BIAS: tl.constexpr,
|
|
):
|
|
i_t, i_h = tl.program_id(0).to(tl.int64), tl.program_id(1)
|
|
|
|
b_A = tl.load(A_log + i_h).to(tl.float32)
|
|
|
|
o_t = i_t * BT + tl.arange(0, BT)
|
|
m_t = o_t < T
|
|
p_g = g + i_h + o_t * H
|
|
p_dg = dg + i_h + o_t * H
|
|
p_dyg = dyg + i_h + o_t * H
|
|
|
|
b_g = tl.load(p_g, mask=m_t, other=0.0).to(tl.float32)
|
|
b_dyg = tl.load(p_dyg, mask=m_t, other=0.0).to(tl.float32)
|
|
|
|
if HAS_BIAS:
|
|
b_g = b_g + tl.load(dt_bias + i_h).to(tl.float32)
|
|
|
|
# gate = -exp(A_log) * softplus(g + bias)
|
|
# d(gate)/d(g) = -exp(A_log) * sigmoid(g + bias) (softplus' = sigmoid)
|
|
# d(gate)/d(A_log) = -exp(A_log) * softplus(g + bias) = gate
|
|
b_neg_expA = -exp(b_A)
|
|
b_yg = b_neg_expA * softplus(b_g)
|
|
b_dg = b_neg_expA * (b_dyg * tl.sigmoid(b_g))
|
|
b_dA = tl.sum(b_dyg * b_yg, 0)
|
|
|
|
tl.store(p_dg, b_dg.to(p_dg.dtype.element_ty), mask=m_t)
|
|
tl.store(dA + i_t * H + i_h, b_dA)
|
|
|
|
|
|
@input_guard
|
|
@dispatch('gated_delta_rule')
|
|
def gdn_gate_chunk_cumsum(
|
|
g: torch.Tensor,
|
|
A_log: torch.Tensor,
|
|
chunk_size: int,
|
|
scale: float = None,
|
|
dt_bias: torch.Tensor | None = None,
|
|
cu_seqlens: torch.LongTensor | None = None,
|
|
chunk_indices: torch.LongTensor | None = None,
|
|
output_dtype: torch.dtype | None = torch.float,
|
|
) -> torch.Tensor:
|
|
B, T, H = g.shape
|
|
BT = chunk_size
|
|
if chunk_indices is None and cu_seqlens is not None:
|
|
chunk_indices = prepare_chunk_indices(cu_seqlens, BT)
|
|
NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices)
|
|
|
|
o = torch.empty_like(g, dtype=output_dtype or g.dtype)
|
|
gdn_gate_chunk_cumsum_scalar_kernel[(NT, B * H)](
|
|
g=g,
|
|
A_log=A_log,
|
|
dt_bias=dt_bias,
|
|
o=o,
|
|
scale=scale,
|
|
cu_seqlens=cu_seqlens,
|
|
chunk_indices=chunk_indices,
|
|
T=T,
|
|
H=H,
|
|
BT=BT,
|
|
REVERSE=False,
|
|
)
|
|
return o
|
|
|
|
|
|
@dispatch('gated_delta_rule')
|
|
def gdn_gate_bwd(
|
|
g: torch.Tensor,
|
|
A_log: torch.Tensor,
|
|
dt_bias: torch.Tensor | None,
|
|
dyg: torch.Tensor,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor | None]:
|
|
H = g.shape[-1]
|
|
T = g.numel() // H
|
|
BT = 32
|
|
NT = triton.cdiv(T, BT)
|
|
|
|
dg = torch.empty_like(g, dtype=torch.float32)
|
|
dA = A_log.new_empty(NT, H, dtype=torch.float32)
|
|
|
|
gdn_gate_bwd_kernel[(NT, H)](
|
|
g=g,
|
|
A_log=A_log,
|
|
dt_bias=dt_bias,
|
|
dyg=dyg,
|
|
dg=dg,
|
|
dA=dA,
|
|
T=T,
|
|
H=H,
|
|
BT=BT,
|
|
)
|
|
|
|
dg = dg.view_as(g).type_as(g)
|
|
dA = dA.sum(0).view_as(A_log).type_as(A_log)
|
|
dbias = dg.view(-1, H).sum(0).to(dt_bias) if dt_bias is not None else None
|
|
|
|
return dg, dA, dbias
|
|
|
|
|
|
@triton.heuristics({
|
|
'HAS_BIAS': lambda args: args['dt_bias'] is not None,
|
|
})
|
|
@fla_cache_autotune(
|
|
configs=[
|
|
triton.Config({'BT': BT}, num_warps=num_warps, num_stages=num_stages)
|
|
for BT in [32, 64, 128]
|
|
for num_warps in [1, 2, 4, 8]
|
|
for num_stages in [2, 3]
|
|
],
|
|
key=['H'],
|
|
**autotune_cache_kwargs,
|
|
)
|
|
@triton.jit(do_not_specialize=['T'])
|
|
def gdn_gate_fwd_kernel(
|
|
g,
|
|
A_log,
|
|
dt_bias,
|
|
yg,
|
|
T,
|
|
H: tl.constexpr,
|
|
BT: tl.constexpr,
|
|
HAS_BIAS: tl.constexpr,
|
|
):
|
|
i_t, i_h = tl.program_id(0).to(tl.int64), tl.program_id(1)
|
|
|
|
b_A = tl.load(A_log + i_h).to(tl.float32)
|
|
|
|
o_t = i_t * BT + tl.arange(0, BT)
|
|
m_t = o_t < T
|
|
p_g = g + i_h + o_t * H
|
|
p_yg = yg + i_h + o_t * H
|
|
b_g = tl.load(p_g, mask=m_t, other=0.0).to(tl.float32)
|
|
if HAS_BIAS:
|
|
b_g = b_g + tl.load(dt_bias + i_h).to(tl.float32)
|
|
b_yg = -exp(b_A) * softplus(b_g)
|
|
tl.store(p_yg, b_yg.to(p_yg.dtype.element_ty), mask=m_t)
|
|
|
|
|
|
@dispatch('gated_delta_rule')
|
|
def gdn_gate_fwd(
|
|
g: torch.Tensor,
|
|
A_log: torch.Tensor,
|
|
dt_bias: torch.Tensor | None = None,
|
|
output_dtype: torch.dtype = torch.float32,
|
|
) -> torch.Tensor:
|
|
H = g.shape[-1]
|
|
T = g.numel() // H
|
|
|
|
yg = torch.empty_like(g, dtype=output_dtype)
|
|
|
|
def grid(meta):
|
|
return (triton.cdiv(T, meta['BT']), H)
|
|
|
|
gdn_gate_fwd_kernel[grid](
|
|
g=g,
|
|
A_log=A_log,
|
|
dt_bias=dt_bias,
|
|
yg=yg,
|
|
T=T,
|
|
H=H,
|
|
)
|
|
return yg
|
|
|
|
|
|
class GDNGateFunction(torch.autograd.Function):
|
|
@staticmethod
|
|
@input_guard
|
|
@autocast_custom_fwd
|
|
def forward(
|
|
ctx,
|
|
g: torch.Tensor,
|
|
A_log: torch.Tensor,
|
|
dt_bias: torch.Tensor | None = None,
|
|
output_dtype: torch.dtype = torch.float32,
|
|
) -> torch.Tensor:
|
|
yg = gdn_gate_fwd(g=g, A_log=A_log, dt_bias=dt_bias, output_dtype=output_dtype)
|
|
ctx.save_for_backward(g, A_log, dt_bias)
|
|
return yg
|
|
|
|
@staticmethod
|
|
@input_guard
|
|
@autocast_custom_bwd
|
|
def backward(ctx, dyg: torch.Tensor):
|
|
g, A_log, dt_bias = ctx.saved_tensors
|
|
dg, dA, dbias = gdn_gate_bwd(g=g, A_log=A_log, dt_bias=dt_bias, dyg=dyg)
|
|
return dg, dA, dbias, None
|
|
|
|
|
|
@torch.compiler.disable
|
|
def fused_gdn_gate(
|
|
g: torch.Tensor,
|
|
A_log: torch.Tensor,
|
|
dt_bias: torch.Tensor | None = None,
|
|
output_dtype: torch.dtype = torch.float32,
|
|
) -> torch.Tensor:
|
|
r"""
|
|
Fused GDN gate computation with autograd support.
|
|
|
|
Computes: ``g = -A_log.exp() * softplus(g + dt_bias)``
|
|
|
|
Args:
|
|
g (torch.Tensor):
|
|
Input tensor of shape `[..., HV]`.
|
|
A_log (torch.Tensor):
|
|
Decay parameter tensor with `HV` elements.
|
|
dt_bias (torch.Tensor | None):
|
|
Optional bias tensor added to `g` before activation, shape `[HV]`.
|
|
output_dtype (torch.dtype):
|
|
The dtype of the output tensor. Default: `torch.float32`.
|
|
|
|
Returns:
|
|
Output tensor of shape `[..., HV]`.
|
|
"""
|
|
return GDNGateFunction.apply(g, A_log, dt_bias, output_dtype)
|