Skip to content

perf(qwen3.6): fuse Q-gate + O-proj Q8_1 quantize into one launch#283

Open
galuis116 wants to merge 1 commit into
gittensor-ai-lab:mainfrom
galuis116:perf-qwen36-gate-q8-fuse
Open

perf(qwen3.6): fuse Q-gate + O-proj Q8_1 quantize into one launch#283
galuis116 wants to merge 1 commit into
gittensor-ai-lab:mainfrom
galuis116:perf-qwen36-gate-q8-fuse

Conversation

@galuis116

Copy link
Copy Markdown
Contributor

Summary

One bit-identical launch fusion on the Qwen3.6 gated-Q attention decode path. On the full-attention (gated-Q) layers, the Q-gate attn *= sigmoid(qgate) (mul_sigmoid_kernel, bf16 store) is immediately followed by the O-projection mmvq path re-reading attn to produce its Q8_1 activation (si_quantize_q8_1_blocks) — two back-to-back launches. mul_sigmoid_q8_kernel does both in one launch (one warp per 32-value Q8_1 block), deleting a per-layer graph node. Decode is CUDA-graph / launch-bound, so per-layer launches cost real time.

Bit-identical to main: the fused kernel bf16-rounds x*sigmoid(gate) (__bfloat162float(__float2bfloat16(...))) exactly as mul_sigmoid's bf16 store, then runs the amax/round/scale reduction copied verbatim from si_quantize_q8_1_blocks. It mirrors the existing gated_norm_q8_warp_kernel (same gated-op + quant fusion, already validated bit-identical). SPARKINFER_GATE_Q8=0 reproduces main exactly. No-op for Qwen3-30B (no q_has_gate) and for non-mmvq wo paths — the guard cannot regress them. Touches only the Q-gate→quant pair; does not modify any mmvq/GDN/MoE/router/shared-expert kernel.

Proof of speedup

  • Tested on RTX 5090 (sm_120)

Decode tok/s (end-to-end, qwen3_gguf_bench Qwen3.6-35B-A3B-UD-Q4_K_M 256, ctx=0, bs=1). Interleaved A/B on one binary (SPARKINFER_GATE_Q8 toggled), 3 runs each:

decode tok/s
before (SPARKINFER_GATE_Q8=0, == main) 405.79
after (this PR, default) 407.21

+1.42 tok/s (+0.35%). Small and honestly reported — the fusion applies only to the 10 full-attention layers (the 30 GDN-linear layers have no uncontested fusable pair). SPARKINFER_GATE_Q8=0 reproduces main exactly.

Correctness

  • Bit-identical to main by construction (bf16-rounded product → identical Q8_1 blocks; reduction order copied verbatim from the standalone quantizer).
  • ctest 7/7 passed (incl. both GPU tests).
  • compute-sanitizer --tool memcheck 0 errors.

On Qwen3.6 gated-Q attention layers the Q-gate (attn *= sigmoid(qgate),
mul_sigmoid_kernel) writes bf16 attn, then the O-projection mmvq path
immediately re-reads it to produce the Q8_1 activation
(si_quantize_q8_1_blocks) — two back-to-back launches. mul_sigmoid_q8_kernel
does both in one launch (one warp per 32-value Q8_1 block).

Bit-identical to main: the fused kernel bf16-rounds x*sigmoid(gate) exactly
as mul_sigmoid's bf16 store, then runs the amax/round/scale reduction copied
verbatim from si_quantize_q8_1_blocks (mirrors the existing gated_norm_q8_warp
fusion). SPARKINFER_GATE_Q8=0 reproduces main. No-op for Qwen3-30B (no
q_has_gate) and non-mmvq wo paths.

Decode tok/s (RTX 5090, Qwen3.6-35B-A3B-UD-Q4_K_M, n=256, ctx=0, bs=1),
interleaved A/B on one binary (SPARKINFER_GATE_Q8 toggled), 3 runs each:
  before (GATE_Q8=0, == main): 405.79   after (default): 407.21  (+0.35%)
ctest 7/7; compute-sanitizer memcheck 0 errors.
@skyrocket2026 skyrocket2026 added area:kernels subsystem (emission weight 0.42) area:runtime subsystem (emission weight 0.26) test-on-5090 Maintainer-approved to evaluate on RTX 5090 (greenlight) eval:none sparkinfer auto-eval verdict: none 128-context UI-only: strongest measured context in sparkinfer eval labels Jul 8, 2026
@skyrocket2026

Copy link
Copy Markdown
Member

