Skip to content

lm_head: one-pass BF16 batch2 LM head on every target (stacked on #153; GB10 Flash-Next MTP verify) - #159

Merged
AzeezIsh merged 4 commits into
mainfrom
perf/lmhead-bf16-batch2-nvidia
Nov 1, 2026
Merged

AzeezIsh merged 4 commits into
mainfrom
perf/lmhead-bf16-batch2-nvidia

Conversation

@AzeezIsh

@AzeezIsh AzeezIsh commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #153. Until #153 merges, the diff includes it; afterwards, only the last commit (25cd5b55d) remains.

What

#153 gated the one-pass BF16 batch2 LM head to HIP, on the premise that the NVIDIA dense_gemv_bf16_batch2 reorders the sum. That premise was wrong. In kernels/gb10/common/ (and kernels/strix/common/), dense_gemv_bf16 also walks 64 lanes per row (BLOCK_SIZE 256 / N_PER_BLOCK 4; the // 32 comment on that line is stale), with the same warp shuffle + smem pair-sum as its batch2 sibling. A structural diff shows batch2 only adds a second accumulator. So the pair is bit-identical on NVIDIA too, and this commit drops the HIP gate.

Why

Qwen3.8-Flash-Next ships a BF16 [248320, 2560] LM head, and its MTP K=2 verify reads it twice per step on GB10 just as it did on gfx1151. On winbox, #153 measured ~+6% decode, byte-identical.

Checks

  • cargo test -p spark-model --lib: 914/914 pass. clippy and fmt are clean.
  • GB10 reiner job 663 (queued): main vs main + this, Flash-Next with MTP on, 5 prompts, alternating ×2. Pass = byte-identical replies. It also reports decode tok/s. MTP verify is active only below the QSA bound on GB10, so the gain shows at the short / 2k prompts.

🤖 Generated with Claude Code

AzeezIsh and others added 4 commits October 2, 2026 06:21
A BF16 LM head at num_tokens == 2 fell back to two dense_gemv calls,
reading the whole weight once per verify token. Qwen3.8-Flash-Next ships
its [248320, 2560] LM head BF16, so every MTP verify step read 1.27 GB
twice: 2 x 5.87 ms of a 111 ms step on gfx1151 (ATLAS_TRACE_LAUNCH_SYNC,
winbox, 2026-10-02). The FP8 and NVFP4 branches already had batch2 arms.

Route it to dense_gemv_bf16_batch2 on HIP only. The strix-hip kernel is
bit-identical to two dense_gemv_bf16 calls: same 32-lane walk per row and
the same shuffle reduction, with the second token as an independent
accumulator. The gb10/strix (NVIDIA) builds use 64 lanes per row plus a
shared-memory reduction, which reorders the sum, so the handle stays 0
there and the fallback runs unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…A/B attribution

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The HIP-only gate rested on a wrong premise: the gb10/strix (NVIDIA)
dense_gemv_bf16 also walks 64 lanes per row with the same smem pair-sum
as its batch2 sibling (the '// 32' comment in dense_gemv_bf16.cu is
stale), so the pair is bit-identical there too. Flash-Next's BF16 LM
head was read twice per MTP verify step on GB10 as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AzeezIsh

AzeezIsh commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

GB10 result (reiner job 664): PASS. M = main 4405bce35 vs C = main + 25cd5b55d. Flash-Next NVFP4 with MTP on (--speculative), util 0.90 / seq 8192, alternating M1 C1 M2 C2.

Why util 0.90 / seq 8192: with MTP pools Flash-Next does not fit at 0.88 (job 663: KV exhausted / no KV at boot), and MTP verify only runs below the 2,051-token QSA bound anyway.

prompt M tok/s (r1 / r2) C tok/s (r1 / r2) Δ wall M → C
code (37 → 256) 26.5 / 26.6 28.5 / 28.3 +6.8% 9.86 → 9.24 s
prose (31 → 200) 24.4 / 24.3 26.0 / 26.0 +6.8% 8.41 → 7.88 s
json (36 → 200) 27.0 / 27.0 28.9 / 28.9 +7.0% 7.62 → 7.13 s
1k (1,070 → 128) 22.8 / 22.6 24.3 / 24.3 +7.0% 6.41 → 6.04 s
2k (2,088 → 128) 20.5 / 20.5 21.6 / 21.7 +5.6% 7.57 → 7.25 s

Ready for review after #153 merges (this is stacked on it). Its own diff is 25cd5b55d only: drop the HIP-only gate, plus comments.

@AzeezIsh

AzeezIsh commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Status (2026-10-06): GB10 gate passed; waiting only on #153 (stacked).

To unblock: merge #153, rebase so only 25cd5b55d remains, rerun CI, merge. Nothing to re-gate: the GB10 result is on main + this commit, and HIP behaviour is #153's.

Owner: Claude.

@AzeezIsh

AzeezIsh commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Included in GB10 integration train #305 on main 8c2b672: merged unchanged. The sparky gate (job 68b) builds #305's exact head and runs tests, three model controls, agentic-50, BFCL ST-995 and error counts. The results will be posted on #305 and here.

@AzeezIsh
AzeezIsh marked this pull request as ready for review November 1, 2026 15:58
@AzeezIsh
AzeezIsh merged commit b45811d into main Nov 1, 2026
25 of 26 checks passed
@AzeezIsh

AzeezIsh commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Merged after the GB10 gate in train #305 (sparky; this PR's head 25cd5b5 merged unchanged on main 8c2b672, compared against an A0 binary of main at that same base).

Byte-identical controls (control.py: 4 requests, temp 0), train vs main:

  • Flash-Next FP4-Local and Flash-Next NVFP4, r1short profile;
  • Qwen3.8-27B NVFP4 at --speculative --num-drafts 1, so every verify is K=2, the path this PR switches to the batch2 BF16 LM head on NVIDIA.

Benchmarks, FP4-Local, train vs main:

train main
BFCL ST-995 87.54 / 85.94 (run-1793547282491478714) 87.54 / 85.94 (run-1793521232941271578)
agentic-webserver 50 47/50, 10.66 s/turn 47/50 at 170c3ed

Earlier standalone gate (job 59, on this PR's merge ref): control identical, BFCL 87.54 / 85.94, agentic 47/50.

No speed claim: I didn't run a K=2 lm_head microbench on GB10.

AzeezIsh added a commit that referenced this pull request Nov 1, 2026
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant