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

1495 lines
55 KiB
Python

"""CuTe DSL Fused Sigmoid Gating Delta Rule Kernel for GDN Decode."""
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.nvgpu import cpasync
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 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 gdn_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 kernel for (N, 1, ...) format."""
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,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_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])
gSrc_batch = h0_source[(pool_idx, i_hv, None, None)]
gSrc = cute.local_tile(gSrc_batch, (TILE_K, TILE_V_SMALL), (0, None))
thr_copy_load = tiled_copy_load.get_slice(tidx)
prefetch_count = cutlass.min(NUM_STAGES - 1, num_v_tiles_per_block)
for v_tile_offset in range(prefetch_count):
v_tile = start_v_tile + v_tile_offset
stage = v_tile_offset % NUM_STAGES
gSrc_tile = gSrc[(None, None, v_tile)]
sData_stage = sData[(None, None, stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_tile)
thr_sData = thr_copy_load.partition_D(sData_stage)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
r_A_log = cutlass.Float32(A_log[i_hv])
r_dt_bias = cutlass.Float32(dt_bias[i_hv])
r_a = cutlass.Float32(a[i_n, 0, i_hv])
r_b = cutlass.Float32(b[i_n, 0, i_hv])
r_g = 0.0
r_beta = 0.0
if in_warp_tid == 0:
x = r_a + r_dt_bias
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
r_g_value = -cute.exp(r_A_log) * softplus_x
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_g = cute.exp(r_g_value)
r_g = cute.arch.shuffle_sync(r_g, 0)
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()
inv_norm_q = 0.0
inv_norm_k = 0.0
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):
v_tile = start_v_tile + v_tile_offset
stage = v_tile_offset % NUM_STAGES
cute.arch.cp_async_wait_group(0)
cute.arch.barrier()
next_v_tile_offset = v_tile_offset + prefetch_count
if next_v_tile_offset < num_v_tiles_per_block:
next_v_tile = start_v_tile + next_v_tile_offset
next_stage = next_v_tile_offset % NUM_STAGES
gSrc_next = gSrc[(None, None, next_v_tile)]
sData_next = sData[(None, None, next_stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_next)
thr_sData = thr_copy_load.partition_D(sData_next)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
v_global = v_tile * TILE_V_SMALL + v_idx
r_v = cutlass.Float32(v[i_n, 0, i_hv, v_global])
sum_hk = 0.0
for k_iter in cutlass.range_dynamic(NUM_K_ITERS_SMALL, unroll=16):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
h_val = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
sum_hk += h_val * r_k_val
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 cutlass.range_dynamic(NUM_K_ITERS_SMALL, unroll=16):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
r_q_val = sQ[k_idx]
h_new = h_old + r_k_val * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * r_q_val
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:
v_global_out = v_tile * TILE_V_SMALL + v_idx
o[(i_n, 0, i_hv, v_global_out)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS_SMALL):
flat_idx = tidx + k_iter * 128
k_write = flat_idx // TILE_V_SMALL
v_write = flat_idx % TILE_V_SMALL
if k_write < TILE_K:
h_val = sData[(k_write, v_write, stage)]
v_global_write = v_tile * TILE_V_SMALL + v_write
h0_source[(pool_idx, i_hv, k_write, v_global_write)] = h_val
cute.arch.barrier()
@cute.kernel
def gdn_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 kernel for varlen decode (1, N, ...) format."""
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,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_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])
gSrc_batch = h0_source[(pool_idx, i_hv, None, None)]
gSrc = cute.local_tile(gSrc_batch, (TILE_K, TILE_V_SMALL), (0, None))
thr_copy_load = tiled_copy_load.get_slice(tidx)
prefetch_count = cutlass.min(NUM_STAGES - 1, num_v_tiles_per_block)
for v_tile_offset in range(prefetch_count):
v_tile = start_v_tile + v_tile_offset
stage = v_tile_offset % NUM_STAGES
gSrc_tile = gSrc[(None, None, v_tile)]
sData_stage = sData[(None, None, stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_tile)
thr_sData = thr_copy_load.partition_D(sData_stage)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
r_A_log = cutlass.Float32(A_log[i_hv])
r_dt_bias = cutlass.Float32(dt_bias[i_hv])
r_a = cutlass.Float32(a[i_n, i_hv])
r_b = cutlass.Float32(b[i_n, i_hv])
r_g = 0.0
r_beta = 0.0
if in_warp_tid == 0:
x = r_a + r_dt_bias
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
r_g_value = -cute.exp(r_A_log) * softplus_x
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_g = cute.exp(r_g_value)
r_g = cute.arch.shuffle_sync(r_g, 0)
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()
inv_norm_q = 0.0
inv_norm_k = 0.0
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):
v_tile = start_v_tile + v_tile_offset
stage = v_tile_offset % NUM_STAGES
cute.arch.cp_async_wait_group(0)
cute.arch.barrier()
next_v_tile_offset = v_tile_offset + prefetch_count
if next_v_tile_offset < num_v_tiles_per_block:
next_v_tile = start_v_tile + next_v_tile_offset
next_stage = next_v_tile_offset % NUM_STAGES
gSrc_next = gSrc[(None, None, next_v_tile)]
sData_next = sData[(None, None, next_stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_next)
thr_sData = thr_copy_load.partition_D(sData_next)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
v_global = v_tile * TILE_V_SMALL + v_idx
r_v = cutlass.Float32(v[0, i_n, i_hv, v_global])
sum_hk = 0.0
for k_iter in cutlass.range_dynamic(NUM_K_ITERS_SMALL, unroll=16):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
h_val = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
sum_hk += h_val * r_k_val
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 cutlass.range_dynamic(NUM_K_ITERS_SMALL, unroll=16):
k_base = k_iter * ROWS_PER_ITER_SMALL
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
r_q_val = sQ[k_idx]
h_new = h_old + r_k_val * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * r_q_val
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:
v_global_out = v_tile * TILE_V_SMALL + v_idx
o[(0, i_n, i_hv, v_global_out)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS_SMALL):
flat_idx = tidx + k_iter * 128
k_write = flat_idx // TILE_V_SMALL
v_write = flat_idx % TILE_V_SMALL
if k_write < TILE_K:
h_val = sData[(k_write, v_write, stage)]
v_global_write = v_tile * TILE_V_SMALL + v_write
h0_source[(pool_idx, i_hv, k_write, v_global_write)] = h_val
cute.arch.barrier()
@cute.kernel
def gdn_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 kernel for (N, 1, ...) format."""
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,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_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])
gSrc_batch = h0_source[(pool_idx, i_hv, None, None)]
gSrc = cute.local_tile(gSrc_batch, (TILE_K, TILE_V), (0, None))
thr_copy_load = tiled_copy_load.get_slice(tidx)
prefetch_count = cutlass.min(NUM_STAGES - 1, num_v_tiles)
for v_tile in range(prefetch_count):
stage = v_tile % NUM_STAGES
gSrc_tile = gSrc[(None, None, v_tile)]
sData_stage = sData[(None, None, stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_tile)
thr_sData = thr_copy_load.partition_D(sData_stage)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
r_A_log = cutlass.Float32(A_log[i_hv])
r_dt_bias = cutlass.Float32(dt_bias[i_hv])
r_a = cutlass.Float32(a[i_n, 0, i_hv])
r_b = cutlass.Float32(b[i_n, 0, i_hv])
r_g = 0.0
r_beta = 0.0
if in_warp_tid == 0:
x = r_a + r_dt_bias
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
r_g_value = -cute.exp(r_A_log) * softplus_x
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_g = cute.exp(r_g_value)
r_g = cute.arch.shuffle_sync(r_g, 0)
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()
inv_norm_q = 0.0
inv_norm_k = 0.0
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
cute.arch.cp_async_wait_group(0)
cute.arch.barrier()
next_v_tile = v_tile + prefetch_count
if next_v_tile < num_v_tiles:
next_stage = next_v_tile % NUM_STAGES
gSrc_next = gSrc[(None, None, next_v_tile)]
sData_next = sData[(None, None, next_stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_next)
thr_sData = thr_copy_load.partition_D(sData_next)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
v_global = v_tile * TILE_V + v_idx
r_v = cutlass.Float32(v[i_n, 0, i_hv, v_global])
sum_hk = 0.0
for k_iter in cutlass.range_dynamic(NUM_K_ITERS, unroll=8):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
h_val = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
sum_hk += h_val * r_k_val
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 cutlass.range_dynamic(NUM_K_ITERS, unroll=8):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
r_q_val = sQ[k_idx]
h_new = h_old + r_k_val * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * r_q_val
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:
v_global_out = v_tile * TILE_V + v_idx
o[(i_n, 0, i_hv, v_global_out)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS):
flat_idx = tidx + k_iter * 256
k_write = flat_idx // TILE_V
v_write = flat_idx % TILE_V
if k_write < TILE_K:
h_val = sData[(k_write, v_write, stage)]
v_global_write = v_tile * TILE_V + v_write
h0_source[(pool_idx, i_hv, k_write, v_global_write)] = h_val
cute.arch.barrier()
@cute.kernel
def gdn_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 kernel for varlen decode (1, N, ...) format."""
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,))
sK = smem.allocate_tensor(cutlass.Float32, smem_k_layout, 128)
sQ = smem.allocate_tensor(cutlass.Float32, smem_q_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])
gSrc_batch = h0_source[(pool_idx, i_hv, None, None)]
gSrc = cute.local_tile(gSrc_batch, (TILE_K, TILE_V), (0, None))
thr_copy_load = tiled_copy_load.get_slice(tidx)
prefetch_count = cutlass.min(NUM_STAGES - 1, num_v_tiles)
for v_tile in range(prefetch_count):
stage = v_tile % NUM_STAGES
gSrc_tile = gSrc[(None, None, v_tile)]
sData_stage = sData[(None, None, stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_tile)
thr_sData = thr_copy_load.partition_D(sData_stage)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
r_A_log = cutlass.Float32(A_log[i_hv])
r_dt_bias = cutlass.Float32(dt_bias[i_hv])
r_a = cutlass.Float32(a[i_n, i_hv])
r_b = cutlass.Float32(b[i_n, i_hv])
r_g = 0.0
r_beta = 0.0
if in_warp_tid == 0:
x = r_a + r_dt_bias
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
r_g_value = -cute.exp(r_A_log) * softplus_x
r_beta = 1.0 / (1.0 + cute.exp(-r_b))
r_g = cute.exp(r_g_value)
r_g = cute.arch.shuffle_sync(r_g, 0)
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()
inv_norm_q = 0.0
inv_norm_k = 0.0
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
cute.arch.cp_async_wait_group(0)
cute.arch.barrier()
next_v_tile = v_tile + prefetch_count
if next_v_tile < num_v_tiles:
next_stage = next_v_tile % NUM_STAGES
gSrc_next = gSrc[(None, None, next_v_tile)]
sData_next = sData[(None, None, next_stage)]
thr_gSrc = thr_copy_load.partition_S(gSrc_next)
thr_sData = thr_copy_load.partition_D(sData_next)
cute.copy(tiled_copy_load, thr_gSrc, thr_sData)
cute.arch.cp_async_commit_group()
v_global = v_tile * TILE_V + v_idx
r_v = cutlass.Float32(v[0, i_n, i_hv, v_global])
sum_hk = 0.0
for k_iter in cutlass.range_dynamic(NUM_K_ITERS, unroll=8):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
h_val = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
sum_hk += h_val * r_k_val
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 cutlass.range_dynamic(NUM_K_ITERS, unroll=8):
k_base = k_iter * ROWS_PER_ITER
k_idx = k_base + k_local
h_old = sData[(k_idx, v_idx, stage)] * r_g
r_k_val = sK[k_idx]
r_q_val = sQ[k_idx]
h_new = h_old + r_k_val * v_new
sData[(k_idx, v_idx, stage)] = h_new
sum_hq += h_new * r_q_val
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:
v_global_out = v_tile * TILE_V + v_idx
o[(0, i_n, i_hv, v_global_out)] = cutlass.BFloat16(sum_hq)
cute.arch.barrier()
for k_iter in range(NUM_K_ITERS):
flat_idx = tidx + k_iter * 256
k_write = flat_idx // TILE_V
v_write = flat_idx % TILE_V
if k_write < TILE_K:
h_val = sData[(k_write, v_write, stage)]
v_global_write = v_tile * TILE_V + v_write
h0_source[(pool_idx, i_hv, k_write, v_global_write)] = h_val
cute.arch.barrier()
return (
gdn_kernel_small_batch,
gdn_kernel_small_batch_varlen,
gdn_kernel_large_batch,
gdn_kernel_large_batch_varlen,
)
def _create_jit_functions():
"""Create JIT-compiled launcher functions for all kernel variants."""
gdn_small, gdn_small_varlen, gdn_large, gdn_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,
):
pool_size, hv_dim, k_dim, v_dim = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
copy_atom = cute.make_copy_atom(
cpasync.CopyG2SOp(cache_mode=cpasync.LoadCacheMode.GLOBAL),
cutlass.Float32,
num_bits_per_copy=128,
)
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),
)
thread_layout_small = cute.make_layout((32, 4), stride=(4, 1))
val_layout_small = cute.make_layout((1, 4))
tiled_copy_load_small = cute.make_tiled_copy_tv(
copy_atom, thread_layout_small, val_layout_small
)
smem_bytes_small = (
4 * TILE_K * TILE_V_SMALL_PADDED * NUM_STAGES
+ 4 * TILE_V_SMALL
+ 4 * TILE_K * 2
+ 64
)
gdn_small(
tiled_copy_load_small,
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,
):
pool_size, hv_dim, k_dim, v_dim = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
copy_atom = cute.make_copy_atom(
cpasync.CopyG2SOp(cache_mode=cpasync.LoadCacheMode.GLOBAL),
cutlass.Float32,
num_bits_per_copy=128,
)
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),
)
thread_layout_small = cute.make_layout((32, 4), stride=(4, 1))
val_layout_small = cute.make_layout((1, 4))
tiled_copy_load_small = cute.make_tiled_copy_tv(
copy_atom, thread_layout_small, val_layout_small
)
smem_bytes_small = (
4 * TILE_K * TILE_V_SMALL_PADDED * NUM_STAGES
+ 4 * TILE_V_SMALL
+ 4 * TILE_K * 2
+ 64
)
gdn_small_varlen(
tiled_copy_load_small,
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,
):
pool_size, hv_dim, k_dim, v_dim = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
copy_atom = cute.make_copy_atom(
cpasync.CopyG2SOp(cache_mode=cpasync.LoadCacheMode.GLOBAL),
cutlass.Float32,
num_bits_per_copy=128,
)
num_v_tiles = cute.ceil_div(v_dim, TILE_V)
base_smem_layout = cute.make_layout(
(TILE_K, TILE_V, NUM_STAGES),
stride=(TILE_V_PADDED, 1, TILE_K * TILE_V_PADDED),
)
thread_layout = cute.make_layout((32, 8), stride=(8, 1))
val_layout = cute.make_layout((1, 4))
tiled_copy_load = cute.make_tiled_copy_tv(copy_atom, thread_layout, val_layout)
smem_bytes = (
4 * TILE_K * TILE_V_PADDED * NUM_STAGES + 4 * TILE_V + 4 * TILE_K * 2 + 64
)
gdn_large(
tiled_copy_load,
h0_source,
base_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,
):
pool_size, hv_dim, k_dim, v_dim = h0_source.layout.shape
n_indices = h0_indices.layout.shape[0]
batch_size = n_indices * hv_dim
copy_atom = cute.make_copy_atom(
cpasync.CopyG2SOp(cache_mode=cpasync.LoadCacheMode.GLOBAL),
cutlass.Float32,
num_bits_per_copy=128,
)
num_v_tiles = cute.ceil_div(v_dim, TILE_V)
base_smem_layout = cute.make_layout(
(TILE_K, TILE_V, NUM_STAGES),
stride=(TILE_V_PADDED, 1, TILE_K * TILE_V_PADDED),
)
thread_layout = cute.make_layout((32, 8), stride=(8, 1))
val_layout = cute.make_layout((1, 4))
tiled_copy_load = cute.make_tiled_copy_tv(copy_atom, thread_layout, val_layout)
smem_bytes = (
4 * TILE_K * TILE_V_PADDED * NUM_STAGES + 4 * TILE_V + 4 * TILE_K * 2 + 64
)
gdn_large_varlen(
tiled_copy_load,
h0_source,
base_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 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, 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, 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, dtype=torch.bfloat16, device="cuda")
h0_source = torch.zeros(pool_size, HV, K, V, 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
scale = K**-0.5
softplus_beta = 1.0
softplus_threshold = 20.0
B_compile = 1 if is_varlen_decode else N
T_compile = N if is_varlen_decode else 1
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=softplus_beta,
softplus_threshold=softplus_threshold,
scale=scale,
B=B_compile,
T=T_compile,
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(
f"CuTe DSL GDN kernel compiled: N={N}, H={H}, HV={HV}, K={K}, V={V}, pool_size={pool_size}, small_batch={use_small_batch}, varlen={is_varlen_decode}"
)
return compiled_kernel
def cutedsl_fused_sigmoid_gating_delta_rule_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 delta rule update."""
B_q, T_q, H, K = q.shape
HV = v.shape[2]
V = v.shape[3]
N = initial_state_indices.shape[0]
is_varlen_decode = B_q == 1 and T_q == N and N > 1
if scale is None:
scale = K**-0.5
use_small_batch = N < SMALL_BATCH_THRESHOLD
if initial_state_source.dim() == 1:
pool_size = initial_state_source.numel() // (HV * K * V)
h0_source = initial_state_source.view(pool_size, HV, K, V)
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}"
)
if is_varlen_decode:
if a.dim() == 3:
a = a.squeeze(0)
if b.dim() == 3:
b = b.squeeze(0)
o = q.new_empty(1, N, HV, V, dtype=torch.bfloat16)
else:
if a.dim() == 2:
a = a.unsqueeze(1)
if b.dim() == 2:
b = b.unsqueeze(1)
o = q.new_empty(N, 1, HV, V, dtype=torch.bfloat16)
q, k, v = [t.contiguous() for t in (q, k, v)]
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]
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=0)
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