⚪ sparkinfer auto-eval — f2f973a

metric value
label eval:none
scored decode (128 ctx · 128-context · Qwen3.6) 415.04 tok/s
vs same-box main 412.53 tok/s → +0.6% (+2.5)
correctness (Qwen3.6 vs llama.cpp) top-1 99.0% · KL 0.0177
Qwen3.6 128-token no-regression gate 415.04 tok/s vs main 412.53 tok/s · pass
Qwen3.6 512-context no-regression gate 407.28 tok/s vs main 405.25 tok/s · pass
Qwen3.6 4k-context no-regression gate 390.79 tok/s vs main 388.62 tok/s · pass
Qwen3.6 16k-context no-regression gate 357.36 tok/s vs main 355.78 tok/s · pass
Qwen3.6 32k-context no-regression gate 320.69 tok/s vs main 319.97 tok/s · pass
Qwen3-30B-A3B guard — accuracy top-1 95.4% · KL 0.0179 · pass
Qwen3-30B-A3B guard — 128-token 485.1 tok/s · pass
Qwen3-30B-A3B guard — 512-context 463.87 tok/s · pass
Qwen3-30B-A3B guard — 4k-context 385.12 tok/s · pass
Qwen3-30B-A3B guard — 16k-context 321.9 tok/s · pass
Qwen3-30B-A3B guard — 32k-context 256.11 tok/s · pass

Within the significance gate — no verified speedup over same-box main.

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 added a commit that referenced this pull request Jul 9, 2026
ai-hpc pushed a commit that referenced this pull request Jul 9, 2026
* feat(polaris): verifiable eval receipts with Ed25519 signatures

Adds cryptographic attestation to the eval pipeline so third parties
can verify results without re-running GPU jobs.

New files under eval/polaris/:
- receipt.py: schema, canonicalization, Ed25519 sign/verify,
  AttestationBuilder, ReceiptValidator, compute_build_hash
- judge.py: assembles unsigned attestation on eval box (no key needed)
- verify.py: standalone CLI verifier — no GPU required
- test_receipt.py: 29 unit tests (building, signing, verification,
  tamper detection, edge cases)
- sparkinfer_eval.pub: Ed25519 public key trust anchor

Modified:
- pr_eval_bot.py: --polaris flag, key loading, attestation signing,
  receipt upload to sparkinfer-log, dashboard fields
- vast_eval.py: --polaris flag, SSH command pins eval/polaris/ from
  origin/main, runs judge.py, passes POLARIS_ATTESTATION through

Trust model: judge assembles on eval box (pinned to origin/main),
bot signs on trusted host. Private key never touches the eval box.

* feat(polaris): upgrade to Intel TDX hardware attestation via Polaris API

Replace Ed25519 software signatures with Polaris TDX hardware attestation
for scoring integrity. The scoring step (correctness gate, guard gate, label
computation) now runs inside an Intel TDX enclave with DCAP-quoted receipts.

New files:
- eval/polaris/scoring.py: self-contained scoring script (stdlib only) that
  runs inside the TDX enclave, replicating evaluate_dual.sh lines 148-212
- eval/polaris/client.py: PolarisClient for the /v1/attest API

Modified files:
- eval/polaris/receipt.py: add build_polaris_receipt() and
  ReceiptValidator.verify_tdx() for TDX receipt assembly and verification.
  verify() auto-detects receipt type (tdx-quote vs Ed25519).
- eval/polaris/verify.py: auto-detect TDX receipts, show Intel DCAP
  verification status, distinguish TDX vs Ed25519 in output.
- eval/pr_eval_bot.py: when POLARIS_API_KEY is set, submit scoring to
  Polaris TDX enclave and build TDX receipts. Ed25519 signing is preserved
  as fallback when only SPARKINFER_POLARIS_PRIVATE_KEY is set.

The Ed25519 path and all 29 existing tests continue to pass unchanged.

* docs: add Polaris verifiable eval receipts documentation

* dashboard: PR #272 -> eval:none (413.26 tok/s)

* fix(eval): 16k/32k guard baseline fallback — bash expansion bug + display

Two bugs caused Qwen3.6 16k/32k guard baselines to show wrong values:

1. evaluate_dual.sh: ${SPARKINFER_P_GUARD_16K_BASELINE:-338.55} treats
   the explicit "0" passed by the bot as a valid non-empty string, so the
   :- expansion never fires and the hardcoded default is never used.
   Fix: two-step fallback — try env var, then hardcoded default if still 0.

2. pr_eval_bot.py: the display fallback for ctx_16384_tps used frontier_tps
   (the 128-context baseline, ~412) instead of a proper 16k default (~339).
   ctx_32768_tps had no fallback at all. Fix: _GUARD_BASE_FALLBACK dict with
   per-context defaults matching evaluate_dual.sh.

* dashboard: PR #282 -> eval:XL (427.28 tok/s)

* fix(eval): trigger Qwen3.6 baseline sweep when any context is missing

Previously the 5-context sweep only ran when 128 baseline was 0. Since
the bot always passes a non-zero 128 baseline, the sweep never ran and
16k/32k baselines were never measured fresh — falling through to broken
${VAR:-default} expansions and eventually to wrong display values.

Now the sweep triggers when ANY of 128/512/4k/16k/32k is 0, so passing
only 128/512/4k (with 16k/32k as 0) still gets a fresh measurement on
the eval box.

* fix(eval): resolve vastai CLI path and update default instance to 44206573

- vast_eval.py: resolve vastai binary via shutil.which / ~/.local/bin/vastai
  so subprocess.run works from bot/cron contexts where PATH may be limited.
- pr_eval_bot.py: update hardcoded VAST_DEFAULT_INSTANCE fallback to 44206573.

* fix(eval): measure Qwen3.6 16k/32k baselines in bot pre-sweep

The same-box Qwen3.6 baseline sweep now covers all 5 contexts (128, 512,
4k, 16k, 32k) instead of just 3. The measured values are passed as
--p-guard-16k/32k-baseline to vast_eval.py, which forwards them to the
eval box via SPARKINFER_P_GUARD_16K/32K_BASELINE env vars. This gives
evaluate_dual.sh real measured baselines to use (instead of falling
through to hardcoded defaults).

* dashboard: PR #282 -> eval:XL (427.05 tok/s)

* fix(eval): point eval log repo and URL to main sparkinfer repo

Previously logs were pushed to gittensor-ai-lab/sparkinfer-log.git which
skyrocket2026 lacks permission to push to. Move to the main sparkinfer
repo and update the log page URL accordingly.

* dashboard: PR #283 -> eval:none (415.04 tok/s)

---------

Co-authored-by: Skyrocket <>
@skyrocket2026

Copy link
Copy Markdown
Member

⚠️ sparkinfer auto-eval errored for f2f973a — re-run manually.

log tail
:0.9647,"guard_128_pass":false,"guard_512_baseline":418.69,"guard_512_ratio":0.9668,"guard_512_pass":false,"guard_4k_baseline":402.32,"guard_4k_ratio":0.9669,"guard_4k_pass":false,"guard_16k_baseline":385.3,"guard_16k_ratio":0.9248,"guard_16k_pass":false,"guard_32k_baseline":363.5,"guard_32k_ratio":0.8794,"guard_32k_pass":false},"guard":{"model":"Qwen3-30B-A3B","model_sha256":"","ctx_128_tps":490.88,"ctx_512_tps":469.98,"ctx_4096_tps":386.78,"ctx_16384_tps":327.08,"ctx_32768_tps":261.81,"top1":0.9658,"kl":0.0184,"speed_ok":true,"accuracy_ok":true}},"verdict":{"model":"Qwen3.6-35B-A3B","label":"REJECT","pass":false,"tps":389.0,"delta_tps":-13.32,"pct_over_frontier":-3.3,"score_context":4096,"best_context_label":"4k-context","context_gains_pct":{"128-context":-3.53,"512-context":-3.32,"4k-context":-3.31,"16k-context":-7.52,"32k-context":-12.06},"regression_labels":["regression-128","regression-512","regression-4k","regression-16k","regression-32k"],"guard_regression_labels":[],"reason":"128-token decode no-regression gate: 410.78 tok/s < 98% of main 425.79 tok/s; 512-context decode no-regression gate: 404.79 tok/s < 98% of main 418.69 tok/s; 4k-context decode no-regression gate: 389.00 tok/s < 98% of main 402.32 tok/s; 16k-context decode no-regression gate: 356.34 tok/s < 98% of main 385.30 tok/s; 32k-context decode no-regression gate: 319.68 tok/s < 98% of main 363.50 tok/s"},"timestamp_utc":"2026-07-09T18:36:26Z"}
>> bare-metal box left running (ssh root@91.224.44.227:50200)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

128-context UI-only: strongest measured context in sparkinfer eval area:kernels subsystem (emission weight 0.42) area:runtime subsystem (emission weight 0.26) eval:none sparkinfer auto-eval verdict: none 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