Views
No views yet
kernels-community/activation
offers 41 build variants and none of them resolve on ROCm — every one is rejected with
backend (cu128) does not match selected backend (rocm713). Triton sidesteps this
entirely by JIT-compiling for whatever GPU is present.triton.testing.do_bench:| kernel | shape | eager | this kernel | speedup |
|---|---|---|---|---|
| RMSNorm | 512×4096 | 0.205 ms | 0.072 ms | 2.84× |
| RMSNorm | 2048×4096 | 1.592 ms | 0.278 ms | 5.73× |
| RMSNorm | 4096×5120 | 3.703 ms | 0.508 ms | 7.29× |
| RMSNorm | 8192×2048 | 3.028 ms | 0.387 ms | 7.82× |
| GEGLU | 512×8192 | 0.233 ms | 0.197 ms | 1.18× |
| GEGLU | 2048×8192 | 0.843 ms | 0.554 ms | 1.52× |
| GEGLU | 4096×4096 | 0.838 ms | 0.550 ms | 1.52× |
scaled_dot_product_attention falls back to math, which materializes the full S×S score
matrix. But there is an AOTriton path — it is gated behind an environment variable:export TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL=1| shape | default (math) | AOTriton flash | this kernel | vs AOTriton |
|---|---|---|---|---|
| (1,32,512,128) | 3.95 ms | 0.530 ms | 0.162 ms | 3.27× |
| (1,32,4096,128) | 204.3 ms | 12.14 ms | 5.84 ms | 2.08× |
| (2,24,4096,64) | 304.2 ms | 11.97 ms | 4.00 ms | 3.00× |
| (1,16,8192,64) | 342.9 ms | 15.43 ms | 6.06 ms | 2.55× |
triton.autotune per (seq_len, head_dim, causal); on gfx1151 it
picks BLOCK_M=32, BLOCK_N=64, num_warps=4, num_stages=2.1import torch.nn.functional as F
2k = get_kernel("axjns/strix-halo-kernels", version=1)
3F.scaled_dot_product_attention = k.sdpa # safe: falls back for unsupported inputssdpa defers to torch for explicit attn_mask, nonzero dropout_p, GQA/MQA, non-4-D
inputs, non-power-of-two head_dim, fp32, and CPU tensors — it never silently returns
wrong numbers for a case the kernel cannot handle.1import torch
2from kernels import get_kernel
3
4k = get_kernel("axjns/strix-halo-kernels", version=1)
5
6# fused RMSNorm over the last dim
7y = k.rms_norm(x, weight, eps=1e-6)
8
9# gated activations
10h = k.swiglu(gate, up) # silu(gate) * up — LLaMA/Qwen/Mistral MLPs
11h = k.geglu(gate, up) # gelu(gate) * up — Flux / SD3 / T5 MLPs
12h = k.geglu_chunked(proj_out) # splits (..., 2*d) then gateslayers provides nn.Module drop-ins that keep the original parameter names, so no
weight surgery or state-dict changes are needed:from kernels import kernelize # swaps compatible layers in-placelayers.RMSNorm — reads self.weight and accepts either variance_epsilon or
eps, since transformers has used both names across versions.layers.SwiGLU — expects gate_proj / up_proj / down_proj (LLaMA-family MLP).layers.GEGLU — expects a proj emitting 2 * inner_dim, then chunks and gates.rms_norm_ref,
swiglu_ref, geglu_ref) across fp32 / fp16 / bf16, 2-D and 3-D inputs,
non-contiguous inputs, and shapes from 4×512 to 4096×5120. Sum-of-squares is
accumulated in fp32 regardless of input dtype — fp16 accumulation over a
4096-wide row loses precision badly.torch.autograd-opaque, so
gradients will not flow through them. Inference and eval only; do not put these in a
training graph expecting it to work.triton.autotune and will re-tune on other GPUs.
RMSNorm and the gated activations use hand-picked block sizes and num_warps, chosen for
RDNA's 32-wide wavefront and an APU's low CU count — a discrete GPU would likely want
different values, and those two are not yet autotuned.1python test_bench.py # RMSNorm + gated activations
2python test_flash.py # attention correctness + headroom
3python sdpa_probe.py # which SDPA backends your build actually hassdpa_probe.py is worth running on any ROCm box before you optimize anything — it tells
you whether you are silently on the math fallback.