Repository navigation
cuda: EXL3 projections behind qlinear (#593) - #740
Conversation
kernels/cuda/exl3.cu decodes EXL3 trellis tiles as checkpoints store them ([K/16, N/16, 16 bits] int16), with core/exl3_format.zig's codebooks (3inst, mcg, mul1) and whole widths 1-8. Rows are rotated by suh and the 128-point Hadamard transform, scaled by 2^-4 and rounded once to fp16; each tile decodes once straight into the B fragments of two mma.m16n8k16 (the tile's value order is theirs), fp32 sums, then the output transform, svh and 2^-3 (docs/recipes/exl3.md, Prompts). Decode rows go 1-16 a launch, read as A fragments from rows written in fragment order; matrices with fewer than 96 column blocks split their input blocks into a shape-fixed number of parts that the last part sums in order. Prompt rows go 64 a block from shared memory. Every row is independent, so a row's bits are the same at every row count on its path. qlinear takes it as Weight.exl3, refused with FormatNotLoaded when the module is absent. tf-cuda-test exl3 [DIR]: every codebook state and random tiles at every width bit-equal to exl3_format; FP64 references from exl3_format.dequantize with a bound for the kernels' rounding order, worst column and aggregate error; decode windows of 1-16 rows and prompts of 2-300 rows byte-equal to each row alone; DIR adds real matrices of a checkpoint and times them.
…3 experts Checked ash hart PRs open+closed (read-only). Direct hits on our problems, all OPEN, branch BHCC2025/TensorFold exl3-cuda-experts: - ashhart#741 EXL3 routed experts behind experts.Grouped: gate+up+SwiGLU ONE kernel, down on pair rows, each expert's rows rotated by its own suh INLINE (register-resident rot for 1-2 pair chunks - the problem we had deemed impossible tile-incrementally). Rides on grouped.zig's Plan (in our tree). 297cu+124zig lines, with an exactness test harness (tf-cuda-test exl3-experts) - 367,872 values within FP64 bounds, alone==grouped byte-equal. - ashhart#740 EXL3 projections behind qlinear (the stacked base). - ashhart#718 branch-free trellis window extraction (perf). - ashhart#729 share decoded weights across prefill rows (perf). - ashhart#728 MiMo-V2.6 CUDA EXL3 two-GPU (another full CUDA EXL3 family). - ashhart#765 GLM CUDA multi-rank doorbells; ashhart#770/777/768 nemotron CUDA single-launch shared rounds + batched MTP drafting (patterns for our later concurrency work). PLAN: vendored exl3_experts.cu/.zig + exl3.cuh into our tree (this commit, unwired). Next: build wiring (fatbin + mod), port moeBlock to Grouped.decode with the router->plan plumbing from grouped.zig, run their exl3-experts test on our checkpoint, parity, bench. Our own grouped kernels (stage-1 parity) stay as fallback.
|
A receipt from a second GB10, on a different checkpoint: Setup. GB10 sm_121, driver 580.178.04, kernel 7.0.0-1019-nvidia, Zig 0.17.0, CUDA 13.0. Tested #741 at
With DIR: the first two real matrices pass with
The test then stops with Both real EXL3 matrices reached before the failure passed; the remaining selected matrices were not tested. Would it make sense to report known non-EXL3 projections as skips, while still failing for missing expected tensors and requiring at least one real EXL3 matrix to be checked? |
…ght, and fails when none is EXL3
|
Thanks for the second GB10 and the |
|
Confirmed on the same
|
|
Thank you, @BHCC2025, for the EXL3 qlinear projections and checkpoint tests. We merged #740 for 1.1.0 in cd4f084, retaining your commits. This merge leaves the tree unchanged because the shared projection work and plain The common implementation comes through #837, with #806's tiles available explicitly and EXL3 supported by qlinear's FP32 decode and prompt outputs. Posted by Ash's AI Assistant Synthena |
EXL3 projections on the native CUDA engine, behind
qlinear: the EXL3 item of #593's step 4, claimed in #593. An operator-only change in the shape of #681: no model uses it yet. Routed experts follow in a stacked PR.What it adds
kernels/cuda/exl3.cu,exl3.cuh: EXL3 trellis tiles as checkpoints store them ([K/16, N/16, 16 * bits]int16), every codebookcore/exl3_format.zigdefines (3inst, mcg, mul1), whole widths 1 to 8. Hand-written, on the structure of the EXLR8 CUDA kernels already in the tree.cuda/exl3_linear.zig: the weight view (trellis,suh,svh, width, codebook) and its kernels;cuda/qlinear.zigtakes it asWeight.exl3, refused withFormatNotLoadedwhen the module is not loaded, like the other formats.tf-cuda-test exl3 [DIR].Arithmetic
One arithmetic for decode and prompt rows, the one
docs/recipes/exl3.md(Prompts) describes for the Python engine's EXL3 path:x * suh, then the 128-point Hadamard transform in a fixed order of fp32 adds, times 2^-4 (exact), rounded once to fp16. fp16 keeps three more mantissa bits than the bf16 activations it comes from; the 2^-4 keeps the transform's growth well inside fp16's range.mma.m16n8k16(a tile's value order is theirs), with fp32 sums. mul1 values come from one fused fp16 multiply-add of an exact sum, the single rounding the format specifies.svhand 2^-3, one rounding to bf16.Decode rows go 1 to 16 a launch, their A fragments read from rows the rotation writes in fragment order. A matrix with fewer than 96 column blocks splits its input blocks into a number of parts fixed by its shape, and the last part to finish adds the parts in part order. Prompt rows go 64 a block through shared memory. Rows never mix, so each row's bits are the same at every row count on its path.
Tests (GB10)
tf-cuda-test exl3 /path/to/GLM-5.3-Flash-exl3-4.05bpw: 12 pass, 0 fail, 0 skip.exl3_format.codebookValuebit for bit.exl3_format.unpack'sW_qbit for bit.exl3_format.dequantize, each value within a bound derived from the kernels' rounding order (fp16 inputs, fp32 sums, bf16 out), worst column and aggregate error reported: 11 synthetic matrices (widths 1-8, all three codebooks, split and unsplit), 98,304 values, worst 0.14 of the bound, worst column 2.8e-3 of the largest output.qlinear.DIR, seven real matrices of turboderp's GLM-5.3-Flash EXL3 4.05bpw (mul1; K=4, 5 and 6): 21,504 FP64 values, worst 0.031 of the bound.Also
zig build test test-cuda-hostpasses,zig fmt --checkis clean, andtools/zig/lean_check.pyreports 539 problems, the same 539 as0efbd447.Timings (GB10, the same matrices)
One row and 16 rows are decode calls; 512 rows is a prompt. ExLlamaV3's own
LinearEXL3on the same tensors and GPU in the right-hand columns, for scale (its decode rounds outputs to fp16).The small matrices sit in L2 when timed back to back, so their single-row times are mostly the two launches (rotation, projection); a captured decode graph removes most of that.
Environment
TensorFold
0efbd447(1.0.8) plus this commit; Zig 0.17.0; NVIDIA GB10 (sm_121), driver 580.178.04, CUDA 13.0.88; Ubuntu 24.04.5, kernel 6.17. Checkpoint: turboderp/GLM-5.3-Flash-exl3, 4.05bpw, revision2a30229e.zig build fatbins -Dsm=86,120,121builds the new kernels for every release target.Not run
bench_concurrent,prefill_coldor drafted-versus-plain receipt; those come with a family (GLM-5.3-Flash and full GLM-5.3 on the native CUDA engine (tracker) #593 step 3).Matrixtakes whole widths); no checkpoint we have uses them.