Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

1518 lines
55 KiB
Python

"""CuTe DSL Fused Sigmoid Gating Delta Rule Kernel for KDA Decode.
This version uses production / Triton-compatible VK state layout:
state.shape == (pool_size, HV, V, K)
The kernel still computes on a logical (K, V) matrix in shared memory. Global
state loads/stores therefore explicitly map:
global(V, K) <-> shared(K, V)
Notes:
- This is a correctness-first implementation for decode.
- It keeps the original small-batch / large-batch split.
- It preserves the previous PAD semantics: if pool_idx < 0 the block does not
load / update / write output or state, consistent with the earlier CuTe path.
"""
import logging
from typing import Dict, Optional, Tuple
import cuda.bindings.driver as cuda
import cutlass
import cutlass.cute as cute
import torch
from cutlass.cute.runtime import from_dlpack
logger = logging.getLogger(__name__)
_compiled_kernels: Dict[Tuple, object] = {}
_cu_seqlens_cache: Dict[Tuple, torch.Tensor] = {}
TILE_K = 128
TILE_V = 32
TILE_V_PADDED = 36
TILE_V_SMALL = 16
TILE_V_SMALL_PADDED = 20
NUM_STAGES = 2
NUM_THREADS = 128
NUM_BLOCKS_PER_STATE_SMALL = 8
NUM_THREADS_LARGE = 256
NUM_WARPS_LARGE = 8
V_PER_WARP = 4
ROWS_PER_ITER = 8
NUM_K_ITERS = TILE_K // ROWS_PER_ITER
SMALL_BATCH_THRESHOLD = 32
def _define_kernels():
"""Define CuTe DSL kernels for KDA normal and varlen decode modes."""
NUM_WARPS_SMALL = 4
V_PER_WARP_SMALL = TILE_V_SMALL // NUM_WARPS_SMALL
ROWS_PER_ITER_SMALL = 32 // V_PER_WARP_SMALL
NUM_K_ITERS_SMALL = TILE_K // ROWS_PER_ITER_SMALL
@cute.kernel
def kda_kernel_small_batch(
tiled_copy_load: cute.TiledCopy,
h0_source: cute.Tensor,
smem_layout_staged: cute.Layout,
num_v_tiles: cutlass.Constexpr[int],
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
o: cute.Tensor,
h0_indices: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
use_qk_l2norm: cutlass.Constexpr[bool],
):
"""Small batch KDA kernel for dense decode: q/k/v shapes (N, 1, ...)."""
del tiled_copy_load
tidx, _, _ = cute.arch.thread_idx()
in_warp_tid = tidx % 32
warp_idx = cute.arch.warp_idx()
warp_idx = cute.arch.make_warp_uniform(warp_idx)
block_idx, _, _ = cute.arch.block_idx()
batch_idx = block_idx // NUM_BLOCKS_PER_STATE_SMALL
batch_inner = block_idx % NUM_BLOCKS_PER_STATE_SMALL
num_v_tiles_per_block = num_v_tiles // NUM_BLOCKS_PER_STATE_SMALL
start_v_tile = batch_inner * num_v_tiles_per_block
i_n = batch_idx // HV
i_hv = batch_idx % HV
i_h = i_hv // (HV // H)
pool_idx = h0_indices[i_n]
if pool_idx >= 0:
k_local = in_warp_tid // V_PER_WARP_SMALL
v_local = in_warp_tid % V_PER_WARP_SMALL
v_base = warp_idx * V_PER_WARP_SMALL
v_idx = v_base + v_local
smem = cutlass.utils.SmemAllocator()
sData = smem.allocate_tensor(cutlass.Float32, smem_layout_staged, 128)
smem_o_layout = cute.make_layout((TILE_V_SMALL,), stride=(1,))
smem_o = smem.allocate_tensor(cutlass.Float32, smem_o_layout, 128)
smem_k_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_q_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_g_layout = cute.make_layout((TILE_K,), stride=(1,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_layout, 128)
sG = smem.allocate_tensor(cutlass.Float32, smem_g_layout, 128)
if tidx < TILE_K:
sK[tidx] = cutlass.Float32(k[i_n, 0, i_h, tidx])
sQ[tidx] = cutlass.Float32(q[i_n, 0, i_h, tidx])
r_A_log = cutlass.Float32(A_log[i_hv])
r_exp_A = cute.exp(r_A_log)
if tidx < TILE_K:
r_a_k = cutlass.Float32(a[i_n, 0, i_hv, tidx])
r_dt_bias_k = cutlass.Float32(dt_bias[i_hv, tidx])
x = r_a_k + r_dt_bias_k
beta_x = softplus_beta * x
softplus_x = 0.0
if beta_x <= softplus_threshold:
exp_beta_x = cute.exp(beta_x)
log_input = cutlass.Float32(1.0 + exp_beta_x)
log_result = cutlass.Float32(cute.log(log_input))
softplus_x = cutlass.Float32(
(cutlass.Float32(1.0) / softplus_beta) * log_result
)
else:
softplus_x = x
sG[tidx] = cute.exp(-r_exp_A * softplus_x)
r_beta = 0.0
if in_warp_tid == 0:
r_b = cutlass.Float32(b[i_n, 0, i_hv])
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_beta = cute.arch.shuffle_sync(r_beta, 0)
cute.arch.barrier()
if use_qk_l2norm:
sum_q_partial = 0.0
sum_k_partial = 0.0
if tidx < TILE_K:
q_val = sQ[tidx]
k_val = sK[tidx]
sum_q_partial = q_val * q_val
sum_k_partial = k_val * k_val
for offset in [16, 8, 4, 2, 1]:
sum_q_partial += cute.arch.shuffle_sync_bfly(
sum_q_partial, offset=offset, mask=-1, mask_and_clamp=31
)
sum_k_partial += cute.arch.shuffle_sync_bfly(
sum_k_partial, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[warp_idx] = sum_q_partial
smem_o[warp_idx + 4] = sum_k_partial
cute.arch.barrier()
if warp_idx == 0:
local_sum_q = 0.0
local_sum_k = 0.0
if in_warp_tid < NUM_WARPS_SMALL:
local_sum_q = smem_o[in_warp_tid]
local_sum_k = smem_o[in_warp_tid + 4]
for offset in [2, 1]:
local_sum_q += cute.arch.shuffle_sync_bfly(
local_sum_q, offset=offset, mask=-1, mask_and_clamp=31
)
local_sum_k += cute.arch.shuffle_sync_bfly(
local_sum_k, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[0] = cute.rsqrt(local_sum_q + 1e-6)
smem_o[1] = cute.rsqrt(local_sum_k + 1e-6)
cute.arch.barrier()
inv_norm_q = smem_o[0]
inv_norm_k = smem_o[1]
if tidx < TILE_K:
sK[tidx] = sK[tidx] * inv_norm_k
sQ[tidx] = sQ[tidx] * scale * inv_norm_q
cute.arch.barrier()
else:
if tidx < TILE_K:
sQ[tidx] = sQ[tidx] * scale
cute.arch.barrier()
for v_tile_offset in range(num_v_tiles_per_block):
stage = v_tile_offset % NUM_STAGES
v_tile = start_v_tile + v_tile_offset
for k_iter in range(NUM_K_ITERS_SMALL):
flat_idx = tidx + k_iter * NUM_THREADS
k_load = flat_idx // TILE_V_SMALL
v_load = flat_idx % TILE_V_SMALL
if k_load < TILE_K:
v_global_load = v_tile * TILE_V_SMALL + v_load
h_val = 0.0
if v_global_load < v.shape[3]:
h_val = cutlass.Float32(
h0_source[(pool_idx, i_hv, v_global_load, k_load)]
)
sData[(k_load, v_load, stage)] = h_val
cute.arch.barrier()
v_global = v_tile * TILE_V_SMALL + v_idx
r_v = 0.0
if v_global < v.shape[3]:
r_v = cutlass.Float32(v[i_n, 0, i_hv, v_global])
sum_hk = 0.0
for k_iter in range(NUM_K_ITERS_SMALL):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
sum_hk += sData[(k_idx, v_idx, stage)] * sG[k_idx] * sK[k_idx]
for offset in [4, 2, 1]:
sum_hk += cute.arch.shuffle_sync_bfly(
sum_hk,
offset=offset * V_PER_WARP_SMALL,
mask=-1,
mask_and_clamp=31,
)
v_new = (r_v - sum_hk) * r_beta
v_new = cute.arch.shuffle_sync(v_new, v_local)
sum_hq = 0.0
for k_iter in range(NUM_K_ITERS_SMALL):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * sG[k_idx]
h_new = h_old + sK[k_idx] * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * sQ[k_idx]
for offset in [4, 2, 1]:
sum_hq += cute.arch.shuffle_sync_bfly(
sum_hq,
offset=offset * V_PER_WARP_SMALL,
mask=-1,
mask_and_clamp=31,
)
if k_local == 0 and v_global < v.shape[3]:
o[(i_n, 0, i_hv, v_global)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS_SMALL):
flat_idx = tidx + k_iter * NUM_THREADS
k_write = flat_idx // TILE_V_SMALL
v_write = flat_idx % TILE_V_SMALL
if k_write < TILE_K:
v_global_write = v_tile * TILE_V_SMALL + v_write
if v_global_write < v.shape[3]:
h0_source[(pool_idx, i_hv, v_global_write, k_write)] = (
sData[(k_write, v_write, stage)]
)
cute.arch.barrier()
@cute.kernel
def kda_kernel_small_batch_varlen(
tiled_copy_load: cute.TiledCopy,
h0_source: cute.Tensor,
smem_layout_staged: cute.Layout,
num_v_tiles: cutlass.Constexpr[int],
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
o: cute.Tensor,
h0_indices: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
use_qk_l2norm: cutlass.Constexpr[bool],
):
"""Small batch KDA kernel for varlen decode: q/k/v shapes (1, N, ...)."""
del tiled_copy_load
tidx, _, _ = cute.arch.thread_idx()
in_warp_tid = tidx % 32
warp_idx = cute.arch.warp_idx()
warp_idx = cute.arch.make_warp_uniform(warp_idx)
block_idx, _, _ = cute.arch.block_idx()
batch_idx = block_idx // NUM_BLOCKS_PER_STATE_SMALL
batch_inner = block_idx % NUM_BLOCKS_PER_STATE_SMALL
num_v_tiles_per_block = num_v_tiles // NUM_BLOCKS_PER_STATE_SMALL
start_v_tile = batch_inner * num_v_tiles_per_block
i_n = batch_idx // HV
i_hv = batch_idx % HV
i_h = i_hv // (HV // H)
pool_idx = h0_indices[i_n]
if pool_idx >= 0:
k_local = in_warp_tid // V_PER_WARP_SMALL
v_local = in_warp_tid % V_PER_WARP_SMALL
v_base = warp_idx * V_PER_WARP_SMALL
v_idx = v_base + v_local
smem = cutlass.utils.SmemAllocator()
sData = smem.allocate_tensor(cutlass.Float32, smem_layout_staged, 128)
smem_o_layout = cute.make_layout((TILE_V_SMALL,), stride=(1,))
smem_o = smem.allocate_tensor(cutlass.Float32, smem_o_layout, 128)
smem_k_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_q_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_g_layout = cute.make_layout((TILE_K,), stride=(1,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_layout, 128)
sG = smem.allocate_tensor(cutlass.Float32, smem_g_layout, 128)
if tidx < TILE_K:
sK[tidx] = cutlass.Float32(k[0, i_n, i_h, tidx])
sQ[tidx] = cutlass.Float32(q[0, i_n, i_h, tidx])
r_A_log = cutlass.Float32(A_log[i_hv])
r_exp_A = cute.exp(r_A_log)
if tidx < TILE_K:
r_a_k = cutlass.Float32(a[i_n, i_hv, tidx])
r_dt_bias_k = cutlass.Float32(dt_bias[i_hv, tidx])
x = r_a_k + r_dt_bias_k
beta_x = softplus_beta * x
softplus_x = 0.0
if beta_x <= softplus_threshold:
exp_beta_x = cute.exp(beta_x)
log_input = cutlass.Float32(1.0 + exp_beta_x)
log_result = cutlass.Float32(cute.log(log_input))
softplus_x = cutlass.Float32(
(cutlass.Float32(1.0) / softplus_beta) * log_result
)
else:
softplus_x = x
sG[tidx] = cute.exp(-r_exp_A * softplus_x)
r_beta = 0.0
if in_warp_tid == 0:
r_b = cutlass.Float32(b[i_n, i_hv])
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_beta = cute.arch.shuffle_sync(r_beta, 0)
cute.arch.barrier()
if use_qk_l2norm:
sum_q_partial = 0.0
sum_k_partial = 0.0
if tidx < TILE_K:
q_val = sQ[tidx]
k_val = sK[tidx]
sum_q_partial = q_val * q_val
sum_k_partial = k_val * k_val
for offset in [16, 8, 4, 2, 1]:
sum_q_partial += cute.arch.shuffle_sync_bfly(
sum_q_partial, offset=offset, mask=-1, mask_and_clamp=31
)
sum_k_partial += cute.arch.shuffle_sync_bfly(
sum_k_partial, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[warp_idx] = sum_q_partial
smem_o[warp_idx + 4] = sum_k_partial
cute.arch.barrier()
if warp_idx == 0:
local_sum_q = 0.0
local_sum_k = 0.0
if in_warp_tid < NUM_WARPS_SMALL:
local_sum_q = smem_o[in_warp_tid]
local_sum_k = smem_o[in_warp_tid + 4]
for offset in [2, 1]:
local_sum_q += cute.arch.shuffle_sync_bfly(
local_sum_q, offset=offset, mask=-1, mask_and_clamp=31
)
local_sum_k += cute.arch.shuffle_sync_bfly(
local_sum_k, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[0] = cute.rsqrt(local_sum_q + 1e-6)
smem_o[1] = cute.rsqrt(local_sum_k + 1e-6)
cute.arch.barrier()
inv_norm_q = smem_o[0]
inv_norm_k = smem_o[1]
if tidx < TILE_K:
sK[tidx] = sK[tidx] * inv_norm_k
sQ[tidx] = sQ[tidx] * scale * inv_norm_q
cute.arch.barrier()
else:
if tidx < TILE_K:
sQ[tidx] = sQ[tidx] * scale
cute.arch.barrier()
for v_tile_offset in range(num_v_tiles_per_block):
stage = v_tile_offset % NUM_STAGES
v_tile = start_v_tile + v_tile_offset
for k_iter in range(NUM_K_ITERS_SMALL):
flat_idx = tidx + k_iter * NUM_THREADS
k_load = flat_idx // TILE_V_SMALL
v_load = flat_idx % TILE_V_SMALL
if k_load < TILE_K:
v_global_load = v_tile * TILE_V_SMALL + v_load
h_val = 0.0
if v_global_load < v.shape[3]:
h_val = cutlass.Float32(
h0_source[(pool_idx, i_hv, v_global_load, k_load)]
)
sData[(k_load, v_load, stage)] = h_val
cute.arch.barrier()
v_global = v_tile * TILE_V_SMALL + v_idx
r_v = 0.0
if v_global < v.shape[3]:
r_v = cutlass.Float32(v[0, i_n, i_hv, v_global])
sum_hk = 0.0
for k_iter in range(NUM_K_ITERS_SMALL):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
sum_hk += sData[(k_idx, v_idx, stage)] * sG[k_idx] * sK[k_idx]
for offset in [4, 2, 1]:
sum_hk += cute.arch.shuffle_sync_bfly(
sum_hk,
offset=offset * V_PER_WARP_SMALL,
mask=-1,
mask_and_clamp=31,
)
v_new = (r_v - sum_hk) * r_beta
v_new = cute.arch.shuffle_sync(v_new, v_local)
sum_hq = 0.0
for k_iter in range(NUM_K_ITERS_SMALL):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * sG[k_idx]
h_new = h_old + sK[k_idx] * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * sQ[k_idx]
for offset in [4, 2, 1]:
sum_hq += cute.arch.shuffle_sync_bfly(
sum_hq,
offset=offset * V_PER_WARP_SMALL,
mask=-1,
mask_and_clamp=31,
)
if k_local == 0 and v_global < v.shape[3]:
o[(0, i_n, i_hv, v_global)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS_SMALL):
flat_idx = tidx + k_iter * NUM_THREADS
k_write = flat_idx // TILE_V_SMALL
v_write = flat_idx % TILE_V_SMALL
if k_write < TILE_K:
v_global_write = v_tile * TILE_V_SMALL + v_write
if v_global_write < v.shape[3]:
h0_source[(pool_idx, i_hv, v_global_write, k_write)] = (
sData[(k_write, v_write, stage)]
)
cute.arch.barrier()
@cute.kernel
def kda_kernel_large_batch(
tiled_copy_load: cute.TiledCopy,
h0_source: cute.Tensor,
smem_layout_staged: cute.Layout,
num_v_tiles: cutlass.Constexpr[int],
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
o: cute.Tensor,
h0_indices: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
use_qk_l2norm: cutlass.Constexpr[bool],
):
"""Large batch KDA kernel for dense decode: q/k/v shapes (N, 1, ...)."""
del tiled_copy_load
tidx, _, _ = cute.arch.thread_idx()
in_warp_tid = tidx % 32
warp_idx = cute.arch.warp_idx()
warp_idx = cute.arch.make_warp_uniform(warp_idx)
batch_idx, _, _ = cute.arch.block_idx()
i_n = batch_idx // HV
i_hv = batch_idx % HV
i_h = i_hv // (HV // H)
pool_idx = h0_indices[i_n]
if pool_idx >= 0:
k_local = in_warp_tid // V_PER_WARP
v_local = in_warp_tid % V_PER_WARP
v_base = warp_idx * V_PER_WARP
v_idx = v_base + v_local
smem = cutlass.utils.SmemAllocator()
sData = smem.allocate_tensor(cutlass.Float32, smem_layout_staged, 128)
smem_o_layout = cute.make_layout((TILE_V,), stride=(1,))
smem_o = smem.allocate_tensor(cutlass.Float32, smem_o_layout, 128)
smem_k_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_q_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_g_layout = cute.make_layout((TILE_K,), stride=(1,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_layout, 128)
sG = smem.allocate_tensor(cutlass.Float32, smem_g_layout, 128)
if tidx < TILE_K:
sK[tidx] = cutlass.Float32(k[i_n, 0, i_h, tidx])
sQ[tidx] = cutlass.Float32(q[i_n, 0, i_h, tidx])
r_A_log = cutlass.Float32(A_log[i_hv])
r_exp_A = cute.exp(r_A_log)
if tidx < TILE_K:
r_a_k = cutlass.Float32(a[i_n, 0, i_hv, tidx])
r_dt_bias_k = cutlass.Float32(dt_bias[i_hv, tidx])
x = r_a_k + r_dt_bias_k
beta_x = softplus_beta * x
softplus_x = 0.0
if beta_x <= softplus_threshold:
exp_beta_x = cute.exp(beta_x)
log_input = cutlass.Float32(1.0 + exp_beta_x)
log_result = cutlass.Float32(cute.log(log_input))
softplus_x = cutlass.Float32(
(cutlass.Float32(1.0) / softplus_beta) * log_result
)
else:
softplus_x = x
sG[tidx] = cute.exp(-r_exp_A * softplus_x)
r_beta = 0.0
if in_warp_tid == 0:
r_b = cutlass.Float32(b[i_n, 0, i_hv])
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_beta = cute.arch.shuffle_sync(r_beta, 0)
cute.arch.barrier()
if use_qk_l2norm:
sum_q_partial = 0.0
sum_k_partial = 0.0
if tidx < TILE_K:
q_val = sQ[tidx]
k_val = sK[tidx]
sum_q_partial = q_val * q_val
sum_k_partial = k_val * k_val
for offset in [16, 8, 4, 2, 1]:
sum_q_partial += cute.arch.shuffle_sync_bfly(
sum_q_partial, offset=offset, mask=-1, mask_and_clamp=31
)
sum_k_partial += cute.arch.shuffle_sync_bfly(
sum_k_partial, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[warp_idx] = sum_q_partial
smem_o[warp_idx + 8] = sum_k_partial
cute.arch.barrier()
if warp_idx == 0:
local_sum_q = 0.0
local_sum_k = 0.0
if in_warp_tid < NUM_WARPS_LARGE:
local_sum_q = smem_o[in_warp_tid]
local_sum_k = smem_o[in_warp_tid + 8]
for offset in [4, 2, 1]:
local_sum_q += cute.arch.shuffle_sync_bfly(
local_sum_q, offset=offset, mask=-1, mask_and_clamp=31
)
local_sum_k += cute.arch.shuffle_sync_bfly(
local_sum_k, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[0] = cute.rsqrt(local_sum_q + 1e-6)
smem_o[1] = cute.rsqrt(local_sum_k + 1e-6)
cute.arch.barrier()
inv_norm_q = smem_o[0]
inv_norm_k = smem_o[1]
if tidx < TILE_K:
sK[tidx] = sK[tidx] * inv_norm_k
sQ[tidx] = sQ[tidx] * scale * inv_norm_q
cute.arch.barrier()
else:
if tidx < TILE_K:
sQ[tidx] = sQ[tidx] * scale
cute.arch.barrier()
for v_tile in range(num_v_tiles):
stage = v_tile % NUM_STAGES
for k_iter in range(NUM_K_ITERS):
flat_idx = tidx + k_iter * NUM_THREADS_LARGE
k_load = flat_idx // TILE_V
v_load = flat_idx % TILE_V
if k_load < TILE_K:
v_global_load = v_tile * TILE_V + v_load
h_val = 0.0
if v_global_load < v.shape[3]:
h_val = cutlass.Float32(
h0_source[(pool_idx, i_hv, v_global_load, k_load)]
)
sData[(k_load, v_load, stage)] = h_val
cute.arch.barrier()
v_global = v_tile * TILE_V + v_idx
r_v = 0.0
if v_global < v.shape[3]:
r_v = cutlass.Float32(v[i_n, 0, i_hv, v_global])
sum_hk = 0.0
for k_iter in range(NUM_K_ITERS):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
sum_hk += sData[(k_idx, v_idx, stage)] * sG[k_idx] * sK[k_idx]
for offset in [4, 2, 1]:
sum_hk += cute.arch.shuffle_sync_bfly(
sum_hk,
offset=offset * V_PER_WARP,
mask=-1,
mask_and_clamp=31,
)
v_new = (r_v - sum_hk) * r_beta
v_new = cute.arch.shuffle_sync(v_new, v_local)
sum_hq = 0.0
for k_iter in range(NUM_K_ITERS):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * sG[k_idx]
h_new = h_old + sK[k_idx] * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * sQ[k_idx]
for offset in [4, 2, 1]:
sum_hq += cute.arch.shuffle_sync_bfly(
sum_hq,
offset=offset * V_PER_WARP,
mask=-1,
mask_and_clamp=31,
)
if k_local == 0 and v_global < v.shape[3]:
o[(i_n, 0, i_hv, v_global)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS):
flat_idx = tidx + k_iter * NUM_THREADS_LARGE
k_write = flat_idx // TILE_V
v_write = flat_idx % TILE_V
if k_write < TILE_K:
v_global_write = v_tile * TILE_V + v_write
if v_global_write < v.shape[3]:
h0_source[(pool_idx, i_hv, v_global_write, k_write)] = (
sData[(k_write, v_write, stage)]
)
cute.arch.barrier()
@cute.kernel
def kda_kernel_large_batch_varlen(
tiled_copy_load: cute.TiledCopy,
h0_source: cute.Tensor,
smem_layout_staged: cute.Layout,
num_v_tiles: cutlass.Constexpr[int],
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
o: cute.Tensor,
h0_indices: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
use_qk_l2norm: cutlass.Constexpr[bool],
):
"""Large batch KDA kernel for varlen decode: q/k/v shapes (1, N, ...)."""
del tiled_copy_load
tidx, _, _ = cute.arch.thread_idx()
in_warp_tid = tidx % 32
warp_idx = cute.arch.warp_idx()
warp_idx = cute.arch.make_warp_uniform(warp_idx)
batch_idx, _, _ = cute.arch.block_idx()
i_n = batch_idx // HV
i_hv = batch_idx % HV
i_h = i_hv // (HV // H)
pool_idx = h0_indices[i_n]
if pool_idx >= 0:
k_local = in_warp_tid // V_PER_WARP
v_local = in_warp_tid % V_PER_WARP
v_base = warp_idx * V_PER_WARP
v_idx = v_base + v_local
smem = cutlass.utils.SmemAllocator()
sData = smem.allocate_tensor(cutlass.Float32, smem_layout_staged, 128)
smem_o_layout = cute.make_layout((TILE_V,), stride=(1,))
smem_o = smem.allocate_tensor(cutlass.Float32, smem_o_layout, 128)
smem_k_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_q_layout = cute.make_layout((TILE_K,), stride=(1,))
smem_g_layout = cute.make_layout((TILE_K,), stride=(1,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_layout, 128)
sG = smem.allocate_tensor(cutlass.Float32, smem_g_layout, 128)
if tidx < TILE_K:
sK[tidx] = cutlass.Float32(k[0, i_n, i_h, tidx])
sQ[tidx] = cutlass.Float32(q[0, i_n, i_h, tidx])
r_A_log = cutlass.Float32(A_log[i_hv])
r_exp_A = cute.exp(r_A_log)
if tidx < TILE_K:
r_a_k = cutlass.Float32(a[i_n, i_hv, tidx])
r_dt_bias_k = cutlass.Float32(dt_bias[i_hv, tidx])
x = r_a_k + r_dt_bias_k
beta_x = softplus_beta * x
softplus_x = 0.0
if beta_x <= softplus_threshold:
exp_beta_x = cute.exp(beta_x)
log_input = cutlass.Float32(1.0 + exp_beta_x)
log_result = cutlass.Float32(cute.log(log_input))
softplus_x = cutlass.Float32(
(cutlass.Float32(1.0) / softplus_beta) * log_result
)
else:
softplus_x = x
sG[tidx] = cute.exp(-r_exp_A * softplus_x)
r_beta = 0.0
if in_warp_tid == 0:
r_b = cutlass.Float32(b[i_n, i_hv])
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_beta = cute.arch.shuffle_sync(r_beta, 0)
cute.arch.barrier()
if use_qk_l2norm:
sum_q_partial = 0.0
sum_k_partial = 0.0
if tidx < TILE_K:
q_val = sQ[tidx]
k_val = sK[tidx]
sum_q_partial = q_val * q_val
sum_k_partial = k_val * k_val
for offset in [16, 8, 4, 2, 1]:
sum_q_partial += cute.arch.shuffle_sync_bfly(
sum_q_partial, offset=offset, mask=-1, mask_and_clamp=31
)
sum_k_partial += cute.arch.shuffle_sync_bfly(
sum_k_partial, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[warp_idx] = sum_q_partial
smem_o[warp_idx + 8] = sum_k_partial
cute.arch.barrier()
if warp_idx == 0:
local_sum_q = 0.0
local_sum_k = 0.0
if in_warp_tid < NUM_WARPS_LARGE:
local_sum_q = smem_o[in_warp_tid]
local_sum_k = smem_o[in_warp_tid + 8]
for offset in [4, 2, 1]:
local_sum_q += cute.arch.shuffle_sync_bfly(
local_sum_q, offset=offset, mask=-1, mask_and_clamp=31
)
local_sum_k += cute.arch.shuffle_sync_bfly(
local_sum_k, offset=offset, mask=-1, mask_and_clamp=31
)
if in_warp_tid == 0:
smem_o[0] = cute.rsqrt(local_sum_q + 1e-6)
smem_o[1] = cute.rsqrt(local_sum_k + 1e-6)
cute.arch.barrier()
inv_norm_q = smem_o[0]
inv_norm_k = smem_o[1]
if tidx < TILE_K:
sK[tidx] = sK[tidx] * inv_norm_k
sQ[tidx] = sQ[tidx] * scale * inv_norm_q
cute.arch.barrier()
else:
if tidx < TILE_K:
sQ[tidx] = sQ[tidx] * scale
cute.arch.barrier()
for v_tile in range(num_v_tiles):
stage = v_tile % NUM_STAGES
for k_iter in range(NUM_K_ITERS):
flat_idx = tidx + k_iter * NUM_THREADS_LARGE
k_load = flat_idx // TILE_V
v_load = flat_idx % TILE_V
if k_load < TILE_K:
v_global_load = v_tile * TILE_V + v_load
h_val = 0.0
if v_global_load < v.shape[3]:
h_val = cutlass.Float32(
h0_source[(pool_idx, i_hv, v_global_load, k_load)]
)
sData[(k_load, v_load, stage)] = h_val
cute.arch.barrier()
v_global = v_tile * TILE_V + v_idx
r_v = 0.0
if v_global < v.shape[3]:
r_v = cutlass.Float32(v[0, i_n, i_hv, v_global])
sum_hk = 0.0
for k_iter in range(NUM_K_ITERS):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
sum_hk += sData[(k_idx, v_idx, stage)] * sG[k_idx] * sK[k_idx]
for offset in [4, 2, 1]:
sum_hk += cute.arch.shuffle_sync_bfly(
sum_hk,
offset=offset * V_PER_WARP,
mask=-1,
mask_and_clamp=31,
)
v_new = (r_v - sum_hk) * r_beta
v_new = cute.arch.shuffle_sync(v_new, v_local)
sum_hq = 0.0
for k_iter in range(NUM_K_ITERS):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * sG[k_idx]
h_new = h_old + sK[k_idx] * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * sQ[k_idx]
for offset in [4, 2, 1]:
sum_hq += cute.arch.shuffle_sync_bfly(
sum_hq,
offset=offset * V_PER_WARP,
mask=-1,
mask_and_clamp=31,
)
if k_local == 0 and v_global < v.shape[3]:
o[(0, i_n, i_hv, v_global)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS):
flat_idx = tidx + k_iter * NUM_THREADS_LARGE
k_write = flat_idx // TILE_V
v_write = flat_idx % TILE_V
if k_write < TILE_K:
v_global_write = v_tile * TILE_V + v_write
if v_global_write < v.shape[3]:
h0_source[(pool_idx, i_hv, v_global_write, k_write)] = (
sData[(k_write, v_write, stage)]
)
cute.arch.barrier()
return (
kda_kernel_small_batch,
kda_kernel_small_batch_varlen,
kda_kernel_large_batch,
kda_kernel_large_batch_varlen,
)
def _create_jit_functions():
"""Create JIT-compiled launcher functions for all KDA kernel variants."""
kda_small, kda_small_varlen, kda_large, kda_large_varlen = _define_kernels()
@cute.jit
def run_small_batch(
cu_seqlens: cute.Tensor,
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
h0_source: cute.Tensor,
h0_indices: cute.Tensor,
o: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
B: cutlass.Constexpr[int],
T: cutlass.Constexpr[int],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
K: cutlass.Constexpr[int],
V: cutlass.Constexpr[int],
use_initial_state: cutlass.Constexpr[bool],
use_qk_l2norm: cutlass.Constexpr[bool],
stream: cuda.CUstream,
):
del cu_seqlens, B, T, K, use_initial_state
_, hv_dim, v_dim, _ = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
num_v_tiles_small = cute.ceil_div(v_dim, TILE_V_SMALL)
smem_layout_small = cute.make_layout(
(TILE_K, TILE_V_SMALL, NUM_STAGES),
stride=(TILE_V_SMALL_PADDED, 1, TILE_K * TILE_V_SMALL_PADDED),
)
smem_bytes_small = (
4 * TILE_K * TILE_V_SMALL_PADDED * NUM_STAGES
+ 4 * TILE_V_SMALL
+ 4 * TILE_K * 2
+ 4 * TILE_K
+ 64
)
kda_small(
None,
h0_source,
smem_layout_small,
num_v_tiles_small,
q,
k,
v,
a,
b,
A_log,
dt_bias,
o,
h0_indices,
softplus_beta,
softplus_threshold,
scale,
H,
HV,
use_qk_l2norm,
).launch(
grid=(batch_size * NUM_BLOCKS_PER_STATE_SMALL, 1, 1),
block=[NUM_THREADS, 1, 1],
smem=smem_bytes_small,
stream=stream,
)
@cute.jit
def run_small_batch_varlen(
cu_seqlens: cute.Tensor,
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
h0_source: cute.Tensor,
h0_indices: cute.Tensor,
o: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
B: cutlass.Constexpr[int],
T: cutlass.Constexpr[int],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
K: cutlass.Constexpr[int],
V: cutlass.Constexpr[int],
use_initial_state: cutlass.Constexpr[bool],
use_qk_l2norm: cutlass.Constexpr[bool],
stream: cuda.CUstream,
):
del cu_seqlens, B, T, K, use_initial_state
_, hv_dim, v_dim, _ = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
num_v_tiles_small = cute.ceil_div(v_dim, TILE_V_SMALL)
smem_layout_small = cute.make_layout(
(TILE_K, TILE_V_SMALL, NUM_STAGES),
stride=(TILE_V_SMALL_PADDED, 1, TILE_K * TILE_V_SMALL_PADDED),
)
smem_bytes_small = (
4 * TILE_K * TILE_V_SMALL_PADDED * NUM_STAGES
+ 4 * TILE_V_SMALL
+ 4 * TILE_K * 2
+ 4 * TILE_K
+ 64
)
kda_small_varlen(
None,
h0_source,
smem_layout_small,
num_v_tiles_small,
q,
k,
v,
a,
b,
A_log,
dt_bias,
o,
h0_indices,
softplus_beta,
softplus_threshold,
scale,
H,
HV,
use_qk_l2norm,
).launch(
grid=(batch_size * NUM_BLOCKS_PER_STATE_SMALL, 1, 1),
block=[NUM_THREADS, 1, 1],
smem=smem_bytes_small,
stream=stream,
)
@cute.jit
def run_large_batch(
cu_seqlens: cute.Tensor,
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
h0_source: cute.Tensor,
h0_indices: cute.Tensor,
o: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
B: cutlass.Constexpr[int],
T: cutlass.Constexpr[int],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
K: cutlass.Constexpr[int],
V: cutlass.Constexpr[int],
use_initial_state: cutlass.Constexpr[bool],
use_qk_l2norm: cutlass.Constexpr[bool],
stream: cuda.CUstream,
):
del cu_seqlens, B, T, K, use_initial_state
_, hv_dim, v_dim, _ = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
num_v_tiles = cute.ceil_div(v_dim, TILE_V)
smem_layout = cute.make_layout(
(TILE_K, TILE_V, NUM_STAGES),
stride=(TILE_V_PADDED, 1, TILE_K * TILE_V_PADDED),
)
smem_bytes = (
4 * TILE_K * TILE_V_PADDED * NUM_STAGES
+ 4 * TILE_V
+ 4 * TILE_K * 2
+ 4 * TILE_K
+ 64
)
kda_large(
None,
h0_source,
smem_layout,
num_v_tiles,
q,
k,
v,
a,
b,
A_log,
dt_bias,
o,
h0_indices,
softplus_beta,
softplus_threshold,
scale,
H,
HV,
use_qk_l2norm,
).launch(
grid=(batch_size, 1, 1),
block=[NUM_THREADS_LARGE, 1, 1],
smem=smem_bytes,
stream=stream,
)
@cute.jit
def run_large_batch_varlen(
cu_seqlens: cute.Tensor,
q: cute.Tensor,
k: cute.Tensor,
v: cute.Tensor,
a: cute.Tensor,
b: cute.Tensor,
A_log: cute.Tensor,
dt_bias: cute.Tensor,
h0_source: cute.Tensor,
h0_indices: cute.Tensor,
o: cute.Tensor,
softplus_beta: cutlass.Constexpr[float],
softplus_threshold: cutlass.Constexpr[float],
scale: cutlass.Constexpr[float],
B: cutlass.Constexpr[int],
T: cutlass.Constexpr[int],
H: cutlass.Constexpr[int],
HV: cutlass.Constexpr[int],
K: cutlass.Constexpr[int],
V: cutlass.Constexpr[int],
use_initial_state: cutlass.Constexpr[bool],
use_qk_l2norm: cutlass.Constexpr[bool],
stream: cuda.CUstream,
):
del cu_seqlens, B, T, K, use_initial_state
_, hv_dim, v_dim, _ = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
num_v_tiles = cute.ceil_div(v_dim, TILE_V)
smem_layout = cute.make_layout(
(TILE_K, TILE_V, NUM_STAGES),
stride=(TILE_V_PADDED, 1, TILE_K * TILE_V_PADDED),
)
smem_bytes = (
4 * TILE_K * TILE_V_PADDED * NUM_STAGES
+ 4 * TILE_V
+ 4 * TILE_K * 2
+ 4 * TILE_K
+ 64
)
kda_large_varlen(
None,
h0_source,
smem_layout,
num_v_tiles,
q,
k,
v,
a,
b,
A_log,
dt_bias,
o,
h0_indices,
softplus_beta,
softplus_threshold,
scale,
H,
HV,
use_qk_l2norm,
).launch(
grid=(batch_size, 1, 1),
block=[NUM_THREADS_LARGE, 1, 1],
smem=smem_bytes,
stream=stream,
)
return (
run_small_batch,
run_small_batch_varlen,
run_large_batch,
run_large_batch_varlen,
)
_jit_functions = None
def _get_jit_functions():
global _jit_functions
if _jit_functions is None:
_jit_functions = _create_jit_functions()
return _jit_functions
def _get_compiled_kernel(N, H, HV, K, V, pool_size, use_small_batch, is_varlen_decode):
"""Get or compile the KDA kernel for given dimensions."""
global _compiled_kernels
key = (N, H, HV, K, V, pool_size, use_small_batch, is_varlen_decode)
if key in _compiled_kernels:
return _compiled_kernels[key]
cu_seqlens = torch.zeros(N + 1, dtype=torch.int32, device="cuda")
if is_varlen_decode:
q = torch.zeros(1, N, H, K, dtype=torch.bfloat16, device="cuda")
k = torch.zeros(1, N, H, K, dtype=torch.bfloat16, device="cuda")
v = torch.zeros(1, N, HV, V, dtype=torch.bfloat16, device="cuda")
a = torch.zeros(N, HV, K, dtype=torch.bfloat16, device="cuda")
b = torch.zeros(N, HV, dtype=torch.bfloat16, device="cuda")
o = torch.zeros(1, N, HV, V, dtype=torch.bfloat16, device="cuda")
else:
q = torch.zeros(N, 1, H, K, dtype=torch.bfloat16, device="cuda")
k = torch.zeros(N, 1, H, K, dtype=torch.bfloat16, device="cuda")
v = torch.zeros(N, 1, HV, V, dtype=torch.bfloat16, device="cuda")
a = torch.zeros(N, 1, HV, K, dtype=torch.bfloat16, device="cuda")
b = torch.zeros(N, 1, HV, dtype=torch.bfloat16, device="cuda")
o = torch.zeros(N, 1, HV, V, dtype=torch.bfloat16, device="cuda")
A_log = torch.zeros(HV, dtype=torch.float32, device="cuda")
dt_bias = torch.zeros(HV, K, dtype=torch.bfloat16, device="cuda")
h0_source = torch.zeros(pool_size, HV, V, K, dtype=torch.float32, device="cuda")
h0_indices = torch.zeros(N, dtype=torch.int32, device="cuda")
cu_seqlens_tensor = from_dlpack(cu_seqlens, assumed_align=16)
q_tensor = from_dlpack(q, assumed_align=16)
k_tensor = from_dlpack(k, assumed_align=16)
v_tensor = from_dlpack(v, assumed_align=16)
a_tensor = from_dlpack(a, assumed_align=16)
b_tensor = from_dlpack(b, assumed_align=16)
A_log_tensor = from_dlpack(A_log, assumed_align=16)
dt_bias_tensor = from_dlpack(dt_bias, assumed_align=16)
h0_source_tensor = from_dlpack(h0_source, assumed_align=16)
h0_indices_tensor = from_dlpack(h0_indices, assumed_align=16)
o_tensor = from_dlpack(o, assumed_align=16)
stream = cuda.CUstream(torch.cuda.current_stream().cuda_stream)
run_small, run_small_varlen, run_large, run_large_varlen = _get_jit_functions()
if use_small_batch:
kernel_func = run_small_varlen if is_varlen_decode else run_small
else:
kernel_func = run_large_varlen if is_varlen_decode else run_large
compiled_kernel = cute.compile(
kernel_func,
cu_seqlens_tensor,
q_tensor,
k_tensor,
v_tensor,
a_tensor,
b_tensor,
A_log_tensor,
dt_bias_tensor,
h0_source_tensor,
h0_indices_tensor,
o_tensor,
softplus_beta=1.0,
softplus_threshold=20.0,
scale=K**-0.5,
B=1 if is_varlen_decode else N,
T=N if is_varlen_decode else 1,
H=H,
K=K,
V=V,
HV=HV,
use_initial_state=True,
use_qk_l2norm=True,
stream=stream,
)
_compiled_kernels[key] = compiled_kernel
logger.info(
"CuTe DSL KDA kernel compiled: "
f"N={N}, H={H}, HV={HV}, K={K}, V={V}, pool_size={pool_size}, "
f"small_batch={use_small_batch}, varlen={is_varlen_decode}"
)
return compiled_kernel
def _normalize_A_log(A_log: torch.Tensor, HV: int) -> torch.Tensor:
if A_log.numel() != HV:
raise ValueError(f"Unexpected A_log shape: {A_log.shape}; expected numel={HV}")
return A_log.reshape(HV).contiguous()
def _normalize_dt_bias(dt_bias: torch.Tensor, HV: int, K: int) -> torch.Tensor:
if dt_bias.numel() != HV * K:
raise ValueError(
f"Unexpected dt_bias shape: {dt_bias.shape}; expected numel={HV * K}"
)
return dt_bias.reshape(HV, K).contiguous()
def _normalize_kda_a(a, *, is_varlen_decode, N, HV, K):
"""Normalize `a` to match the compile-time shape expected by the kernel.
varlen kernel compiled shape: (N, HV, K) -- 3D
dense kernel compiled shape: (N, 1, HV, K) -- 4D
"""
if is_varlen_decode:
# Target: (N, HV, K) -- 3D
if a.dim() == 2 and a.shape == (N, HV * K):
return a.view(N, HV, K)
if a.dim() == 3 and a.shape == (N, HV, K):
return a # already correct
if a.dim() == 4 and a.shape == (1, N, HV, K):
return a.squeeze(0) # remove leading dim
raise ValueError(f"Unexpected a shape for varlen: {a.shape}")
else:
# Target: (N, 1, HV, K) -- 4D
if a.dim() == 2 and a.shape == (N, HV * K):
return a.view(N, 1, HV, K)
if a.dim() == 3 and a.shape == (N, HV, K):
return a.unsqueeze(1)
if a.dim() == 4 and a.shape == (N, 1, HV, K):
return a
raise ValueError(f"Unexpected a shape for dense: {a.shape}")
def cutedsl_fused_sigmoid_gating_kda_update(
A_log: torch.Tensor,
dt_bias: torch.Tensor,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
a: torch.Tensor,
b: torch.Tensor,
initial_state_source: torch.Tensor,
initial_state_indices: torch.Tensor,
cu_seqlens: Optional[torch.Tensor] = None,
scale: Optional[float] = None,
use_qk_l2norm_in_kernel: bool = True,
softplus_beta: float = 1.0,
softplus_threshold: float = 20.0,
) -> torch.Tensor:
"""CuTe DSL implementation of fused sigmoid gating KDA update.
State layout contract:
initial_state_source.shape == (pool_size, HV, V, K)
Dense decode:
q/k: (N, 1, H, K)
v: (N, 1, HV, V)
a: (N, 1, HV, K)
b: (N, 1, HV)
Varlen decode:
q/k: (1, N, H, K)
v: (1, N, HV, V)
a: (N, HV, K) or (1, N, HV, K)
b: (N, HV) or (1, N, HV)
"""
A_log = A_log.contiguous()
B_q, T_q, H, K = q.shape
HV = v.shape[2]
V = v.shape[3]
N = initial_state_indices.shape[0]
assert K == TILE_K, f"Current CuTe DSL KDA kernel requires K={TILE_K}, got {K}"
assert (
V % TILE_V_SMALL == 0
), f"Current CuTe DSL KDA kernel requires V % {TILE_V_SMALL} == 0, got V={V}"
assert (
V % TILE_V == 0
), f"Current CuTe DSL KDA kernel requires V % {TILE_V} == 0, got V={V}"
assert (V // TILE_V_SMALL) % NUM_BLOCKS_PER_STATE_SMALL == 0, (
"Small-batch KDA kernel requires num_v_tiles_small divisible by "
f"{NUM_BLOCKS_PER_STATE_SMALL}, got V={V}"
)
is_varlen_decode = B_q == 1 and T_q == N and N > 1
if scale is None:
scale = K**-0.5
else:
assert scale > 0, f"scale must be positive, got {scale}"
use_small_batch = N < SMALL_BATCH_THRESHOLD
if initial_state_source.dim() == 1:
pool_size = initial_state_source.numel() // (HV * V * K)
h0_source = initial_state_source.view(pool_size, HV, V, K)
elif initial_state_source.dim() == 4:
pool_size = initial_state_source.shape[0]
h0_source = initial_state_source
else:
raise ValueError(
f"Unexpected initial_state_source shape: {initial_state_source.shape}"
)
a = _normalize_kda_a(a, is_varlen_decode=is_varlen_decode, N=N, HV=HV, K=K)
if is_varlen_decode:
# varlen b compiled: (N, HV) -- 2D
if b.dim() == 3:
b = b.squeeze(0) # (1, N, HV) -> (N, HV)
# b should be 2D (N, HV)
o = q.new_empty(1, N, HV, V, dtype=torch.bfloat16)
else:
# dense b compiled: (N, 1, HV) -- 3D
if b.dim() == 2:
b = b.unsqueeze(1)
# b should be 3D (N, 1, HV)
o = q.new_empty(N, 1, HV, V, dtype=torch.bfloat16)
q, k, v, a, b = [t.contiguous() for t in (q, k, v, a, b)]
dt_bias = dt_bias.contiguous()
global _cu_seqlens_cache
if cu_seqlens is not None:
cu_seqlens_to_use = cu_seqlens
else:
cache_key = (N, str(q.device))
if cache_key not in _cu_seqlens_cache:
_cu_seqlens_cache[cache_key] = torch.arange(
N + 1, dtype=torch.int32, device=q.device
)
cu_seqlens_to_use = _cu_seqlens_cache[cache_key]
A_log = _normalize_A_log(A_log, HV)
dt_bias = _normalize_dt_bias(dt_bias, HV, K)
h0_source = h0_source.contiguous()
initial_state_indices = initial_state_indices.contiguous()
if cu_seqlens is not None:
cu_seqlens = cu_seqlens.contiguous()
cu_seqlens_tensor = from_dlpack(
cu_seqlens_to_use.detach(), assumed_align=16
).mark_layout_dynamic(leading_dim=0)
q_tensor = from_dlpack(q.detach(), assumed_align=16).mark_layout_dynamic(
leading_dim=q.ndim - 1
)
k_tensor = from_dlpack(k.detach(), assumed_align=16).mark_layout_dynamic(
leading_dim=k.ndim - 1
)
v_tensor = from_dlpack(v.detach(), assumed_align=16).mark_layout_dynamic(
leading_dim=v.ndim - 1
)
a_tensor = from_dlpack(a.detach(), assumed_align=16).mark_layout_dynamic(
leading_dim=a.ndim - 1
)
b_tensor = from_dlpack(b.detach(), assumed_align=16).mark_layout_dynamic(
leading_dim=b.ndim - 1
)
A_log_tensor = from_dlpack(A_log.detach(), assumed_align=16).mark_layout_dynamic(
leading_dim=0
)
dt_bias_tensor = from_dlpack(
dt_bias.detach(), assumed_align=16
).mark_layout_dynamic(leading_dim=dt_bias.ndim - 1)
h0_source_tensor = from_dlpack(
h0_source.detach(), assumed_align=16
).mark_layout_dynamic(leading_dim=h0_source.ndim - 1)
h0_indices_tensor = from_dlpack(
initial_state_indices.detach(), assumed_align=16
).mark_layout_dynamic(leading_dim=0)
o_tensor = from_dlpack(o.detach(), assumed_align=16).mark_layout_dynamic(
leading_dim=o.ndim - 1
)
stream = cuda.CUstream(torch.cuda.current_stream().cuda_stream)
compiled_kernel = _get_compiled_kernel(
N, H, HV, K, V, pool_size, use_small_batch, is_varlen_decode
)
compiled_kernel(
cu_seqlens_tensor,
q_tensor,
k_tensor,
v_tensor,
a_tensor,
b_tensor,
A_log_tensor,
dt_bias_tensor,
h0_source_tensor,
h0_indices_tensor,
o_tensor,
stream,
)
return o