Tasks/Chips & Compute Systems/GPU Kernels

Paged Ragged GQA Decode Attention

Decode attention over a paged, ragged KV cache

paged_ragged_gqa_decode_speedup Chips & Compute Systems GPU Kernels
instruction.mdthis is what the agent is given

You inherit a correct batch-vectorized PyTorch implementation of grouped-query decode attention over a paged, variable-length KV cache. Make it as fast as possible while it keeps computing the same thing within the accuracy the verifier enforces.

Hard Constraints

  • Keep the callable signature in methods/main/solver.py unchanged. Modify implementation files only under methods/main.
  • Inputs are CUDA tensors. q has shape (batch, query_heads, head_dim). k_cache and v_cache have NHD layout (physical_pages, page_size, kv_heads, head_dim).
  • page_indptr, page_indices, and last_page_len are CUDA int32 tensors. They encode a CSR page table with at least one page per request; the final valid page length is in [1, page_size].
  • Query and cache tensors share dtype, either float16 or bfloat16. query_heads is divisible by kv_heads; each contiguous query-head group maps to one KV head.
  • Return one fresh CUDA tensor with shape (batch, query_heads, head_dim) and the input floating dtype. Do not mutate or alias any input.
  • Implement the documented attention for every request: gather only pages named by its page-table row, trim the final page, optionally apply the specified Llama half-split RoPE to query position kv_len - 1 and key positions 0..kv_len-1, retain only the last window_left + 1 tokens when window_left >= 0, form scores = q @ k.T * sm_scale, apply scores = logits_soft_cap * tanh(scores / logits_soft_cap), apply softmax in each GQA head, and multiply by values. RoPE frequencies are theta^(-arange(0, head_dim, 2)/head_dim) / rope_scale; mode 0 means no positional encoding and mode 1 means ROPE_LLAMA.
  • The sealed panel uses page tables, request lengths, shapes, dtypes and tensor values you have not seen. Your implementation must stay correct and fast on them. Do not enumerate visible cases, fixed seeds, or any other property you can only observe in the public panel.
  • Caching an output across calls, mutating metadata, or replacing the timing primitives is detected and scored as incorrect.
  • Submission code may import torch, triton, standard typing/math helpers, and sibling modules. It may not access files, processes, clocks, environment secrets, network services, verifier paths, or runtime introspection.

What You Have

  • methods/main/solver.py: the correct batch-vectorized PyTorch starting implementation covering the full callable ABI.
  • problems/visible_spec.json: a public development panel with real shuffled physical page tables and ragged request lengths.
  • protocol.py: the public data contract, the reference semantics, and the exact measurement protocol the verifier uses.
  • selfcheck.py: an isolated visible evaluator. It generates fresh values, checks every output against an independent float32 formulation, and reports visible_geomean_speedup against the inherited starter. Higher is better.
  • last_page_len gives each request's valid tail length; slots past it are unwritten cache memory and must not be attended to.
  • /app/methods/experiment_log.md: the append-only experiment ledger. Record each attempt's visible latency, its visible_geomean_speedup, and the keep/revert decision, and save the matching source under /app/methods/versions/vN/.

What You Submit

The contents of /app/methods, with your final implementation at /app/methods/main/solver.py. The evaluator imports:

from solver import paged_gqa_decode

Module-level compilation caches and reusable workspaces are allowed. They must be keyed by general runtime properties and stay correct when a fresh process presents unseen cases. Planning and compilation happen during untimed warmup; timed calls still receive fresh q, k_cache, and v_cache tensors.

How It Is Judged

Each sealed case runs the verifier's own hash-bound copy of the inherited starter immediately before your candidate and immediately after it. All three runs use fresh isolated processes, identical inputs and seeds, the same correctness checks, and the same timer. The case score is the bracketed baseline latency divided by your candidate latency, and the cases are combined by geometric mean. The inherited starter therefore scores 1.0x.

Before a case scores at all it must pass output structure, finite values, numerical accuracy, input immutability, no-alias, fresh-call, timing-integrity, dispersion, and baseline-drift checks. Any failed case zeroes the entire score. Timing measurement is defined in protocol.py; input generation, validation and output transfer are all outside the timed window.

Run the visible loop with:

python /app/selfcheck.py

Metric

geometric mean raw speedup over the eight sealed cases · higher is better

bracketed starter latency / candidate median latency, per sealed case, on one RTX A6000

anchorvisible setheld-outreward
Binherited PyTorch starter1.00x1.00x0.00
SFlashInfer expert (pinned commit)74.81x73.55x0.60
UHBM I/O floor at 768 GB/s233.38x1.00
normalisation
m <= B0
B < m <= S0.6 * (m - B) / (S - B)
m > Smin(1, 0.6 + 0.4 * (m - S) / (U - S))

m = this run's held-out metric  ·  B = inherited PyTorch starter  ·  S = FlashInfer expert (pinned commit)  ·  U = HBM I/O floor at 768 GB/s

B = 1. Each case maps against its own S and U, capped at 1, then the eight rewards are averaged; one failed case zeroes the run.

Rollouts

105 minwall clock
$31.31spend
49.5Mtokens
46versions, 23 kept
0 15 30 45 60 75 $0 $7.5 $15 $22 $30 cumulative spend on the run visible raw speedup vs starter, higher is better FlashInfer expert · visible · 74.81x v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v33 v34 v35 v36 v37 v38 v39 v40 v41 v42 v43 v44 v45
keptrevertedno scoreturning point
  1. v0Inherited batch-vectorized PyTorch full-ABI starter0.9904220 min · $0.18
  2. v1Fused Triton score kernel with relative RoPE, softmax workspace, value kernelReplace the whole PyTorch pipeline with one metadata-driven Triton kernel walking the page table.3.473810 min · $1.54
  3. v2Widen the value output tile from 32 to 64 dims, halving probability reads4.700414 min · $2.14
  4. v3One head-dimension-wide value tile per program5.701718 min · $2.92
  5. v464-head score tiles; 32-token RoPE tiles, 64-token non-RoPE tiles7.371622 min · $3.92
  6. v5Online softmax fused into the full-dimension value pass8.791226 min · $4.54
  7. v6Store capped logits in input dtype, keep float32 accumulators9.372832 min · $5.80
  8. v7Exp-based tanh identity instead of libdevice tanhquick proxy only32 min · $6.03
  9. v8Per-request early termination in the online value loop10.152836 min · $6.74
  10. v9Cached ragged score-tile schedule instead of a batch-by-max rectangleLaunch only the token blocks each request retains, instead of padding every request to the batch maximum.17.360439 min · $7.48
  11. v10128-head, narrow-token score tiles to cut key rereadsquick proxy only40 min · $7.65
  12. v1132-query-head by half-dimension value tilesquick proxy only41 min · $8.17
  13. v12256-token split-sequence value partials with stable online reductionSplit each long sequence across programs and reduce the partials, so long rows stop under-filling the GPU.34.392348 min · $10.20
  14. v13One log-sum-exp scalar per partial instead of max plus denominatorquick proxy only50 min · $11.06
  15. v14One-chunk requests bypass the reduction and write output directlyquick proxy only52 min · $11.84
  16. v15224-token split; 128/320/384/512/1024 also probedquick proxy only54 min · $12.36
  17. v1632-query by 128-dim partial and reduction tilesquick proxy only55 min · $12.72
  18. v17Store unnormalized partial numerators to cancel a divide/multiplyquick proxy only60 min · $14.38
  19. v18Cache RoPE inverse frequencies and load them in the score kernelquick proxy only61 min · $15.16
  20. v19Initialize the reduction from the first partial, not a neutral statequick proxy only61 min · $15.40
  21. v20Flash-style QK/soft-cap/softmax/V fusion inside each split partialquick proxy only62 min · $15.76
  22. v21Store partial max and denominator in input dtypequick proxy only63 min · $15.93
  23. v22Score-kernel launch sweep: 4/8/16 warps, 1/2 stagesquick proxy only64 min · $16.27
  24. v23Materialize a dense logical-to-physical token map during planningquick proxy only65 min · $16.62
  25. v24Batches whose retained length is at most 256 skip the reductionsynthetic shapes only66 min · $16.90
  26. v25512-token value splits for non-RoPE rows, 256 for RoPE rows34.879269 min · $17.93
  27. v26768-token non-RoPE split probequick proxy only69 min · $18.12
  28. v27High-order rational soft-cap tanh replacing libdevice tanh35.545873 min · $19.37
  29. v28Lower-order clamped rational tanh35.298377 min · $20.47
  30. v29Minimal clamped 3/2 rational tanh35.447581 min · $21.62
  31. v30Statically unroll the value-partial token loopquick proxy only81 min · $21.91
  32. v31Reduction reshaped to 32x128 and 64x64 tilesquick proxy only82 min · $22.26
  33. v32Cache the full relative RoPE sine/cosine tablequick proxy only84 min · $22.58
  34. v33Pad the internal logits row stride to 32 elements34.752887 min · $23.80
  35. v3432-head ragged score tilesquick proxy only88 min · $24.10
  36. v35Shape-derived score/value tiles for small GQA groups and head dimssynthetic shapes only89 min · $24.66
  37. v361024-token value chunks for the smaller head dimensionssynthetic shapes only90 min · $25.20
  38. v3764-token RoPE score tile for head_dim 64 onlysynthetic shapes only91 min · $25.42
  39. v38Compile out partial-statistic stores when one chunk writes the outputsynthetic shapes only92 min · $25.76
  40. v39Window-zero shortcut: copy the last valid V per GQA mappingsynthetic shapes only93 min · $26.00
  41. v40Broadcast each last-token V tile across 16 query heads per programsynthetic shapes only93 min · $26.33
  42. v41Compact one-block flash kernel for retained lengths up to 32synthetic shapes only95 min · $27.14
  43. v42Compact flash kernel extended through 64 tokens, 32/64 token tilessynthetic shapes only96 min · $27.55
  44. v43Compact flash kernel to 128 tokens for head_dim 64 and 128synthetic shapes only97 min · $27.86
  45. v44Compact flash limit raised to 256 tokens for head_dim 64 only35.141398 min · $28.29
  46. v45Clamp the rational soft-cap input at +/-5 for arbitrary magnitudes35.4029102 min · $29.84

v12 already reached 34.39x, at 48 min and $10.20. The last 33 versions cost $18 more for 2%, and 21 of them were judged on a quick proxy.

On the hidden set

held-out metricreward
inherited PyTorch starter1.00x0.00
FlashInfer expert (pinned commit)73.55x0.60
HBM I/O floor at 768 GB/s233.38x1.00
this run34.71x0.2816