Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions bench/results/2026-10-04-community-2x-arc-pro-b60/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Community benchmark on 2x Intel Arc Pro B60

Measured on 2026-10-04 by LocalXPU. This is the SYCL port (`sycl/`) on two Arc Pro B60 cards, one request at a time, at an 8,192-token context. It is a short test: two prompt sizes, 2 to 3 requests each. It is not a 4K/32K/128K sweep.

## Hardware and software

- 2x Intel Arc Pro B60 24 GB (`8086:e211`, 456 GB/s each), PCIe 3.0 x8 per card; Ryzen 5 5600; 64 GB DDR4; storage type not recorded
- Ubuntu 24.04, kernel 6.17 (`xe` driver), Level Zero V2, compute runtime NEO 26.09.37435.12, oneAPI DPC++ 2026.1.1, oneMKL 2026.1.0
- Strata `6f32ec0` plus two `sycl/` fixes (the build fix for #559/#626 and the `cudaStreamQuery` fix in the ring wait); engine reports `0.1.39-sycl`; AOT `-DSTRATA_SYCL_AOT=bmg-g21`; source build, no Docker
- Nothing else ran on the GPUs. The kernel log was watched for `xe` resets during every run: none.

## Model and configuration

- Coder IQ1_M (`Qwen3.8-Flash-Next-GSQ-RCO-IQ1_M`, shard hashes checked against the Hugging Face LFS ids), `--layer-split 24`
- Flash-Next IQ2_XS (`Qwen3.8-Flash-Next-GSQ-RCO-IQ2_XS`), `--layer-split 25`
- Packs from `tools/iq_pack.py`, MTP layer from `tools/mtp_fetch.py` / `mtp_pack.py` / `mtp_rt.py`, profiles from `data/`
- Context 8,192; KV int8; `--spec 4 --spec-min-p 0.5`; greedy (`temperature 0`), `enable_thinking: false`; no vision

`data/coder2.json` and `data/iq2b.json` are the exact server configs (engine arguments and environment); the engine's startup and stage lines are in `data/coder2.engine.txt`, `data/iq2b.engine.txt` and, for the one-card run, `data/B2.run.txt`. Served with
`sycl/serve/server_intel.py --engine strata --config <json> --port 8085`.

## Method

Requests went through `/v1/chat/completions`, one at a time. Prompt and decode speed are the server's own `timings` object
(`prompt_per_second`, `predicted_per_second`), per request; `bench.py` prints them (copy in `data/`, raw output in `data/c2-*.txt`, `data/i2-*.txt`).
Model loading is not included. Three prompts:

- short: "Write a Python function that returns the n-th Fibonacci number" (23 prompt tokens cold, 7 fresh after the cached prefix)
- 2K code: about 2,000 tokens of the repository's `tools/*.py` plus "write a binary search tree class" (1,922 prompt tokens, none reused); the second turn reuses the first
- 2K prose: about 2,100 tokens of source plus "say what this code is for" (2,129 prompt tokens, none reused); the repeat reuses 2,122 of them

## Results

All experts were resident in VRAM on both cards in both runs (engine line `layer split: 100% of the experts resident`), so `STRATA_VERIFY_NO_HOST=1` was set.

| Configuration | Prompt tokens (fresh) | Reused | Generated | Requests | Prompt tok/s | Decode tok/s | Drafts accepted |
| --- | ---: | ---: | ---: | ---: | --- | --- | --- |
| Coder IQ1_M, short | 23 / 7 | 0 / 16 | 159 | 3 | 50.4 cold, 74-80 | 52.8 first, 56.6 and 56.6 | 112/138, 114/133 |
| Coder IQ1_M, 2K code | 1,922 | 0 | 256 | 1 | 425.3 | 57.2 | 186/208 |
| Coder IQ1_M, 2K code, next turn | 21 | 2,178 | 256 | 1 | 61.4 | 71.4 | 209/212 |
| Coder IQ1_M, 2K prose | 2,129 | 0 | 128 | 1 | 427.9 | 40.1 | 70/98 |
| Coder IQ1_M, 2K prose, repeat | 7 | 2,122 | 128 | 1 | 78.1 | 41.3 | 70/98 |
| IQ2_XS, short | 23 / 7 | 0 / 16 | 112 | 2 | 50.4 cold, 81.7 | 58.6 and 61.3 | 84/88, 84/89 |
| IQ2_XS, 2K code | 1,922 | 0 | 256 | 1 | 445.1 | 55.5 | 183/213 |
| IQ2_XS, 2K code, next turn | 284 | 1,915 | 256 | 1 | 177.0 | 72.5 | 211/217 |
| IQ2_XS, 2K prose | 2,129 | 0 | 104 | 1 | 436.4 | 38.8 | 56/87 |
| IQ2_XS, 2K prose, repeat | 7 | 2,122 | 104 | 1 | 79.3 | 40.0 | 56/87 |

The two prose rows are slower because fewer drafts were accepted (71% and 64%), not because of the context: the 256-token code reply at the same context ran 57.2 and 55.5.
Time to first token was not measured separately.

One card, Coder IQ1_M, 4,042 of 12,288 experts mirrored in pinned RAM (`STRATA_MIRROR_MIB=8192`, `ONEAPI_DEVICE_SELECTOR=level_zero:0`),
by-hand `--tokens` run, 11-token prompt, 128 greedy tokens: **11.9 tok/s** (11.2 to 11.9 over five builds that differ only in experiments), coherent Python.
Without the ring-wait fix this configuration stopped at layer 15 with `never rang (graph finished)` in 5 of 5 tries.
The engine's stage table shows `wait for rings` at 186.8 ms per round, about 3.9 ms per layer. It drops to 72.9 ms and 25.1 tok/s with `kSpinMax` 2,000 in
`sycl/include/strata/sycl_doorbell.hpp`, which suggests the GPU does not see the host's flag stores during the spin kernel and waits out the bound. That change
risks proceeding before a slow host answers, so it is not part of this report.

Memory after load (`xpu-smi`, MiB, card 0 / card 1): Coder 14,592 / 17,456; IQ2_XS 20,246 / 20,573. The tightest card kept 3.7 GB free of the 24 GB with `--vram-reserve-mib 1536`.

## Correctness and limitations

- Both models, `/v1/chat/completions`: a Fibonacci function executed against the first ten values and `fn(50) == 12586269025`; a Euclid `gcd` executed on four pairs; a palindrome check (IQ2_XS); `sum(i*i for i in range(1,11))` answered `385`. All passed.
- A three-turn conversation (a name and a number, then arithmetic on it, then a lambda using both) was answered correctly by both models, and the conversation cache reused 41 and 74 tokens.
- No NaN, garbage or repetition loop in any output.
- Not done: the 19-token chat-templated prompt from the docs (no token-for-token comparison with the reference text), streaming, the Anthropic route, tool calls, contexts above 2.2K, `--layer-split auto`, a run with `STRATA_VERIFY_NO_HOST=1` and experts outside VRAM.
- Kernel tests (`ctest`, one B60): 21 of 25 pass; `iq_parity` passes for all 10 formats once its fixtures exist; `ple_parity` needs the Q2_0 shard; `s2_expert_grouped_parity` fails 4 bitwise comparisons while its host-reference check is within tolerance; `conversation_snapshot_test` aborts with `munmap_chunk(): invalid pointer`.
- One `xe` GT reset on card 0, during the first `iq_parity` run (it did not finish in 300 s and was killed by a timeout; the same binary then passed in under 90 s). The card recovered without a reboot.

Paths in `data/` were shortened: `<data root>` is the directory with the models, packs and MTP layer, `<strata checkout>` the Strata source tree.
54 changes: 54 additions & 0 deletions bench/results/2026-10-04-community-2x-arc-pro-b60/data/B2.run.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
IDS=7734,264,12654,709,421,4523,279,307,7324,76938,1324
strata generate: PCIe probe: 7.0 GB/s host->device (best of 7.0 7.0 7.0 7.0) -> pcie_frac 0.19 (default 0.55)
strata generate: this CPU has no AVX-512: the expert kernels run on AVX-2 (multi-token for the i-quant gate/up rows)
strata generate: native pack: <data root>/packs/coder-iq1_m experts (largest blob 2.66 MB), token embedding IQ4_XS in mapped host memory (322 MiB)
strata generate: 1406 MiB of weights loaded from <data root>/packs/coder-iq1_m (302 canonical tensors skipped: served natively)
strata generate: 300 native projection matrices, 2018.88 MiB of weights
strata generate: the SSD is kept awake while rows are read: one page of the table after 100 ms without a read, until 60 s after the last request (STRATA_SSD_KEEPALIVE=0 turns it off)
strata generate: profile data/expert-profile-coder.bin: 12288 ranked pairs, built for 12288 slots
strata generate: PLE on, table 320001536 rows of <data root>/models/coder-iq1_m/Qwen3.8-Flash-Next-GSQ-RCO-IQ1_M-00002-of-00002.gguf
strata mtp: draft layer loaded, 802 MiB of VRAM (experts 675, dense 111), files read in 0.45 s (1746 MiB/s)
strata generate: experimental native Q5_K head, 521472000 bytes
strata generate: GPU 0: Intel(R) Arc(TM) Pro B60 Graphics, compute capability 20.1
strata generate: WARNING: this engine was compiled with CUDA 12.8 headers but loaded a CUDA 0.2 runtime (libcudart): GPU properties can read wrong and some kernels go unused. Rebuild it against one toolkit (cmake -DCUDAToolkit_ROOT=<the toolkit>, with its libcudart.so present) (#542)
strata generate: experts streamed from the GGUF on demand (--stream-experts): no host arena; every expert must fit the VRAM cache
strata generate: expert cache auto: 18.90 GiB free, 1536 MiB reserved (+218 MiB for the draft head) -> 6331 slots
strata generate: expert cache 8246 slots, 15.70 GiB of VRAM; policy is
strata generate: the GPU computes the experts in the cache; it rounds differently from the CPU,
so a reply can differ slightly from a run without the cache (same quality:
bench/results/2026-09-27-cache-parity).
PROFILE, ranked by routing frequency, no eviction.
strata generate: profile fill: 15.70 GiB in 2.5 s (6.79 GB/s, pipelined)
strata generate: pre-filled 8246 of 8246 slots from the profile; slot 0 verified
strata generate: 4042 of 4042 experts missing from VRAM mirrored in pinned host memory (7.72 GiB, 1.3 s); the GPU reads them over PCIe
strata generate: R4 hit path ON - resident experts are computed on the GPU
strata generate: 5 expert-pool workers + the host thread
strata generate: session is up (engine 0.1.39-sycl)
strata generate: sampling greedy
strata generate: token graph hit path: 8246 resident experts, decided on the device
strata generate: prompt path allocates its own buffers (no cache slots to borrow)
strata mtp: draft head over 106299 tokens (212.9 MiB)
strata generate: prefill 10 tokens in 1 chunks, 563.9 ms (17.7 tok/s); experts streamed 354 (354 by DMA, host 5.2 ms), resident 1689; PLE 2.7 ms
strata verify: window up to 6 tokens, 74.0 MiB of device buffers
strata verify: captured the 1-token window (upload <FIXME: Placeholder>, sync <FIXME: Placeholder>)
strata verify: captured the 2-token window (upload <FIXME: Placeholder>, sync <FIXME: Placeholder>)
strata verify: captured the 3-token window (upload <FIXME: Placeholder>, sync <FIXME: Placeholder>)
strata verify: captured the 4-token window (upload <FIXME: Placeholder>, sync <FIXME: Placeholder>)
strata verify: captured the 5-token window (upload <FIXME: Placeholder>, sync <FIXME: Placeholder>)
strata verify: captured the 6-token window (upload <FIXME: Placeholder>, sync <FIXME: Placeholder>)
strata generate: window and draft graphs captured in 125 ms
speculation 50 rounds of 6, drafts accepted 81 of 98 (0.827), 2.62 tokens per round
window sizes T1:11 T2:8 T3:3 T4:28 T5:0 T6:0 (min draft probability 0.50)
suffix drafts 3 windows, drafts accepted 8 of 9
accepted per round 0:14 1:10 2:7 3:19 4:0 5:0
verify window wait for rings 186.845 pool 10.966 host 1.142 commit 9.963 ms/round; CPU experts 2.50 distinct / 3.33 routed per layer
pool multi gate/up 6.183 quantize 0.025 down 2.868 ms/round; 27.1 GB/s over the rows phases; CPU pool call 10.958 ms/round
dispatch plan 0.100 activation quantize 0.254 jobs 1.514 run 9.080 ms/round
adaptive tier 1069 experts swapped into the VRAM tier (every 4 rounds, 0.839 ms/round)
pcie experts 0.21 distinct experts per layer read over PCIe (share 49/256 of the misses)
mtp 10.095 ms/round drafting (49 rounds), MTP prompt 6.8 ms, 1015 MiB of VRAM
prompt : 7734 264 12654 709 421 4523 279 307 7324 76938 1324
output : 1608 48575 13 271 727 15336 1393 1590 198 262 413 307 2564 220 16 25 198 285 460 307 198 262 460 15336 1393 12 16 8 478 15336 1393 12 17 8 271 760 709 3294 369 264 4145 29812 7873 314 279 76938 8240 13 4213 11 424 369 524 10727 364 3349 2663 314 307 1521 424 95272 3205 942 279 1788 2663 5081 2942 13 1946 7042 14588 11 567 628 958 20781 1954 310 3436 8334 16100 2663 13 248046 198 248045 846 198 7734 264 12654 709 421 4523 279 307 7324 76938 1324 1608 48575 248046 198 248045 74455 198 248068 198 760 1156 6587 264 12654 709 421 4523 279 307 7324 76938 1324 1608 48575 13 1061 369
decode 128 tokens in 10735.2 ms -> 11.92 tok/s
prefill 10 tokens in 563.9 ms -> 17.73 tok/s (time to first token 809.0 ms)
EXIT=0
41 changes: 41 additions & 0 deletions bench/results/2026-10-04-community-2x-arc-pro-b60/data/bench.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import json,sys,re,urllib.request,time,glob
URL="http://127.0.0.1:8085/v1/chat/completions"
def ask(msgs,max_tokens=256):
body={"messages":msgs,"max_tokens":max_tokens,"temperature":0,"chat_template_kwargs":{"enable_thinking":False}}
t=time.time()
r=urllib.request.urlopen(urllib.request.Request(URL,json.dumps(body).encode(),{"content-type":"application/json"}),timeout=900)
d=json.load(r); d["wall"]=time.time()-t
return d
def show(tag,d):
tm=d["timings"];u=d["usage"]
print(f"[{tag}] prompt_n={tm['prompt_n']} cache_n={tm['cache_n']} prompt {tm['prompt_per_second']:.1f} tok/s | decode {tm['predicted_n']} tok {tm['predicted_per_second']:.1f} tok/s | drafts {tm.get('draft_n_accepted')}/{tm.get('draft_n')} | wall {d['wall']:.1f}s | finish {d['choices'][0]['finish_reason']}")
def code(text):
m=re.search(r"```python\n(.*?)```",text,re.S); return m.group(1) if m else text
def run_tests(src,tests):
g={}
try:
exec(src,g); exec(tests,g); return "PASS"
except Exception as e: return f"FAIL {e!r}"
mode=sys.argv[1]
if mode=="short":
for i in range(2):
d=ask([{"role":"user","content":"Write a Python function that returns the n-th Fibonacci number"}],256); show(f"fib run{i}",d)
print(d["choices"][0]["message"]["content"])
print("fib tests:",run_tests(code(d["choices"][0]["message"]["content"]),"fn=[v for k,v in list(globals().items()) if callable(v) and not k.startswith('_') and k!='exec'][-1]\nassert [fn(i) for i in range(10)]==[0,1,1,2,3,5,8,13,21,34]; assert fn(50)==12586269025"))
d=ask([{"role":"user","content":"Write a Python function gcd(a, b) that returns the greatest common divisor using Euclid's algorithm. Only the code."}],200); show("gcd",d)
c=d["choices"][0]["message"]["content"]; print(c)
print("gcd tests:",run_tests(code(c),"assert gcd(48,18)==6; assert gcd(17,5)==1; assert gcd(0,9)==9; assert gcd(270,192)==6"))
d=ask([{"role":"user","content":"What is the output of this Python code? print(sum(i*i for i in range(1,11))) Answer with just the number."}],20); show("known",d); print(d["choices"][0]["message"]["content"], "(expected 385)")
if mode=="long":
src="".join(open(f).read() for f in sorted(glob.glob("<strata checkout>/tools/*.py"))[:6])[:int(sys.argv[2])]
msgs=[{"role":"user","content":"Here is some Python source:\n\n"+src+"\n\nIn one paragraph, say what this code is for, then write a short Python function that reverses a string."}]
for i in range(2):
d=ask(msgs,256); show(f"long run{i}",d)
print(d["choices"][0]["message"]["content"][:1200])
if mode=="long2":
fs=sorted(glob.glob("<strata checkout>/tools/*.py"))
src="".join(open(f).read() for f in fs[6:12])[:7000]
msgs=[{"role":"user","content":"Here is some Python source:\n\n"+src+"\n\nWrite a Python implementation of a binary search tree class with insert, search, delete and in-order traversal methods, with docstrings."}]
d=ask(msgs,256); show("long2 BST",d)
d2=ask(msgs+[{"role":"assistant","content":d["choices"][0]["message"]["content"]},{"role":"user","content":"Now add a height() method."}],256); show("long2 turn2",d2)
print(d["choices"][0]["message"]["content"][:600])
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id": "chatcmpl-9a1c5ae9d2084c8f9b34ac8c", "object": "chat.completion", "created": 1791157128, "model": "coder-iq1_m", "choices": [{"index": 0, "message": {"role": "assistant", "content": "```python\ndef fibonacci(n: int) -> int:\n \"\"\"Return the n-th Fibonacci number (0-indexed).\n \n fibonacci(0) = 0\n fibonacci(1) = 1\n fibonacci(2) = 1\n fibonacci(3) = 2\n ...\n \"\"\"\n if n < 0:\n raise ValueError(\"n must be a non-negative integer\")\n if n == 0:\n return 0\n if n == 1:\n return 1\n \n a, b = 0, 1\n for _ in range(2, n + 1):\n a, b = b, a + b\n return b\n```"}, "finish_reason": "stop"}], "usage": {"prompt_tokens": 23, "completion_tokens": 159, "total_tokens": 182, "prompt_tokens_details": {"cached_tokens": 0}}, "timings": {"cache_n": 0, "prompt_n": 23, "prompt_ms": 456.3, "prompt_per_token_ms": 19.839, "prompt_per_second": 50.4, "predicted_n": 159, "predicted_ms": 3009.9, "predicted_per_token_ms": 18.93, "predicted_per_second": 52.8, "draft_n": 138, "draft_n_accepted": 112}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[long run0] prompt_n=2129 cache_n=0 prompt 427.9 tok/s | decode 128 tok 40.1 tok/s | drafts 70/98 | wall 8.2s | finish stop
[long run1] prompt_n=7 cache_n=2122 prompt 78.1 tok/s | decode 128 tok 41.3 tok/s | drafts 70/98 | wall 3.2s | finish stop
This code is a comprehensive integration test script for a batched inference engine (likely named "Strata") that verifies the correctness of multi-slot decoding, specifically ensuring that tokens generated in parallel slots match those generated in solo mode, even when long prompts are interleaved with other decoding operations or when requests are continued from checkpoints. The script sets up various scenarios involving different prompt lengths, slot assignments, and continuation logic to validate that the engine's internal state management, expert caching, and token generation remain consistent across different execution paths.

```python
def reverse_string(s: str) -> str:
return s[::-1]
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[long2 BST] prompt_n=1922 cache_n=0 prompt 425.3 tok/s | decode 256 tok 57.2 tok/s | drafts 186/208 | wall 9.0s | finish length
[long2 turn2] prompt_n=21 cache_n=2178 prompt 61.4 tok/s | decode 256 tok 71.4 tok/s | drafts 209/212 | wall 3.9s | finish length
```python
class BinarySearchTree:
"""A binary search tree implementation with insert, search, delete, and in-order traversal."""

def __init__(self):
"""Initialize an empty binary search tree."""
self.root = None

def insert(self, value):
"""Insert a value into the binary search tree.

Args:
value: The value to insert into the tree.

Returns:
None
"""
if self.root is None:
self.root = _Node(value)
return

current = self.root
while True:
if value < curr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[fib run0] prompt_n=7 cache_n=16 prompt 74.3 tok/s | decode 159 tok 56.6 tok/s | drafts 114/133 | wall 2.9s | finish stop
[fib run1] prompt_n=7 cache_n=16 prompt 80.1 tok/s | decode 159 tok 56.6 tok/s | drafts 114/133 | wall 2.9s | finish stop
```python
def fibonacci(n: int) -> int:
"""Return the n-th Fibonacci number (0-indexed).

fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(2) = 1
fibonacci(3) = 2
...
"""
if n < 0:
raise ValueError("n must be a non-negative integer")
if n == 0:
return 0
if n == 1:
return 1

a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
```
fib tests: PASS
[gcd] prompt_n=37 cache_n=0 prompt 71.7 tok/s | decode 32 tok 60.8 tok/s | drafts 23/24 | wall 1.0s | finish stop
```python
def gcd(a, b):
while b:
a, b = b, a % b
return a
```
gcd tests: PASS
[known] prompt_n=42 cache_n=0 prompt 69.5 tok/s | decode 4 tok 25.1 tok/s | drafts 4/6 | wall 0.8s | finish stop
385 (expected 385)
Loading