Repository navigation
Conversation
jschmied
force-pushed
the
lfm3-xpu
branch
3 times, most recently
from
November 1, 2026 11:44
3bcb1f5 to
d9e490c
Compare
This was referenced Nov 1, 2026
Without torch (or with --numpy) the forward runs on numpy: MLX affine and bf16 weights are dequantized exactly; NVFP4 and FP8 stay torch-only and are refused by name. --bf16-points rounds where the Intel GPU engine stores bf16 (embedding rows and RoPE cos/sin too, attention P and logits never). dump writes the float64 hidden state after every layer and check replays an engine's greedy replies teacher-forced. The collect and miss-table CLI is unchanged.
…, gated GQA attention over a ring, sandwich norm, SwiGLU) and their FP64 op tests
…state and KV rings) loading by names.zig, its checkpoint test against ref64.py's float64 dumps
…pt projections on nem_prow_gen12 without DPAS, Iris Xe measurements
…pied on the device LFM3's per-stream state (the conv rows of 13 layers, the KV slots of 11, the position) moves out of the layers into a sequence the model binds; the engine makes, frees and binds sequences and saves and restores prompt states (the conv rows, the full layers' slots up to the mark, the sliding rings whole). The lane host serves LFM3 on one sequence until the engine verifies windows (a later commit).
… and RoPE with the KV store lfm3_mv.cl takes attn.cl's qmv_row verbatim (same compile options, so the same contraction) and runs w1 and w3 of a row with their SwiGLU in one launch, and q, k, v and the gate in one; lfm3_rope_store normalises and rotates the q and k heads and stores k and v in one launch, through the same per-head function lfm3_qk_norm_rope now calls. Both RoPE kernels require sub-groups of 16: their norm sums one partial a sub-group into part[4], which a compiler-chosen width of 8 would overrun and 32 would sum in another order. Every value is rounded as before: the greedy tokens of the three reference prompts are unchanged. Decode on Iris Xe: 15.2 to 17.2 tok/s (LFM3_FUSE selects the fusions for A/B timing).
On Iris Xe a prompt window of 128 rows beats 32 and 64 with the 8 x 4 prompt tiles (1,024-token prefill 84 against 78 tok/s at 64). The greedy tokens of the three reference prompts are unchanged at windows of 128 and of 64.
…ream, verified windows, shared forwards LFM3's engine takes the contract Gemma 4 E4B's takes on the XPU lane host (copy_only): one sequence a stream in up to 8 slots, the lane core's context copies verified in windows of up to 16 rows (verifyRows, keepRows), several streams' windows in one forward (verifyMulti, slotOf), window costs timed at load (1, 2, 4, 8 and 16 rows, the widths between interpolated: 8 s to load instead of 21 s, the same round plans), and prompt states kept anywhere (cache_rules). One line in the adapter (xpu_nem.zig): the host takes a family's copy_match when its table row declares one, before XPU_COPY_MATCH. LFM3's is 8: at the core's 4, copies cost a short chat 6% on Iris Xe; at 8, prose and code decode 65% and 38% faster than with --no-drafts, and the two chats within 2% of it (session noise). A window's rows sum as the decode steps do: its projections and head run on pdense, whose bf16 and fp32 outputs equal qmv4_bf and qmv4_f32 bit for bit (tf-xpu-lfm3_rows-test). Conv and attention run on each stream's own state and position; lfm3_conv_hist also records each row's B * x, so keeping the first c rows of a window restores the conv state those rows leave. Decode ids are kernel arguments, so no host memory the caller may reuse is read later. tf-xpu-lfm3_streams-test checks streams in shared forwards, freed and joined mid-way, and windows rolled back to their kept rows, against each stream alone.
…les, with copy drafts, streams and the prompt cache
xpu_model.zig drops its own invFreq (theta^e in f32, 1-3 ulp from torch in 16 of the full layers' 32 frequencies and 28 of the sliding layers' 64) for Rotary.inverseFrequencies() (f64 pow, then f32: torch's values), and sizes the sliding rings and windows by cfg.ring and LayerType.window, as the CUDA backend does.
…loader (2.7x TTFT at 1k), verified windows keep pdense pgemm (lfm3_mv.cl) tiles 64 x 64 in SLM as vit_gemm does and dequantizes q * scale + bias as it loads a weight tile; each output sums an fma chain a 64-wide k block and adds the blocks in order, so a row's sum does not depend on how the prompt is cut. Model.forwardPrompt routes every projection of a prompt window to it (prefill, prefillSpan, prefillWindows); verified windows, shared rounds and decode keep pdense and qmv, so drafts stay bit-equal to one-row steps. lfm3_pgemm checks it against FP64 in groups of 64 and 32 (the other group size as the planted control) and every row alone = in a window; lfm3_chunks checks the model's windows of 64, 128, 256 and a ragged cut bit for bit (rows, logits, the next step), pdense's order as the control. On Iris Xe through the server, TTFT at 1,024 tokens goes from 12.3-12.4 s to 4.6-4.7 s and at 8,192 from 116.3-116.7 s to 55.4-55.6 s.
…apper, as main's lfm3 config now imports rope_freq
Contributor
Author
|
Rebased onto main Iris Xe (Gen12LP, WSL2), on
Spark (GB10), Marking it ready for review. |
jschmied
marked this pull request as ready for review
November 3, 2026 18:44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1666. Rebased onto main
f23603801: #1692 and #1839 are merged now, so their three commits are gone from here; eleven commits remain. The only remaining stack item isref64.py's numpy path (8bf9d952chere, also carried by #1634): the checkpoint test reads its float64 dumps.Checks on this head (
5943daef0): Spark (GB10): lean 425 on the base, 425 on the head, 0 new;zig build test, the CI set (test-kernel-contract test-golden test-server-security test-pull-security fuzz-cortheon) andgit diff --checkpass. The Iris Xe cells below are frombb38b814b; the rebased head gives the same tokens, float64 gaps and SPIR-V bytes on the Iris Xe (see the comment of 2026-11-03).5943daef0gives the XPU LFM3 module core'srope_freq, which main's lfm3 config now imports.What it adds
names.zig, MLX 4-bit group 64 as stored;ref64.py's float64 dumps.xpu-nativeserves LFM3 through main's lane host (copy_only), with the contract Gemma 4 E4B's engine implements:xpu_nem.zig): the host takes a family'scopy_matchwhen its table row declares one. LFM3 declares 8. At the core's default of 4, copies cost a short chat 6 % on Iris Xe.tf-xpu-lfm3_rows-test, bit for bit);nem_prow_gen12in windows of 128 rows.bb38b814b, a different summation order): prompt windows takepgemm(lfm3_mv.cl), which tiles 64 × 64 in SLM asvit_gemmdoes and dequantizes MLX 4-bit as it loads a weight tile. Each output sums an fma chain per 64-wide k block and adds the blocks in order, so a row's sum doesn't depend on how the prompt is cut. Verified windows, shared rounds and decode keeppdenseandqmv, so drafts stay bit-equal to one-row steps.docs/recipes/xpu-arc.md.Checks (Iris Xe, WSL2)
After the rebase onto
a068484a0: B2's 64 tokens on all three prompts,--parallel1/2/4 equal to solo, cache resume equal to cold,tf-xpu-lfm3_rows-test28/28, the tgllp images byte-equal to the previous head, and bmg's 14 images byte-equal to main's (lfm3,lfm3_mvandnem_prow_gen12are the new ones).Forward against float64: every layer of 3 prompts is at 0.95–1.05× the bf16 gap. The 64 greedy tokens match the CUDA engine (lfm3 CUDA: serve the block FP8 export weight-only and the MLX 4-bit export as stored (stacked on #1508) #1634, MLX 4-bit) on all 3 prompts, and match again after every speed change, at windows of 128 and 64, with each fusion on and off.
Streams: at
--parallel1, 2 and 4, 5 concurrent replies are byte-equal to solo, in two runs each.tf-xpu-lfm3_streams-testchecks streams in shared forwards, streams freed and joined mid-way, and windows rolled back, each against the stream alone.RoPE kernels: these now require sub-groups of 16. Before, the compiled width could change a reply from one process to the next.
Prompt cache: a resumed second turn is byte-equal to the cold one, at 1k and 8k tokens. TTFT is 0.56 s against 13.25 s at 1k, and 0.80 s against 122.6 s at 8k.
Speed, server:
--no-draftspgemm4.6–4.7 spgemm55.4–55.6 sWith copy drafts on, prose decodes 65 % and code 38 % faster than with
--no-drafts. The two chats stay within 2 % of--no-drafts, which is within the session noise. Prompts are timed with--context 16384, decode with--context 4096.pgemmagainst float64:lfm3_pgemmchecks it in groups of 64 and 32 (the other size as the planted control) and every row alone equal to the row in a window.lfm3_chunkschecks windows of 64, 128, 256 and a ragged cut bit for bit (rows, logits, the next step), withpdense's order as the control. Through the model, per layer against float64: the 750-row prompt, 1k and 8k are the same (≤ 1.002×) or closer (mean −0.3 to −0.7 %). The two short prompts are further: +5.3 % (20 rows) and +2.9 % (19 rows), single layers up to +8 %. At op level on real weights both orders have fp32 error 2e-7 and 0 bf16 flips, and three orders rank differently on each short prompt. B2's 64 tokens are unchanged; two chat replies changed at near-ties (one now float64's top-1, one now 0.075 behind it).Spark (GB10), head
bb38b814b: lean reports no new findings;zig build test, the contract, golden, security and fuzz steps pass;git diff --checkis clean.Overlaps
atc overlapreports file conflicts with:xpu/.I'll rebase onto whichever lands first.