qwen36 MoE prefill: fix MOE_GPU device-tilemap correctness (#586) + int8 projection default#601
Draft
James-CUDA wants to merge 2 commits into
Draft
Conversation
Member
⏳ Needs a benchmark to be evaluatedYou ticked Tested on RTX 5090 but neither the decode nor the Qwen3.5 prefill before → after table shows a real claimed improvement. The on-device eval won't run until at least one does. Decode (end-to-end, not an isolated-kernel microbench): bench/scripts/bench.sh --download # baseline (before)
bench/scripts/bench.sh --download # your branch (after)Prefill pp (Qwythos — report your best of 4k/32k/64k from the bench/scripts/bench.sh --download --ctx 4096 # try 4096 / 32768 / 65536 / 131072
bench/scripts/bench.sh --download --ctx 4096 # your branchThen the bot greenlights it on the next poll and evaluates it on an RTX 5090. |
James-CUDA
marked this pull request as draft
July 23, 2026 23:52
…fill (+10.6% pp @512, +18% @4k) The Qwen3.6-256E MoE prefill kept attn/GDN/router-feeding projections on bf16 out of caution (the top-k router can turn per-token int8 error into different expert picks). Measured on Qwen3.6-35B-A3B UD-Q4_K_M this stays inside the prefill fidelity veto (batched-vs-token-loop TOP1 0.875 @512 / 1.0 @4k, bar 0.80; KL ~0.016-0.020, bar 0.05), and the scored accuracy is untouched: qwen3_gguf_score teacher-forces via the bf16 decode forward_token path, which SPARKINFER_PREFILL_I8 never reaches (verified byte-identical PPL/ARGMATCH with the flag on and off). Default use_i8 ON for the MoE path (was !moe); scoped to Qwen3.6 by the n_experts==256 batched-MoE guard (Qwen3-30B is 128E and never enters this path, so its guard is untouched). Adds a bf16 fallback past bf16_minctx (96k) for the GDN int8 drift, mirroring the dense path; all eval contexts (<=32k) stay on the int8 fast path. SPARKINFER_PREFILL_I8=0 restores bf16 (A/B).
James-CUDA
force-pushed
the
perf/qwen36-moe-next-opt
branch
from
July 24, 2026 00:14
7c012c1 to
f0fcbf4
Compare
…d down dequant no-op SPARKINFER_PREFILL_MOE_GPU=1 (device tilemap, skips the per-layer D2H counts sync) is silently broken on main: the down projection's masked Q->int8 dequant has cols=mffn=512, which the vectorized fast-mask kernel declines (cols not a multiple of 1024), and launch_gguf_dequant_rows_i8_mask ignored that false return — so the int8 down weights were never written and the down GEMM ran on stale/garbage data. Batched-vs-token-loop prefill_check collapses to TOP1 ~0.375 / KL ~0.35 @512 (this is the gittensor-ai-lab#586 corruption that forced the path default-off). Fix: launch_gguf_dequant_rows_i8_mask now falls back to a correct masked slow kernel (single-pass, register-resident, same dequant/amax/round as the host deq_rows_i8_kernel; dead experts exit via the counts mask) when the fast path declines. Restores prefill_check to TOP1 0.94 / KL ~0.01 @512 with MOE_GPU=1. Keeps SPARKINFER_PREFILL_MOE_GPU default OFF: the device path is not currently a prefill speedup (it must dequant all experts per group rather than the host path's live-only coalesced runs), so this is a correctness fix for the opt-in flag, not a perf change. The default host-tilemap path is unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent Qwen3.6 MoE-prefill changes:
1.
fix(qwen36): restore MoE device-tilemap (MOE_GPU) correctness.SPARKINFER_PREFILL_MOE_GPU=1(device tilemap, skips the per-layer D2H counts sync) is silently broken onmain: the down projection's maskedQ→int8dequant runs withcols=moe_ffn=512, which the vectorized fast-mask kernel declines (cols not a multiple of 1024), andlaunch_gguf_dequant_rows_i8_maskignored thatfalsereturn — so the int8 down weights were never written and the down GEMM ran on stale data (the #586 corruption). Fix: fall back to a correct masked slow kernel (single-pass, register-resident, same dequant/amax/round as the hostdeq_rows_i8_kernel; dead experts exit via the counts mask). The default host-tilemap path is untouched andMOE_GPUstays default OFF (the device path isn't a prefill speedup — it dequants all experts per group vs the host path's live-only coalesced runs — so this fixes the opt-in flag, not perf).prefill_check … 512 16,MOE_GPU=12.
perf(qwen36): int8 tensor-core projections default-on for the MoE-hybrid prefill. Flips theuse_i8default for the Qwen3.6-256E MoE path (attn/GDN/router-feeding projections), scoped to Qwen3.6 by the existingn_experts==256guard (Qwen3-30B is 128E, untouched). Scored accuracy is provably unchanged (qwen3_gguf_scoreteacher-forces via the bf16 decodeforward_tokenpath, whichSPARKINFER_PREFILL_I8never reaches — verified byte-identical PPL/ARGMATCH on/off); prefill fidelity holds (TOP1 0.875 @512 / 0.94 @4k, bar 0.80).Proof of speedup
sm_120) — int8 flip is within noise vs the current fp8 default; no end-to-end speed claim against current main. The primary contribution is the correctness fix.Prefill pp tok/s — Qwen3.6-35B-A3B-UD-Q4_K_M, RTX 5090 (int8 default vs
SPARKINFER_PREFILL_I8=0fp8, current main):