Skip to content
KernelIndex
Search⌘K

submission 714636

anuragj0803 · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

No package. Vendor the mirrored source: 193 lines, June 9 Researcher Reciprocity License v1.0.

mla_amd.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-amd-mixed-mla-714636?include=source"
interfacepython
Compatibility
measured onAMD Instinct MI355X
declared hardwareAMD Instinct MI355X
architecturesgfx950
dtypesbf16, int32

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
AMD Instinct MI355X
36.6µs
#87 of 766
2026-04-03

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:6dd7da7cc62f420d2f8c76f078b0e566324dbcf6d3f1c383b74d5f3c4a2cbed5
license declaredunknown
license concludedunknown
authorsanuragj0803
imported2026-08-15

Techniques

Extracted from the mirrored source by pattern, never inferred. Each row cites its line.

fp4MLA — MXFP4 KV cache (2x less bandwidth than FP8).

Kernel source

mla_amd.py193 lines
#!POPCORN leaderboard amd-mixed-mla
#!POPCORN gpu MI355X
"""
MLA — MXFP4 KV cache (2x less bandwidth than FP8).

The task provides kv_data["mxfp4"] = (fp4x2_tensor, e8m0_scale).
This is 2x smaller than the FP8 KV cache we currently use.
For kv=8192 shapes, memory bandwidth dominates — halving it could
cut those shapes by 30-40%.

Question: does mla_decode_fwd accept fp4x2 KV directly?
Probe the API and report what happens.

If mxfp4 MLA kernel doesn't exist, fall back to fp8 path.
"""
import torch
import triton
import triton.language as tl
from task import input_t, output_t
from aiter.mla import mla_decode_fwd
from aiter import dtypes as aiter_dtypes
from aiter import get_mla_metadata_info_v1, get_mla_metadata_v1

FP8_DTYPE = aiter_dtypes.fp8
BF16 = torch.bfloat16
_FP8_MAX = float(torch.finfo(FP8_DTYPE).max)
_meta_cache = {}
_alloc_cache = {}
_FIXED_AMAX = 16.0
_call = 0
_mxfp4_works = None  # None=untested, True/False


@triton.jit
def _q_to_fp8_kernel(q_ptr, out_ptr, scale_ptr, amax_ptr,
                     FP8_MAX: tl.constexpr, N, BLOCK: tl.constexpr):
    amax = tl.load(amax_ptr)
    amax = tl.where(amax < 1e-12, 1e-12, amax)
    scale = amax / FP8_MAX
    if tl.program_id(0) == 0:
        tl.store(scale_ptr, scale)
    pid = tl.program_id(0)
    offs = pid * BLOCK + tl.arange(0, BLOCK)
    mask = offs < N
    x = tl.load(q_ptr + offs, mask=mask, other=0.0).to(tl.float32)
    x = x / scale
    x = tl.clamp(x, -FP8_MAX, FP8_MAX)
    tl.store(out_ptr + offs, x.to(out_ptr.dtype.element_ty), mask=mask)


def _build_meta(batch_size, kv_seq_len, q_seq_len, nq, nkv,
                num_kv_splits, page_size, dtype_q, dtype_kv, qo_indptr, kv_indptr):
    total_kv = batch_size * kv_seq_len
    if page_size == 1:
        num_pages = total_kv
        kv_indptr_pages = kv_indptr
        seq_lens = kv_indptr[1:] - kv_indptr[:-1]
        kv_last_page_len = seq_lens.to(torch.int32)
    else:
        num_pages = total_kv // page_size
        kv_indptr_pages = kv_indptr // page_size
        seq_lens = kv_indptr[1:] - kv_indptr[:-1]
        kv_last_page_len = (seq_lens % page_size).to(torch.int32)
        kv_last_page_len = torch.where(kv_last_page_len == 0, page_size, kv_last_page_len)
    kv_gran = max(1, 16 // page_size)
    info = get_mla_metadata_info_v1(
        batch_size, q_seq_len, nq, dtype_q, dtype_kv,
        is_sparse=False, fast_mode=False,
        num_kv_splits=num_kv_splits, intra_batch_mode=True,
    )
    work = [torch.empty(s, dtype=t, device="cuda") for s, t in info]
    (wm, wi, wis, ri, rfm, rpm) = work
    get_mla_metadata_v1(
        qo_indptr, kv_indptr_pages, kv_last_page_len,
        nq // nkv, nkv, True,
        wm, wis, wi, ri, rfm, rpm,
        page_size=page_size, kv_granularity=kv_gran,
        max_seqlen_qo=q_seq_len, uni_seqlen_qo=q_seq_len,
        fast_mode=False, max_split_per_batch=num_kv_splits,
        intra_batch_mode=True, dtype_q=dtype_q, dtype_kv=dtype_kv,
    )
    kv_indices = torch.arange(num_pages, dtype=torch.int32, device="cuda")
    return (wm, wi, wis, ri, rfm, rpm, kv_indices, kv_last_page_len, kv_indptr_pages, page_size)


def custom_kernel(data: input_t) -> output_t:
    global _call, _mxfp4_works
    _call += 1

    q, kv_data, qo_indptr, kv_indptr, config = data
    batch_size = config["batch_size"]
    nq, nkv = config["num_heads"], config["num_kv_heads"]
    dq, dv = config["qk_head_dim"], config["v_head_dim"]
    q_seq_len = config["q_seq_len"]
    sm_scale = config["sm_scale"]
    kv_seq_len = config["kv_seq_len"]

    page_size = 1 if kv_seq_len <= 1024 else 8
    dtype_q = FP8_DTYPE

    if batch_size <= 4: num_kv_splits = 4
    elif batch_size <= 64: num_kv_splits = 8
    else: num_kv_splits = 8 if kv_seq_len <= 1024 else 16

    # Try MXFP4 KV on first call
    if _mxfp4_works is None and _call <= 2:
        try:
            kv_fp4, kv_scale_fp4 = kv_data["mxfp4"]
            if _call == 1:
                print(f"[MLA] mxfp4 KV: fp4={kv_fp4.dtype} shape={kv_fp4.shape} "
                      f"scale={kv_scale_fp4.dtype} shape={kv_scale_fp4.shape}", flush=True)

            # Try building metadata with fp4x2 dtype
            fp4_dtype = kv_fp4.dtype
            cache_key = ("mxfp4", batch_size, kv_seq_len, num_kv_splits, page_size)
            meta = _build_meta(batch_size, kv_seq_len, q_seq_len, nq, nkv,
                              num_kv_splits, page_size, dtype_q, fp4_dtype, qo_indptr, kv_indptr)
            wm, wi, wis, ri, rfm, rpm, kv_indices, kv_last_page_len, kv_indptr_pages, ps = meta

            kv_buffer_4d = kv_fp4.view(-1, ps, nkv, kv_fp4.shape[-1])

            # Quantize Q to fp8
            alloc_key = ("mxfp4_mla", q.shape[0], nq, dv, dq)
            if alloc_key not in _alloc_cache:
                amax_buf = torch.full((1,), _FIXED_AMAX, dtype=torch.float32, device="cuda")
                _alloc_cache[alloc_key] = (
                    torch.empty((q.shape[0], nq, dv), dtype=BF16, device="cuda"),
                    amax_buf,
                    torch.empty(1, dtype=torch.float32, device="cuda"),
                    torch.empty(q.shape[0] * nq * dq, dtype=FP8_DTYPE, device="cuda"),
                )
            o, amax_buf, scale_buf, q_fp8_flat = _alloc_cache[alloc_key]

            N = q.numel(); BLOCK = 4096; grid = ((N + BLOCK - 1) // BLOCK,)
            _q_to_fp8_kernel[grid](q, q_fp8_flat, scale_buf, amax_buf,
                                   FP8_MAX=_FP8_MAX, N=N, BLOCK=BLOCK)

            mla_decode_fwd(
                q_fp8_flat.view(q.shape[0], nq, dq), kv_buffer_4d, o,
                qo_indptr, kv_indptr_pages, kv_indices, kv_last_page_len,
                q_seq_len, page_size=ps, nhead_kv=nkv,
                sm_scale=sm_scale, logit_cap=0.0, num_kv_splits=num_kv_splits,
                q_scale=scale_buf, kv_scale=kv_scale_fp4,
                intra_batch_mode=True,
                work_meta_data=wm, work_indptr=wi, work_info_set=wis,
                reduce_indptr=ri, reduce_final_map=rfm, reduce_partial_map=rpm,
            )
            print(f"[MLA] MXFP4 KV SUCCESS!", flush=True)
            _mxfp4_works = True
            return o
        except Exception as e:
            print(f"[MLA] MXFP4 KV failed: {e}", flush=True)
            _mxfp4_works = False

    # Standard FP8 path
    cache_key = (batch_size, kv_seq_len, num_kv_splits, page_size)
    if cache_key not in _meta_cache:
        _meta_cache[cache_key] = _build_meta(
            batch_size, kv_seq_len, q_seq_len, nq, nkv,
            num_kv_splits, page_size, dtype_q, FP8_DTYPE, qo_indptr, kv_indptr)

    wm, wi, wis, ri, rfm, rpm, kv_indices, kv_last_page_len, kv_indptr_pages, ps = _meta_cache[cache_key]

    kv_buffer_fp8, kv_scale = kv_data["fp8"]
    kv_buffer_4d = kv_buffer_fp8.view(-1, ps, nkv, kv_buffer_fp8.shape[-1])

    alloc_key = ("fp8", q.shape[0], nq, dv, dq)
    if alloc_key not in _alloc_cache:
        amax_buf = torch.full((1,), _FIXED_AMAX, dtype=torch.float32, device="cuda")
        _alloc_cache[alloc_key] = (
            torch.empty((q.shape[0], nq, dv), dtype=BF16, device="cuda"),
            amax_buf,
            torch.empty(1, dtype=torch.float32, device="cuda"),
            torch.empty(q.shape[0] * nq * dq, dtype=FP8_DTYPE, device="cuda"),
        )
    o, amax_buf, scale_buf, q_fp8_flat = _alloc_cache[alloc_key]

    N = q.numel(); BLOCK = 4096; grid = ((N + BLOCK - 1) // BLOCK,)
    _q_to_fp8_kernel[grid](q, q_fp8_flat, scale_buf, amax_buf,
                           FP8_MAX=_FP8_MAX, N=N, BLOCK=BLOCK)

    mla_decode_fwd(
        q_fp8_flat.view(q.shape[0], nq, dq), kv_buffer_4d, o,
        qo_indptr, kv_indptr_pages, kv_indices, kv_last_page_len,
        q_seq_len, page_size=ps, nhead_kv=nkv,
        sm_scale=sm_scale, logit_cap=0.0, num_kv_splits=num_kv_splits,
        q_scale=scale_buf, kv_scale=kv_scale,
        intra_batch_mode=True,
        work_meta_data=wm, work_indptr=wi, work_info_set=wis,
        reduce_indptr=ri, reduce_final_map=rfm, reduce_partial_map=rpm,
    )
    return o
scrolls · 193 lines total

Source code from GPU Mode and the KernelBot dataset · June 9 Researcher Reciprocity License v1.0

Best evidence level for this revision: reported

JSON