Repository navigation
Conversation
The EXL3 reader admitted Qwen3.5-MoE checkpoints only. A dense checkpoint's MLP now loads as three Dense projections (gate, up, down) beside the attention, delta and head projections the reader already loads, and the grouped experts' scratch is sized only for MoE. A dense EXL3 checkpoint drafts as an MLX one does: the DFlash2 head already projects through Dense. MoE EXL3 checkpoints still refuse a drafter. No kernel or arithmetic changes.
…ows, projection groups, PDL (same bits) The Python engine's EXL3 speed series (ashhart#260) on the native Dense module. Weight.sk/wk let a family fix a shape's plan (K splits, warps); the mid-M kernels and linear_wc give the linear's bits at 17-128 rows; matmulGroup runs up to three layers of one K over the same rows in one rotation and one linear launch; the rotations and decode-row linears launch with programmatic dependent launch (TF_EXL3_PDL=0: plain), and counters are zeroed once. tf-cuda-test exl3-plans, exl3-midm and exl3-shapes check and time them.
… (same bits) At decode rows an EXL3 engine's projMany puts its EXL3 members in one cuda.exl3 group launch where groupable (q/k/v, qkv/z, and gate/up through projMany); each member's bits are its own launch's. Dense.reserveGroup sizes a group's scratch at load, before any graph capture, and Dense.reserve sizes split-K scratch by planOf, so a fixed plan's splits are reserved too. The loader fixes (K splits, warps) for the Qwen3.8-27B projection shapes.
…ntract records for the new launches pdl_wait and pdl_trigger compile to nothing below sm_90, and Linear.load turns dependent launches on only for compute capability 9.0 and newer. tf_exl3_group_8_2_4 and _8 are written out, so kernel_symbols finds their names. The registry takes the 2-warp linears, the mid-M kernels, linear_wc, the groups and rot_group (exl3_dense_records.zig), with usage from nvcc 13.0.88 fatbins for sm_80, 86, 89, 120 and 121, and the existing exl3_linear records refreshed.
|
Pushed
Still to do before review: gates 1-4 for the new kernels against |
…ch and the fixed one (exl3-plan-f64)
|
Thanks @BHCC2025. The same-bits parts read as same-bits to us. The mid-M kernels and
Then we'll run |
… MLP, counters zeroed on the call's stream, the replacements in the kernel contract - The 27B's fixed plans and exl3-plan-f64 are gone: every projection takes plan()'s launch (Weight.sk/wk stay). - reserveGroups runs after the layer's MLP is set; a host test checks a dense layer's gate/up group decodes 1-16 rows without allocating. - scratch() no longer zeroes: grownCounters zeroes a grown counter buffer on the call's stream. - contract_exl3.zig registers the mid-M kernels, linear_wc, the groups and rot_group in gates 1-4 as byte-equal replacements of linear_body (rot_in), each with a planted control; host tests for groupable, wcPick and planOf.
|
Thanks for the careful read. All four are addressed in be7c2a7:
Receipt for be7c2a7, on one GB10 (sm_121, nvcc 13.0.88, driver 580.178.04):
Same-bits parts alone against #1465, same checkpoint, prompts and settings as before:
Exactness on this build: window == step and tree == step (48 steps, 64.0 ms a step); alone and serial 20/20 equal, 0 failed; drafted = |
|
Thanks @BHCC2025. On One contract gap is left. |
… in the kernel contract - contract_exl3 registers the mid-M kernels at 3 and 5 bits too, each with a control, and its served shapes reach every form plan() gives a width: both row passes, 4 and 8 warps, split and unsplit K, every linear_wc tile. The groups gain 4 warps unsplit. - exl3.pick is matmul's dispatch alone. A host test sweeps K and N to 16384 through it and groupable, and fails on a form no registration's gate-3 shapes run. A family's fixed plan keeps the linear. - Gate 4's stress weights are all-ones states, one weight at every width (0xaaaa decodes to two at 3 and 5 bits).
|
Thanks. The gap was wider than 3 and 5 bits, so I checked the whole dispatch rather than only those two widths. Fixed in 59123e5. What was missing. I listed every kernel form
The fix.
Receipt for 59123e5, on one GB10 (sm_121, nvcc 13.0.88, driver 580.178.04):
The served path is unchanged: no family fixes a plan, so every 27B projection takes the same launch as on be7c2a7, and that receipt still applies. |
|
@BHCC2025, good news and one ask. Our last run of this on a GB10 (the cut-down head, with the dense EXL3 27B at 4.00 bpw) was GREEN: replies equal to main and no cell slower. It was ready to land. Since then #1475 landed. It zeroes the split-K counters once as they grow, on the calls' stream ( Could you rebase onto current main and keep main's |
Stacked on #1465 (its commit comes first here; this PR's own commits are the last six). It carries #260's EXL3 speed series, from the Python engine, onto the native
Densemodule, as the closing note on #260 suggested once the native engine had an EXL3 reader.What this changes
exl3 cuda, kernels.Weight.sk/wklet a family fix a shape's plan (K splits, warps). No family here fixes one: every launch isplan()'s. A fixed plan keeps the linear.linear_wccover 17-128 rows (prompt rows and wide verify windows) withlinear_body's bits.exl3.pickismatmul's choice among them and the linear.matmulGroupruns up to three layers of one K over the same rows in one rotation and one launch.TF_EXL3_PDL=0: plain). Below sm_90 the waits compile to nothing and the launches are plain.tf-cuda-test exl3-midm,exl3-plansandexl3-shapescheck and time them;exl3-contractruns their kernel-contract registrations alone.qwen3_5 cuda, family.projManyputs an EXL3 engine's decode-row members in one group launch (q/k/v, qkv/z, gate/up).Dense.reserveGroupsizes a group's scratch at load (after the layer's MLP is set), before graph capture.Dense.reservesizes split-K scratch byplanOf. A grown counter buffer is zeroed on the call's stream.The arithmetic
linear_wc, the group launch androt_groupreturn the bytes of the one-weightlinear_body(androt_in) launch atplan()'s plan. Every projection keepsplan()'s launch, so no summation order changes.Receipt
Environment.
mainatb30d5b9d, plus qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465, plus this. One DGX Spark (GB10, sm_121, 128 GB unified memory), Ubuntu 24.04.5, driver 580.178.04, nvcc 13.0.88, Zig 0.17.0,zig build install native -Dsm=121.turboderp/Qwen3.8-27B-exl3@113cf7ab(4.00bpw), drafterz-lab/Qwen3.8-27B-DFlash2@50307d4c.--context 131072; greedy unless stated. Same machine, prompts and settings as qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465's receipt.Same bits (
tf-cuda-test, synthetic weights at every 27B shape, mul1 4-bit; the head 6-bit).exl3-midm: the mid-M kernels andlinear_wcequal the linear byte for byte at 17, 24, 32, 33, 48, 49, 64, 100 and 128 rows, and the mid-M kernels alone at 24, 48, 64 and 128. The group launches (q k v, gate up, qkv z) equal each member's own launch at 1, 7 and 16 rows.PASS exl3.midm,PASS exl3.group.exl3: 20 pass, 0 fail. The 9 skips are the Python engine's fixture checks (no fixture directory given).exl3-tiles: 5 pass, 0 fail.Exactness, served.
tf-qwen35-cuda ... step|window|treeover 48 ids: window logits equal step logits byte for byte, and so do tree logits.bench_concurrent.py --alone --serial --levels 1,4 --temperatures 0,1.0(drafts on): alone 20/20 equal, serial 20/20 equal, 0 failed."draft": false: an 8,020-token chat, 256 reply tokens. Fresh, the cached repeat and"draft": falsegive one reply hash./healthstaysok.Speed, against qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465 alone on the same machine:
bench_openaidrafts on,fibonacci-raw/gpu-chat-no-thinkbench_openai, no drafterbench_concurrent4 streams, code / chat, greedybench_concurrent4 streams, code / chat, temperature 1.0Cold prefill:
bench_cache.py --require-cold --prompt-file,--prompt-cache-gib 0, a fresh server per length. These are qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465's exact token arrays (this repository'szig/src/**/*.zigunder the checkpoint's tokenizer). All four qualified cold.Kernel contract.
registry.zig(exl3_dense_records.zig), and 60 existingexl3_linearrecords are refreshed. All come fromkernel-contract-limitsover fatbins for sm_80/86/89/120/121 with nvcc 13.0.88; the audit has no stale or failing EXL3 record.zig build kernel-contracton the GB10 (sm_121), this branch:SUMMARY 426 kernels: 426 pass gates 1-4, 0 fail, 5 known findings; 318 controls: 318 red as planted, 0 missed; 195 limit problems.qwen35/qwen35_kv8attention,qwen4_hc,qwen4_ple,qwen4_projection,qwen4_gdnandnvfp4_experts_tilerecords, which this PR does not touch. On qwen3_5 cuda: dense EXL3 checkpoints (Qwen3.8-27B), with DFlash2 drafts #1465's branch the run lists the same gate and control findings up to the limits section (322 identical FAIL/KNOWN lines).zig/tests/cuda/contract_exl3.zig): eight replacements oflinear_body, each with a planted control that goes red at gate 3, plusrot_groupagainstrot_in.linear_wcat 4 and 6 bits, at 1 to 128 rows. Their served shapes reach every formplan()gives those widths: both row passes, 4 and 8 warps, split and unsplit K, and everylinear_wctile.exl3-contract:SUMMARY exl3 8 kernels: 8 pass gates 1-4, 0 fail; 6 controls: 6 red as planted, 0 missed.exl3.pickandgroupable. It fails on any form the dispatch reaches that no registration's gate-3 shapes run.Tests run.
zig build teston Linux x86_64: 0 failed.zig fmt --checkis clean.python3 tools/zig/lean_check.py: 428 problems, the same asmain. Zero new.Not run, and why.
Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo.plan()'s summation order.