Skip to content

[FlyDSL] Add FlyDSL Radix-Select TopK Path to the Existing Per-Row Decode Interface - #5011

Open
lirui927 wants to merge 23 commits into
mainfrom
lirui/flydsl_topk
Open

[FlyDSL] Add FlyDSL Radix-Select TopK Path to the Existing Per-Row Decode Interface#5011
lirui927 wants to merge 23 commits into
mainfrom
lirui/flydsl_topk

Conversation

@lirui927

@lirui927 lirui927 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Add a FlyDSL Radix-Select TopK Path to the Existing Per-Row Decode Interface

Motivation

The latency of the existing HIP one-block implementation increases with the effective context length, making it less efficient for long-context decode workloads.

This PR aims to:

  • Improve long-context decode TopK performance on gfx950 and gfx942.
  • Preserve compatibility with the existing top_k_per_row_decode interface.
  • Make N and next_n runtime parameters so different context lengths reuse the same compiled kernel.
  • Support optional deterministic output.
  • Support optional values output, defaulting to None.
  • Fall back to HIP for unsupported or ungated shapes.

Technical Details

Multi-CTA Radix-Select

The long-sequence path splits each row into 16 chunks. Multiple CTAs build local histograms in parallel, and three 11/11/10-bit radix-selection passes progressively determine the TopK threshold. stable=False gathers results directly from the threshold, while stable=True additionally computes chunk prefixes and writes results in original-index order for deterministic output. Launchers are cached by (rows, K, stable), while N, next_n, and stride are runtime parameters.

Short-Sequence Path

For physical widths up to 20K, one workgroup completes all three radix-selection passes in a single kernel launch. The histogram, prefix scan, and deterministic writeback remain in LDS and require no cross-workgroup synchronization. stable=False reuses the same result; deterministic ordering is stronger than required but remains valid for the unordered TopK interface.

Public Dispatch

The FlyDSL path is currently enabled only on gfx950 for:

  • K ∈ {512, 1024, 2048, 4096}
  • gfx950, stable=True
    • width ≤ 20K and rows ≤ 128
    • 32K ≤ width < 64K and rows ≤ 16
    • width ≥ 64K and rows ≤ 32
  • gfx950, stable=False
    • width ≥ 128K and rows ≤ 32
  • gfx942, stable=True
    • width ≤ 20K and rows ≤ 64
    • width ≥ 128K and rows ≤ 16
  • gfx942, stable=False
    • width ≥ 512K and rows ≤ 32

Other architectures, shapes, K values, or environments without FlyDSL fall back to HIP.

Set AITER_DISABLE_FLYDSL_TOPK_DECODE=1 to force the HIP path.

Test Plan

Run on gfx942/gfx950 with FlyDSL available:

pytest op_tests/flydsl_tests/test_flydsl_topk_per_row_decode.py -q
  • Dispatch gate boundaries (test_public_topk_decode_gate): arch/K/width/rows at each FlyDSL enable/disable edge, including unsupported K values.
  • Public-interface e2e (test_public_topk_decode_e2e): random logits through top_k_per_row_decode compared against the Torch reference on both FlyDSL and HIP fallback paths; covers stable=True/False and slightly unaligned physical/effective lengths.
  • CUDA Graph (test_public_topk_decode_cuda_graph): capture and replay on the long-row multi-kernel path.

Representative shapes:

  • K: 256, 512, 2048, 4096 (plus unsupported K at boundaries)
  • Rows: gate max, max+1, and small counts (1, 4, 16, 17, 32)
  • Widths: short (≤20K), mid (32K–64K on gfx950), long (≥128K on gfx942 / ≥512K for unstable gfx942)

Test Result

Correctness:

  • Gate + e2e + CUDA Graph suite: 29/29 passed on gfx942 and gfx950.
    The following results were measured with values=None. Enabling values output generally adds less than 2% performance overhead.

Kernel Performance at Dispatch Boundaries

gfx950 / MI355X Kernel Performance at Dispatch Boundaries

The following results use the final code on gfx950 with K=2048 and unpadded inputs. CUDA Graph replay measures the kernel path. Speedup is HIP / FlyDSL; values greater than 1 indicate that FlyDSL is faster.

stable width rows FlyDSL (µs) HIP (µs) Speedup
True 20K 1 36.28 44.25 1.22×
True 20K 16 35.74 46.96 1.31×
True 20K 32 36.05 47.11 1.31×
True 20K 128 36.47 46.75 1.28×
True 32K 1 41.22 45.33 1.10×
True 32K 4 41.94 49.20 1.17×
True 32K 16 41.95 47.61 1.13×
True 64K 1 41.00 55.87 1.36×
True 64K 4 42.52 56.00 1.32×
True 64K 16 42.55 60.81 1.43×
True 64K 32 48.01 61.96 1.29×
True 128K 1 44.78 75.46 1.69×
True 128K 4 45.61 83.74 1.84×
True 128K 16 45.59 84.86 1.86×
True 128K 32 52.38 84.87 1.62×
True 256K 1 49.55 127.67 2.58×
True 256K 4 50.82 126.98 2.50×
True 256K 16 50.78 127.96 2.52×
True 256K 32 65.60 128.68 1.96×
True 512K 1 59.30 176.13 2.97×
True 512K 4 61.19 177.11 2.89×
True 512K 16 69.97 209.99 3.00×
True 512K 32 91.76 237.39 2.59×
True 1M 1 79.77 311.17 3.90×
True 1M 4 81.36 314.62 3.87×
True 1M 16 99.23 419.87 4.23×
True 1M 32 134.78 420.65 3.12×
False 128K 1 40.18 63.55 1.58×
False 128K 4 41.93 63.63 1.52×
False 128K 16 42.11 73.51 1.75×
False 128K 32 47.31 73.08 1.54×
False 256K 1 43.67 98.84 2.26×
False 256K 4 44.39 97.40 2.19×
False 256K 16 46.02 114.29 2.48×
False 256K 32 58.02 115.47 1.99×
False 512K 1 50.21 164.26 3.27×
False 512K 4 52.55 195.00 3.71×
False 512K 16 59.58 196.85 3.30×
False 512K 32 82.21 222.96 2.71×
False 1M 1 64.59 301.94 4.67×
False 1M 4 66.34 301.74 4.55×
False 1M 16 88.54 403.70 4.56×
False 1M 32 122.81 405.21 3.30×
  • Across 77 dynamic-N cases with K=2048 and stable=True, FlyDSL CUDA Graph performance was faster than HIP in all 77 cases, with speedups ranging from 1.02× to 4.02× and a median of 1.94×.
  • Different N values reuse the same compiled kernel without additional JIT compilation.
  • ATOM C2/MTP4, 1M physical width and 85,027 effective length: FlyDSL 43.95 µs vs HIP 62.56 µs, a 1.42× speedup.
  • 2M physical width and 57,344 effective length: FlyDSL 43.05 µs vs HIP 54.25 µs, a 1.26× speedup.

The following cases use the maximum row count at each dispatch boundary, plus a 1M-length case.

K stable width rows FlyDSL (µs) HIP (µs) Speedup
512 True 20K 128 37.09 44.37 1.20×
512 True 32K 16 41.93 44.64 1.06×
512 True 64K 32 48.56 56.17 1.16×
512 True 1M 32 133.21 398.79 2.99×
512 False 128K 32 46.76 69.42 1.48×
512 False 1M 32 122.82 394.21 3.21×
4096 True 20K 128 36.47 46.83 1.28×
4096 True 32K 16 41.45 61.12 1.47×
4096 True 64K 32 48.08 95.64 1.99×
4096 True 1M 32 134.22 1303.13 9.71×
4096 False 128K 32 47.56 75.36 1.58×
4096 False 1M 32 123.92 413.03 3.33×

gfx942 / MI308 Kernel Performance at Dispatch Boundaries

The following results use lirui-vllm-latest-0623 with FlyDSL 0.3.2 on AMD Instinct MI308X (gfx942). CUDA Graph replay measures the kernel path. Each value is the median of 101 graph replays after 10 warmup replays. Speedup is HIP / FlyDSL; values greater than 1 indicate that FlyDSL is faster. All listed cases matched the HIP reference.

stable K width rows FlyDSL (µs) HIP (µs) Speedup
True 2048 20K 1 33.80 41.80 1.24x
True 2048 20K 16 34.08 45.32 1.33x
True 2048 20K 32 33.88 45.16 1.33x
True 2048 20K 64 34.76 45.60 1.31x
True 2048 128K 1 44.52 85.24 1.91x
True 2048 128K 4 44.56 96.32 2.16x
True 2048 128K 16 77.88 97.12 1.25x
True 2048 512K 1 62.04 223.08 3.60x
True 2048 512K 4 64.08 263.72 4.12x
True 2048 512K 16 134.24 283.12 2.11x
True 2048 1M 1 88.56 481.88 5.44x
True 2048 1M 4 95.96 479.44 5.00x
True 2048 1M 16 215.80 513.68 2.38x
False 2048 512K 1 51.00 208.32 4.08x
False 2048 512K 4 52.92 244.72 4.62x
False 2048 512K 16 109.32 264.36 2.42x
False 2048 512K 32 164.88 264.00 1.60x
False 2048 1M 1 69.44 390.20 5.62x
False 2048 1M 4 76.60 456.04 5.95x
False 2048 1M 16 168.68 490.60 2.91x
False 2048 1M 32 266.92 493.68 1.85x
K stable width rows FlyDSL (µs) HIP (µs) Speedup
512 True 20K 64 34.48 45.32 1.31x
512 True 128K 16 78.80 84.60 1.07x
512 True 1M 16 215.04 493.36 2.29x
512 False 512K 32 162.32 252.20 1.55x
512 False 1M 32 263.04 481.24 1.83x
4096 True 20K 64 34.76 45.60 1.31x
4096 True 128K 16 78.24 182.92 2.34x
4096 True 1M 16 215.32 1474.77 6.85x
4096 False 512K 32 199.08 276.76 1.39x
4096 False 1M 32 317.44 506.64 1.60x

Based on these measurements, gfx942 dispatch is gated conservatively to:

  • stable=True: width <= 20K && rows <= 64, or width >= 128K && rows <= 16
  • stable=False: width >= 512K && rows <= 32

Submission Checklist

Add a compile-time-specialized multi-CTA radix-select path with optional deterministic output for the existing per-row decode interface.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every PR:

  • ✅ Pre-checks (submodule verification, code formatting)
  • ✅ Aiter op tests (gfx942 + gfx950)
  • ✅ Triton tests on MI35X (only when aiter/ops/triton/** or related paths are changed)

Extended tests (opt-in via labels):

Label Tests
ci:gfx1250-ffm-triton Run the five-shard gfx1250 FFM Triton test suite
ci:triton-300x Run an additional Triton test job on MI300X in PRs; main branch always runs both MI35X and MI300X
ci:sglang SGLang integration tests: DeepSeek-R1-MXFP4 accuracy, Qwen 3.5 accuracy
ci:atom ATOM benchmark: DeepSeek-R1-0528, GPT-OSS-120B
ci:atom_full ATOM accuracy suite for PR and main models from ATOM models_accuracy.json
ci:vllm vLLM benchmark: GPT-OSS-120B, DeepSeek-R1-0528, Kimi-K2.5
ci:all All standard extended tests (excludes ci:atom_full)

Only add ci:atom_full for FlyDSL or Triton upgrades.
Add labels via the sidebar or gh pr edit 5011 --add-label <label>

PR title tags:
Component tags ([Triton/Gluon], [HIP], [CK], [ASM], ...) are added to the PR title automatically from the changed files and re-synced on every push — change-type tags like [fix]/[Perf] and op tags like [MLA] are left untouched. Add the no-auto-title label to opt this PR out of title tagging.

lirui927 and others added 2 commits August 26, 2026 04:43
Reuse radix histograms for stable selection, add wide-count support for long contexts, and align launcher caching and validation with other FlyDSL operators.

Co-authored-by: Cursor <cursoragent@cursor.com>
Vectorize stable writes with a compile-time width choice and remove redundant scan barriers to improve long-context latency without regressing short rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
@samremes

Copy link
Copy Markdown
Contributor

FYI, there is also this PR with a similar kind of kernel: #4355 which seems faster than this in many cases in our quick comparisons.

@lirui927

lirui927 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

FYI, there is also this PR with a similar kind of kernel: #4355 which seems faster than this in many cases in our quick comparisons.

@samremes Thanks for your great job.
I tested the two PRs on the typical workloads we care about: topk=2048, batch size 8–32, with an average row length of around 700K–1M. The performance of the two PRs is quite similar in these cases (stable=False).
However, we need to support stable=True. I noticed that your implementation doesn’t support it yet, so stable=True currently falls back to the HIP implementation.
Would you be able to add support for stable=True? If so, that would be great!
I also noticed that the gfx950 gate only covers widths of 131K/163K and a maximum of 15 rows. This means our typical workloads are currently falling back to the HIP implementation by default. However, based on my testing, the kernel actually performs better than HIP for these workloads as well.
Would it be possible to extend the gfx950 gate to cover these cases too?

GPU:MI355X ROCm:7.2.4 FlyDSL:0.3.1 HIP:7.2.53211

Scenario Rows Mean live length Our FlyDSL µs HIP µs HIP/ours PR #4355 µs PR/ours
near 1M 8 964,866 70.7 303.7 4.30× 81.8 1.16×
near 1M 26 979,712 121.3 385.9 3.18× 124.0 1.02×
near 1M 28 981,201 135.4 377.0 2.78× 128.0 0.95×
near 1M 32 979,973 141.2 377.6 2.67× 136.8 0.97×
near 700K 8 662,920 56.4 188.7 3.35× 66.6 1.18×
near 700K 26 666,359 95.8 252.6 2.64× 99.3 1.04×
near 700K 28 667,365 104.8 253.1 2.42× 101.9 0.97×
near 700K 32 667,541 110.1 252.9 2.30× 108.5 0.99×

lirui927 and others added 5 commits August 27, 2026 00:48
Initialize empty-row state and overwrite unused output slots so CUDA Graph replays cannot retain stale indices.

Co-authored-by: Cursor <cursoragent@cursor.com>
Reduce long-row scan and dispatch overhead with adaptive three-pass radix, vectorized histogram reduction, fused stable prefixing, cached workspace, and short-row direct fill.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use a 1024-thread, 16-chunk data geometry with a 256-thread reduce to improve three-pass radix performance across stable and unstable batches.

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove four-pass and shape-specific routing so every decode path uses the tuned 11-bit radix geometry.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add single-launch stable paths and reuse dynamic-N multi-kernel binaries so model workloads avoid repeated launches and per-width compilation.

Co-authored-by: Cursor <cursoragent@cursor.com>
lirui927 and others added 2 commits August 31, 2026 02:12
Route long single-row calls through the safe multi-launch path because concurrent graph replays can deadlock non-cooperative barriers, while retaining the barrier-free short path.

Co-authored-by: Cursor <cursoragent@cursor.com>
Route measured gfx950 shapes through dynamic FlyDSL kernels with HIP fallback, and remove unused persistent paths to keep graph-safe execution maintainable.

Co-authored-by: Cursor <cursoragent@cursor.com>
@lirui927 lirui927 changed the title [FlyDSL] Add TopK [FlyDSL] Add FlyDSL Radix-Select TopK Path to the Existing Per-Row Decode Interface Aug 31, 2026
Reuse the faster deterministic short-row kernel for unordered calls, whose output remains valid for the weaker ordering contract.

Co-authored-by: Cursor <cursoragent@cursor.com>
@lirui927
lirui927 marked this pull request as ready for review August 31, 2026 09:43
@lirui927
lirui927 requested a review from a team August 31, 2026 09:43
lirui927 and others added 8 commits August 31, 2026 04:54
Apply the formatter output required by the code-style check.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep focused dispatch, end-to-end accuracy, and CUDA Graph coverage while removing redundant performance and implementation-detail cases.

Co-authored-by: Cursor <cursoragent@cursor.com>
Enable only the measured short and long gfx942 ranges while retaining HIP fallback for unstable intermediate shapes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Reduce the short-context stable dispatch limit on gfx942 so FlyDSL is
only selected for smaller batch sizes where it reliably wins.

Co-authored-by: Cursor <cursoragent@cursor.com>
Update gate and e2e cases so short-context stable dispatch boundaries
match the tightened gfx942 FlyDSL row gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace inline fly_rocdl.update_dpp calls with the shared DPP helper.

Co-authored-by: Cursor <cursoragent@cursor.com>
lirui927 and others added 2 commits August 31, 2026 06:47
Add the blank line Ruff I001 expects between third-party and local imports.

Co-authored-by: Cursor <cursoragent@cursor.com>
Specialize kernels at compile time to write selected values inline while preserving the no-values path and signed-zero ordering.

Co-authored-by: Cursor <cursoragent@cursor.com>
lirui927 and others added 2 commits September 1, 2026 00:46
Fall back to HIP for unsupported FlyDSL signatures and align edge-case ordering while covering ties, NaNs, and MTP row lengths.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants