Skip to content

Arc A770 (DG2) support and fixes: 4 GiB handling, host-visible flags, chunked mirror, XMX-off build, A770 docs - #1

Closed
samsonnguyen wants to merge 56 commits into
maxfridbe:intel-arc-0.1.39from
samsonnguyen:a770-dg2-upstream
Closed

samsonnguyen wants to merge 56 commits into
maxfridbe:intel-arc-0.1.39from
samsonnguyen:a770-dg2-upstream

Conversation

@samsonnguyen

@samsonnguyen samsonnguyen commented Oct 5, 2026 •

Copy link
Copy Markdown

Building on top of your perf branch - Niko1221#809.

If this isn't merged in, then i can request for a re-pull to Niko1221 upstream without the perf changes merged in.

What this is

The changes needed to run the SYCL port on an Arc A770 (Alchemist / DG2-G10, 16 GB, xe driver), found while running the Coder IQ1_M and the original model's IQ3_XXS on it. It is based on intel-arc-0.1.39 (everything from this branch is already merged in, so the diff is only the A770 work). Most items are bugs that silently produced garbage (token 0 forever, NaNs) rather than crashes, so each has a test or a written root cause.

Also added: docs/INTEL_A770.md (bring-up, what differs from the B70, measurements, the 4 GiB bug write-up) and docs/INTEL_A770_ISSUES.md (a short catalogue of the Intel driver / Level Zero / IGC / oneMKL problems hit, with workarounds, for anyone who wants to report them upstream to Intel). The A770 material was moved out of docs/INTEL.md, which now points to it.

Changes, grouped (can be split into separate PRs if you prefer)

Build for DG2

  • STRATA_SYCL_NO_XMX (IGC for dg2 crashes on the joint_matrix kernels), STRATA_SYCL_LARGE_BUFFERS (-ze-opt-greater-than-4GB-buffer-required), STRATA_GGML_NATIVE (so a binary built on another CPU does not die with "Illegal instruction"); ze_loader link; AOT builds need IGC_EnableDPEmulation=1 OverrideDefaultFP64Settings=1 (no fp64 on the A770).
  • sycl/include/dpct/device.hpp: Level Zero Sysman fallback for free-memory queries on xe.

Host/GPU handshake and waits

  • Flag words the GPU polls are allocated with zeMemAllocHost and the uncached flag; a kernel polling plain sycl::malloc_host memory never saw host stores on the A770. Bounded waits now fail loudly with the flag's name, with per-verifier timeout counters (wait_timeout_registry.hpp).
  • The native router's work-group barrier after some work-items returned hung the first decode window; removed.

Expert cache, mirror and prompt borrowing

  • The pinned host mirror is a list of chunks of at most 3 GiB (Level Zero caps one host allocation near 4 GiB), with append mode reconciled with intel-arc-0.1.39's lend mirror; ring slot ownership for the streaming ring (expert_ring_slots.hpp).
  • oneMKL's bf16/f16 to fp32 GEMM returns zeros when an input matrix starts 4 GiB or more into an allocation (reproducer: sycl/tests/repro/mkl_gemm_4gib.cpp). With prompt borrowing and a cache over 4 GiB, prompts of ~1.5k tokens and up decoded to token 0. Fix: the expert cache is laid out in reverse (coldest slots, which are the ones lent to the prompt path, at the lowest addresses) and the loan is capped at 3.9 GiB. It switches on automatically when the arena exceeds 4 GiB; STRATA_CACHE_REVERSE=0/1 forces it. Kernels still index slots through the offset table.
  • GPU-only adaptive swaps only pick victims that have a host mirror (otherwise a later route to an evicted expert has no fallback in STRATA_VERIFY_NO_HOST mode).

Kernels

  • Q6_K served from a 224-byte-stride repack (type tag 114), opt-in with STRATA_Q6K_REPACK=1; mmvq_bench --selftest compares it against the unpacked kernel and fails on a mismatch.

Other: sycl/tools/Dockerfile.serve (optional serving image).

How it was tested

On an A770 (Ubuntu 26.04, kernel 7.0, xe, oneAPI 2026.1): builds clean; host-only tests (sycl/tests: ring slots, timeout registry, cache layout, mirror) pass; mmvq_bench --selftest passes (max abs diff 4.9e-4, dequant exact). End to end: the Coder IQ1_M and the original model's IQ3_XXS serve correctly, including a 90k-token prompt at a 262144 context (prompts ~550 tok/s, decode 16-17 tok/s on a code prompt, 12-14 on real chats). A/B of this branch against the previous A770 build on four fixed greedy prompts: speed within +-3%, outputs correct. Numbers are in docs/INTEL_A770.md.

Not tested, please look at

  • Nothing here was run on a B70/B65. Items that apply to every SYCL card and could change B70 behaviour: the reversed cache layout (it activates automatically for an arena over 4 GiB, which a 32 GB card will have), the chunked mirror, the per-verifier timeout counters, and the swap-victim rule. If you can run the B70 benchy before and after, that would settle it. The Q6_K repack is off by default.
  • The byte-budget prefill ring from upstream main (Prefill chunk ring Niko1221/Strata#583) is not ported here (it measured no difference on the A770).
  • The ~55 commits include several merge commits and some with generated messages; happy to squash or split by group above.

🤖 Generated with Claude Code

https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu

yes, AI was used. Combination of claude code and gpt

samsonnguyen and others added 30 commits October 3, 2026 13:35
…ix path

IGC crashes with a floating point exception (ocloc -device dg2-g10) on the
two joint_matrix kernels, xmx_gemm_iq and qsa_prompt_attn_xmx. Both are
opt-in and slower than the default paths on the B70, and their callers
already fall back when they return false.

With -DSTRATA_SYCL_NO_XMX=ON both entry points become `return false` stubs
and the kernels are not instantiated (SYCL extracts device code from the
template instantiation, so an early return alone is not enough).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…issing

On the xe driver (Arc A-series) the oneAPI Level Zero adapter answers
UR_DEVICE_INFO_GLOBAL_MEM_FREE with UR_RESULT_ERROR_UNINITIALIZED even with
ZES_ENABLE_SYSMAN=1, so sycl::aspect::ext_intel_free_memory is never set,
dpct::get_memory_info reports 0 bytes free and --expert-cache auto sizes the
cache to zero slots. Level Zero Sysman itself answers correctly
(zesMemoryGetState tracks allocations: 16268 MiB free, 13191 after a 3 GiB
allocation, 16263 after freeing it).

Fall back to zesMemoryGetState through the device's native Level Zero handle
when the aspect is missing. Where the aspect exists the path is unchanged.
strata_core now links ze_loader.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
--stream-experts mirrors the experts that did not fit VRAM in one
sycl::malloc_host. A single host allocation is capped at about 4 GiB on
Arc A-series (max_mem_alloc_size 4095 MiB; UR_L0_ENABLE_RELAXED_ALLOCATION_LIMITS
lifts the device cap only), so a 14 GiB mirror failed with "no pinned host
memory" and every miss then stayed on the SSD.

The mirror is now a list of chunks of whole blobs, at most 3 GiB each, and
each (layer, expert) keeps a direct pointer instead of an offset. If a later
chunk cannot be allocated the earlier ones are kept as a partial mirror.
On a 16 GB A770 all 7448 experts missing from VRAM were mirrored (14.2 GiB,
3.4 s).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…ever reach

route<NE> returns early for every work-item with local_id(1) != 0 and then
calls a work-group barrier. CUDA lets exited threads satisfy __syncthreads;
on Arc A-series (dg2) the barrier never completes, the first decode window
hangs with the GPU busy and the xe driver eventually resets the compute
engine. The barrier protects nothing (the values live in private arrays), so
it is removed.

Adds native_router_parity: the native router (256 and 512 experts, one and
six tokens, gaussian / negative-offset / all-ties logits) against a host
reference. The kernel had no test of its own; router_top10_parity covers the
generic kernel only. 0 mismatches on the A770.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
The expert cache is one allocation larger than 4 GiB once the card has the
room (9.2 GiB on a 16 GB A770). With UR_L0_ENABLE_RELAXED_ALLOCATION_LIMITS=1
the allocation succeeds, but IGC addresses a buffer with 32-bit offsets
unless the kernel is compiled with -ze-opt-greater-than-4GB-buffer-required.
Kernels reading a slot more than 4 GiB into the cache then misread it
without any error: a resident IQ4_NL expert 5.96 GiB into the cache
dequantised to NaN (285,388 non-finite values of 1.6M) while an identical
expert in a small staging buffer dequantised correctly, which made the
prompt path poison the residual stream from layer 1 on and the model emit
only token 0. Xe2 addresses buffers statelessly, so the B70 never saw it.

With -DSTRATA_SYCL_LARGE_BUFFERS=ON the AOT ocloc options carry the flag.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…eck macro's 0

verify.cpp and session.cpp ported cudaStreamQuery as
`q = DPCT_CHECK_ERROR(queue.ext_oneapi_empty())`. The macro evaluates the
expression for its error code and returns 0, so `q != 1` was always true and
any layer that took longer than one flush interval (2 ms) without ringing was
reported as "layer N never rang (graph finished)" while the graph was still
running. `q` is now 1 while the queue still has work and 0 once it is empty,
which is what the check was written for.

On the A770 this was what stopped the host-handshake path (no
STRATA_VERIFY_NO_HOST) at layer 1; with it the path runs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…speed, open issues

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
GgufExpertSource::blob() entered the slot in where_ while holding the lock and
then did the three preads outside it, so a second caller asking for the same
expert in that window took the "still resident" branch and was handed a pointer
to a half-filled buffer. The CPU pool's workers, or the two tokens of one verify
window, do ask for the same expert at once. The slot is now reserved under the
lock and entered in where_ once the read is complete; a concurrent caller reads
the expert into a slot of its own.

Found by reading the code while chasing the adaptive-swap corruption on the A770.
It is a real race, but fixing it did not by itself change that failure.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
An adaptive swap (the default, --adapt-every 4) evicts a resident expert into
the missing set. At startup only the experts that miss VRAM are mirrored in
pinned host memory, so an evicted expert is a miss the GPU cannot reach on its
own. The device plan then waits for the host to compute it, and where the
device-to-host handshake does not hold (the A770: kernel writes to host-mapped
memory are not reliably visible while the graph runs) the GPU's bounded wait
gives up and the window carries on with garbage. The first window after the first
swap produced NaN, which the recurrent GDN state keeps for good: the output was
correct for four tokens and token 0 after that.

Verified on the A770 that the swap itself was sound (the device residency table
matched the host's, and the swapped-in slots were byte-identical to the GGUF),
and that mirroring every expert makes the output correct across 16 swap rounds
(1,536 experts swapped, tokens identical to the run without swaps).

With swaps on, the resident experts are now mirrored after the real misses, within
the same RAM cap, so a victim is always reachable. STRATA_MIRROR_ALL=1 forces it
and STRATA_MIRROR_ALL=0 turns it off. It costs about 9 GiB more pinned host memory
on the 16 GB card.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…ice-to-host wait problem

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
… every card for a split

stage_room() never queried free VRAM: dpct dropped the cudaMemGetInfo inside an if-with-initializer, so every
later stage of a --layer-split saw 0 bytes and its expert cache had "no room". Restored, plus a fixups.py rule.
strata-sycl.sh forwards ONEAPI_DEVICE_SELECTOR and defaults to level_zero:gpu for a --layer-split (the image
pins level_zero:0). Reported and verified on 2x B70 in the PR Niko1221#423 review.

Also xmx_int8_bench: an int8 XMX IQ4_NL GEMM prototype - correct, but slower than dequant + oneMKL (not ported).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
…ndard Intel bench

INTEL.md keeps how the port works and how to run it; every measured speed moves to INTEL_PERFORMANCE.md (current
numbers, dated history, llama.cpp on the same card), with the results posted on PR Niko1221#423 for the Arc Pro B50, the
B580 and 2x B70 copied as their posters reported them.

sycl/benchy.sh (v1): every set-up model x 20 / 2,185 / 8K / 40K / 128K / 256K-token prompts (sycl/bench/v1), each
with its serve config, from a cold page cache. Reports PP, TTFT, TG, drafts accepted, experts offloaded to the RAM
mirror, VRAM, RAM, SSD reads, load time and power, with the system and build. INTEL_PERFORMANCE.md asks submitters
for that report plus their configs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
The reference benchy v1 run: Coder IQ1_M (256K), IQ2_XS and Swift 1.5 (128K) x 20 / 2,185 / 8K / 40K / 128K /
256K-token prompts, cold page cache, every run complete. The 32K test-config numbers stay beside it, labelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
The verify window's device waits on the host's flags are bounded (kSpinMax) because an unbounded spin that never sees
its flag wedges the card. A wait that gave up simply let the window go on without what the host was to write, and nothing
said so: the output was garbage and the recurrent GDN state kept it for good (token 0 forever after one bad window).

Each wait that gives up now counts itself, per flag (A: the plan, B: the PCIe copies, M: the CPU's results), in
host-visible memory. After the window the verifier reads the counts and, if any wait gave up, prints which, prints the
host's own view (how far it got, how often it saw the GPU ring, which flags it raised) and stops the window with an error.
STRATA_WAIT_TIMEOUT=warn only reports. The bound can be set with STRATA_SPIN_MAX (default unchanged: 20,000 polls).

This is how the adaptive-swap corruption and the missing host-to-GPU stores were pinned down on the A770: the waits gave up
although the host reported raising every flag.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…l sees the host's stores

The host raises flags A, B and M while a kernel on the GPU polls them. On the Arc A770 (xe driver, oneAPI 2026.1) memory from
sycl::malloc_host does not work for that: a kernel polling it did not see a host store made after the kernel started, under any
load (system-scope relaxed and acquire atomics, device-scope, volatile, with and without a system fence), after 60 million
polls. The host reported raising every flag for all 48 layers while 34-46 GPU waits gave up per window, even with the bound
raised 100x, and every layer then paid the whole bound.

Memory from zeMemAllocHost with ZE_HOST_MEM_ALLOC_FLAG_BIAS_UNCACHED (or _BIAS_WRITE_COMBINED) was seen within microseconds in
every trial. The same call with no flags was not reliable (it failed twice in a row and then worked, in one run; failed in
others), which is why the flag is explicit.

The flag words are now allocated that way (host_alloc_coherent, HostUse::kControl) on the Level Zero backend. Nothing else needs
it: the plan and result rows are read by later kernels, and a kernel launch refreshes the GPU's view; making them uncached cost
42 ms per window. With STRATA_VERIFY_NO_HOST nothing waits on the host and the flags keep the plain allocation: the uncached
flags alone cost the same 42 ms there for a reason that was not found.

Measured on the A770, Coder IQ1_M, 128 tokens: the host-plan path (CPU computes part of the misses beside the GPU, the design
the engine was written for) went from zeros at the first token to correct output at 8.0-8.6 tok/s; the device plan with the host
handshake no longer produces garbage when an expert is not mirrored; the GPU-only path is unchanged at 7.14 tok/s.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…ok/s, what stays open

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…nger the limit

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…n GPU-only mode

With STRATA_VERIFY_DEVICE_PLAN and STRATA_VERIFY_NO_HOST nothing on the host sees which experts a window routes to (the usage
the adaptive tier ranks comes from the host pool), so the swaps never had anything to act on and the static profile's hit rate
stayed at about a third: about two thirds of every expert read crossed PCIe from the pinned mirror. unitrace on the A770
showed it: the GPU was 88% busy, and 78 ms of the 141 ms of kernel time per decode round was native_gu_port / native_down_port
reading mirrored experts at about 9 GB/s.

resident_plan_kernel now counts, per (layer, expert), how often an expert is routed to (a relaxed device atomic per routed
id, into a table indexed like the residency table; resident_plan_set_usage). Before each adaptation the engine reads the
counters, clears them and adds them to the usage the adaptive tier ranks, in both the serve and the CLI loop. Off unless
the run is GPU-only with adaptive swaps on.

Measured on the A770, Coder IQ1_M, 128 tokens: 7.14 -> 9.76 and 9.74 tok/s (two runs), 9.77 at 256 tokens; kernel time per
round 141 -> 100 ms (the expert kernels 78 -> 31 ms); output identical to the run without swaps.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…7 -> 10.37 tok/s

A Q6_K block is 210 bytes, so every 16-byte load in the decode mat-vec starts at a 2-byte-aligned address and the kernel
splits it into two aligned loads and a shift (load16_a2). The loaders now pad each block to 224 bytes on the host before the
upload (type tag 114, native_mmvq_serving_type / native_mmvq_pack; STRATA_Q6K_STRIDE224=0 keeps the GGUF layout), and a
variant of the wide kernel reads the padded blocks with plain aligned loads and the activations as scalar loads, at most four
columns per launch (five or six in one launch spill registers: 0.14x). The prefill dequantiser reads tag 114 too; an
unknown tag still throws instead of misreading.

mmvq_bench (new check against the 210-byte kernel; bit-identical results), same build, two columns: 1.12-1.32x over the model's
Q6_K shapes. Whole GPU-only decode on the A770, 128 tokens, identical tokens: 10.37 and 10.36 tok/s against 9.77 and 9.76.
Load time is unchanged (the repack of about 1.9 GB is a fraction of a second); the Q6_K weights grow 6.7% (2,019 -> 2,105 MiB).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
Upstream moved the B70 measurement tables to docs/INTEL_PERFORMANCE.md and rewrote the "Not done" list; the only textual
conflict was docs/INTEL.md, resolved by taking upstream's tail and keeping the Arc A770 section after it. The
sources auto-merged (generate.cpp, verify.cpp, iq_kernels.dp.cpp, CMakeLists.txt).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
xmx_int8_bench (new upstream) is a joint_matrix int8 DPAS prototype; IGC for dg2 dies on it with a floating point exception
at AOT link, the same as on the fp16 joint_matrix kernels, so the target now sits behind the option that already
compiles those out. (Upstream measured it 0.38-0.98x of dequant + oneMKL on the B70: not worth an engine port.)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…m merge

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…an the runner's CPU

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
maxfridbe and others added 26 commits October 4, 2026 06:43
….py it cites

Fixed: the missing "How to build it" section (docker build + build.sh with AOT/REPO/BUILD_DIR), the strata-sycl.sh
path, the llama.cpp-only scope of the persistent-cache rule, the kernel count, where the CUDA headers live (mounted
for migration, not in the image), the draft layer's VRAM, stream-all's 1,024-token minimum chunk, the expert XMX
switch that is not in the tree, the metrics image that is not in the repo, and the HAND-merge list's reasons.
sycl/probe/*.cpp and sycl/tools/rank_kernels.py were cited but lived outside the repo; they are in it now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
setup_intel.py --check judged each model by the first card only: with two cards the IQ2_XS/IQ3_XXS/IQ3_S fit in
VRAM across both (layer split). Also names the Intel path where setup's AMD line still said docs/AMD_HIP.md.
INTEL.md: a two-die layer split needs a comma-list AOT build (checked: bmg-g21,bmg-g31 compiles).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
…them by DMA

With --stream-experts the slots a prompt lends had their experts read from the GGUF for every chunk and again for
the refill after the prompt. They are now mirrored once in pinned host memory beside the VRAM misses (the resident
RAM mode's idea, upstream), and the stream-all walk DMAs pinned experts. Same bytes into the same slots: outputs
identical (Coder 2,185 / 8K / 40K, IQ2_XS 2,185 / 8K, cold cache). Prompt reading on the served configs: Coder
456 -> 610 / 802 -> 946 / 988 -> 1,046 tok/s, IQ2_XS 303 -> 500 / 565 -> 720; refill 720-2,240 -> 200-380 ms.
2.3 GiB more RAM for the Coder, 1.9 GiB for the IQ2_XS; 2.1 s at start (taken out of the one-shot TTFT clock,
added to benchy's load time). STRATA_LEND_MIRROR=0: off.

Also: host-side timers for the prompt path's per-layer expert grouping (STRATA_PREFILL_TIMING=1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
…ot worth it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
The warp kernel scored every (query, block) with a sub-group, re-reading the pooled key and spending 20 shuffles
per pair (~1.1 TFLOP/s). The scores are now pooled keys x the batch's indexer queries as oneMKL fp32 GEMM tiles
(8,192 blocks, 32 MB) plus a relu-sum kernel that also gives block n_bid its dead key. sel_scores_bench: 7.3-8.4x
at 40K-256K, relative error 2-4e-7 against FP64. Coder TTFT: 256K 328.6 -> 258.2 s, 128K 138.9 -> 122.9 s.
FP32-level, not bitwise (like the CUDA build's 3xTF32 path): outputs part at a near-tie after 13-93 tokens, the
same text either way. STRATA_SELECT_GEMM=0: the warp kernel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
…ll chunks)

The batched attention's warp kernel split a cell's 256 dims over a sub-group and reduced with a 5-step shuffle tree
per query head (60 shuffles a cell). The prompt path now scores with one work-item per cell holding its key row and
all 12 heads' dots, the query heads read from local memory (a broadcast), 128-cell chunks; the values pass and the
merge are the warp kernel's. Decode keeps the warp kernel (bitwise unchanged). attn_bench (32 queries x 2,048 cells,
INT8 KV, 131K context): 0.57 -> 0.37 ms; the K/V gather floor is 0.07 ms, so the kernel is arithmetic-bound, not
gather-bound. Coder TTFT 40K 37.2 -> 33.5 s, 128K 123.0 -> 111.3 s, 256K 258.3 -> 235.1 s. FP32 in another summation
order (not bitwise): outputs part at a near-tie after 25-44 tokens, the same text. kv_stream_parity and
kv_hybrid_parity pass; qsa_prompt_attn_parity passes but for the Q4_0 tensor-core cases the port refuses (as
before). All four KV formats. STRATA_ATTN_PERCELL=0: the warp kernel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
…ores, per-cell attention)

INTEL.md describes the three changes and their switches, and corrects the old claim that the prompt attention is
gather-bound (the K/V fetch is a fifth of its time). INTEL_PERFORMANCE.md adds their A/B measurements and the two
new XMX results, and notes that the "host grouping" share of the 40K breakdown was mostly the profiler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
…ze; benchy's mirror column

qsa_set_bytes (the count bytes_needed uses) lacked the 32 MB sel_S tile init allocates, so a chunk whose QSA buffer
set is the largest (256 tokens: a 20-token prompt) failed with "device buffers ... do not fit". Benchy's
"offloaded" column also read the lend mirror's line; it reads the VRAM misses' line only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
Coder 256K TTFT 328.8 -> 235.1 s, 128K 140.9 -> 111.2 s; IQ2_XS / Swift 128K ~191 -> ~156 s. All 16 runs complete
(the 20-token prompts included, after 16e5ae0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb4SRxFuLpffQAAXBf4HxA
…L >4 GiB GEMM bug and its reproducer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…versions, PCIe link

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…replaces the machine write-up)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…rc-0.1.39

A/B on the A770 against 41ee109 (same config, four fixed greedy prompts, two passes): decode and prompt speed within +-3%, outputs differ at the token level
but stay correct. The merged tree keeps all A770 fixes (audit counts unchanged).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…t in INTEL.md

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
…nto the A770 branch

strata-sycl.sh: Podman support combined with the explicit environment array; the empty options array no longer trips set -u on older bash.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CjbiQBP63VFfAvn5SA6tu
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants