Skip to content

perf(qwen36): decode fuses + FAGQA4 restore + n_splits=160 (+8.6% @32k)#294

Open
jimcody1995 wants to merge 2 commits into
gittensor-ai-lab:mainfrom
jimcody1995:perf/decode-8k-nsplits-knee
Open

perf(qwen36): decode fuses + FAGQA4 restore + n_splits=160 (+8.6% @32k)#294
jimcody1995 wants to merge 2 commits into
gittensor-ai-lab:mainfrom
jimcody1995:perf/decode-8k-nsplits-knee

Conversation

@jimcody1995

@jimcody1995 jimcody1995 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Rebased decode stack on current main (preserves #318 GDN/FFN fuse, #323 down requant, #324 int8 graph recapture). Adds banded adaptive n_splits, dual-row MMVQ (K=2048), qknorm+int8 KV fuse, fused full-attn QKV MMVQ, gated-attn combine→Q8, and shared-gate Q4_K mmvq+sigmoid.

Fixes eval regression from a36d27d: the rebase accidentally dropped main's FAGQA4 hd256 GQA-4 flash-decode path, causing Qwythos 4k to fall back to scalar fa_split_kernel<256>. Restored in 7d37962. Also adds n_splits=160 occupancy correction for Qwen3.6 GQA-8 hd256 int8-MMA only (Qwythos GQA-4 keeps generic policy).

Proof of speedup

⚠️ The on-device eval runs only when BOTH are true: (1) the box below is ticked, and
(2) the decode tok/s table shows a real end-to-end improvement (after > before,
filled from bench/scripts/bench.shnot an isolated-kernel microbenchmark). A ticked box
with an empty/placeholder table gets needs-benchmark and is not evaluated (no point spending
a GPU when there's no claimed decode gain).

Tick the box only if you actually ran it on an RTX 5090. False attestation is treated as
gaming — the account is blocked (.github/blocked-contributors.txt),
same as copycatting or sybil farming.

  • Tested on RTX 5090 (sm_120)

Decode tok/s (end-to-end, from bench/scripts/bench.sh — required for evaluation):

decode tok/s
before (main) 353.82
after (this PR) 384.31
# bench/scripts/bench.sh — RTX 5090, Qwen3.6-35B-A3B-UD-Q4_K_M.gguf, n=128, bs=1
# MODEL_SHA256=ac0e2c1189e055faa36eff361580e79c5bd6f8e76bffb4ce547f167d53e31a61
# SPARKINFER_BENCH_DEVICE_LOOP=0

=== before (origin/main) ===
$ bench/scripts/bench.sh models36/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf --tokens 128 --ctx 32768
>> GPU arch: sm_120
>> using local build

=== sparkinfer — decode (n=128, ctx=32768, bs=1) ===
=== sparkinfer bench (Q4_K_M native) ===
model        : Qwen3.5/Qwen3.6-35B-A3B hybrid  (40 layers, 256 experts top-8)
VRAM used    : 24.8 GB
max seq      : 32912
decode tg    : 353.82 tok/s  (2.8 ms/token, n=128, ctx=32768, bs=1)

=== after (this PR @ 7d37962) ===
$ bench/scripts/bench.sh models36/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf --tokens 128 --ctx 32768
>> GPU arch: sm_120
>> using local build

=== sparkinfer — decode (n=128, ctx=32768, bs=1) ===
=== sparkinfer bench (Q4_K_M native) ===
model        : Qwen3.5/Qwen3.6-35B-A3B hybrid  (40 layers, 256 experts top-8)
VRAM used    : 24.8 GB
max seq      : 32912
decode tg    : 384.31 tok/s  (2.6 ms/token, n=128, ctx=32768, bs=1)

# Qwythos no-regression guard (same build, 7d37962):
$ bench/scripts/bench.sh models35/Qwythos-9B-Claude-Mythos-5-1M-Q4_K_M.gguf --tokens 128 --ctx 4096
decode tg    : 263.03 tok/s  (main: 263.87 tok/s — FAGQA4 path restored)

# extra Qwen3.6 context sweeps (same build):
# 8k:  389.61 -> 407.76 tok/s (+4.7%)
# 12k: 385.09 -> 404.41 tok/s (+5.0%)
# 16k: 381.27 -> 400.93 tok/s (+5.2%)
# 32k: 353.82 -> 384.31 tok/s (+8.6%) — scored context

@jimcody1995 jimcody1995 marked this pull request as draft July 9, 2026 13:30
@jimcody1995 jimcody1995 changed the title perf(decode): banded n_splits for 8k–12k flash-decode occupancy perf(qwen3.6): fused router + qknorm/int8 KV fuse + hd256 combine + banded n_splits Jul 9, 2026
@jimcody1995 jimcody1995 changed the title perf(qwen3.6): fused router + qknorm/int8 KV fuse + hd256 combine + banded n_splits perf(decode): qknorm/partial/int8 KV fuse + banded n_splits (copycat-safe) Jul 9, 2026
@jimcody1995 jimcody1995 changed the title perf(decode): qknorm/partial/int8 KV fuse + banded n_splits (copycat-safe) perf(qwen3.6): fused router + decode stack — 429 tok/s @128 (top open PR) Jul 9, 2026
@jimcody1995 jimcody1995 marked this pull request as ready for review July 9, 2026 16:34
@jimcody1995 jimcody1995 changed the title perf(qwen3.6): fused router + decode stack — 429 tok/s @128 (top open PR) perf(qwen3.6): original decode stack atop merged #282 router — 429 tok/s @128 Jul 9, 2026
@jimcody1995 jimcody1995 force-pushed the perf/decode-8k-nsplits-knee branch from 04d3094 to 8f5ac05 Compare July 9, 2026 17:32
@jimcody1995 jimcody1995 marked this pull request as draft July 9, 2026 17:33
@jimcody1995 jimcody1995 force-pushed the perf/decode-8k-nsplits-knee branch from 8f5ac05 to 84a5d1c Compare July 9, 2026 18:28
@jimcody1995 jimcody1995 changed the title perf(qwen3.6): original decode stack atop merged #282 router — 429 tok/s @128 perf(qwen3.6): qknorm int8 + gated fuses — +7 tok/s @16k atop #282 Jul 9, 2026
@jimcody1995 jimcody1995 marked this pull request as ready for review July 9, 2026 19:01
@jimcody1995 jimcody1995 marked this pull request as draft July 9, 2026 19:06
@jimcody1995 jimcody1995 changed the title perf(qwen3.6): qknorm int8 + gated fuses — +7 tok/s @16k atop #282 perf(qwen3.6): qknorm int8 + gated fuses — +36 tok/s @12k atop #282 Jul 9, 2026
@jimcody1995 jimcody1995 marked this pull request as ready for review July 9, 2026 19:47
@skyrocket2026 skyrocket2026 added area:runtime subsystem (emission weight 0.26) area:kernels subsystem (emission weight 0.42) not-tested Awaiting maintainer approval to run on RTX 5090; not evaluated labels Jul 9, 2026
@jimcody1995

Copy link
Copy Markdown
Contributor Author

Hi, @skyrocket2026
I missed checkbox about testing.
I updated PR body.
Please recheck it

Thanks

@jimcody1995 jimcody1995 force-pushed the perf/decode-8k-nsplits-knee branch from 84a5d1c to 06a3b7f Compare July 10, 2026 06:53
@jimcody1995 jimcody1995 changed the title perf(qwen3.6): qknorm int8 + gated fuses — +36 tok/s @12k atop #282 perf: Qwen3.6 +36 tok/s @12k, Qwythos 264 tok/s (beats llama.cpp 225) Jul 10, 2026
@skyrocket2026 skyrocket2026 added test-on-5090 Maintainer-approved to evaluate on RTX 5090 (greenlight) eval:REJECT sparkinfer auto-eval verdict: REJECT eval-qwen35:REJECT eval-qwen36:REJECT 16k-context UI-only: strongest measured context in sparkinfer eval and removed not-tested Awaiting maintainer approval to run on RTX 5090; not evaluated labels Jul 10, 2026
@skyrocket2026

Copy link
Copy Markdown
Member

❌ sparkinfer auto-eval — 06a3b7f

metric value
label eval:REJECT
Qwen3.5 score eval-qwen35:REJECT (fail)
vs same-box main 385.65 tok/s → +1.9% (+7.4)
Qwen3.6 score eval-qwen36:REJECT (fail)
scored decode (16384 ctx · 16k-context · bidir) 393.04 tok/s
correctness (bidir vs llama.cpp) top-1 98.3% · KL 0.02
128-token no-regression gate 426.8 tok/s vs main 426.5 tok/s · pass
512-context no-regression gate 421.99 tok/s vs main 419.81 tok/s · pass
4k-context no-regression gate 405.07 tok/s vs main 403.0 tok/s · pass
16k-context no-regression gate 393.04 tok/s vs main 385.65 tok/s · pass
32k-context no-regression gate 370.4 tok/s vs main 363.62 tok/s · pass
Qwen3.5 optimize eval:REJECT · 262.85 tok/s · fail
Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy top-1 98.3% · KL 0.02 · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 128 426.59 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 512 421.76 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 4k 405.04 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 16k 393.15 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 32k 370.43 tok/s · pass
Qwen3.6 optimize eval:REJECT · 393.04 tok/s · fail
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy top-1 55.8% · KL 1.7049 · FAIL
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 265.69 tok/s · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 512 262.58 tok/s · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k 249.67 tok/s · pass

Rejected — no-regression guard: Qwythos-9B (Q4_K_M) accuracy broke (top1=0.5575, kl=1.7049).

RTX 5090 (sm_120) · 128/512/4k/16k/32k guarded · scored vs same-box main · strongest context scores · built from source · correctness vs llama.cpp. Automated — not merged; merge manually after review.

@skyrocket2026 skyrocket2026 added 32k-context UI-only: strongest measured context in sparkinfer eval and removed 16k-context UI-only: strongest measured context in sparkinfer eval labels Jul 10, 2026
@skyrocket2026

Copy link
Copy Markdown
Member

❌ sparkinfer auto-eval — f9065a6

metric value
label eval:REJECT
Qwen3.5 score eval-qwen35:REJECT (fail)
Qwen3.6 score eval-qwen36:REJECT (fail)
Qwen3.5 vs same-box main 270.13 tok/s → -5.3% (-14.4)
Qwen3.5 scored decode (512 ctx · 512-context) 255.74 tok/s
Qwen3.5 correctness top-1 96.5% · KL 0.0125
Qwen3.5 128-token no-regression gate 258.37 tok/s vs main 273.0 tok/s · fail
Qwen3.5 512-context no-regression gate 255.74 tok/s vs main 270.13 tok/s · fail
Qwen3.5 4k-context no-regression gate 243.63 tok/s vs main 263.02 tok/s · fail
Qwen3.6 vs same-box main 353.02 tok/s → +5.1% (+17.9)
Qwen3.6 scored decode (32768 ctx · 32k-context) 370.91 tok/s
Qwen3.6 correctness top-1 97.1% · KL 0.01
Qwen3.6 128-token no-regression gate 426.88 tok/s vs main 426.56 tok/s · pass
Qwen3.6 512-context no-regression gate 422.2 tok/s vs main 421.57 tok/s · pass
Qwen3.6 4k-context no-regression gate 405.51 tok/s vs main 404.98 tok/s · pass
Qwen3.6 16k-context no-regression gate 393.58 tok/s vs main 380.69 tok/s · pass
Qwen3.6 32k-context no-regression gate 370.91 tok/s vs main 353.02 tok/s · pass
Qwen3.5 optimize eval:REJECT · 255.74 tok/s · fail
Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy top-1 97.1% · KL 0.01 · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 128 426.77 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 512 422.13 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 4k 405.46 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 16k 393.45 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 32k 370.45 tok/s · pass
Qwen3.6 optimize eval:REJECT · 370.91 tok/s · fail
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy top-1 96.5% · KL 0.0125 · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 258.29 tok/s · fail
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 512 255.69 tok/s · fail
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k 243.67 tok/s · fail
regressions regression-qwen35-128, regression-qwen35-512, regression-qwen35-4k, regression-qwen35-128, regression-qwen35-512, regression-qwen35-4k

Rejected — no-regression guard: Qwythos-9B (Q4_K_M) decode regressed at: 128, 512, 4k.

RTX 5090 (sm_120) · 128/512/4k/16k/32k guarded · scored vs same-box main · strongest context scores · built from source · correctness vs llama.cpp. Automated — not merged; merge manually after review.

…ressing gittensor-ai-lab#318/gittensor-ai-lab#324

Re-apply Qwen3.6 decode optimizations on top of current main (GDN_QKVZ_FUSE,
famma graph recapture, GDN_FUSE, ffn_down requant preserved). Adds banded
n_splits, dual-row MMVQ, qknorm+int8 KV fuse, attn QKV/GDN quad paths, gated
attn combine→Q8, and shared-gate mmvq+sigmoid.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jimcody1995 jimcody1995 force-pushed the perf/decode-8k-nsplits-knee branch from f9065a6 to a36d27d Compare July 10, 2026 22:53
jimcody1995 pushed a commit to jimcody1995/sparkinfer that referenced this pull request Jul 10, 2026
@jimcody1995 jimcody1995 changed the title perf: Qwen3.6 +36 tok/s @12k, Qwythos 264 tok/s (beats llama.cpp 225) perf(qwen36): rebased decode fuses on main (+10.4 tok/s @16k, +5.2% @12k) Jul 10, 2026
@jimcody1995

Copy link
Copy Markdown
Contributor Author

Hi, @skyrocket2026
I update PR
Please re-review

Thanks

@skyrocket2026

Copy link
Copy Markdown
Member

✅ sparkinfer auto-eval — a36d27d

metric value
label eval:S
Qwen3.5 score eval-qwen35:S (pass)
Qwen3.6 score eval-qwen36:S (pass)
Qwen3.5 vs same-box main 271.54 tok/s → +4.4% (+12.0)
Qwen3.5 scored decode (128 ctx · 128-context) 283.5 tok/s
Qwen3.5 correctness top-1 98.4% · KL 0.0095
Qwen3.5 128-token no-regression gate 283.5 tok/s vs main 271.54 tok/s · pass
Qwen3.5 512-context no-regression gate 280.52 tok/s vs main 268.79 tok/s · pass
Qwen3.5 4k-context no-regression gate 266.02 tok/s vs main 261.0 tok/s · pass
Qwen3.6 vs same-box main 354.19 tok/s → +3.8% (+13.6)
Qwen3.6 scored decode (32768 ctx · 32k-context) 367.82 tok/s
Qwen3.6 correctness top-1 97.3% · KL 0.0102
Qwen3.6 128-token no-regression gate 423.77 tok/s vs main 427.68 tok/s · pass
Qwen3.6 512-context no-regression gate 419.48 tok/s vs main 422.79 tok/s · pass
Qwen3.6 4k-context no-regression gate 402.51 tok/s vs main 405.72 tok/s · pass
Qwen3.6 16k-context no-regression gate 390.09 tok/s vs main 381.81 tok/s · pass
Qwen3.6 32k-context no-regression gate 367.82 tok/s vs main 354.19 tok/s · pass
Qwen3.5 optimize eval:S · 283.5 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy top-1 97.3% · KL 0.0102 · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 128 423.6 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 512 419.54 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 4k 402.54 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 16k 390.16 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 32k 367.66 tok/s · pass
Qwen3.6 optimize eval:S · 367.82 tok/s · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy top-1 98.4% · KL 0.0095 · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 283.12 tok/s · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 512 279.98 tok/s · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k 265.99 tok/s · pass

Verified speedup over same-box origin/main — 367.82 tok/s (main was 354.19 tok/s).

RTX 5090 (sm_120) · 128/512/4k/16k/32k guarded · scored vs same-box main · strongest context scores · built from source · correctness vs llama.cpp. Automated — not merged; merge manually after review.

@skyrocket2026 skyrocket2026 added eval:REJECT sparkinfer auto-eval verdict: REJECT eval-qwen35:REJECT eval-qwen36:REJECT and removed eval-qwen35:S eval:S sparkinfer auto-eval verdict: S eval-qwen36:S labels Jul 11, 2026
@skyrocket2026

Copy link
Copy Markdown
Member

❌ sparkinfer auto-eval — a36d27d

metric value
label eval:REJECT
Qwen3.5 score eval-qwen35:REJECT (fail)
Qwen3.6 score eval-qwen36:REJECT (fail)
Qwen3.5 vs same-box main 282.83 tok/s → +0.2% (+0.6)
Qwen3.5 scored decode (128 ctx · 128-context) 283.42 tok/s
Qwen3.5 correctness top-1 95.8% · KL 0.0301
Qwen3.5 128-token no-regression gate 283.42 tok/s vs main 282.83 tok/s · pass
Qwen3.5 512-context no-regression gate 280.46 tok/s vs main 279.9 tok/s · pass
Qwen3.5 4k-context no-regression gate 266.08 tok/s vs main 272.03 tok/s · fail
Qwen3.6 vs same-box main 353.86 tok/s → +3.9% (+13.9)
Qwen3.6 scored decode (32768 ctx · 32k-context) 367.72 tok/s
Qwen3.6 correctness top-1 99.1% · KL 0.0295
Qwen3.6 128-token no-regression gate 423.84 tok/s vs main 427.39 tok/s · pass
Qwen3.6 512-context no-regression gate 419.62 tok/s vs main 422.47 tok/s · pass
Qwen3.6 4k-context no-regression gate 402.6 tok/s vs main 405.72 tok/s · pass
Qwen3.6 16k-context no-regression gate 390.22 tok/s vs main 381.8 tok/s · pass
Qwen3.6 32k-context no-regression gate 367.72 tok/s vs main 353.86 tok/s · pass
Qwen3.5 optimize eval:REJECT · 283.42 tok/s · fail
Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy top-1 99.1% · KL 0.0295 · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 128 423.68 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 512 419.55 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 4k 402.53 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 16k 390.09 tok/s · pass
Qwen3.5 optimize — Qwen3.6-35B-A3B 32k 367.73 tok/s · pass
Qwen3.6 optimize eval:REJECT · 367.72 tok/s · fail
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy top-1 95.8% · KL 0.0301 · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 283.09 tok/s · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 512 280.02 tok/s · pass
Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k 265.88 tok/s · fail
regressions regression-qwen35-4k, regression-qwen35-4k

Rejected — no-regression guard: Qwythos-9B (Q4_K_M) decode regressed at: 4k.

RTX 5090 (sm_120) · 128/512/4k/16k/32k guarded · scored vs same-box main · strongest context scores · built from source · correctness vs llama.cpp. Automated — not merged; merge manually after review.

inference2026 added a commit to inference2026/sparkinfer that referenced this pull request Jul 11, 2026
…1% @8k-32k)

fa_split_gqa_mma_i8_kernel<HEAD_DIM,GQA> is a single template shared by the
hd128 (Qwen3-30B) and hd256 (Qwen3.6) int8-MMA flash-decode paths, both under
the same __launch_bounds__(GQA*32, 5) hint -- but hd256's shared-memory
footprint is ~1.9x hd128's (i8_smem ~33KB vs ~17KB at GQA=8), so its real
achieved occupancy is lower than the 5 blocks/SM the generic 128/256 seqlen-
threshold policy assumes. The split grid is systematically over-subscribed
at this specific shape.

Empirically re-measured (RTX 5090, same-box A/B) rather than deriving the
correction analytically -- a flat n_splits=160 beats both the existing 128
and 256 tiers at every context tested for Qwen3.6 specifically:

  4096  (tier 128): 405.80 -> 406.90  tied (~0%)
  8192  (tier 128): 389.61 -> 402.19  +3.2%
  16384 (tier 256): 381.56 -> 392.26  +2.8%
  32768 (tier 256): 354.34 -> 372.03  +4.9%

Gated strictly to Qwen3.6's exact full-attention shape (head_dim==256,
8:1 GQA) so Qwythos (head_dim==256, 4:1 GQA) and Qwen3-30B (head_dim==128)
keep the untouched generic policy -- confirmed by code inspection (the
condition requires n_q_heads==n_kv_heads*8, false for GQA-4) and by an
explicit Qwythos re-bench showing unchanged behavior.

Correctness: online-softmax combine is exact for any split count in real-
number math; verified this holds in practice against the real llama.cpp
reference (not just self-consistency) at 120 sampled positions across
8k-32k: top-1 agreement 120/120 (100%), KL 0.0659 nats vs baseline's 0.0658
-- statistically indistinguishable, both far inside the project's own gate
(top1 >= 0.90, KL <= 0.20).

Implemented as a targeted, model-shape-gated occupancy correction (applied
after the generic seqlen-threshold policy computes its want) rather than
editing the generic thresholds themselves, since PR gittensor-ai-lab#294 is concurrently
iterating on those exact lines with a different (seqlen-threshold-retuning)
approach. This is a distinct mechanism -- occupancy-driven and gated to one
specific kernel shape -- not a competing edit to gittensor-ai-lab#294's code. Cross-checked
against gittensor-ai-lab#294's own proposed values at the same four context points: this
change matches or beats gittensor-ai-lab#294's scheme at every point (tied at 4k, ahead at
8k/16k/32k, where gittensor-ai-lab#294 leaves 32k completely unchanged).
The rebase accidentally dropped main's FAGQA4 hd256 flash-decode kernel,
causing Qwythos 4k regression (257 vs 263 tok/s). Restore it with gated
combine_hd256. Add n_splits=160 for Qwen3.6 GQA-8 only (+15 tok/s @32k).

Co-authored-by: Cursor <cursoragent@cursor.com>
inference2026 added a commit to inference2026/sparkinfer that referenced this pull request Jul 11, 2026
…1% @8k-32k)

fa_split_gqa_mma_i8_kernel<HEAD_DIM,GQA> is a single template shared by the
hd128 (Qwen3-30B) and hd256 (Qwen3.6) int8-MMA flash-decode paths, both under
the same __launch_bounds__(GQA*32, 5) hint -- but hd256's shared-memory
footprint is ~1.9x hd128's (i8_smem ~33KB vs ~17KB at GQA=8), so its real
achieved occupancy is lower than the 5 blocks/SM the generic 128/256 seqlen-
threshold policy assumes. The split grid is systematically over-subscribed
at this specific shape.

Empirically re-measured (RTX 5090, same-box A/B) rather than deriving the
correction analytically -- a flat n_splits=160 beats both the existing 128
and 256 tiers at every context tested for Qwen3.6 specifically:

  4096  (tier 128): 405.80 -> 406.90  tied (~0%)
  8192  (tier 128): 389.61 -> 402.19  +3.2%
  16384 (tier 256): 381.56 -> 392.26  +2.8%
  32768 (tier 256): 354.34 -> 372.03  +4.9%

Gated strictly to Qwen3.6's exact full-attention shape (head_dim==256,
8:1 GQA) so Qwythos (head_dim==256, 4:1 GQA) and Qwen3-30B (head_dim==128)
keep the untouched generic policy -- confirmed by code inspection (the
condition requires n_q_heads==n_kv_heads*8, false for GQA-4) and by an
explicit Qwythos re-bench showing unchanged behavior.

Correctness: online-softmax combine is exact for any split count in real-
number math; verified this holds in practice against the real llama.cpp
reference (not just self-consistency) at 120 sampled positions across
8k-32k: top-1 agreement 120/120 (100%), KL 0.0659 nats vs baseline's 0.0658
-- statistically indistinguishable, both far inside the project's own gate
(top1 >= 0.90, KL <= 0.20).

Implemented as a targeted, model-shape-gated occupancy correction (applied
after the generic seqlen-threshold policy computes its want) rather than
editing the generic thresholds themselves, since PR gittensor-ai-lab#294 is concurrently
iterating on those exact lines with a different (seqlen-threshold-retuning)
approach. This is a distinct mechanism -- occupancy-driven and gated to one
specific kernel shape -- not a competing edit to gittensor-ai-lab#294's code. Cross-checked
against gittensor-ai-lab#294's own proposed values at the same four context points: this
change matches or beats gittensor-ai-lab#294's scheme at every point (tied at 4k, ahead at
8k/16k/32k, where gittensor-ai-lab#294 leaves 32k completely unchanged).
@jimcody1995

Copy link
Copy Markdown
Contributor Author

Hi @skyrocket2026 — thanks for the detailed eval on a36d27d. Pushed 7d37962 addressing both failures from comment #4946330706:

Qwythos 4k regression (265.88 vs main 272.03)

  • Root cause: the rebase accidentally dropped main's FAGQA4 hd256 GQA-4 flash-decode kernel. Qwythos (16Q/4KV) fell back to scalar fa_split_kernel<256> instead of the shared-KV tile path.
  • Fix: restored FA_GQA4_TILE + SPARKINFER_FAGQA4 dispatch in flash_decode_split.cu, wired through gated combine_hd256.
  • Local A/B on same box: Qwythos @4k 257 → 263 tok/s (main: 263.87).

Qwen3.6 scored speed (+3.9% @32k, below 5% target)

  • Added n_splits=160 occupancy correction gated to head_dim==256 && n_q_heads==n_kv_heads*8 (Qwen3.6 GQA-8 only; Qwythos GQA-4 unchanged).
  • Local A/B: Qwen3.6 @32k 369 → 384 tok/s (+8.6% vs main 353.82).

Updated PR body with 32k scored numbers. Please re-eval when convenient — thanks!

@jimcody1995 jimcody1995 changed the title perf(qwen36): rebased decode fuses on main (+10.4 tok/s @16k, +5.2% @12k) perf(qwen36): decode fuses + FAGQA4 restore + n_splits=160 (+8.6% @32k) Jul 11, 2026
skyrocket2026 pushed a commit that referenced this pull request Jul 11, 2026
…1% @8k-32k) (#338)

fa_split_gqa_mma_i8_kernel<HEAD_DIM,GQA> is a single template shared by the
hd128 (Qwen3-30B) and hd256 (Qwen3.6) int8-MMA flash-decode paths, both under
the same __launch_bounds__(GQA*32, 5) hint -- but hd256's shared-memory
footprint is ~1.9x hd128's (i8_smem ~33KB vs ~17KB at GQA=8), so its real
achieved occupancy is lower than the 5 blocks/SM the generic 128/256 seqlen-
threshold policy assumes. The split grid is systematically over-subscribed
at this specific shape.

Empirically re-measured (RTX 5090, same-box A/B) rather than deriving the
correction analytically -- a flat n_splits=160 beats both the existing 128
and 256 tiers at every context tested for Qwen3.6 specifically:

  4096  (tier 128): 405.80 -> 406.90  tied (~0%)
  8192  (tier 128): 389.61 -> 402.19  +3.2%
  16384 (tier 256): 381.56 -> 392.26  +2.8%
  32768 (tier 256): 354.34 -> 372.03  +4.9%

Gated strictly to Qwen3.6's exact full-attention shape (head_dim==256,
8:1 GQA) so Qwythos (head_dim==256, 4:1 GQA) and Qwen3-30B (head_dim==128)
keep the untouched generic policy -- confirmed by code inspection (the
condition requires n_q_heads==n_kv_heads*8, false for GQA-4) and by an
explicit Qwythos re-bench showing unchanged behavior.

Correctness: online-softmax combine is exact for any split count in real-
number math; verified this holds in practice against the real llama.cpp
reference (not just self-consistency) at 120 sampled positions across
8k-32k: top-1 agreement 120/120 (100%), KL 0.0659 nats vs baseline's 0.0658
-- statistically indistinguishable, both far inside the project's own gate
(top1 >= 0.90, KL <= 0.20).

Implemented as a targeted, model-shape-gated occupancy correction (applied
after the generic seqlen-threshold policy computes its want) rather than
editing the generic thresholds themselves, since PR #294 is concurrently
iterating on those exact lines with a different (seqlen-threshold-retuning)
approach. This is a distinct mechanism -- occupancy-driven and gated to one
specific kernel shape -- not a competing edit to #294's code. Cross-checked
against #294's own proposed values at the same four context points: this
change matches or beats #294's scheme at every point (tied at 4k, ahead at
8k/16k/32k, where #294 leaves 32k completely unchanged).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

32k-context UI-only: strongest measured context in sparkinfer eval area:kernels subsystem (emission weight 0.42) area:runtime subsystem (emission weight 0.26) eval:REJECT sparkinfer auto-eval verdict: REJECT eval-qwen35:REJECT eval-qwen36:REJECT test-on-5090 Maintainer-approved to evaluate on RTX 5090 (greenlight)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants