Skip to content

xpu: EXL3 trellis weights on Intel GPUs (Xe2): kernels, Level Zero host, checks #1328

Open

Description

@teamlewis-bot

Proposal

TensorFold reads EXL3 checkpoints on CUDA (#1113 and #1295 extend that path). I propose the same for the XPU backend. The kernels decode the EXL3 trellis (2 to 8 bits, mul1 codebook) on the fly in OpenCL C. A Level Zero host builds the device descriptor tables and encodes projections, routed experts and a fused routed MLP. The model code stays backend-neutral: one shared tensor-name table for qwen3_5 EXL3 under zig/src/families/qwen3_5/.

The branch is ready on v1.1.5 and merges clean onto main. Tested on one Intel Arc Pro B70 (Xe2, 32 GB) with Sage-A1.5 EXL3 4.0 bpw. I open the pull request after we agree on the gate.

Proposed gate

  1. A NumPy float64 reference decoder of the trellis checks every kernel. Limits: 1e-5 for fp32 sums, 1e-3 for the fp16 fused MLP.
  2. zig build xpu-tests -Dxpu unit tests pass, and skip cleanly without an XPU device.
  3. Greedy tokens through a model runner match the reference at 1k and 30k context.
  4. No change to Metal or CUDA code.

Files

zig/kernels/xpu/exl3.cl, zig/src/xpu/exl3.zig, zig/src/xpu/exl3_variants.zig, zig/tests/xpu/exl3.zig, tools/xpu/exl3_*.py, zig/build/xpu.zig, and one new file zig/src/families/qwen3_5/exl3_names.zig. 19 files in 6 commits.

.github/atc.json has no area for zig/kernels/xpu/ or zig/src/xpu/. Do you want an xpu area?

Overlap

No file is shared with the open pull requests #1125, #1137, #1113, #1295, #1281 or #728. git merge-tree against main is clean.

Measured

Sage-A1.5 EXL3 4.0 bpw on one B70: 116 tok/s decode at a 1k prompt, 178 tok/s aggregate at 8 streams, greedy eval 38/41. On Xe2 the trellis decode is ALU-bound, at about 650 G weights/s. The MLX 4-bit path with MTP (separate issue) reaches 236 tok/s on the same card. So EXL3 on Xe2 is the low-VRAM option, not the fast one.

Credits

exl3xpu (0xSero, MIT) for the dp4a byte sum and the fp16 fma codebook method. exllamav3 (turboderp, MIT) for the format.

Activity

  1. ashhart commented on Nov 1, 2026

    @ashhart
    Owner

    Thanks @teamlewis-bot, and thanks for opening this before the PR. We'd take it. Your gate is the base: the float64
    NumPy reference with its limits, xpu-tests passing and skipping cleanly without a device, greedy tokens at 1k and
    30k, and no Metal or CUDA change. Three additions:

    1. A planted control for each kernel in the float64 check, so a wrong decode (a swapped codebook entry or a
      dropped bit, for example) makes it fail. XPU has no kernel contract runner yet, so the check binary does that job.
    2. Greedy tokens against CUDA's EXL3 path on the same checkpoint and prompts, beside the NumPy reference. A
      second engine on the same weights catches what one reference can't.
    3. Refusals by name for anything the kernels don't decode (bit widths outside 2-8, codebooks other than mul1),
      before any weight is loaded.

    The speed table should go in the PR as you wrote it: EXL3 on Xe2 is the low-VRAM option, and the MLX 4-bit path is
    the fast one. We'll add an xpu area to .github/atc.json, so claims under zig/src/xpu/ and zig/kernels/xpu/
    have their own place. Then please rebase onto main, claim it there and open the PR. We have no XPU machine, so your
    B70 receipts are the device cells.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions