perf(qwen36): hd256/GQA-8 occupancy-corrected KV-split count (+2.8-5.1% @8k-32k)#338
Conversation
🐈 Flagged: copycat (real-time guard)This PR re-submits substantially the same diff (≥85% line overlap) as the earlier #318 by a different author. Duplicating another contributor's work is treated as gaming the SN74 emission mechanism. The account has been blocked and this PR closed. See |
🚩 Flagged: eval-gamingBlocked account(s) for gaming the SN74 emission mechanism (sybil / coordinated duplicate farming): See |
Maintainer clearance: copycat false positiveCleared — this was not gaming. Why it was flagged: the 3-line GQA-4 Why it is not a copycat:
Action taken: Note: fix is already on |
…r-338 fix(copycat): clear #338 false positive
Mark gittensor-ai-lab#338 as a cleared build-fix false positive (literal overlap with gittensor-ai-lab#318 call-site boilerplate). inference2026 was not on blocked-contributors.txt.
⏳ Needs a benchmark to be evaluatedYou ticked Tested on RTX 5090 but the decode before → after tok/s table is still empty / placeholder (or shows no gain). The on-device eval won't run until it shows a real improvement. Fill it from the end-to-end decode bench (not an isolated-kernel microbench): bench/scripts/bench.sh --download # baseline (before)
bench/scripts/bench.sh --download # your branch (after)Then the bot greenlights it on the next poll and evaluates it on an RTX 5090. |
9a0ae75 to
8a6de37
Compare
✅ sparkinfer auto-eval —
|
| metric | value |
|---|---|
| label | eval:S |
| Qwen3.5 score | eval-qwen35:none (pass) |
| Qwen3.6 score | eval-qwen36:S (pass) |
| Qwen3.5 vs same-box main | 283.18 tok/s → +0.5% (+1.4) |
| Qwen3.5 scored decode (128 ctx · 128-context) | 284.59 tok/s |
| Qwen3.5 correctness | top-1 95.4% · KL 0.0268 |
| Qwen3.5 128-token no-regression gate | 284.59 tok/s vs main 283.18 tok/s · pass |
| Qwen3.5 512-context no-regression gate | 281.54 tok/s vs main 280.25 tok/s · pass |
| Qwen3.5 4k-context no-regression gate | 273.07 tok/s vs main 272.14 tok/s · pass |
| Qwen3.6 vs same-box main | 354.56 tok/s → +4.9% (+17.4) |
| Qwen3.6 scored decode (32768 ctx · 32k-context) | 372.01 tok/s |
| Qwen3.6 correctness | top-1 99.0% · KL 0.0177 |
| Qwen3.6 128-token no-regression gate | 427.15 tok/s vs main 427.74 tok/s · pass |
| Qwen3.6 512-context no-regression gate | 421.36 tok/s vs main 422.84 tok/s · pass |
| Qwen3.6 4k-context no-regression gate | 406.43 tok/s vs main 405.82 tok/s · pass |
| Qwen3.6 16k-context no-regression gate | 392.63 tok/s vs main 381.72 tok/s · pass |
| Qwen3.6 32k-context no-regression gate | 372.01 tok/s vs main 354.56 tok/s · pass |
| Qwen3.5 optimize | eval:none · 284.59 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy | top-1 99.0% · KL 0.0177 · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 128 | 427.3 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 512 | 421.28 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 4k | 406.16 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 16k | 392.71 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 32k | 372.01 tok/s · pass |
| Qwen3.6 optimize | eval:S · 372.01 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy | top-1 95.4% · KL 0.0268 · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 | 284.08 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 512 | 281.05 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k | 272.62 tok/s · pass |
Verified speedup over same-box origin/main — 372.01 tok/s (main was 354.56 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.
|
The round's |
…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).
8a6de37 to
844c415
Compare
✅ sparkinfer auto-eval —
|
| metric | value |
|---|---|
| label | eval:S |
| Qwen3.5 score | eval-qwen35:none (pass) |
| Qwen3.6 score | eval-qwen36:S (pass) |
| Qwen3.5 vs same-box main | 302.03 tok/s → +0.4% (+1.3) |
| Qwen3.5 scored decode (128 ctx · 128-context) | 303.32 tok/s |
| Qwen3.5 correctness | top-1 92.9% · KL 0.0354 |
| Qwen3.5 128-token no-regression gate | 303.32 tok/s vs main 302.03 tok/s · pass |
| Qwen3.5 512-context no-regression gate | 299.39 tok/s vs main 298.23 tok/s · pass |
| Qwen3.5 4k-context no-regression gate | 289.67 tok/s vs main 289.05 tok/s · pass |
| Qwen3.6 vs same-box main | 354.56 tok/s → +4.9% (+17.5) |
| Qwen3.6 scored decode (32768 ctx · 32k-context) | 372.04 tok/s |
| Qwen3.6 correctness | top-1 97.2% · KL 0.0187 |
| Qwen3.6 128-token no-regression gate | 427.31 tok/s vs main 428.03 tok/s · pass |
| Qwen3.6 512-context no-regression gate | 421.34 tok/s vs main 422.92 tok/s · pass |
| Qwen3.6 4k-context no-regression gate | 406.44 tok/s vs main 406.14 tok/s · pass |
| Qwen3.6 16k-context no-regression gate | 392.73 tok/s vs main 381.74 tok/s · pass |
| Qwen3.6 32k-context no-regression gate | 372.04 tok/s vs main 354.56 tok/s · pass |
| Qwen3.5 optimize | eval:none · 303.32 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy | top-1 97.2% · KL 0.0187 · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 128 | 427.22 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 512 | 421.4 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 4k | 406.29 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 16k | 392.65 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 32k | 371.99 tok/s · pass |
| Qwen3.6 optimize | eval:S · 372.04 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy | top-1 92.9% · KL 0.0354 · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 | 303.0 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 512 | 299.26 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k | 289.36 tok/s · pass |
Verified speedup over same-box origin/main — 372.04 tok/s (main was 354.56 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.
|
✅ Auto-merged as the round's |
Summary
hd256/GQA-8 occupancy-corrected KV-split count for Qwen3.6's int8-MMA long-context decode.
fa_split_gqa_mma_i8_kernel<HEAD_DIM,GQA>is shared by hd128 (Qwen3-30B) and hd256 (Qwen3.6) under the same__launch_bounds__(GQA*32, 5)hint, but hd256's smem footprint is ~1.9x hd128's — its real occupancy is lower than the generic 128/256 seqlen-threshold policy assumes, over-subscribing the split grid. Gated to Qwen3.6's exact shape only (head_dim==256 && GQA==8); Qwythos/Qwen3-30B keep the untouched generic policy.Proof of speedup
sm_120)Decode tok/s (end-to-end, from
bench/scripts/bench.sh— required for evaluation):Correctness
Online-softmax combine is mathematically exact for any split count in real-number arithmetic; verified this holds in practice against the real llama.cpp reference (not just self-consistency vs sparkinfer's own baseline) — the project's
accuracy.shgate uses only a 101-token prompt and 2048-token llama-server context, so it structurally cannot exercise this code path. Built a custom long-context comparison: llama-server at-c 40000, 120 sampled positions across 8192-32768 (natural text, teacher-forced), comparing top-1/KL for both baseline and this PR's patched build:Statistically indistinguishable from baseline, both far inside the project's gate (top1 >= 0.90, KL <= 0.20).
Relationship to #294
#294 is concurrently iterating on the same generic seqlen-threshold block with a different (threshold-retuning) approach. This PR is a distinct, model-shape-gated occupancy correction applied after the generic policy computes
want, not an edit to #294's lines. Cross-checked against #294's own proposed values at the same 4 context points — this change matches or beats #294's scheme everywhere (tied at 4k, ahead at 8k/16k/32k; #294 leaves 32k completely unchanged since its formula assumed hd128's occupancy for hd256 too).Test plan
qwen3_gguf_bench+qwen3_gguf_scoreon RTX 5090 (sm_120)