Skip to content

feat(kernel): add gfx950 Gluon bf16 MLA decode and prefill kernels#664

Open
Yu-Zhewen wants to merge 1 commit into
mainfrom
Yu-Zhewen/gluon-mla-gfx950
Open

feat(kernel): add gfx950 Gluon bf16 MLA decode and prefill kernels#664
Yu-Zhewen wants to merge 1 commit into
mainfrom
Yu-Zhewen/gluon-mla-gfx950

Conversation

@Yu-Zhewen

Copy link
Copy Markdown
Contributor

Kernels included

  • gluon_mla_decode_bf16_gfx950 — absorbed MLA decode over a paged compressed KV cache (kv_lora_rank=512, qk_rope=64, page_size=64, q_len=1). Two regimes selected by head count:
    • bh16bn64num_q_heads ≤ 16, 2-D (batch, split) grid.
    • bh64num_q_heads ∈ {64, 128}, 3-D XCD-aware grid (requires batch % 64 == 0).
    • Supports return_lse; logit_cap is not supported (guarded).
  • gluon_mla_prefill_bf16_gfx950 — non-absorbed varlen MLA prefill (qk_head_dim=192, v_head_dim=128), causal and non-causal, with optional return_lse. logit_cap is not supported (guarded).

Performance comparison

Decode — bh16bn64 regime (num_q_heads ≤ 16, 2-D grid)

h = number of query heads, b = batch size (number of sequences), s = sequence length in tokens (KV context length for decode; per-sequence query/KV length for prefill). speedup = triton ref latency ÷ gluon latency (higher = gluon faster).

config gluon (µs) triton ref (µs) speedup
h=8 b=1 s=512 40.40 104.20 2.6x
h=8 b=1 s=1024 41.28 203.32 4.9x
h=8 b=1 s=2048 40.76 401.92 9.9x
h=8 b=1 s=8192 39.64 1590.58 40.1x
h=8 b=1 s=16384 57.48 3174.36 55.2x
h=8 b=1 s=32768 61.00 6358.51 104.2x
h=8 b=8 s=512 40.28 108.88 2.7x
h=8 b=8 s=1024 40.04 212.96 5.3x
h=8 b=8 s=2048 35.16 419.60 11.9x
h=8 b=8 s=8192 36.64 1641.98 44.8x
h=8 b=8 s=16384 64.76 3303.32 51.0x
h=8 b=8 s=32768 101.12 6584.32 65.1x
h=8 b=32 s=512 35.28 113.20 3.2x
h=8 b=32 s=1024 32.98 218.52 6.6x
h=8 b=32 s=2048 30.48 435.56 14.3x
h=8 b=32 s=8192 96.72 1693.58 17.5x
h=8 b=32 s=16384 153.46 3309.32 21.6x
h=8 b=32 s=32768 247.28 6593.44 26.7x
h=16 b=1 s=512 40.12 103.76 2.6x
h=16 b=1 s=1024 41.80 202.54 4.8x
h=16 b=1 s=2048 40.60 399.75 9.8x
h=16 b=1 s=8192 39.00 1581.62 40.6x
h=16 b=1 s=16384 58.92 3160.12 53.6x
h=16 b=1 s=32768 61.28 6302.03 102.8x
h=16 b=8 s=512 41.12 105.40 2.6x
h=16 b=8 s=1024 40.28 212.20 5.3x
h=16 b=8 s=2048 34.08 421.91 12.4x
h=16 b=8 s=8192 36.76 1642.46 44.7x
h=16 b=8 s=16384 65.24 3301.44 50.6x
h=16 b=8 s=32768 102.04 6562.88 64.3x
h=16 b=32 s=512 35.00 142.12 4.1x
h=16 b=32 s=1024 33.08 275.64 8.3x
h=16 b=32 s=2048 32.64 532.21 16.3x
h=16 b=32 s=8192 97.60 2091.84 21.4x
h=16 b=32 s=16384 154.32 4012.45 26.0x
h=16 b=32 s=32768 247.80 8155.51 32.9x

Decode — bh64 regime (num_q_heads ∈ {64,128}, 3-D XCD grid, batch % 64 == 0)

config gluon (µs) triton ref (µs) speedup
h=64 b=64 s=512 35.36 994.69 28.1x
h=64 b=64 s=1024 46.60 1926.10 41.3x
h=64 b=64 s=2048 70.84 3641.16 51.4x
h=64 b=64 s=8192 168.40 15288.24 90.8x
h=64 b=64 s=16384 271.84 30628.47 112.7x
h=64 b=128 s=512 49.08 1954.98 39.8x
h=64 b=128 s=1024 72.08 3637.64 50.5x
h=64 b=128 s=2048 111.92 7627.21 68.1x
h=64 b=128 s=8192 274.00 30686.15 112.0x
h=64 b=128 s=16384 501.81 62196.24 123.9x
h=128 b=64 s=512 36.28 1952.90 53.8x
h=128 b=64 s=1024 52.16 3640.28 69.8x
h=128 b=64 s=2048 77.72 7155.04 92.1x
h=128 b=64 s=8192 228.96 30143.71 131.7x
h=128 b=64 s=16384 437.60 60348.06 137.9x
h=128 b=128 s=512 47.16 3658.60 77.6x
h=128 b=128 s=1024 72.28 7193.12 99.5x
h=128 b=128 s=2048 118.16 15116.15 127.9x
h=128 b=128 s=8192 422.24 60669.38 143.7x
h=128 b=128 s=16384 929.61 123527.31 132.9x

Prefill — causal

config gluon (µs) triton ref (µs) speedup
h=16 b=1 s=1024 44.16 64.68 1.5x
h=16 b=1 s=2048 76.40 159.04 2.1x
h=16 b=1 s=4096 236.08 573.65 2.4x
h=16 b=1 s=8192 579.85 2097.98 3.6x
h=16 b=1 s=16384 1962.94 7712.39 3.9x
h=16 b=2 s=1024 46.12 85.40 1.9x
h=16 b=2 s=2048 135.96 301.96 2.2x
h=16 b=2 s=4096 311.46 1109.89 3.6x
h=16 b=2 s=8192 1013.53 3900.80 3.8x
h=16 b=2 s=16384 3854.70 16132.29 4.2x
h=16 b=4 s=1024 86.44 159.84 1.8x
h=16 b=4 s=2048 179.80 575.41 3.2x
h=16 b=4 s=4096 576.88 1978.92 3.4x
h=16 b=4 s=8192 2087.74 8330.84 4.0x
h=16 b=4 s=16384 7810.01 33018.20 4.2x
h=16 b=8 s=1024 120.46 308.90 2.6x
h=16 b=8 s=2048 347.22 1128.05 3.2x
h=16 b=8 s=4096 1207.21 4475.24 3.7x
h=16 b=8 s=8192 4150.24 17853.73 4.3x
h=16 b=8 s=16384 16739.51 71001.96 4.2x
h=16 b=16 s=1024 226.64 593.53 2.6x
h=16 b=16 s=2048 689.97 2460.34 3.6x
h=16 b=16 s=4096 2324.22 10030.46 4.3x
h=16 b=16 s=8192 9010.21 40054.65 4.4x
h=16 b=16 s=16384 35254.48 159451.28 4.5x
h=64 b=1 s=1024 86.40 159.96 1.9x
h=64 b=1 s=2048 182.36 577.97 3.2x
h=64 b=1 s=4096 593.93 2182.00 3.7x
h=64 b=1 s=8192 2068.56 8349.04 4.0x
h=64 b=1 s=16384 7844.83 33085.84 4.2x
h=64 b=2 s=1024 118.28 309.00 2.6x
h=64 b=2 s=2048 351.12 1128.01 3.2x
h=64 b=2 s=4096 1210.79 4506.24 3.7x
h=64 b=2 s=8192 4112.44 17889.37 4.4x
h=64 b=2 s=16384 16632.72 70984.70 4.3x
h=64 b=4 s=1024 228.52 593.37 2.6x
h=64 b=4 s=2048 691.37 2456.32 3.6x
h=64 b=4 s=4096 2268.24 10058.34 4.4x
h=64 b=4 s=8192 9064.17 40237.27 4.4x
h=64 b=4 s=16384 35374.14 158699.91 4.5x
h=64 b=8 s=1024 450.96 1314.47 2.9x
h=64 b=8 s=2048 1422.25 5137.33 3.6x
h=64 b=8 s=4096 5256.55 20163.24 3.8x
h=64 b=8 s=8192 20586.71 79286.74 3.9x
h=64 b=8 s=16384 72980.94 340369.26 4.7x
h=64 b=16 s=1024 875.13 2681.81 3.1x
h=64 b=16 s=2048 2731.35 10202.02 3.7x
h=64 b=16 s=4096 10434.10 40504.88 3.9x
h=64 b=16 s=8192 38242.11 171214.94 4.5x
h=64 b=16 s=16384 137732.67 681654.05 4.9x

Signed-off-by: Yu-Zhewen zhewenyu@amd.com

Add absorbed MLA decode (kv_lora_rank=512, rope=64) with two regimes --
bh16bn64 for <=16 heads and bh64 for {64,128} heads on a 3-D XCD-aware
grid -- plus a non-absorbed MLA prefill kernel (qk=192, v=128, causal
and non-causal). Register both under the gfx950 gluon solution, extend
the mla_decode_with_kvcache traits with num_q_heads for regime dispatch,
and add parity tests against the reference kernels.

Signed-off-by: Yu-Zhewen <zhewenyu@amd.com>
@Yu-Zhewen Yu-Zhewen requested review from a team as code owners July 13, 2026 15:16

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d11449bcb8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

traits={
"q_len": frozenset({1}),
# bh16bn64 (<=16 heads) + bh64 ({64, 128} heads, large batch).
"num_q_heads": frozenset(range(1, 17)) | {64, 128},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not route small-batch 64-head decode to Gluon

When num_q_heads is 64 or 128 but the batch is not divisible by 64 (for example normal single-request decode), this registration still wins because the traits only check head count. The selected gluon_mla_decode_bf16_gfx950 implementation then raises NotImplementedError for that batch shape, so default dispatch fails instead of falling back to the existing Triton MLA decode. Add a dispatch trait/gate for the batch constraint or avoid advertising the 64/128-head regime unless the dispatcher can prove the large-batch requirement.

Useful? React with 👍 / 👎.

if WITHIN_2GB:
gl.amd.cdna4.async_copy.buffer_load_to_shared(bufs_kv0, Kv_c_cache, offs_k_c0, mask=offs_n_nope0[None, :] < split_kv_end)
else:
gl.amd.cdna4.async_copy.global_load_to_shared(bufs_kv0, Kv_c_cache + offs_k_c0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mask large-cache loads for empty split-K tiles

When WITHIN_2GB is false, over-provisioned max_seqlen_k can launch empty trailing splits (split_kv_start == split_kv_end), and the page-table load above is fully masked. This fallback still performs an unmasked global_load_to_shared using the resulting undefined page numbers, unlike the masked buffer_load path, so large KV caches can dereference arbitrary addresses for short/empty splits before the reduce masks them out. The global-load fallback needs an equivalent guard or to avoid issuing loads for empty splits.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the gluon is backed by the shared mla kernel api. maybe we can re-use the existing tests in tokenspeed-kernel/test/ops/test_attention_mla.py?

# buffer_load uses a scalar base + 32-bit offsets; KV pools > 2 GB fall back
# to global_load (64-bit pointers).
max_kv_bytes = kv_c.shape[0] * kv_c.stride(0) * kv_c.element_size()
within_2gb = max_kv_bytes <= 0x80000000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this within_2gb estimation safe? maybe we should have a test to cover it

stride_mid_lse_s,
Final_lse, # RETURN_LSE only: merged fp32 lse [B, H] (else None)
stride_final_lse_b,
stride_final_lse_h,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are all these strides variables not constexpr?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can restructure decode like prefill (tokenspeed-kernel-amd/python/tokenspeed_kernel_amd/ops/attention/gluon/mla_prefill_bf16_gfx950.py) to split config, program and pipeline structure. so it is easier to read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants