perf(qwen36): Q8_0→Q4_K requant of GDN ssm_out projections (+2.8% @128)#355
Conversation
…attn_gate) (+11.5% @128) Qwen3.6-35B-A3B UD ships the Gated-DeltaNet input projections — attn_qkv (wqkv) and attn_gate (the z gate) — as Q8_0 on all 30 GDN layers. These are the single largest per-token weight read in decode, larger than the full-attention q/o of gittensor-ai-lab#353 or the ssm_out of gittensor-ai-lab#355. Requantizing both to Q4_K once at model load reads ~47% fewer bytes on those matvecs. No decode-kernel change: after the stored type flips Q8_0→Q4_K, the GDN qkv+z projection already branches on tensor type and routes through the existing Q4_K fused kernel (launch_mmvq_gdn_qkv_z_pack2) automatically. The fit reuses the merged Lloyd-max coordinate-descent Q4_K quantizer (proj_q4k_lloyd.cu) — high enough quality to keep all 30 GDN layers inside the accuracy gate (an affine fit does not). Gated to the Qwen3.6 fingerprint; a strict no-op on every other model. Extends the existing SPARKINFER_ATTN_REQUANT_Q4K mode — set it to "attn_q,attn_output" to restore the gittensor-ai-lab#353-only behavior. Decode tok/s (RTX 5090, same-binary A/B vs gittensor-ai-lab#353-only baseline): 128: 437.57 -> 488.08 +11.54% 4096: 415.79 -> 461.19 +10.92% 16384: 410.65 -> 455.78 +10.99% 32768: 394.31 -> 433.94 +10.05% Correctness (natural text, 1500 teacher-forced positions, Q4_K vs Q8_0 baseline): top-1 96.3% (gate >= 0.90), KL 0.021 (gate <= 0.20), PPL 2.820 vs 2.788.
…attn_gate) (+11.5% @128) Qwen3.6-35B-A3B UD ships the Gated-DeltaNet input projections — attn_qkv (wqkv) and attn_gate (the z gate) — as Q8_0 on all 30 GDN layers. These are the single largest per-token weight read in decode, larger than the full-attention q/o of gittensor-ai-lab#353 or the ssm_out of gittensor-ai-lab#355. Requantizing both to Q4_K once at model load reads ~47% fewer bytes on those matvecs. No decode-kernel change: after the stored type flips Q8_0→Q4_K, the GDN qkv+z projection already branches on tensor type and routes through the existing Q4_K fused kernel (launch_mmvq_gdn_qkv_z_pack2) automatically. The fit reuses the merged Lloyd-max coordinate-descent Q4_K quantizer (proj_q4k_lloyd.cu) — high enough quality to keep all 30 GDN layers inside the accuracy gate (an affine fit does not). Gated to the Qwen3.6 fingerprint; a strict no-op on every other model. Extends the existing SPARKINFER_ATTN_REQUANT_Q4K mode — set it to "attn_q,attn_output" to restore the gittensor-ai-lab#353-only behavior. Decode tok/s (RTX 5090, same-binary A/B vs gittensor-ai-lab#353-only baseline): 128: 437.57 -> 488.08 +11.54% 4096: 415.79 -> 461.19 +10.92% 16384: 410.65 -> 455.78 +10.99% 32768: 394.31 -> 433.94 +10.05% Correctness (natural text, 1500 teacher-forced positions, Q4_K vs Q8_0 baseline): top-1 96.3% (gate >= 0.90), KL 0.021 (gate <= 0.20), PPL 2.820 vs 2.788.
✅ sparkinfer auto-eval —
|
| metric | value |
|---|---|
| label | eval:M |
| Qwen3.5 score | eval-qwen35:none (pass) |
| Qwen3.6 score | eval-qwen36:M (pass) |
| Qwen3.5 vs same-box main | 294.23 tok/s → +0.1% (+0.3) |
| Qwen3.5 scored decode (128 ctx · 128-context) | 294.55 tok/s |
| Qwen3.5 correctness | top-1 92.7% · KL 0.0395 |
| Qwen3.5 128-token no-regression gate | 294.55 tok/s vs main 294.23 tok/s · pass |
| Qwen3.5 512-context no-regression gate | 291.74 tok/s vs main 291.66 tok/s · pass |
| Qwen3.5 4k-context no-regression gate | 282.8 tok/s vs main 282.68 tok/s · pass |
| Qwen3.6 vs same-box main | 411.16 tok/s → +3.0% (+12.2) |
| Qwen3.6 scored decode (512 ctx · 512-context) | 423.38 tok/s |
| Qwen3.6 correctness | top-1 92.0% · KL 0.068 |
| Qwen3.6 128-token no-regression gate | 429.13 tok/s vs main 417.52 tok/s · pass |
| Qwen3.6 512-context no-regression gate | 423.38 tok/s vs main 411.16 tok/s · pass |
| Qwen3.6 4k-context no-regression gate | 404.99 tok/s vs main 394.87 tok/s · pass |
| Qwen3.6 16k-context no-regression gate | 402.54 tok/s vs main 392.58 tok/s · pass |
| Qwen3.6 32k-context no-regression gate | 385.69 tok/s vs main 376.21 tok/s · pass |
| Qwen3.5 optimize | eval:none · 294.55 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy | top-1 92.0% · KL 0.068 · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 128 | 429.09 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 512 | 423.44 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 4k | 404.85 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 16k | 402.43 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 32k | 385.45 tok/s · pass |
| Qwen3.6 optimize | eval:M · 423.38 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy | top-1 92.7% · KL 0.0395 · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 | 294.43 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 512 | 291.68 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k | 282.7 tok/s · pass |
Verified speedup over same-box origin/main — 423.38 tok/s (main was 411.16 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.
…attn_gate) (+11.5% @128) (#267) * perf(qwen36): Q8_0→Q4_K requant of GDN input projections (attn_qkv + attn_gate) (+11.5% @128) Qwen3.6-35B-A3B UD ships the Gated-DeltaNet input projections — attn_qkv (wqkv) and attn_gate (the z gate) — as Q8_0 on all 30 GDN layers. These are the single largest per-token weight read in decode, larger than the full-attention q/o of #353 or the ssm_out of #355. Requantizing both to Q4_K once at model load reads ~47% fewer bytes on those matvecs. No decode-kernel change: after the stored type flips Q8_0→Q4_K, the GDN qkv+z projection already branches on tensor type and routes through the existing Q4_K fused kernel (launch_mmvq_gdn_qkv_z_pack2) automatically. The fit reuses the merged Lloyd-max coordinate-descent Q4_K quantizer (proj_q4k_lloyd.cu) — high enough quality to keep all 30 GDN layers inside the accuracy gate (an affine fit does not). Gated to the Qwen3.6 fingerprint; a strict no-op on every other model. Extends the existing SPARKINFER_ATTN_REQUANT_Q4K mode — set it to "attn_q,attn_output" to restore the #353-only behavior. Decode tok/s (RTX 5090, same-binary A/B vs #353-only baseline): 128: 437.57 -> 488.08 +11.54% 4096: 415.79 -> 461.19 +10.92% 16384: 410.65 -> 455.78 +10.99% 32768: 394.31 -> 433.94 +10.05% Correctness (natural text, 1500 teacher-forced positions, Q4_K vs Q8_0 baseline): top-1 96.3% (gate >= 0.90), KL 0.021 (gate <= 0.20), PPL 2.820 vs 2.788. * re-eval: verify Qwen3.6 accuracy passes after rebase to ce33e7f Re-benchmarked on RTX 5090 (75.152.195.46). vs #353-only main on same box: 128: 438.9 -> 489.3 tok/s (+11.5%) 512: 432.2 -> 481.7 tok/s (+11.4%) 4k: 415.7 -> 461.2 tok/s (+10.9%) 16k: 411.0 -> 455.6 tok/s (+10.9%) 32k: 394.2 -> 433.6 tok/s (+10.0%) Correctness (fuzzed prompt, teacher-forced vs llama.cpp): top-1 93.6%, KL 0.026 (gate >= 90%, <= 0.20). Full evaluate.sh + bidir harness pass locally. Prior eval:REJECT (top1=0, kl=99) was accuracy.sh infra failure, not model regression.
|
The round's |
e05a38a to
e7b37b6
Compare
|
Rebased onto |
|
log tail |
|
The round's |
1 similar comment
|
The round's |
e7b37b6 to
0221481
Compare
|
The round's |
|
The round's |
2 similar comments
|
The round's |
|
The round's |
|
The round's |
2 similar comments
|
The round's |
|
The round's |
❌ sparkinfer auto-eval —
|
| 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 | 295.36 tok/s → +0.5% (+1.3) |
| Qwen3.5 scored decode (128 ctx · 128-context) | 296.69 tok/s |
| Qwen3.5 scored prefill | not measured (0 pp tok/s on all contexts) |
| Qwen3.5 correctness | top-1 95.8% · KL 0.0293 |
| Qwen3.5 128-token no-regression gate | 296.69 tok/s vs main 295.36 tok/s · pass |
| Qwen3.5 4k-context no-regression gate | 283.17 tok/s vs main 285.02 tok/s · pass |
| Qwen3.5 64k-context no-regression gate | 283.49 tok/s vs main 283.28 tok/s · pass |
| Qwen3.5 4k prefill no-regression gate | 286.46 pp tok/s vs main 288.21 pp tok/s · pass |
| Qwen3.5 32k prefill no-regression gate | 282.96 pp tok/s vs main 283.02 pp tok/s · pass |
| Qwen3.5 64k prefill no-regression gate | 0.0 pp tok/s vs main 282.64 pp tok/s · fail |
| Qwen3.5 128k prefill no-regression gate | 0.0 pp tok/s · pass |
| Qwen3.6 vs same-box main | 465.41 tok/s → +3.2% (+14.8) |
| Qwen3.6 scored decode (128 ctx · 128-context) | 480.18 tok/s |
| Qwen3.6 correctness | top-1 94.9% · KL 0.0497 |
| Qwen3.6 128-token no-regression gate | 480.18 tok/s vs main 465.41 tok/s · pass |
| Qwen3.6 512-context no-regression gate | 473.25 tok/s vs main 459.48 tok/s · pass |
| Qwen3.6 4k-context no-regression gate | 453.82 tok/s vs main 441.04 tok/s · pass |
| Qwen3.6 16k-context no-regression gate | 415.89 tok/s vs main 434.14 tok/s · fail |
| Qwen3.6 32k-context no-regression gate | 425.52 tok/s vs main 413.59 tok/s · pass |
| Qwen3.5 optimize | eval:REJECT · 296.69 tok/s · fail |
| Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy | top-1 0.0% · KL 99.0 · FAIL |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 128 | 203.36 tok/s · fail |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 512 | 193.32 tok/s · fail |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 4k | 192.59 tok/s · fail |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 16k | 188.53 tok/s · fail |
| Qwen3.6 optimize | eval:REJECT · 480.18 tok/s · fail |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy | top-1 95.8% · KL 0.0293 · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 | 304.75 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k | 292.5 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 32k | 113.01 tok/s · fail |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 64k | 113.04 tok/s · fail |
| regressions | regression-qwen36-128, regression-qwen36-512, regression-qwen36-4k, regression-qwen36-16k, regression-qwen36-32k, regression-qwen35-32k, regression-qwen35-64k, regression-qwen35-32k, regression-qwen35-64k |
Rejected — no-regression guard: Qwythos-9B (Q4_K_M) decode regressed at: 32k, 64k.
RTX 5090 (sm_120) · 128/512/4k/16k/32k guarded · Qwen3.5 prefill at 4k/32k/64k · scored vs same-box main · built from source · correctness vs llama.cpp. Automated — not merged; merge manually after review.
✅ sparkinfer auto-eval —
|
| metric | value |
|---|---|
| label | eval:L |
| Qwen3.5 score | eval-qwen35:none (pass) |
| Qwen3.6 score | eval-qwen36:L (pass) |
| Qwen3.5 vs same-box main | 283.3 tok/s → -0.0% (-0.1) |
| Qwen3.5 scored decode (32768 ctx · 32k-context) | 283.21 tok/s |
| Qwen3.5 scored prefill | not measured (0 pp tok/s on all contexts) |
| Qwen3.5 correctness | top-1 95.6% · KL 0.0215 |
| Qwen3.5 128-token no-regression gate | 294.95 tok/s vs main 295.36 tok/s · pass |
| Qwen3.5 4k-context no-regression gate | 284.85 tok/s vs main 285.02 tok/s · pass |
| Qwen3.5 32k-context no-regression gate | 283.21 tok/s vs main 283.3 tok/s · pass |
| Qwen3.5 64k-context no-regression gate | 283.15 tok/s vs main 283.28 tok/s · pass |
| Qwen3.5 4k prefill no-regression gate | 288.01 pp tok/s vs main 288.21 pp tok/s · pass |
| Qwen3.5 32k prefill no-regression gate | 282.99 pp tok/s vs main 283.02 pp tok/s · pass |
| Qwen3.5 64k prefill no-regression gate | 282.62 pp tok/s vs main 282.64 pp tok/s · pass |
| Qwen3.5 128k prefill no-regression gate | 0.0 pp tok/s · pass |
| Qwen3.6 vs same-box main | 465.41 tok/s → +3.1% (+14.6) |
| Qwen3.6 scored decode (128 ctx · 128-context) | 479.96 tok/s |
| Qwen3.6 correctness | top-1 96.7% · KL 0.0281 |
| Qwen3.6 128-token no-regression gate | 479.96 tok/s vs main 465.41 tok/s · pass |
| Qwen3.6 512-context no-regression gate | 473.03 tok/s vs main 459.48 tok/s · pass |
| Qwen3.6 4k-context no-regression gate | 453.61 tok/s vs main 441.04 tok/s · pass |
| Qwen3.6 16k-context no-regression gate | 447.07 tok/s vs main 434.14 tok/s · pass |
| Qwen3.6 32k-context no-regression gate | 425.53 tok/s vs main 413.59 tok/s · pass |
| Qwen3.5 optimize | eval:none · 283.21 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy | top-1 96.7% · KL 0.0281 · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 128 | 480.25 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 512 | 473.3 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 4k | 453.9 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 16k | 447.17 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B 32k | 425.5 tok/s · pass |
| Qwen3.6 optimize | eval:L · 479.96 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy | top-1 95.6% · KL 0.0215 · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 128 | 295.01 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 4k | 284.92 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 32k | 283.19 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) 64k | 283.16 tok/s · pass |
Verified speedup over same-box origin/main — 479.96 tok/s (main was 465.41 tok/s).
RTX 5090 (sm_120) · 128/512/4k/16k/32k guarded · Qwen3.5 prefill at 4k/32k/64k · scored vs same-box main · built from source · correctness vs llama.cpp. Automated — not merged; merge manually after review.
d12766f to
60bd5c4
Compare
✅ sparkinfer auto-eval —
|
| metric | value |
|---|---|
| label | eval:M |
| Qwen3.5 score | eval-qwen35:none (pass) |
| Qwen3.6 score | eval-qwen36:M (pass) |
| Qwen3.5 vs same-box main | 283.3 tok/s → -0.0% (-0.1) |
| Qwen3.5 scored decode (32768 ctx · 32k-context) | 283.21 tok/s |
| Qwen3.5 scored prefill (65536 ctx · 64k-context) | 282.62 pp tok/s · eval-prefill:none |
| Qwen3.5 correctness | top-1 95.6% · KL 0.0215 |
| Qwen3.5 128-token no-regression gate | 294.95 tok/s vs main 295.36 tok/s · pass |
| Qwen3.5 4k-context no-regression gate | 284.85 tok/s vs main 285.02 tok/s · pass |
| Qwen3.5 32k-context no-regression gate | 283.21 tok/s vs main 283.3 tok/s · pass |
| Qwen3.5 64k-context no-regression gate | 283.15 tok/s vs main 283.28 tok/s · pass |
| Qwen3.5 4k prefill no-regression gate | 288.01 pp tok/s vs main 288.21 pp tok/s · pass |
| Qwen3.5 32k prefill no-regression gate | 282.99 pp tok/s vs main 283.02 pp tok/s · pass |
| Qwen3.5 64k prefill no-regression gate | 282.62 pp tok/s vs main 282.64 pp tok/s · pass |
| Qwen3.5 128k prefill no-regression gate | 0.0 pp tok/s · pass |
| Qwen3.6 vs same-box main | 465.41 tok/s → +3.1% (+14.6) |
| Qwen3.6 scored decode (128 ctx · 128-context) | 479.96 tok/s |
| Qwen3.6 correctness | top-1 96.7% · KL 0.0281 |
| Qwen3.6 128-token no-regression gate | 479.96 tok/s vs main 465.41 tok/s · pass |
| Qwen3.6 512-context no-regression gate | 473.03 tok/s vs main 459.48 tok/s · pass |
| Qwen3.6 4k-context no-regression gate | 453.61 tok/s vs main 441.04 tok/s · pass |
| Qwen3.6 16k-context no-regression gate | 447.07 tok/s vs main 434.14 tok/s · pass |
| Qwen3.6 32k-context no-regression gate | 425.53 tok/s vs main 413.59 tok/s · pass |
| Qwen3.5 optimize | eval:none · 283.21 tok/s · pass |
| Qwen3.5 optimize — Qwen3.6-35B-A3B guard accuracy | top-1 96.7% · KL 0.0281 · pass |
| Qwen3.5 optimize — Qwythos-9B (Q4_K_M) 128 | 294.95 tok/s · pass |
| Qwen3.5 optimize — Qwythos-9B (Q4_K_M) 4k | 284.85 tok/s · pass |
| Qwen3.5 optimize — Qwythos-9B (Q4_K_M) 32k | 283.21 tok/s · pass |
| Qwen3.5 optimize — Qwythos-9B (Q4_K_M) 64k | 283.15 tok/s · pass |
| Qwen3.6 optimize | eval:M · 479.96 tok/s · pass |
| Qwen3.6 optimize — Qwythos-9B (Q4_K_M) guard accuracy | top-1 95.6% · KL 0.0215 · pass |
| Qwen3.6 optimize — Qwen3.6-35B-A3B 128 | 479.96 tok/s · pass |
| Qwen3.6 optimize — Qwen3.6-35B-A3B 512 | 473.03 tok/s · pass |
| Qwen3.6 optimize — Qwen3.6-35B-A3B 4k | 453.61 tok/s · pass |
| Qwen3.6 optimize — Qwen3.6-35B-A3B 16k | 447.07 tok/s · pass |
| Qwen3.6 optimize — Qwen3.6-35B-A3B 32k | 425.53 tok/s · pass |
Verified speedup over same-box origin/main — 479.96 tok/s (main was 465.41 tok/s).
RTX 5090 (sm_120) · 128/512/4k/16k/32k guarded · Qwen3.5 prefill at 4k/32k/64k · scored vs same-box main · built from source · correctness vs llama.cpp. Automated — not merged; merge manually after review.
Proof of speedup
sm_120)Decode tok/s (end-to-end,
qwen3_gguf_bench,Qwen3.6-35B-A3B-UD-Q4_K_M.gguf, 128 decode tokens), this PR default vsSPARKINFER_ATTN_REQUANT_Q4K=attn_q,attn_output,qkv,attn_gate(currentmain), same binary:main: q/o + GDN-input requant,ssm_outnative Q8_0)ssm_outlives on all 30 GDN layers, whose O(1)-state matvecs are a fixed per-token cost independent of KV depth, so the requant helps across contexts. A byte-reducing requant cannot slow decode. Qwythos-9B is unchanged (no-op).Relation to open PRs
ssm_outis the GDN output projection — complementary to the merged #267 (GDN input projectionsattn_qkv/attn_gate) and to #353 (full-attention q/o); no PR requantizes it. This PR touches onlyruntime/src/models/qwen35.cpp(+11/−1): it adds anssm_outtoken to the existing requant mode, a per-layer floor env, andssm_outto the Qwen3.6 default, reusing the mergedproj_q4k_lloyd.cufitter with no kernel changes. Added-line containment against every requant PR on that file is 0.0% (block ≥ 85%, warn ≥ 75%); per-function max 0.0% (warn ≥ 92%).