Skip to content

qwen3_5 cuda: the DFlash2 drafter reads a dense EXL3 head over its two vocabulary spans - #1507

Merged
ashhart merged 3 commits into
ashhart:mainfrom
diwakergupta:exl3-draft-head
Oct 30, 2026
Merged

ashhart merged 3 commits into
ashhart:mainfrom
diwakergupta:exl3-draft-head

Conversation

@diwakergupta

@diwakergupta diwakergupta commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

This is the rest of #1474 that #1465 doesn't have, as @ashhart asked when closing #1474. It is stacked on #1465: review the last two commits.

  • The drafter's EXL3 head over its two vocabulary spans. On qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465, DFlash2 projects all 248,320 columns of a dense EXL3 target's head. It now projects only the two spans it proposes from, {0..98,304} and {248,032..248,320}.
    • exl3.Weight.columns(from, n) (zig/src/cuda/exl3.zig) is a view of whole 128-column blocks of the stored matrix that keeps the parent's strides. It has a host test.
    • The .x3 arm of head() (cuda_draft_frame.zig) takes the first span, and for the tail the last exl3.wide_blocks (64) blocks, from column 240,128, dropping the first 7,904 columns when packing. It shares the affine head's projection and cuMemcpy2DAsync packing.
    • Both views plan their K split as the whole head does (one split, eight warps), so the logits keep qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465's bits. exl3.plan() gives a shape one split from 64 blocks up, and wide_blocks names that threshold. The tail reads about 30 MB more a round than a 384-column tail would, and still skips ~545 MB.
  • exl3 in the family's formats (cuda_native.zig), and the exl3.md and cuda.md lines that said the native engine serves no EXL3 pack.
  • qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465's refusal for MoE EXL3 targets is kept in openDrafter and openMtp.

Receipt

Checklist

@ashhart

ashhart commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Thanks @diwakergupta, this is what we invited when we closed #1474: Weight.columns with its host test, the .x3 arm of head(), exl3 in formats with the doc lines, and #1465's MoE EXL3 refusal kept in openDrafter and openMtp. Merged onto current main, fmt, lean (no new findings), test-exl3-cuda-host with your column-view test and the qwen3_5 draft-frame, formats and admission steps pass.

One change before we run it:

  1. The −3.5% long-prompt greedy cell comes from the tail view's K split. plan() (zig/src/cuda/exl3.zig:63) depends on the column count. The 98,304-column first span plans like the full head (one split, eight warps), but the 384-column tail from cuda_draft_frame.zig:164 plans eight splits and four warps, so its logits, and some proposals, change. For this head, any view of 64 blocks or more plans like the full head. If the tail view takes the last 8,192 columns (from 240,128), its logits keep qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465's bits and every cell's tokens per round should match qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465's. That reads about 30 MB more a round at the 6-bit head, against ~545 MB still skipped, and still fits d.logits. Please add a host test that the tail view plans as the full head does (red with today's 384 columns), and rerun the speed table.

A heads-up: this PR and #1475 add tests at the same place in exl3.zig, so whichever lands second needs a trivial rebase that keeps both.

Then we'll run the dense EXL3 pack with DFlash2 on a GB10 against #1465: token hashes, drafted == plain, concurrent == solo at two and four streams, tokens per round and per-stream speed.

@diwakergupta

Copy link
Copy Markdown
Contributor Author

Thanks @ashhart, that was the cause. Pushed f056dbb5:

  • The tail view now takes the last 64 blocks, from column 240,128. exl3.wide_blocks names plan()'s 64-block threshold, which plan() now uses too. Both span views plan as the whole head does: one split, eight warps.
  • The new host test Draft frames: an EXL3 head's span views plan their K split as the whole head does builds a 248,320 × 5,120 head and checks both views against the whole head's plan. With the 384-column tail it fails with expected 1, found 8.

I reran the speed table on the 3090 Ti, alternating #1465 and this branch twice. It's in the description.

On this machine test-qwen35-draft-frame-host's the real hold adapter … test aborts at a cuLaunchKernel inside gemvSeg, and it does the same on #1465. The other 11 pass, the new one included. The full checklist suite: 444/444 steps, and lean shows no new findings.

@ashhart

ashhart commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Thanks @diwakergupta, that's the fix:

  • The 64-block tail view plans as the whole head does.
  • wide_blocks only names plan()'s existing threshold.
  • The new test goes red on the 384-column tail.

All 144 hashes equal #1465's, which is the result we wanted. Tokens per round also moved between #1465's own runs, so
we'll compare them over repeated runs.

Next, after #1465, we'll run the dense pack with DFlash2 on a GB10 against #1465: token hashes, drafted against plain,
concurrent against alone at two and four streams, tokens per round, and per-stream speed. We've started the CI runs that were waiting on our approval.

@diwakergupta

Copy link
Copy Markdown
Contributor Author

Hi @ashhart, checking in on the GB10 comparison against #1465. CI on f056dbb5: desktop and zig-windows were cancelled, not failed. If you can rerun them on a fresh merge with main, that would clear them. Is anything else needed from me here?

diwakergupta and others added 3 commits October 30, 2026 15:59
…o vocabulary spans

On ashhart#1465 the drafter projects all 248,320 columns of an EXL3 target's head.
It now projects only the spans it proposes from, as column views of the
stored matrix: exl3.Weight.columns keeps the parent's strides and moves to
whole 128-column blocks, with a host test. The x3 arm shares the affine
head's packing path. MoE EXL3 targets still refuse a drafter.

The family's formats list exl3, and exl3.md and cuda.md no longer say the
native engine serves no EXL3 pack.
…t plans as the whole head does

The 384-column tail planned eight K splits where the full head plans one,
so its logits and some proposals changed. The view now starts at column
240,128 (exl3.wide_blocks blocks), keeping ashhart#1465's bits. A host test checks
that both span views plan as the whole head does.
@ashhart
ashhart merged commit 22f3d28 into ashhart:main Oct 30, 2026
9 checks passed
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.

2 participants