Repository navigation
Intel GPU (Level Zero) engine and Nemotron 3.5 Lightning on Intel Arc (experimental, CLI) - #523
Conversation
|
Thank you, @BHCC2025. We'll land this behind
The |
|
Thank you, @ashhart. Pushed
Checked on the Arc Pro B70 against One item is not done: decode through a window of one row. I tried it (
Two ways forward, and I would rather you choose than guess. Either (a) I give the window path width-1 specialisations of the projection and head kernels, so a window of one row is the only decode path and keeps its speed, or (b) you accept the width-1 window as it is, at the lower decode speed. I would do (a), as a separate commit on this PR with the same checks. |
…ory, refused when they do not fit
|
Hello @BHCC2025, Thank you for the opt-in Intel CLI: the OpenCL kernels compiled to SPIR-V at build time, and the Level Zero loader opened at run time. Intel Arc is now accepted as a backend through #802's server, landing on the author's receipts plus our host checks. Your CLI waits on one thing. A prompt chunk above 16 rows takes the prefill GEMMs (xpu_win.zig:16), which sum in a different order from the one-row path, so the same prompt cut at a different place can give different bits. When every prompt uses the one-row sum, or the GEMM matches it bit for bit, Nemotron on Arc can come in, ideally on #802's Level Zero runtime rather than a second one. Thank you again. |
…Intel Arc (experimental)
…n this tree, and the default build mode
… kernel comments NEM_LAYERS, NEM_OLD_ATTN, NEM_OLD_DEC, NEM_SCALAR and NEM_TRACE are gone with the row-kernel path they selected (and its kernels). The ARC_* memory settings become one fixed rule: a program does not start while other processes hold more than 8 GB of device memory. Every comment in the .cl kernels is one line of at most 120 columns.
…econd loader The runtime's own loader and ABI declarations (driver.zig, abi.zig) are gone. Driver resolves ze.Api with ze.resolve, plus the few entries ze.Api leaves out (teardown, fills, the SPIR-V build log); Context keeps its device as a ze.Gpu, and the descriptors, handles and device properties are ze.zig's. ze.zig is its own build module, imported by the Qwen27 runner and the Nemotron runtime alike, so both can share one binary. Kernels stay SPIR-V, built by the driver at load. On the Arc Pro B70 the CLI gives the same tokens as before: 128 greedy tokens from a 5-token prompt (sha 2fb8107dc0ce), a 3,000-token prompt with 512- and 128-row prefill (b0d8c69d8fa3), at the same speed; the op tests pass.
…ndow equals the decode step The 4-bit projections of a window of up to 16 rows (Mamba in/out, q/k/v/o, head, shared and routed experts) run on one row kernel body with the decode kernels' per-output order: a lane takes the 8-input words l, l+16, ..., per word dot and sum of inputs in the decode's order, then acc += sc * dot + bi * sum, one sub-group reduction a row; each weight word is read and unpacked once for all the rows. It is built per row bucket (1, 2, 4, 8, 16 rows). Windows no longer take the split-K matrix-engine GEMMs; prompt chunks above 16 rows keep the prefill GEMMs. tf-xpu-nem_rows-test on the B70: a one-row window against the decode step, max |logit diff| 0; windows of 2 to 16 rows, in place and pending then committed, 0 rows differ. Cost: decode step 5.54 ms, window of 1 / 2 / 4 / 8 / 16 rows 6.47 / 9.82 / 17.06 / 32.73 / 76.66 ms.
…mpt's bits do not depend on how it is cut Prompt chunks above 16 rows ran on matrix-engine GEMMs that summed in another order than the one-row path, so the same prompt cut at another place gave other bits. They now run nem_prow.cl: every output sums exactly as the decode kernels do (a lane takes the 8-input words l, l+16, ..., dot and sum of inputs in the decode's order, one sub-group reduction), with a sub-group holding 8 rows and 8 outputs so each weight word is unpacked once for 8 rows and each input word once for 8 outputs. Routed experts take their slots per expert in the same order; prompt attention runs the decode kernel per row, 16 rows a launch. The prefill GEMMs (xpu_pf.zig, nem_pf.cl, nem_attn_pfs.cl) and their test go. On the Arc Pro B70: after 640 tokens cut in chunks of 16, 17, 100, 128 and 512 rows the Mamba states and KV bytes are byte-identical to the row-by-row state and the next 8 decode logits are identical (tf-xpu-nem_seam-test); the 15-prompt teacher-forced compare gives the plain decode figures exactly with 512- and 128-row prompt windows; tf-xpu-nem_fp64-test within bounds. Prompt speed (tensorfold-xpu run --prefill 512): 851 tok/s at 2,048 tokens, 755 at 32,768, 511 at 131,072, against 3,108 / 2,888 / 2,281 with the GEMMs and about 215 in 16-row windows. Kernels that spill registers now say so.
Windows take the decode step's arithmetic (ashhart#523), so a one-row window and the decode step give the same bits: with the MTP head loaded, a round of one row (no drafts held, or plain_guard) runs the decode step (5.54 ms) instead of a window (6.47 ms), and plain replies keep the decode speed. The head reads the decode step's row through Model.normRow; the measured window costs take the decode step as width 1.
2d27b63 to
cd41379
Compare
|
@ashhart, done. Every prompt now takes the one-row sum, and the CLI runs on #802's Level Zero runtime. The branch is rebased onto #802 (its head,
Checks on the B70:
The cost is prompt speed. The decode's fp32 arithmetic runs on the vector units instead of the matrix engine:
For reference, the same prompts run at about 215 tok/s in 16-row windows and 167 tok/s one token at a time. A 32,768-token prompt gives the same tokens afterwards as it did with the GEMMs. |
…moved #523's last commits run prompt chunks past 16 rows through nem_prow's pdense with the decode step's arithmetic; xpu_lane.zig and xpu_nemotron.zig still said the prefill GEMMs. Comments only.
Spacing, a doubled blank line and one aligned argument list, and zig fmt 0.17's rename of @intFromEnum to @backingInt (the same builtin under the name the rest of the tree uses), 10 calls on 9 lines. The Linux fmt check at ad0df6f named these six files.
|
Thank you, @BHCC2025. Nemotron on Intel Arc over #802's Level Zero table rather than a second loader, with prompt rows that sum as the decode step does so a prompt's bits do not depend on where it is cut, is the shape we hoped for. Your seven commits ship in TensorFold 1.0.12 as they are, merged in We have no Intel GPU, so this lands on your Arc Pro B70 receipts and our host checks: with stand-in tools the XPU builds pass for x86_64 Linux (the server and runner, and every Posted by Ash's AI Assistant Synthena |
Part 1 of 3. Opened after the issue that proposes the backend and the checks below (#522).
What this adds
An experimental Linux build,
zig build -Dxpu, that runs Nemotron 3.5 Lightning 30B-A3B (MLX affine 4-bit, group 64) on anIntel Arc Pro B70 (Battlemage, 32 GB). OpenCL C kernels are compiled to SPIR-V with
oclocat build time and run throughLevel Zero, using the matrix engine for prompt GEMMs, prompt attention and decode attention. It has no CUDA or Metal code in its path.
zig/src/xpu/: the runtime (driver, context, memory, modules, streams, loader for the checkpoint).zig/src/families/nemotron/xpu*.zig: the Nemotron operators and model for this runtime; the family keeps its weights andoperators, no scheduler is added.
zig/kernels/xpu/*.cl: the kernels (Mamba2, MoE, attention, 4-bit matvec, GEMMs).zig/src/cli/xpu_main.zig:tensorfold-xpu(devices,runon token ids) and the runnertf-xpu-test.zig/tests/xpu/: op and model tests (xpu-testsstep),tools/zig/xpu_*: forced-token comparison against the reference,the long-context needle, the one-process-at-a-time GPU guard, fixture generators and the small shipped reference files.
docs/recipes/xpu-arc.md,THIRD_PARTY_NOTICES.mdandLICENSES/: recipe and notices.Not in this PR: the native server backend (part 2) and Qwen, EXL3 and GGUF on Intel (part 3). Not supported anywhere: NVFP4 and
FP8-Mamba checkpoints (refused at load with a message), MTP or copy drafts, grammars, prompt cache, other cards.
Gates (CONTRIBUTING.md)
python3 tools/zig/lean_check.py: 512 problems, the same as upstream 1.0.2 (f8fe17d); none addedzig build -Dxpu,xpu-tests,zig build test: 85 of 86 pass. The one failure,direct_io: direct reads return the same bytes as buffered reads, fails the same way on an unmodified upstream 1.0.2 checkout on the test machinetest-goldenTHIRD_PARTY_NOTICES.mdsection for the Intel GPU engine; MLX license text inLICENSES/.clfiles; the compiled SPIR-V is byte-identical to the commented sourcesExactness
Run with
tf-xpu-nem_rows-test,nem_prefill,nem_slot,nem_seam,nem_fp64,nem_attn_dec,nem_attn_pf(see the recipe):byte-identical for chunks of 64, 128 and 512 (640 tokens, real text and random ids).
difference is reported in the recipe (worst conv difference 0.68 against magnitude 33, next 8 decode steps up to 0.94 logit,
same top-1 on real text).
reported): all classes within the test bounds; table in the recipe.
mean absolute log-probability difference 0.0233; every top-1 miss is listed with both margins. KL divergence against BF16 over
the first 64 chunks: 0.0864 mean, same top-1 87.24%.
within 0.25 logit. Our engine is deterministic (41 of 41 reruns identical).
Receipts
Same machine (Intel Arc Pro B70, Linux 7.0.0
xe, Zig 0.17.0), checkpointTensorFold/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-MLX-4bitrevision
d9d758fb83953437f7263256b0d96157e2a348b8, public prompt token ids shipped undertools/zig/prompts/.af29e8d9691ain plain and 512-row prefillNEM_OLD_DEC=1)NEM_OLD_ATTN=1selects the earlier per-row kernels): 8,192 tokens 3.45 s to 2.62 s, 32,768 tokens 26.6 s to 11.3 s, 131,000 tokens 389.5 s to 57.5 sReproduce
zig builddefaults to-Doptimize=fast(ReleaseFast); every number in this description was measured with it, and a debug build is much slower.The guard takes the GPU lock, waits while other processes hold over 8 GB of device memory and stops a command only with SIGINT.
Do not wrap a script that takes the guard itself (
xpu_nem_needle.py,xpu_needle.py,xpu_spec_identity.sh, and the comparisonscripts when
TF_GPU_LOCKis set): the inner guard would wait forever on the lock the outer one holds.The fixtures contain measurements of TensorFold 0.6.4 on CUDA and slices of public text (wikitext-2, CC BY-SA 3.0); see
tools/zig/fixtures/README.md. No weight slices are shipped.Out of scope and open questions