Skip to content

perf(cake_tinygemm2): add a STAGES=16 kernel tier for single-wave large-K shapes - #4423

Merged
bkryu merged 1 commit into
flashinfer-ai:mainfrom
xslingcn:tinygemm2-sm100-stage16-family
Aug 11, 2026
Merged

bkryu merged 1 commit into
flashinfer-ai:mainfrom
xslingcn:tinygemm2-sm100-stage16-family

Conversation

@xslingcn

@xslingcn xslingcn commented Aug 8, 2026

Copy link
Copy Markdown
Member

📌 Description

Adds a third ring-depth tier to the tinygemm2_sm100 family (#4274) and
moves ring selection into the binding. All six kernels are regenerated from
the current CAKE generator state, which also updates the tensor-map
parameter passing to a single by-value __grid_constant__ pack.

  • New STAGES=16 ring for single-wave large-K shapes. The 4/8-stage
    rings were tuned for K up to ~4K; at larger K the 8-deep ring no longer
    covers the weight-stream latency when the working set is not L2-resident,
    and trails the reference kernel's own 16-deep configuration. The new tier
    closes that: at N=8/M=128/K=7168 (bias path) it measures 4.93 µs vs the
    reference's 5.22 µs on GB300, and 5.31 µs vs 5.56 µs on B200
    (cold-L2
    CUPTI).
  • Ring selection in the binding, following the reference
    csrc/tinygemm2.cu launcher convention: stage 4 for K <= 1024 or grids
    past 2x the SM count (unchanged), stage 16 for single-wave grids with
    K >= 4608 (measured crossover on both GB300 and B200), stage 8 otherwise.
    The Python dispatcher becomes a single call into the combined op.
  • Dispatch gates on exact compute capabilities (10, 0)/(10, 3). SM107
    passes the previous major == 10 predicate but must keep the reference
    path instead of erroring in the binding.
  • The dynamic-SMEM attribute is now set once per (kernel, device) instead
    of on every launch.

Correctness contract unchanged: bitwise equality with
csrc/tinygemm2.cu
— verified per-variant and through the dispatcher
(torch.equal, batch 1-64, K to 7168, M to 4096, on B200 and GB300), and
end-to-end in SGLang serving with zero token flips (gpt-oss-120b, and
Mistral-Large-3 whose router GEMM sits in the new tier).
tests/model_optimizations/test_tinygemm2_sm100.py extends to the stage-16
variants and long-K parity shapes. compute-sanitizer synccheck is clean on
all six variants.

Limitations: unchanged from #4274 — the nobias path stays on the reference
kernel.

📊 End-to-end serving validation — Mistral-Large-3 (675B FP8), SGLang, TP4, GB300

(CUDA graphs on, production defaults)

Correctness: 32 fixed greedy prompts — token streams bitwise identical between arms; GSM8K-200: CAKE 0.950 / ref 0.945.

Performance (CAKE = this PR's kernels via default dispatch, ref = reference tinygemm2; throughput in mean output tok/s, ITL is median in ms):

concurrency throughput (CAKE) throughput (ref) ITL (CAKE) ITL (ref)
1 100.7 101.0 9.47 9.48
8 488.6 487.9 15.47 15.48
32 1068 1073 28.90 28.92
128 3000 2997 41.31 41.44

🔍 Related Issues

Follow-up to #4274. Related to #4254 (CAKE-generated kernel progress
tracker).

🚀 Pull Request Checklist

Thank you for contributing to FlashInfer! Before we review your pull request, please make sure the following items are complete.

✅ Pre-commit Checks

  • I have installed pre-commit by running pip install pre-commit (or used your preferred method).
  • I have installed the hooks with pre-commit install.
  • I have run the hooks manually with pre-commit run --all-files and fixed any reported issues.

🧪 Tests

  • Tests have been added or updated as needed.
  • All tests are passing (unittest, etc.).

Reviewer Notes

The generated device sections are frozen artifacts (regeneration happens
outside this repo, as with #4262/#4274); review focus is best spent on the
binding section of csrc/tinygemm2_sm100.cu (pack construction, stage
selection, launch attributes), the dispatcher in
flashinfer/gemm/routergemm.py, and the test additions. The single-TU
merge follows the mechanical transform documented in the file header.

Summary by CodeRabbit

  • New Features

    • Added support for larger matrix workloads through new stage16 execution variants.
    • Automatically selects the appropriate execution stage based on workload size and hardware.
    • Added direct launch support for all available execution variants.
    • Expanded compatibility for supported compute capabilities and newer CUDA versions.
  • Bug Fixes

    • Improved handling of large reduction sizes and deep-ring workloads.
  • Tests

    • Added coverage for larger K dimensions, including 7168 and 14336, and stage16 variants.

…sor-map ABI

Regenerated all variants from the current Loom generator state and merged
them into the single TU following the same mechanical transform as the
original freeze:

- adds the STAGES=16 ring as a third tier: at long K the 8-deep ring's
  in-flight budget no longer covers the weight-stream latency once the
  working set is not L2-resident (cold-L2 CUPTI on GB300: +0.7/+1.8/+4.4us
  vs the 16-deep configuration at K=7168/14336/28672, ~0.2us warm cost);
  ring selection moves into the binding, mirroring the reference launcher
  convention — single-wave grids with K>=4608 take stage 16, multi-wave
  grids keep stage 8 (halved residency loses 2-6us)
- all six kernels use the generator's current trailing by-value
  __grid_constant__ tensor-map pack ABI (one LoomTensorMapPack<2>)
- dynamic-SMEM opt-in set once per (kernel, device); the SM100/SM103
  verdict cached per device
- python dispatch becomes a single hop into the combined op; dispatch
  gates on exact compute capabilities (10, 0)/(10, 3) so other 10.x
  devices keep the reference path instead of erroring in the binding
- tests: stage16 variants join the per-variant battery; parity shapes add
  single-wave long-K rows (8, 128, 7168) and (1, 128, 14336)

Correctness contract unchanged: bitwise equality with csrc/tinygemm2.cu.
On GB300 the six instances are bit-identical to the reference across a
40-shape battery (batch 1-64, K to 7168, M to 4096); at N=8/M=128/K=7168
the new tier measures 4.93us vs the reference's 5.22us (cold-L2 CUPTI).
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds stage16 and stage16 PDL SM100 tinygemm2 kernels. It packs tensor-map arguments, centralizes stage selection in the CUDA binding, updates router GEMM capability checks, and expands parity coverage for large-K inputs.

Changes

SM100 tinygemm2 kernel implementation

Layer / File(s) Summary
Packed tensor-map ABI for existing kernels
csrc/tinygemm2_sm100.cu
Stage4 and stage8 kernels use LoomTensorMapPack<2>. They use immediate barrier counts and typed bfloat16 output stores.
Stage16 ring kernels
csrc/tinygemm2_sm100.cu
Stage16 and stage16 PDL add 16-stage shared-memory rings, packed tensor-map loads, synchronization, and output stores.
Binding validation and stage dispatch
csrc/tinygemm2_sm100.cu, flashinfer/gemm/routergemm.py
The binding validates supported devices, builds packed tensor maps, selects stage4, stage8, or stage16, and launches PDL or non-PDL variants. Router GEMM forwards arguments directly and enables the path for SM100 or SM103 with CUDA 12.8 or newer.
Stage coverage validation
tests/model_optimizations/test_tinygemm2_sm100.py
Tests add large-K and deep-ring parity shapes and direct coverage for stage16 variants.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RouterGEMM
  participant SM100Binding
  participant StageSelector
  participant TinyGEMM2Kernel
  participant Output
  RouterGEMM->>SM100Binding: submit GEMM arguments
  SM100Binding->>StageSelector: evaluate reduction size and CTA count
  StageSelector->>TinyGEMM2Kernel: launch selected stage and PDL variant
  TinyGEMM2Kernel->>Output: store bfloat16 GEMM results
Loading

Possibly related PRs

Suggested reviewers: yzh119, bkryu, dhiraj113

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding a STAGES=16 kernel tier for large-K single-wave shapes.
Description check ✅ Passed The description covers the required sections, explains the implementation and rationale, lists related issues, and reports tests and validation results.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (2)
csrc/tinygemm2_sm100.cu (1)

2199-2212: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Value-initialize config and attrs.

cudaLaunchConfig_t config; and cudaLaunchAttribute attrs[1]; are declared without initialization. The code assigns every field that the current CUDA header defines, so the launch is correct today. If a later CUDA toolkit adds a field to cudaLaunchConfig_t, the unassigned field carries stack garbage and cudaLaunchKernelEx fails in a way that is hard to diagnose. Value-initialization removes that dependency at no runtime cost.

♻️ Proposed hardening
   if (pdl) {
-    cudaLaunchConfig_t config;
-    cudaLaunchAttribute attrs[1];
+    cudaLaunchConfig_t config = {};
+    cudaLaunchAttribute attrs[1] = {};
     config.gridDim = grid;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@csrc/tinygemm2_sm100.cu` around lines 2199 - 2212, Value-initialize the
cudaLaunchConfig_t config and cudaLaunchAttribute attrs declarations in the pdl
launch path before assigning their fields. Update the declarations used by
cudaLaunchKernelEx so any future CUDA struct fields default safely without
changing the existing launch configuration.
tests/model_optimizations/test_tinygemm2_sm100.py (1)

52-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a shape at the exact kStage16MinK boundary.

The new entries cover K=7168 and K=14336, which land well inside the stage16 tier, and K=4096, which lands in the stage8 tier. No entry sits at K=4608, the exact value of kStage16MinK in csrc/tinygemm2_sm100.cu. The selection uses in_features >= kStage16MinK, so the boundary itself is the value most likely to break if the constant or the comparison changes. One extra entry pins it.

As per coding guidelines: "Write tests for new operations".

♻️ Proposed additional shape
     (8, 1024, 1024),
+    (8, 128, 4608),
     (8, 128, 7168),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/model_optimizations/test_tinygemm2_sm100.py` around lines 52 - 63, Add
a PARITY_SHAPES entry with K equal to the exact kStage16MinK boundary value,
4608, while keeping the existing shapes unchanged. Ensure the new case exercises
the selection path that uses in_features >= kStage16MinK.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@csrc/tinygemm2_sm100.cu`:
- Around line 2199-2212: Value-initialize the cudaLaunchConfig_t config and
cudaLaunchAttribute attrs declarations in the pdl launch path before assigning
their fields. Update the declarations used by cudaLaunchKernelEx so any future
CUDA struct fields default safely without changing the existing launch
configuration.

In `@tests/model_optimizations/test_tinygemm2_sm100.py`:
- Around line 52-63: Add a PARITY_SHAPES entry with K equal to the exact
kStage16MinK boundary value, 4608, while keeping the existing shapes unchanged.
Ensure the new case exercises the selection path that uses in_features >=
kStage16MinK.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5808b9d0-3027-4580-98bc-1dbe9965ad45

📥 Commits

Reviewing files that changed from the base of the PR and between 29196cf and f6403f6.

📒 Files selected for processing (3)
  • csrc/tinygemm2_sm100.cu
  • flashinfer/gemm/routergemm.py
  • tests/model_optimizations/test_tinygemm2_sm100.py

@bkryu bkryu added the run-ci label Aug 10, 2026
@bkryu

bkryu commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

/bot run tests/gemm

@flashinfer-bot

Copy link
Copy Markdown
Collaborator

GitLab MR !1179 has been created, and the CI pipeline #62018422 is currently running. I'll report back once the pipeline job completes.

@bkryu
bkryu enabled auto-merge (squash) August 11, 2026 06:53
@flashinfer-bot

Copy link
Copy Markdown
Collaborator

[SUCCESS] Pipeline #62018422: 18/18 executed test jobs passed

@bkryu
bkryu merged commit 78e463b into flashinfer-ai:main Aug 11, 2026
103 of 109 checks passed
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.

3 participants