Compare commits

...
707 Commits
Author SHA1 Message Date
Niko1221 7695859aa1 Canonical Q2_0 pack: the measured PCIe share is opt-in on Windows (STRATA_PCIE_AUTO_CANON=1); RTX 5070: code +2.5%, a short answer -3.3%, 5 pairs 2026-10-11 16:19:59 +02:00
Niko1221 7c035d729f STRATA_ADAPT_OVERLAP: opt-in on Windows (RTX 5070, Q2_0: story -2.6%, code -2.1% with it, 5 pairs; IQ3_XXS neutral); Linux CUDA keeps it on 2026-10-11 14:34:41 +02:00
Niko1221 0d0f106d5a docs/MULTI_GPU.md: the default pipeline's tokens are the serial loop's up to CPU rounding (4x R9700, default flags: 10 of 10 prompts matched over 160 tokens) 2026-10-11 14:08:31 +02:00
Niko1221 60abf63edc HIP layer split: the route tail skip's default stays off from 60% of the experts cached (90% elsewhere); measured on 2x R9700: Q2_0 story -6.2% at 70%, -1.8% at 60%, IQ3_S about 0 at 60% 2026-10-11 12:35:58 +02:00
Niko1221 003ea8c678 Merge PR #1892 (lask3802): the --serve NaN guard (non-finite head logits drop the caches and re-read from token 0; STRATA_NAN_GUARD=0 off); bitwise identical otherwise, cost under 0.2% 2026-10-11 12:01:30 +02:00
lask3802 f332a85570 Non-finite logits (#879): a guard with self-healing
Rarely a window's logits become NaN and the reply degenerates into token 0
('!') repeated (#606); the poison sits in the conversation's K/V and
recurrent state, so with the prompt cache every later turn stays poisoned
until it is read again from token 0. The q8_1 clamps (2b726ba, f6601bf,
7a0e302) remove the overflows found so far; this guard catches whatever
cause is left, and drops the poisoned state instead of serving it again.

On by default, as a fix: STRATA_NAN_GUARD=0 turns it off. It only reads
flags, so an answer without a NaN is the one the engine gives with the
guard off (compared byte for byte on a GPU, below).

Engine:
- every verify-window graph checks its head logits beside the greedy pick
  (logits_nonfinite_rows: 16 blocks a row, one flag word each, into mapped
  memory); Verifier::note_nonfinite reads them after the sync the window
  already does (run, run_slot_rows, batch_poll, pl_finish). No extra sync.
  With STRATA_NAN_GUARD=0 no kernel is recorded.
- --serve: a flagged window emits none of its tokens, one stderr line names
  the position and row, and the request ends with `DONE ... nonfinite` (a
  batch slot: `BDONE <slot> <n> nonfinite`; an admission: BADM <slot> 0).
  The next request first drops the live session, every checkpoint, every
  parked conversation (ConversationCache::clear) and every idle slot, and
  reads its prompt from token 0; a slot still running then finishes its
  reply and is not kept as a cache after it. A request that ends on it does
  not keep its conversation live (#1620's cancelled-read path is skipped
  for it).
  Covered: the serial decode loop (one GPU or a layer split), the
  --pipeline-windows decode loop of a layer split, the prompt's short tail
  (read_windows, and read_windows_pl with --pipeline-windows), and both
  batch-slot loops (run_slot_rows, and the --batch-groups pipeline's
  batch_poll). Not covered: the one-shot command line (no --serve) and the
  SYCL port in sycl/, which has its own verify.cpp and generate.cpp.
- STRATA_NAN_INJECT=<n>[,<n>...] (debug): the n-th window the guard checks
  reports row 0 as non-finite.

Server: EngineNonFinite on that finish. A request that has sent nothing yet
is sent again once (its usage is the retry's: the failed pass's DONE is
dropped); a reply that had started ends as "length"; a second fire on the
retry ends with an error (503 before an answer, an error event in a
stream). With replicas the router forgets what that replica held.
repeat_stop_tokens stays as the backstop.

Checks: serve tests (python -m unittest discover -s serve -t . -p
"test_*.py": 697 tests, OK, 11 skipped; NonFiniteLogits,
test_nonfinite_in_batch_mode and test_nonfinite_forgets_the_replica are
new). ctest with CUDA_VISIBLE_DEVICES=-1: 24 pass (conversation_cache_test
with clear() among them), 3 skip for want of a GPU, and the 43 GPU parity
tests were not run on a GPU. Built for CUDA 13.0 on Linux (GCC, sm_75 +
sm_86); generate.cpp, verify.cpp and verify_kernels.cu compile with
MSVC 2022 + CUDA 13.0, and conversation_cache_test and conv_cache_test
pass there; built for HIP (ROCm 7.0, gfx1100, compile only) and SYCL
(oneAPI 2026.1.1; it takes only the new declaration in verify_kernels.hpp
and the new inline ConversationCache::clear(), and calls neither).

On a GPU (RTX 3080, RVN IQ3_S, strata.service's one-GPU config):
- Answers, guard on vs STRATA_NAN_GUARD=0, at fixed expert sets
  (--pcie-frac 0 --adapt-every 0 --adapt-swaps 0 --mtp-max-t 1
  --suffix-draft 0, STRATA_IQ_MT_MIN=1 STRATA_PREFILL_CPU_SHARE=0), a chat
  replay of 2 x 4 turns on a 16K-token system prompt: byte-identical in 3
  pairs (one with STRATA_ROUTE_TAIL_SKIP=0, one with STRATA_SH_STREAM=0),
  and two guard-on runs 8 of 8. One more guard-off run differed from every
  other run from its first answer, and a second guard-off run matched the
  guard-on runs: a difference between runs of this config, not the guard.
- Cost, Nsight Systems: logits_nonfinite_kernel 424 launches, 4.77 us on
  average (7.78 us at most), 2.0 ms against 21.6 s of decode: 0.009%.
- STRATA_NAN_INJECT=1,60: the first window of a new chat flagged, the
  server sent the request again from token 0 and the client saw one normal
  answer; the 60th, mid-reply in the next turn, ended that reply as
  "length" after 58 tokens; the turn after it reused 0 cached tokens and
  answered normally. The engine logged exactly the two injected windows.
2026-10-11 12:01:22 +02:00
Niko1221 248dbee5b8 Merge PR #1737 (Ethan): route-resident swap one warp per token; bitwise equal to the scalar kernel, opt-in STRATA_ROUTE_RESIDENT decode +1.6-2.4% (R9700 32 GB) / +5.7-7.8% (16 GB-eq) 2026-10-11 11:22:28 +02:00
Ethan 115f62b00a route-resident parity: the swap against a scalar reference
route_resident_k is one warp per token and the form it replaced walked all 512 experts on a single
thread.  Nothing in the tree checked that the two land on the same expert and leave the same ids,
weights and counters, and the option's whole justification is that it moves misses across PCIe and not
the model.

src/kernels/route_resident_parity.cu: 240 random cases over 1 to 8 tokens, a 40-84% resident table,
random rank windows, margins from -0.5 to 1.5, and half the cases rounding the logits onto a coarse
grid so the tie rule (the smallest expert index wins) is exercised.  Half the cases get the router's
own top-10 and half get ids with repeats, which is what the already-picked test is for.  The reference
is the spec written out plainly - a serial walk with the same comparisons - and not a copy of the
kernel this branch replaced; it runs on the device so that expf, and so the softmax bits, are the same
function on both sides.

ids and the four counters have to match bit for bit.  The weights are a softmax ratio and this
reference is a second compilation of it, so they are compared within 8 ulp and the worst seen is
reported; the kernel itself is bit-identical to the serial form it replaced, which was measured with
both kernels side by side in one translation unit.  A case that swaps nothing still has to leave the
weights untouched, which the sentinel they start from checks, and the test fails if no case swapped at
all.

    route_resident_parity: 240 cases, 205 with swaps (2004 swaps), 0 mismatches, weights within 6 ulp

Registered in ctest next to the other router parity tests, TIMEOUT 120.
2026-10-11 10:52:44 +02:00
Ethan 33fd01b413 route-resident: one warp per token in the swap kernel
The swap kernel is the only work STRATA_ROUTE_RESIDENT adds to a window, and it was written as one
thread per token: with a verify window of 1 to 8 rows that is up to 8 threads of a block, each doing,
per rank in [lo, hi], a 512-expert scan with a ten-way "already picked" test inside it.  On this
machine that is about 4 x 512 x 12 scalar operations running on six threads, on the critical stream,
once per layer, and it shows up as its own cost in the window.

STRATA_VERIFY_PROFILE, three legs in one session, same prompts, same expert cache (8083 slots, one
learned profile), medians of the per-window stage table:

    configuration                        hc-read1+router   waitB    ms/window   pcie_e
    option off                                2.25          9.30      21.85      1.87
    option on, one thread per token           5.00          5.01      21.97      1.00
    option on, this commit                    2.58          4.87      19.75      0.95

The option redirects about a miss per layer-window away from PCIe and saves 4.3 ms of waiting for it;
the serial swap spends 2.75 ms back, which is why the option measured as a wash.  In the warp form the
swap costs 0.33 ms, so the waiting it saved stays saved.

End to end, twelve matched pairs of engine restarts, nine decode samples a leg, three greedy 150-token
prompts, the learned expert profile copied back before every leg so both arms start from the same
8083-slot cache.  The first six pairs ran the unpatched build first, the last six the patched build
first, so a within-pair order effect would show up as the two halves disagreeing:

    pair  order   decode unpatched   patched    delta    ms/window unpatched   patched    delta
     1    A->B         104.1          115.7     +11.1%         18.64          17.01      -8.7%
     2    A->B         103.5          107.4      +3.8%         18.47          18.12      -1.9%
     3    A->B         111.4          107.7      -3.3%         17.71          18.26      +3.1%
     4    A->B         101.4          113.0     +11.4%         19.11          17.03     -10.9%
     5    A->B         105.8          107.6      +1.7%         19.05          18.05      -5.2%
     6    A->B         104.8          105.6      +0.8%         18.60          17.83      -4.1%
     7    B->A         106.6          113.4      +6.4%         18.70          17.16      -8.2%
     8    B->A         103.1          110.2      +6.9%         18.14          17.29      -4.7%
     9    B->A         106.9          111.5      +4.3%         17.91          17.26      -3.6%
    10    B->A         104.3          114.7     +10.0%         17.81          17.11      -3.9%
    11    B->A         108.3          115.9      +7.0%         18.47          16.61     -10.1%
    12    B->A         104.5          113.6      +8.7%         18.43          17.76      -3.6%

    twelve legs each:   unpatched  decode median 104.7 (101.4-111.4)   ms/window median 18.47 (17.71-19.11)
                        this commit decode median 112.2 (105.6-115.9)  ms/window median 17.27 (16.61-18.26)

    decode      median +6.6%   mean +5.7%   faster in 11 of 12 pairs   (sign test p = 0.006)
    ms/window   median -4.4%   mean -5.2%   shorter in 11 of 12 pairs  (sign test p = 0.006)
    first six pairs: +2.7% decode, -4.7% window     last six: +7.0% decode, -4.3% window

pcie_e, the engine's own count of experts per layer-window that cross PCIe, is the same in both arms
(leg medians 0.63 against 0.64), so the same experts are routed and the difference is the kernel.

The swap's cost, and so its removal, scales with the tail experts the option actually redirects.  Four
more pairs with a 4000-slot cache instead of the automatic 8083 (pcie_e 1.83 against 1.78, i.e. about
three times the traffic of the series above), two pairs each way:

    pair  order   decode unpatched   patched    delta    ms/window unpatched   patched    delta
     1    X->Y          83.8           92.0      +9.8%         21.94          20.60      -6.1%
     2    Y->X          84.6           92.5      +9.3%         23.97          20.53     -14.4%
     3    X->Y          82.2           85.5      +4.0%         23.69          22.65      -4.4%
     4    Y->X          81.0           87.7      +8.3%         24.54          22.21      -9.5%

    decode median +8.8%, ms/window median -7.8%, 4 of 4 pairs, both orders.

What this changes is the shape, not the choice.  One warp per token: lane j reads experts j, j+32, ...
(coalesced instead of a serial walk), the warp reduces to the best logit in a shuffle tree, and the
ranks are still visited in order, because a swap changes the set already picked.  The choice is the
same bits: a strictly greater logit wins, the smallest expert index breaks ties, the already-picked
test runs against the ids as they are at that rank, a token with no swap keeps the router's weights
untouched, and the softmax is the same expression.

Parity.  400 random cases - half of them built with many equal logits, random lo/hi, margins from
-0.5 to 1.5, 1 to 8 tokens, about 60% of the experts resident, routed ids with repeats - compare the
two implementations bit for bit on the ids, on the weights (their raw bits) and on the four counters.
The harness carries both kernels side by side; 0 mismatches.

For context, the option's price is the margin's and not the kernel's, and this commit does not touch
it.  Teacher-forced log-probabilities over 7677 positions of one 7.6k-token text (technical prose,
source code and Chinese), top-64, against the same text with the option off:

    margin   argmax same   KL (top-64)   teacher-forced loss   perplexity
    off      -             0.000         3.2033                -
    0.25     84.9%         0.235         3.2238                +0.6%
    0.50     83.0%         0.293         3.4360                +7.3%
    1.00     63.5%         1.001         4.7751                +49%

Two runs with the option off are bit-identical, so that spread is the option and not the machine.
0.25 keeps nearly all of the waitB saving for a fraction of the distortion; the default 1.0 is not
worth its price.

Tests: the parity harness is a standalone .cu (both kernels, the random cases, the bit comparison).
Happy to wire it into the test suite next to the other kernel tests if you want it in the tree.
2026-10-11 10:52:44 +02:00
Niko1221 5ef500d48f Merge f-1712: the prompt chunk's step records and token ids go up from a pinned buffer (#1712) 2026-10-11 10:51:53 +02:00
Niko1221 46d22108ec HIP layer split: the route tail skip's default stays off from 70% of the experts cached (90% elsewhere); 2x R9700 16 GB-eq Q2_0 story -6.1% with it at 73% 2026-10-11 10:46:14 +02:00
Niko1221 a5f82ab77d Merge f-143-dense (via the trial merge 94f6d18d): CUDA sm_86 Q3_K interleaved kernel and one column; bitwise exact, A4000 decode +2.0-2.7% 2026-10-11 10:22:39 +02:00
Niko1221 2eba71548d Merge f-143-intel: B70 prompt and decode speed (copy engine, wide kv_append, no GEMM staging on bmg, lend mirror, fused dequant, lean verify graph, the xe expert ranking); same output tokens, B70 4K prompt 8.5 -> 3.3 s (128K config), decode +14-21% on prompts outside the ranking's corpus 2026-10-11 09:27:15 +02:00
Niko1221 b97a36a49a prefill: the chunk's step records and token ids go up from a pinned buffer, not pageable memory (#1712)
The QSA step records of a chunk were uploaded with an unchecked cudaMemcpyAsync from a
std::vector.  On gfx1100 the first text prompt after a picture one read garbage records
(5 of 5 faults on a two-request repro, 0 of 5 with a pinned, event-guarded source).  The
token id upload has the same shape and now takes the same path; both check the result.

Reported and traced by mantovaniluca91, #1712.
2026-10-11 09:16:52 +02:00
Niko1221 4d4f8bd50b docs/INTEL.md: server latency numbers 2026-10-11 09:10:59 +02:00
Niko1221 94f6d18d54 Merge f-143-dense: CUDA sm_86 Q3_K on the interleaved path and one column (STRATA_MMVQ_IL_Q3K=0 / STRATA_MMVQ_IL_T1=0 off) 2026-10-11 09:06:29 +02:00
Niko1221 3d5c16efe0 docs/INTEL.md: the 0.1.43 prompt and decode speed changes on the B70 2026-10-11 08:27:12 +02:00
Niko1221 8cb26e6095 SYCL: data/expert-profile-sycl.bin, the experts' ranking for a card on xe, built with make_profile_mix.py from the routing of 45 varied prompts and eight document summaries; setup_intel writes it instead of the shipped profile there (i915 keeps the shipped one). B70: +5..64% decode (mean +21%) on 12 unseen prompts, the same tokens. The copy-engine queue is Battlemage-only by default (it hung the A750's prompt) 2026-10-11 08:25:30 +02:00
Niko1221 ddd84269e4 Merge f-143-amdmg: --pipeline-windows 2 by default on gfx12 layer splits of 3+ stages (exact; 4x R9700 code +7-12%, 4K +9-11%, 32K +8-19%); STRATA_PCIE_AUTO_SPLIT=1 opt-in 2026-10-11 08:18:26 +02:00
Niko1221 b03304d934 HIP: --pipeline-windows 2 is on by default for gfx12 layer splits of 3+ stages (STRATA_PIPELINE_AUTO=0 / --pipeline-windows 0 off); STRATA_PCIE_AUTO_SPLIT=1 (opt-in) refines a two-stage split's PCIe shares per stage 2026-10-11 08:16:49 +02:00
Niko1221 218df14283 SYCL copy engine: one queue per device (a layer split asks once per card) 2026-10-11 07:58:41 +02:00
Niko1221 9e3ba42755 tools: make_profile_mix.py (an expert profile from several routing traces, each trace counting the same, with weights), collect_profile_traces.py (the traces of bench/profile_corpus.json's 45 varied prompts through the engine) 2026-10-11 07:58:34 +02:00
Niko1221 46a77907bb SYCL decode window: under STRATA_VERIFY_NO_HOST with the device plan the graph drops the nodes nobody answers (doorbell publish, three flag waits, the plan copy, the PCIe-share group and its staging); -2% decode on a B70, same tokens (STRATA_VERIFY_LEAN=0 turns it off) 2026-10-11 06:51:51 +02:00
Niko1221 331b591b36 Merge f-143-amdk: gfx12 decode node cuts (HC norm from registers, host-flag wait folded into the copy, HC up kernel owning its q8_1 groups, hit counts in the combine); bitwise exact, +1.6-2.0% decode 2026-10-11 06:01:01 +02:00
Niko1221 a9917a5524 gfx12 decode: the HC up kernel owns whole q8_1 groups and writes its consumer's image itself (STRATA_QFUSE_HC, no atomics), moe_hit_add folded into the combine (STRATA_FUSE_HITS); STRATA_FUSE_CONVAB (conv + a/b in one launch, opt-in, measured slower) 2026-10-11 05:59:48 +02:00
Niko1221 beba872b86 SYCL: the experts in the prompt loan's slots (the cache's last 10%, at most 1,200) are mirrored in pinned memory too, so the lent slots are refilled by DMA, not by a single-threaded GGUF read: 930 slots 916 -> 84 ms (STRATA_LEND_MIRROR=0 turns it off) 2026-10-11 05:53:27 +02:00
Niko1221 7a07f2811a SYCL prompt path: no GEMM operand staging on Battlemage (STRATA_SYCL_GEMM_STAGE=1 brings it back): the A770 oneMKL bug (operands past 4 GiB) did not show on a B70, first-token logits are byte-identical at 4K and 49K, and the 10 MB copy per expert was ~10% of a prompt 2026-10-11 05:44:57 +02:00
Niko1221 5a0df89817 SYCL prompt path: kv_append writes the host KV copy and the staging pool 4 bytes a lane (STRATA_KV_WIDE_HOST=0: the 1-byte stores): with --kv-resident the 1-byte stores into pinned host memory ran at ~30 MB/s, 2 s of a 4K prompt on a B70 2026-10-11 05:30:32 +02:00
Niko1221 cd5660593e SYCL copy engine: the stager's issued-flag is a copy, not a fill kernel (a copy-only command list runs no kernel: the driver aborted the lent-slot prompt of the 128K config) 2026-10-11 05:30:32 +02:00
Niko1221 751095962c SYCL prompt path: an expert's gate/up and down dequantized in one launch (iq_dequant_expert_f16, the same values; STRATA_PF_DQ_FUSED=0: two launches), -2% on a B70 4K prompt 2026-10-11 04:45:51 +02:00
Niko1221 7fc02cb5c2 gfx12 decode: the split HC norm from registers (-1.8 us a read), the host-flag waits folded into the copies that follow them (-2 nodes a layer); STRATA_VERIFY_NODES lists the window graph on HIP; the stage profile splits the FFN-side HC read; STRATA_DUMMY_NODES=k measures what an empty graph node costs 2026-10-11 04:04:07 +02:00
Niko1221 bb519f5bca bench: fused_gr_bench cold-weight mode with the norm/down/up split and an output hash; router_chain_bench (router GEMV, top-10 and doorbell per layer in a graph) 2026-10-11 04:04:07 +02:00
Niko1221 01fe68ddab SYCL: the prompt path's expert copies run on the GPU's copy engine (an immediate command list on the copy-only queue group, wrapped as a SYCL queue); the Level Zero v2 adapter ran them on the compute engine, ~13% of a B70 prompt. STRATA_COPY_ENGINE=0 turns it off 2026-10-11 04:00:52 +02:00
Niko1221 70f9c41772 SYCL: the grouped XMX expert kernels on Battlemage (STRATA_PF_XMX=1/2, opt-in): sub-group 16, joint_matrix 8x16x16, built for AOT bmg and lnl (STRATA_XMX_SG16); Alchemist keeps 8x8x16 2026-10-11 04:00:40 +02:00
Niko1221 5008962499 SYCL: STRATA_USAGE_DUMP=PATH counts every routed expert in the device plan (an atomic per entry) and writes the counts in --dump-routing format; a measurement of how much residency matters under STRATA_VERIFY_NO_HOST 2026-10-11 04:00:40 +02:00
Niko1221 195d379125 CUDA sm_86: Q3_K on the interleaved 2-4 column path and one column on the same kernel, sm_86 table (STRATA_MMVQ_IL_Q3K=0 / STRATA_MMVQ_IL_T1=0 turn them off)
IlQ3K runs q3_q8_dot_impl's arithmetic with the scales and signed values done once per chunk; bitwise native_mmvq's output
for T 1-4 and rows 1/2/4 (mmvq_il_parity). Q2_0 pack: the Q3_K multi-column kernels were 14% of GPU kernel time; the new kernel
takes 2560x10240 at 4 columns from 120 to 70 us on an RTX A4000. The shared table (sm_80+/sm_120) is unchanged.
2026-10-11 03:03:31 +02:00
Niko1221 da3fe1f119 mmvq_il_parity: the engine's real shapes, one column, Q3_K, MMVQ_IL_ONLY filter, table emit by worst ratio; fused_gr_bench: engine's staged read, L2-cold weights, GB/s 2026-10-11 03:03:31 +02:00
Niko1221 7c3fc48e86 Merge f-143-q2pcie: measured PCIe share for the canonical Q2_0 pack on CUDA (conflict with the gfx12 PcieAuto default resolved: one pf_old, canonical pack CUDA only) 2026-10-11 02:24:23 +02:00
Niko1221 ab97f17415 PcieAuto for the canonical pack: probe the link and refine the share from the CPU pool's speed (CUDA; start value stays 0.2; STRATA_PCIE_FRAC_DEFAULT=old and --pcie-frac keep it fixed) 2026-10-11 01:49:53 +02:00
Niko1221 c69adca61f Version 0.1.43: project version and the setup minimum engine; DETAILS lines for the gfx12 defaults 2026-10-11 01:49:39 +02:00
Niko1221 f5a0126762 Merge f-143-amd: gfx12 decode defaults 2026-10-11 01:49:18 +02:00
Niko1221 bf1e083f8a Merge f-143-amdpp: gfx12 chunk rule and PF_FUSED default 2026-10-11 01:49:17 +02:00
Niko1221 4ad4a25877 Merge f-143-kernels: P100 sm_60 tables 2026-10-11 01:49:17 +02:00
Niko1221 b5e040da40 Merge f-143-prefetch: adapt overlap default-on CUDA 2026-10-11 01:49:13 +02:00
Niko1221 85d90002b6 serve: a slot request that ends at its stop token reads its BDONE before it leaves, so its decode time and tokens are the slot leg's (#1869) 2026-10-11 00:56:31 +02:00
Niko1221 7475df931a HIP: the measured PCIe share (PcieAuto) is the default on gfx1200/gfx1201; STRATA_PCIE_FRAC_DEFAULT=old opts out; other AMD parts unchanged 2026-10-11 00:42:35 +02:00
Niko1221 ad2aef7648 HIP: STRATA_ROUTE_TAIL_SKIP=7 is the default on gfx1200/gfx1201 under the CUDA rules (#1884 cached-share line included); other AMD parts unchanged 2026-10-11 00:42:25 +02:00
Niko1221 606eb1080c generate: decode_defaults_arch_ok() (gfx1200/gfx1201 on HIP, always on CUDA) for the HIP decode defaults 2026-10-11 00:42:09 +02:00
Niko1221 5f9bbf61c2 serve: a request moved from the solo path to a batch slot keeps the solo leg's prompt figures, decode time and tokens (#1869, #1870) 2026-10-11 00:34:36 +02:00
Niko1221 5a66924d8d gfx12: the fused int8 prompt experts are on by default for IQ3_S and IQ3_XXS packs; STRATA_PF_FUSED=0 turns them off 2026-10-11 00:19:22 +02:00
Niko1221 cf09057a86 gfx12 (Linux): a bare --prefill auto may take 16384-token chunks; explicit --prefill N, auto:N and STRATA_PREFILL_AUTO_MAX are untouched; STRATA_GFX12_CHUNK=0 or STRATA_GFX12_DEFAULTS=0 keeps 8192 2026-10-10 23:50:49 +02:00
Niko1221 0d761d99aa Resident RAM mode overlap: opt-in (STRATA_ADAPT_OVERLAP_RESIDENT=1); exact, +7.0% on an A4000, -2.6% on a RAM-bound 3060 box 2026-10-10 23:00:37 +02:00
Niko1221 6ab541f625 CUDA: the tail-skip default stays off when 90% or more of the experts are in the GPU cache (#1884); STRATA_TAIL_SKIP_MAX_CACHED moves the line 2026-10-10 22:16:48 +02:00
Niko1221 2eaeda2e4b STRATA_ADAPT_OVERLAP in the resident RAM mode: soft_commit_exchanges makes a staged evicted expert look committed (pinned, device alias into its exchange buffer) while in's RAM place stays the copy source 2026-10-10 22:14:33 +02:00
Niko1221 8c34c475da STRATA_ADAPT_OVERLAP_SLOW_US: test hook that slows the adaptive copies so the windows wait for them 2026-10-10 22:12:27 +02:00
Niko1221 0636c4642f serve: a request handed from a batch slot back to the solo path keeps the slot leg's decode time and tokens (#1869); timings.prompt_n is what the engine read, the rest is cache_n (#1870) 2026-10-10 22:05:52 +02:00
Niko1221 cd78501359 strata_bench: skip the agent session when the context is below ~10K instead of sending a request that fails with HTTP 400 2026-10-10 22:00:17 +02:00
Niko1221 f95f2eabbd STRATA_ADAPT_OVERLAP on by default for CUDA (=0 turns it off), docs; the h2d copy-engine microbenchmark 2026-10-10 20:37:02 +02:00
Niko1221 e7558f8c5f CUDA sm_60: expert mode 8 (grid and activations in shared memory, SwiGLU fused) is the Tesla P100's default, STRATA_SM60_TABLE=0 or STRATA_EXP_MODE=0 turns it off
Bitwise equal to the CUDA layout on all 48 layers at 1-4 tokens a group (native_expert_bench); a 28-group VRAM call 17.6 -> 15.5 ms over the layers at T=1, 24.1 -> 22.6 at T=2, even at T=3, +2.6% at T=4.
2026-10-10 20:01:31 +02:00
Niko1221 9ef4484248 STRATA_ADAPT_OVERLAP=1 (opt-in): the adaptive tier's copies land while the next decode window runs, a layer waits for its own copies 2026-10-10 18:49:32 +02:00
Niko1221 e5387d41ec CUDA sm_60: interleaved 2-4 column GEMV with a Pascal rows table (P100), STRATA_SM60_TABLE=0 turns it off
Bitwise equal to native_mmvq (mmvq_il_parity, T 2-4, rows 1/2/4); table read off mmvq_il_parity --bench --emit-table on a Tesla P100 (three identical runs).
2026-10-10 18:39:38 +02:00
Niko1221 86070e7845 STRATA_TRACE: VRAM marks after the context, the pack weights, the session and the head (the 0.1.43 VRAM audit) 2026-10-10 18:30:35 +02:00
Niko1221 885f9f7016 Version 0.1.42.1: project version and the setup minimum engine 2026-10-10 18:24:47 +02:00
Niko1221 00cc68e77a Merge f-142d-bench into f-1421 2026-10-10 18:24:33 +02:00
Niko1221 bccef1cf81 Merge f-142d-numa into f-1421 2026-10-10 18:24:33 +02:00
Niko1221 02a4216fea Merge f-142d-unmap into f-1421 2026-10-10 18:24:33 +02:00
Niko1221 61b3fb5dd3 Merge f-142-pciefrac into f-142 2026-10-10 14:49:37 +02:00
Niko1221 0e9af37041 docs/DETAILS: the PCIe share set from the CPU pool's speed (CUDA, single GPU); measurements and opt-outs 2026-10-10 14:48:49 +02:00
Niko1221 c2cbf8c112 Merge f-142c-triage into f-142 2026-10-10 13:54:32 +02:00
Niko1221 407ea8192a Merge f-142c-small into f-142 2026-10-10 13:54:28 +02:00
Niko1221 879096d66c Benchmark: backend wording, run time text 2026-10-10 13:46:55 +02:00
Niko1221 ed5536ea88 file_expert_source_test: fix the cache-hint test string 2026-10-10 13:35:47 +02:00
Niko1221 6b8c2e74d7 Benchmark: prompt sizes calibrated by slope, agent re-read counts only earlier turns, no doubled quant in the folder name 2026-10-10 13:25:09 +02:00
Niko1221 dfaebd9d9f Benchmark mode: BENCH.bat / bench.sh and tools/strata_bench.py measure an install through the HTTP API and write a community report 2026-10-10 13:17:12 +02:00
Niko1221 52a2dd10ac Windows GGUF-in-place: close every GGUF shard view once the file tier reads unbuffered (NTFS serializes unbuffered reads of a mapped file) 2026-10-10 13:13:17 +02:00
Niko1221 8c80a3ba12 Start-up after another model: judge pinning by the emptiest NUMA node, say how much is file cache when RAM is short, opt-in STRATA_DROP_CACHE_ON_EXIT 2026-10-10 13:12:47 +02:00
Niko1221 aa7b0cb13e CUDA: the default --pcie-frac is refined in the first decode windows from the CPU pool's measured speed (single GPU, no --pcie-frac); STRATA_PCIE_FRAC_DEFAULT=old keeps the start-up rule 2026-10-10 13:01:46 +02:00
Niko1221 d3e9106e1d tools tests: follow #1792 (no version range without CUDA, STRATA_VCVARS), and the effort-end test no longer collides on a case-insensitive disk 2026-10-10 11:11:20 +02:00
Niko1221 0ae808f37d tools tests: the llama.cpp archive test passes a plain path (a file:// URI with a drive letter is not found on Windows) 2026-10-10 11:04:37 +02:00
Niko1221 a02cacbb3d docs: card order keeps the config's order for cards within 5% (#1760) 2026-10-10 11:02:19 +02:00
Niko1221 f63a57e726 prefill gemm: BF16 on Pascal/Volta converts the weight in tiles when the conversion buffers do not fit, instead of falling through to the raw BF16 cuBLAS call (#1757) 2026-10-10 11:02:14 +02:00
Niko1221 184012cac4 serve: cards whose speed scores are within 5% keep the config's card order (#1760) 2026-10-10 11:02:14 +02:00
Niko1221 47cb731026 serve: telemetry test for the AMD PCIe link (#1733): the reader gains the pcie_* keys, and the hop walk is tested 2026-10-10 10:51:32 +02:00
Niko1221 4d0d11f72d bench: COMMUNITY.md rows and the docs list entries for #1739 #1751 #1784 #1793 #1799 #1802 #1811 2026-10-10 10:47:13 +02:00
duncanmcqueen 27528ca53d docs: Intel Arc A770 decode speed investigation record (PR #1765; local paths removed) 2026-10-10 10:47:13 +02:00
Mitsuasa513 83b2b676dd bench: community report, Radeon Pro VII 16 GB (gfx906), ROCm 6.2 (PR #1793; build and server logs left out) 2026-10-10 10:47:13 +02:00
Hcl192088 d5400b54f5 bench: community report, RTX 4070 12 GB, IQ2_XS, modified engine (PR #1739; hostname redacted) 2026-10-10 10:47:13 +02:00
kgmkm 3bfc708d07 bench: community report, RTX 5060 Ti (WSL2), IQ3_S, engine 0.1.41 (PR #1751) 2026-10-10 10:47:13 +02:00
VykosX 432a45c708 bench: community report, RTX 5060 Ti / Xeon E5-2680 v4 (PR #1784) 2026-10-10 10:47:13 +02:00
gun-powder 80ea56af12 bench: community report, 2x Tesla P40, IQ3_S, 131K (PR #1799) 2026-10-10 10:47:13 +02:00
isoux35 7f980b5bd2 bench: community report, RX 9060 XT 16 GB, IQ2_XS and IQ3_XXS (PR #1802) 2026-10-10 10:47:12 +02:00
Daniel Spitzer c50a217a99 bench: community report, RX 9070 16 GB, IQ3_XXS, engine 0.1.41 (PR #1811) 2026-10-10 10:47:12 +02:00
Niko1221 70ac6dd41f serve: repeat_penalty / repeat_last_n (llama.cpp's names) for the config's sampling block and for requests (#1819) 2026-10-10 10:45:03 +02:00
Niko1221 2ea10c2e9a sycl: iq_parity and ple_parity are registered as the top-level build does (#1795) 2026-10-10 10:44:09 +02:00
architectds 6b392641af verify: service returns before any CUDA call while nothing has rung
The pipelined decode loop asks both stages' windows for service millions of times a second while they wait on their
doorbells, and every call switched the device (OnDevice: cudaGetDevice and two cudaSetDevice) before finding nothing
to do. On a 0.1.41 split (RTX 3060 + RTX 5070 Ti, Windows) the host stalled inside one of those cudaSetDevice calls
after 919 million loop passes, with both stages' windows waiting on it (stall dump: the serving thread in cudart from
Verifier::service, every other engine thread idle). Now a call with nothing rung, no WDDM flush due (2 ms) and no
timeout due returns 0 first - the same answer the loop below gives - so the driver is called only to serve a layer,
to flush, or to time out.
2026-10-10 10:43:10 +02:00
Niko1221 093edf2474 build: guard the CUDA::cuda_driver link for the HIP stand-in targets 2026-10-10 10:43:10 +02:00
Kurokosama b0eb396528 serve/web: add a favicon to the web app and the API Monitor
The pages had no <link rel="icon"> and serve/web had no icon file, so browsers asked for
/favicon.ico, got a 404 and showed the default blank icon in the tab.

serve/web/favicon.svg is the layer stack the app already uses (serve/web/sprite.svg, i-layers),
redrawn on a 32-unit viewBox so it stays legible at 16 px, in the existing accent token #10b981.

Checked: curl /web/favicon.svg -> 200 image/svg+xml, both pages come back with the icon link,
and the icon rendered at 16/32/48/64 px in Chrome.
2026-10-10 10:42:55 +02:00
phoenixclyde 7f3c0da241 telemetry: the Monitor's PCIe tile had no link for AMD cards on Linux
_Nvml.read() reports pcie_gen, pcie_gen_max, pcie_width and the two
throughput counters; _Amd.read() (the amdgpu sysfs reader from #301)
reports none of them, so on Linux the Monitor's PCIe tile shows only
dashes while the NVIDIA tile fills in.

This adds the link the card actually gets: the narrowest and slowest hop
between the root port and the card, from each hop's max_link_speed and
max_link_width.  A card that negotiates Gen4 on its own hop but sits
behind a Gen3 root port really runs at Gen3, and that is the number a
bandwidth budget needs.  pcie_own_gen keeps the card's own hop aside so
the gap is visible, and pcie_path reports how many hops could be read --
the bottleneck is only claimed when all of them could, because a kernel
or a container that hides part of /sys/devices would otherwise report the
card's own hop as the whole path.

Measured on one MI50 32 GB in a Gen3 x16 slot (lspci: the root port at
00:02.0 runs at 8 GT/s x16, the bridge above the card is downgraded, and
the card's own hop negotiates 16 GT/s), /metrics reports pcie_gen 3,
pcie_gen_max 3, pcie_own_gen 4, pcie_width 16 and pcie_path 4/4.

Throughput is deliberately left out: amdgpu's pcie_bw is three counters
whose documented use is a total, not a direction, while _Nvml's
pcie_rx_mb is receive-only, so filling that name from pcie_bw would
label a combined figure as one direction.  Happy to follow up separately
if you want the tile to show a rate.

Hardware the numbers come from: one Radeon Instinct MI50 32 GB (gfx906,
PCI 1002:66a1, VBIOS 113-D1631711-100) on a Xeon E5-2696 v4 (Broadwell,
no AVX-512), in a PCIe 3.0 x16 slot, 128 GB RAM, Ubuntu, kernel
7.0.0-34-generic, ROCm 7.14 in a container.
2026-10-10 10:42:55 +02:00
phoenixclyde dc0ee3acf7 prefill: the gfx906 build reached the CUDA branch of arm_cpu_share
Prefill::arm_cpu_share() asks "is this AMD?" with
#if !defined(STRATA_USE_HIP), but the gfx906 compat build (PR #638)
compiles as HIP *without* STRATA_USE_HIP -- see src/core/expert_cache.cpp
:5-6, which guards the same question with
#if defined(STRATA_USE_HIP) || defined(STRATA_HIP_GFX906).

On gfx906 the CUDA branch is therefore live: the CPU share is armed by
default for chunks below 1,024 tokens and prints its banner, against this
file's own note at :161 ("the layer split, batch slots and HIP stay off").
Seven other sites already carry the || defined(STRATA_HIP_GFX906) form.

Measured on one MI50 32 GB (gfx906), CLI generate, a 4-token prompt, one
binary with one set of weights and one config, only the environment
differing, power saving off:

  default  experts streamed 309 (295 by DMA, host 174.0 ms), resident 607,
           on the CPU 197 (share 0.90)          prefill 928.3 ms
  =0       experts streamed 506 (492 by DMA, host 177.3 ms), resident 606,
           on the CPU 0 (share 0.00)            prefill 929.5 ms

Output token ids are identical (271 248068 198 760) and the 0.13%
difference is inside this machine's run-to-run spread (1.2-1.7%), so on
this card the share engages and buys nothing.  #1595 and #1729 report the
default costing on CUDA; this is the mechanism that turns it on for AMD.

CUDA and SYCL do not define STRATA_HIP_GFX906, so the added check is a
no-op there and nothing on those backends changes.

Hardware the numbers come from: Xeon E5-2696 v4 (Broadwell, 22C/44T, no
AVX-512), one Radeon Instinct MI50 32 GB (gfx906, PCI 1002:66a1, VBIOS
113-D1631711-100, 34,342,961,152 B of VRAM), PCIe 3.0 x16 effective (the
root port runs 8 GT/s x16 and the bridge above the card is downgraded;
the card's own hop negotiates 16 GT/s, about 15.75 GB/s end to end),
128 GB RAM, Ubuntu, kernel 7.0.0-34-generic, ROCm 7.14 in a container,
built with -DSTRATA_HIP_GFX906=ON and -DCMAKE_HIP_ARCHITECTURES=gfx906.
2026-10-10 10:42:55 +02:00
phoenixclyde 6a7c6f8275 web: the unlabelled sparklines were scaled by their own noise
spark(id, values, max) sets the axis top to max(|max|, the series' own
maximum, 1e-9).  gpu_util, temperature, cpu, vram and power pass an
explicit maximum, but the pcie, tok/s, prefill and disk cards do not, so
those axes zoom onto whatever the series happens to hold.

An idle amdgpu pcie counter reads 0.01-0.02 MB/s (50 packets x 256 B per
second), and that becomes a full-amplitude square wave in the Monitor
while the tile's own text says 0.0 MB/s.  Measured in the live DOM: the
pcie line's y values are only {4.0, 30.0}, i.e. the baseline and the top.

A floor of 1 only ever lowers the axis - the series maximum still wins -
so a real burst keeps its scale, and the cards that pass a maximum are
untouched.
2026-10-10 10:42:55 +02:00
chaog992 031fddc62c tools tests: test_amd_is_not_asked mocks the Linux HIP engine build too (it wrote sys.prefix/.strata-rocm.json)
IQ4XS.test_amd_is_not_asked mocks get_prebuilt_hip, the engine setup.main takes on Windows. On Linux setup.main
compiles the HIP engine instead (build_engine_hip), which the test did not mock. Without a usable system ROCm 7, as
on the tested machine, rocm_root then fetched AMD's ROCm index
(https://rocm.nightlies.amd.com/v2/gfx120X-all/rocm/), wrote Path(sys.prefix)/.strata-rocm.json and tried to run
<python dir>/rocm-sdk. Where sys.prefix is not writable (the system Python) that is a PermissionError. Where it is
(a venv), the test passed while setup.main stopped at the ROCm step with exit code 1, and a stamp with the same
version and index can make a later setup in that venv skip the ROCm install.

The test now mocks build_engine_hip the same way, so on Linux setup.main runs to the end (exit code 0) with no
network request, no subprocess and no write outside the test's temp folder. setup.py is unchanged; the Windows path
never reaches build_engine_hip, so it runs as before.

Tests: python3 -m unittest tools.test_setup_unsloth (WSL2): with the system Python 3.12.3 FAILED (errors=1) before,
38 tests OK after; in venvs on 3.10.22, 3.12.3, 3.13.7 and 3.14.8 OK before and after, and no stamp after. With the
system Python the other 27 tools/test_setup_*.py files give the same results as before.
2026-10-10 10:42:55 +02:00
Hugua700 db5b1f19d6 setup: find_vcvars() without CUDA asks vswhere for no version range (#881)
The `[16.0,18.0)` range is CUDA 13's requirement, but it was applied to every caller: with the
only Visual Studio installed being 2026 (version 18), vswhere finds nothing and find_vcvars()
returns None.  That makes the CPU image encoder unbuildable on such a machine - build_vision_cpu()
cannot run - so --vision cpu stays unreachable there even with the gate in hip_vision() open.

#985 relaxed the range for CUDA 13.3+ by adding `cuda_v`, which is right for CUDA callers.  A
caller with no CUDA now passes nothing and gets no range, which is what
tools/hip/build_windows.bat already does when it calls vswhere directly.

- `cuda_v is None` -> no -version argument at all.  CUDA callers are unchanged: cuda_v=(13, 0)
  still refuses VS 18, cuda_v=(13, 3) still takes it.
- STRATA_VCVARS=<vcvars64.bat> picks an install by hand, for a vswhere that cannot be used.

Verified on the RX 6800M / gfx1031 machine of #881 (Windows 11, Chinese locale, the only C++ tools
are Visual Studio 2026 / 18.4.11605):

  find_vcvars()               -> <VS 18>\VC\Auxiliary\Build\vcvars64.bat
  find_vcvars(cuda_v=(13, 0)) -> None
  find_vcvars(cuda_v=(13, 3)) -> <VS 18>\VC\Auxiliary\Build\vcvars64.bat
2026-10-10 10:42:55 +02:00
leadgtr7 7650292cfd sycl mtp: slot drafters copy the shared draft head's type, as src/core/mtp.cpp does since #1110
With a draft-vocab head (rt/draft_vocab.bin) the slot drafters of --batch-mtp share the main drafter's head but kept
dhead_type_ at -1, so the first draft_first() of a batch admission threw 'mtp: unsupported native MMVQ GGML type'
and the engine exited (#1121). The CUDA source got this line in #1110; the SYCL port did not.
2026-10-10 10:42:55 +02:00
David d73108c7e8 build: link CUDA MMQ error reporting to the driver API 2026-10-10 10:42:55 +02:00
GRAB Racem 253e09f18d serve: the reasoning loop check also sees a loop written as one long word (#1753)
A periodic loop emitted as digits (a 24k-digit string cycling an 11-digit
pattern) is a single word to the word count and never one token twice
(the tokenizer has no multi-digit tokens), so both #606 and #728 passed
it and the run burned the whole reasoning budget.  The reasoning's last
2,048 chars are now checked for a period of at most 64 chars, and a
periodic tail reads as full coverage.  The opt-in path only: the default
path and the word count are unchanged.
2026-10-10 10:42:44 +02:00
leadgtr7 b9cba34bc2 serve: effort_position "end" reads the SYCL engine binary, not its launcher script, for --tail-role-token
With the Intel port the configured exe is sycl/serve/strata-sycl.sh, a shell script that runs the repo's
STRATA_SYCL_BIN (default build-sycl-aot/strata) in a container. effort_end_args looked for --tail-role-token in the
script, never found it, and fell back to the top-of-prompt effort with "needs engine 0.1.39 or newer", even on a
0.1.41 engine that has the option. engine_binary() resolves the launcher to the binary it runs; any other exe is read
as before. tools/test_effort_end_args.py covers both paths.
2026-10-10 10:42:44 +02:00
nekomario28 72843d4ca9 sycl: use the source version for setup model checks
The SYCL project declaration uses a CMake variable, not a literal
version. Reuse setup's parser so the stub no longer reports version 0.
2026-10-10 10:42:44 +02:00
Hulk 153416719d fix(setup): discard damaged llama.cpp source archives 2026-10-10 10:42:44 +02:00
Niko1221 cdbf458684 Version 0.1.42: project version and the setup minimum engine (0.1.41.1 was not released; its fixes are in 0.1.42) 2026-10-10 01:23:45 +02:00
Niko1221 d5f319bffc Merge f-142-cuda-p1r into f-142 (inert lookahead stats, cache-policy replay tool, docs) 2026-10-10 01:23:07 +02:00
Niko1221 136d33cdc3 Merge f-142-tailskip into f-142 (STRATA_ROUTE_TAIL_SKIP=7 default on CUDA serve) 2026-10-10 01:23:07 +02:00
Niko1221 7bbfab810b docs/DETAILS: the tail skip default (KL, task check, speeds, opt-out); start-up line wording 2026-10-10 01:22:27 +02:00
Niko1221 51cc86291b docs: STRATA_LOOKAHEAD_STATS and tools/replay_cache_policy.py 2026-10-09 23:10:26 +02:00
Niko1221 b4e3407bd2 STRATA_ROUTE_TAIL_SKIP=7 on by default on CUDA serve (=0 off): single GPU and layer splits; not with every expert in VRAM, --batch slots, a peer or helper GPU, HIP; one start-up line names the opt-out 2026-10-09 22:52:22 +02:00
Niko1221 223655ac91 replay_cache_policy: policy options on the command line 2026-10-09 21:09:03 +02:00
Niko1221 9462c58287 tools/replay_cache_policy.py: replay a --dump-routing trace through the adaptive expert tier's policy offline 2026-10-09 21:08:49 +02:00
Niko1221 b02a03d97c Look-ahead stats: vote-ranked top-M coverage of the misses and of the PCIe share 2026-10-09 20:47:12 +02:00
Niko1221 1d735110ae Router look-ahead stats: report the predictor thread's time per window 2026-10-09 20:47:12 +02:00
Niko1221 e247c61127 Router look-ahead: STRATA_LOOKAHEAD_STATS scores each layer's prediction against the real routing (measurement only) 2026-10-09 20:47:12 +02:00
Niko1221 8b0c241ca0 sycl: build q2_avx1.cpp and iq_avx1.cpp (the AVX1 expert rows native_expert.cpp calls; the SYCL strata target failed to link without them) 2026-10-09 19:09:54 +02:00
Niko1221 3ff62ec140 docs: batch-MTP on a layer split with all experts in VRAM lost 21.9% (2x R9700); it pays only when the experts do not fit 2026-10-09 18:58:08 +02:00
Niko1221 7a4bc813ba conversation_transfer_test: wrap the calls of the non-blocking copy helper 2026-10-09 18:57:40 +02:00
Niko1221 a6346cdcea conversation snapshot/checkpoint copies: private non-blocking stream instead of the legacy stream (HIP capture conflict) 2026-10-09 18:57:40 +02:00
Niko1221 bb930bc8be Merge f-142b-amd into f-142 2026-10-09 18:57:36 +02:00
Niko1221 5a90e36cb3 Merge f-142b-intel into f-142 2026-10-09 18:57:36 +02:00
Niko1221 54d7f7d7c5 Merge f-142b-cuda into f-142 2026-10-09 18:57:36 +02:00
Niko1221 6e1085c1de Merge f-142b-serve into f-142 2026-10-09 18:57:35 +02:00
Niko1221 ca2730b14b docs: PF_FUSED KL re-check on IQ3_S / IQ3_XXS / Q2_0 (stays opt-in) 2026-10-09 18:11:08 +02:00
Niko1221 0b3139ec18 gfx906 build: refuse a wave32 card at start with a message (#1728); docs: community-tested, opt-in note 2026-10-09 18:11:01 +02:00
Niko1221 e33edd05bf bench #1726: keep the reporter's background-workload list complete (a desktop chat app was open) 2026-10-09 16:51:42 +02:00
Andrey Bash 354f982118 gfx906: add opt-in reduce12 QSA score reduction
Follow query-swizzle #1661 with the existing reduce-scatter pairing for
12 heads. Keep default paths and softmax/value/merge unchanged. Public
component parity and timing passed; model evidence uses frozen stack.
2026-10-09 16:48:03 +02:00
Andrey Bash 254f5aeca9 docs(gfx906): record 200K and API qualification for the combined candidate
Record archived-control output parity, verified candidate process, tools, CPU vision and cache restoration. No fresh 200K speed comparison or production promotion is claimed.
2026-10-09 16:48:03 +02:00
Andrey Bash 94bf9e5870 perf(gfx906): add guarded opt-in query LDS swizzle for batched QSA
Preserve FP operation order and invert a bijective shared-query layout at its vector loads. Limit the path to the current gfx906 wave64 device, INT8 pools and warp scoring. Default remains off.

Add deterministic GPU parity/timing and host-guard probes, plus scoped component and model receipts. The clean upstream build passed both-card byte checks; model gains are recorded separately on the frozen experimental stack.
2026-10-09 16:48:03 +02:00
Niko1221andmaxious 3540500232 sycl: the PCIe probe's pinned source (PR #1669) only on a layer split; a single card keeps the pageable source
Pinned memory reads the link 1.7x faster (A750 10.3 -> 17.2 GB/s, B70 13.6 -> 25.6), which moves the derived default pcie_frac
(A750 0.28 -> 0.47) and cost 10% of decode there (median 12.07 -> 10.67 tok/s, 4 of 5 interleaved pairs slower). The B70 is unchanged
(0.37 -> 0.55, 31.4 vs 31.4). The fault the PR fixes is a layer split's.

Co-authored-by: maxious <[email protected]>
2026-10-09 16:45:52 +02:00
Niko1221 a084fd2d72 serve: opt-in prevent_sleep keeps Windows awake while a request is running or queued (#1727) 2026-10-09 16:45:37 +02:00
Niko1221 392841e586 bench: COMMUNITY.md rows and the docs list entry for #1722 #1725 #1726; AI-tool mentions removed from the #1722 and #1726 READMEs 2026-10-09 16:44:46 +02:00
mgeldi 3971f78c98 bench: RTX 5090 Swift IQ3_XXS 1M tuning and native NVMe restore (PR #1722) 2026-10-09 16:44:46 +02:00
libratechw c82d2c16f6 bench: community report, RTX 3060 12 GB, Flash-Next Q2_0/IQ2_XS/IQ3_XXS/IQ3_S, engine 0.1.41 (PR #1725) 2026-10-09 16:44:46 +02:00
Enky 31946c78b9 bench: community report, RTX 5080 16 GB + Xeon w7-2475X, UD-IQ4_XS at a 786K context (YaRN x3), engine 0.1.41 (PR #1726) 2026-10-09 16:44:46 +02:00
Katsumi Takeuchi 9310d3bdf8 sycl serve: _pump routes --batch slot lines (BT/BDONE) and ends on a fatal line, as serve/server.py
server_intel.py replaces StrataEngine._pump to keep LP lines beside their token, but its copy predates --batch: the
slots' BT / BDONE lines went to the main queue, so with "parallel" every request stopped after one token. It also
missed the fatal-line handling and last_err. Both are now as in serve/server.py's _pump.
2026-10-09 16:44:40 +02:00
Katsumi Takeuchi 10be33ff9a sycl: with STRATA_VERIFY_NO_HOST and a mirror, adaptive swaps are off (they left experts in neither)
The host mirror holds the experts missing from VRAM at the start. An adaptive swap moves an expert that was resident
then out of VRAM, so it is in neither; the device plan then waits for the host to serve it, and with
STRATA_VERIFY_NO_HOST (the xe default in strata-sycl.sh) nobody does. Seen after a --batch window, whose host path
counts the misses the tier swaps on: the next single request on one card gave token 0 ("!!!!") at 0.8-1.3 tok/s.
The start already refuses experts in neither under NO_HOST; this keeps that true after the start.
2026-10-09 16:44:39 +02:00
Eli Elgaev e910e41034 dequant: coalesced Q8_0 path (four threads per block, 16-byte stores)
The prompt path dequantizes each dense Q8_0 projection (GDN qkv/gate/out,
QSA q/k/v/out, shared expert, hc) into the GEMM scratch before cuBLAS.
dequant_kernel runs one thread per 32-value block, so a warp's 2-byte
stores land 64 bytes apart: about 70 GB/s of writes on a V100.

dequant_q8_0_kernel gives each block to four threads, 8 values each,
written with 16-byte stores: a warp reads 8 consecutive blocks and writes
512 consecutive bytes. The arithmetic per value is unchanged (d * q), so
the output is the same bits. It is used for Q8_0 when the output rows are
contiguous (ld == cols) and the pointers are aligned (blocks to 2 bytes,
output to 16); everything else takes dequant_kernel as before.

V100-SXM2-16GB, every 2-D Q8_0 tensor of Qwen3.8-Flash-Next UD-Q4_K_XL
(498 tensors), each call timed on its own:

  attn_qkv 10240 x 2560, FP16   0.740 -> 0.106 ms   (70 -> 495 GB/s written)
  attn_q   12288 x 2560, FP16   0.887 -> 0.126 ms
  ssm_out   2560 x 6144, FP16   0.451 -> 0.065 ms
  all 498 tensors, FP16        138.5 -> 20.4 ms
  all 498 tensors, BF16        140.8 -> 20.5 ms
  all 498 tensors, FP32        190.9 -> 35.4 ms

An FNV-1a hash over all outputs is equal before and after for FP32, BF16
and FP16. Whole prompts on 4x V100 (where this was first used), 4,096-token
chunks: 18K 4,537 -> 4,708, 65K 6,540 -> 6,766, 123K 6,783 -> 6,985 tok/s.
2026-10-09 16:43:30 +02:00
bossman aab7e30d01 cpu: iq_avx1 - IQ4_XS gate/up stays on ggml's generic dot (the 128-bit grid kernel measures 0.7-1.0x against its auto-vectorized generic on an E5-2470 v2; the kernel stays built and parity-tested, only the dispatch declines it) 2026-10-09 16:29:59 +02:00
bossman dd7b0b5f07 cpu: AVX1 128-bit expert kernels (iq_avx1) - the i-quant rows on CPUs with no AVX2
The whole i-quant family (IQ3_XXS, IQ3_S, IQ2_S, IQ4_XS gate/up, IQ4_NL down) and the
fork's Q2_0 down have x86 dots only under __AVX2__ in ggml-cpu; on an AVX1 floor
(STRATA_ISA_FLOOR=avx, issues #595/#623/#1375: Xeon E5 v1-v2, and AVX-512-only packs)
the CPU-computed experts fall to the scalar _generic dots.  iq_avx1.cpp is the older
CPUs' copy of iq_avx2.cpp in the kq_avx1.cpp pattern: a separate TU compiled
-msse4.2 -mavx -mno-avx2 -mno-fma -mno-f16c, gated at runtime behind cpu_avx1_ok() and
only when AVX2 is absent, so no mainstream path changes.  128-bit integer lanes
(pshufb/sign/maddubs are SSSE3), 256-bit float accumulation, software half-to-float,
no gathers (the Fmt32 non-gather path), one IEEE mul per value like the AVX-2 copy.
Plus q8k_quant_avx1, the byte-identical Q8_K activation quantizer in SSE4.2/AVX1.

iq_avx1_parity: all six formats, nt=1..8, bit-exact across widths (#152) and within
1e-5 aggregate of ggml's generic dots; q8k_quant_parity now checks the AVX1 copy too.
gu rows take the AVX-2 mt_min policy (STRATA_IQ_MT_MIN), down rows always; kill switch
STRATA_NO_IQ128=1.  E5-2470 v2 (Ivy Bridge): 1.2-3.1x per expert at nt>=2, 2.0x on the
Q8_K quantizer; serve prefill +36-41%, warm decode 22-25 -> 46 tok/s.

Known pre-existing gap found while testing: pool_tasks_test SIGILLs on non-AVX-512
machines even on a clean tree (it predates the ISA floor and is not gated).
2026-10-09 16:29:59 +02:00
bossman 7b14a443ee cpu: q2_avx1 - the Q2_0 rows and the Q8_1 quantizer for CPUs with AVX but no AVX2 (fixes #1699)
q2_rows_any and act_quant_any had two rungs, AVX-512 and AVX2, and the else branch
checked nothing: an AVX-only CPU was handed AVX2 code and died with SIGILL (found on
an E5-2470 v2; pool_tasks_test and q2_bitplane_parity crash on a clean tree, and any
future caller inherits the trap).  The third rung: q2_avx1.cpp in the iq_avx1.cpp
containment pattern (-msse4.2 -mavx -mno-avx2 -mno-fma -mno-f16c, a #error guard,
called only behind cpu_avx1_ok() && !cpu_avx2_ok()).  128-bit integer lanes (maddubs,
madd), 256-bit float accumulation, software fp16 scales; the legacy kernel keeps the
AVX-2 kernel's lane pairing and reduction tree (its FMA becomes a mul+add, last bits),
and the bit-plane kernel and its image builder port as-is (pure SSE).  The AVX-2 and
AVX-512 paths are untouched.  q2_bitplane_parity now runs on the AVX-2 pair where the
CPU has AVX2 and on the AVX1 pair below it, and skips cleanly without AVX.

E5-2470 v2: pool_tasks_test 168 bitwise comparisons pass, q2_bitplane_parity PASS -
both SIGILL'd before.
2026-10-09 16:29:59 +02:00
Niko1221 72a67c8e90 sycl: the grouped XMX kernels (sub-group 8, joint_matrix 8x8x16) are Arc A-series only: an AOT build for Xe2 / Xe-HPC leaves them out (STRATA_NO_DG2_XMX), STRATA_PF_XMX is ignored with a message on Battlemage, and xmx_group_parity skips there
PR #1711 as posted made the Arc Pro B70 AOT build (bmg-g31) fail to link: the backend compiler rejects the sub-group 8 kernel.
2026-10-09 16:01:57 +02:00
Niko1221 115e8ce334 Windows HIP: the auto cache's 2560 MiB reserve floor also covers 16 GB cards (they report 15.9 GiB) (#1709; Windows HIP test pending) 2026-10-09 15:50:58 +02:00
W1nge 268647d2ec prefill: take expert blobs from a helper's VRAM cache when it holds them
A helper GPU's expert cache already holds a copy of the blobs the prompt path
streams, and the helpers compute nothing while a prompt is read on a serial
request path. Add RemotePrefillSource, an ExpertSource that wraps the file
source and asks the helpers first: a blob a helper holds is copied
cache-to-host on that helper's own stream, misses are merged into one call to
the fallback's existing batched reads, and every other decision is forwarded
unchanged.

Opt-in through STRATA_PREFILL_HELPER_SOURCE; it applies only where the serve
path is serial (one GPU, no layer split, no batch slots) and the prompt path
streams every expert the main card does not hold. Same-day A/B on an
11-request fixture: 91.5 s -> 65.7 s of prefill (-28.3%) and 92.1 GB (64.8%)
fewer bytes read from the files.
2026-10-09 15:49:23 +02:00
Niko1221 acee40a597 serve: a sampling field sent as a numeric string is read and used, not a 400; only values that cannot be read as the right type are refused (#1663) 2026-10-09 15:43:57 +02:00
Niko1221 40bdd86d6d serve: content parts the server does not read (tool_reference, document, search_result, server tool blocks, file, audio) are left out as before, with a debug line, not a 400 (#1664)
Real clients send them next to text the model can answer from.
2026-10-09 15:37:32 +02:00
Niko1221 a6ae247561 serve: a reasoning_effort key in the run config sets the thinking level of requests that name none; --reasoning-effort in args is lifted into it with a note (#1641) 2026-10-09 15:36:37 +02:00
Niko1221 ccd0062fa8 bench: drop the tool-attribution line from the 2x RTX 3080 report (the numbers are the submitter's) 2026-10-09 15:36:37 +02:00
Niko1221 5267eb6565 docs: COMMUNITY_BENCHMARKS.md lists the three new reports once, after the existing ones 2026-10-09 15:36:36 +02:00
Niko1221 4ed0ed68d7 bench: COMMUNITY.md rows for the community folders ported from #1638 #1647 #1655 #1681 #1692 #1695 #1716 #1717 #1657 #1640; the 6900 XT 0.1.40.1 row now has the numbers #1657 added 2026-10-09 15:36:36 +02:00
zihaomu 22b3b80970 bench: single R9700 performance report (PR #1638) 2026-10-09 15:36:36 +02:00
noon-at-cgn 9a06a54aa8 bench: community report, 2x RTX 3080 20 GB, UD-Q4_K_XL, 2 x 262K lanes in a 512K pool (PR #1640) 2026-10-09 15:36:36 +02:00
Mitsuasa513 3c2eb23a00 bench: community report, a Tesla V100 (sm_70) confirms the opt-in Volta decode kernels (PR #1647) 2026-10-09 15:36:36 +02:00
Cass 2d49e6178d bench: community report, 4x RX 7900 XT (gfx1100), Flash-Next IQ3_S at 262K, five opt-ins off vs on, engine 0.1.41 (PR #1655) 2026-10-09 15:36:36 +02:00
Vop3p fed30695a1 bench: community report, 2x RX 6900 XT (gfx1030) + Ryzen 5 5600X, IQ3_S at 131K on 0.1.41 (PR #1657) 2026-10-09 15:36:36 +02:00
KarlGrier c8250a09b4 bench: community report, RTX 5080 16 GB + RTX 4060 Ti 8 GB (helper cache), IQ3_S at 4K / 32K / 128K on 0.1.41 (PR #1681) 2026-10-09 15:36:36 +02:00
David 5d7b7b6719 bench: existing RoPE vs YaRN at 512K and 1M, with setup guide (PR #1692) 2026-10-09 15:36:36 +02:00
Your Name a43b94c4c4 bench: P4 prefill 78.8 -> 114.8 tok/s with existing auto sizing; expert reuse roofline (PR #1695) 2026-10-09 15:36:36 +02:00
nsbradley88 257b6c2b4c bench: community report, Strix Halo laptop (Radeon 8060S, 70 W), v0.1.41, UD-IQ4_XS (PR #1716) 2026-10-09 15:36:36 +02:00
libratechw 1fe02e5de2 bench: community report, Radeon 8060S (gfx1151) on a Ryzen AI Max+ 395, UD-IQ4_XS, engine 0.1.41 (PR #1717) 2026-10-09 15:36:36 +02:00
Moge800 e651c0cd16 docs: improve Japanese README wording and readability 2026-10-09 15:36:36 +02:00
nekomario28 cc75882480 docs: STRATA_PREFILL_CPU_SHARE=auto is not repeatable run to run (#1684)
The share is picked from wall-clock timings, so the same short-prompt
request can split the experts differently each run and the answer moves
in its last bits - a resumed conversation's re-read need not match its
first (31 of 1,536 teacher-forced argmax tokens differed in the
reporter's test).  A fixed share repeats; 0 gives a run without it
byte-for-byte.
2026-10-09 15:36:36 +02:00
Niko1221 92507aac38 setup: on a unified-memory APU write the expert cache as a number that leaves the OS its RAM, not auto (#1715) 2026-10-09 15:24:07 +02:00
LeGeRyChEeSe 66aaed2119 tools/agent_turns_bench.py: decode A/B on coding-agent turns over this repository, per-turn ratios
Five simulated agent sessions over the repository's own files (system prompt, four tools, real files as tool
results, at most 2000 lines per read): 23 turns from ~2.5K to ~100K tokens, consecutive turns sharing their prefix.
Arms as ab_engine.py takes them; rounds mirror the arm order on fresh servers; prints the median of the per-turn
decode ratios to the first arm and the turns it won. Nothing personal and nothing downloaded.
2026-10-09 15:23:32 +02:00
LeGeRyChEeSe 65268d9961 STRATA_ROUTE_TAIL_SKIP=R: missed experts routed only at rank >= R are neither copied nor computed
A missed expert that every token of the verify window routes at rank R or lower in weight contributes zero
(no renormalisation): less PCIe traffic and CPU expert work. Counted in the request log. Opt-in, changes
the output.
2026-10-09 15:23:32 +02:00
LeGeRyChEeSe 2de9ae061f STRATA_ROUTE_PRIOR=lambda: cache-conditioned routing in verify windows (opt-in, changes the output)
The top-10 is picked again with a logit bonus lambda for the experts resident in VRAM; the weights stay the
router's probabilities renormalised over the 10 picked. After 'Mixture of Cache-Conditional Experts'
(arXiv 2412.00099), without training. Complements STRATA_ROUTE_RESIDENT (a tail swap within a margin):
every rank can move, by an amount the logits decide.
2026-10-09 15:23:32 +02:00
sergiywith f04ab568fe Resident RAM mode on Windows: the GPU cache's fill and the RAM copy read experts.bin unbuffered, the view closed meanwhile
With --resident-experts (setup's low-RAM mode when the experts the GPU does not hold fit the RAM) the start copies
the profile fill of the GPU cache and the RAM copy (pin_cache_complement) through the mapped view of experts.bin.
Every page it touches sits in the process working set beside the page-locked copy until #467's trim after the fill
and the VirtualUnlock after the copy: on a PC short of RAM that pages other memory out, and the copies run at the
page-fault rate (~0.85 GB/s here).

FileExpertSource::begin_startup_unbuffered closes the view, the section and the buffered handle, opens the
unbuffered handles (open_direct) and reads the first blob both ways; if the open fails or the two differ, the view is
mapped again and the start copies through it as before.  The fill (prefetch_pairs) and the copy (pin_cache_complement)
then take their existing unbuffered paths (read_direct: requests merged up to 32 MiB).  end_startup_unbuffered maps
the view again (map_view, the size checked) and closes the unbuffered handles and the stage buffers, so the file tier
is chosen as before.  Same bytes in the same places.

Windows and experts.bin only: the call is under _WIN32, and the GGUF in place, an unbuffered file tier and runs
without a RAM copy are unchanged.  STRATA_STARTUP_UNBUFFERED=0 gives the mapped copy back; STRATA_VERIFY_COMPLEMENT=1
prints checksums of the filled cache slots and of the RAM copy.  pin_cache_complement no longer VirtualUnlocks the
range of a closed view.

IQ2_XS (33 GiB experts.bin), RTX 5090, Samsung 9100 PRO, --resident-experts --expert-cache auto (~15,200 slots, a
16.4 GiB RAM copy), interleaved against fb58e0db:
- 21 GiB available (a RAM ballast; #467's 32 GB PC had 20.7 GiB), 6 pairs: ready 42.5 -> 9.6 s (chat), 40.7 -> 10.2 s
  (32K); the fill 842 -> 6,914 MB/s, the copy 13.7 -> 3.3 s; lowest available 0.25 -> 4.5 GiB; pages written out
  ~60,000 -> 579-3,340; 3 of 12 base starts held available RAM under 1 GiB for 2 s, none of 12 here.
- 128 GB: peak working set 32.6 -> 18.3 GiB; a restart with the file cached 13.6 -> 8.3 s; the 32K prompt and decode
  unchanged.
- Same bits: p2k 200 tokens and first-token logits, 32K 64 / 256 tokens, as fb58e0db; STRATA_VERIFY_COMPLEMENT=1
  checksums identical to STRATA_STARTUP_UNBUFFERED=0 (12,595 and 15,438 slots).
2026-10-09 15:23:14 +02:00
Niko1221andRealScaniX 1a62f6df6e serve: a tool set that comes back in another order keeps the order it was first seen in, so the prompt prefix and its cache survive (#1624)
Based on RealScaniX's sorted-by-name change; the first request of a set stays byte for byte as the client sent it.

Co-authored-by: RealScaniX <[email protected]>
2026-10-09 15:23:02 +02:00
W1nge 29d93c6407 test: keep vision device isolation independent of local GPU order 2026-10-09 15:22:14 +02:00
chaog992 efaa87aba2 tools tests: align test_setup_risk's WSL mock with install() (the KV-streaming rule test failed inside WSL)
RulesAgreeWithTheWrittenConfig.test_the_streaming_rule_is_the_flag_setup_writes runs setup.main() through
test_setup_golden's install(), which mocks is_wsl() to False, and then asked setup.kv_streaming_wanted() outside
that mock. Inside WSL the real is_wsl() is True, so the rule answered "no streaming" while the config setup wrote
for the mocked PC has --kv-resident, and the test failed at its first 131072 case (Q2_0, int8, auto). With is_wsl()
the same on both sides, setup and the rule agree in all 54 cases, inside WSL and out.

The test method now runs under the same is_wsl mock as install(), the test-only fix the unsloth tests got for #974
(d1ec32b). setup.py is unchanged; where is_wsl() is already False (Windows, native Linux) the test runs as before.

Tests: python3 tools/test_setup_risk.py (WSL2, Python 3.12.3: was FAILED (failures=1), now 40 tests OK); the other
27 tools/test_setup_*.py files give the same results as before.
2026-10-09 15:22:14 +02:00
hash13 bafc8dd556 monitor: show prefill rate in recent requests 2026-10-09 15:22:13 +02:00
zeevo 95452866b2 add a prometheus metrics endpoint 2026-10-09 15:22:01 +02:00
midagedev 1f6958c13b serve: a sampling field of the wrong type is a 400 that names it
A request with "temperature": "0.7", "top_k": 40.0 or "seed": true was
answered 200 and sampled greedy: the value replaced the configured
default in run()'s merge, then sampling_keys dropped it as not a number.
check_request_sampling refuses it by name before anything is sent, as
POST /settings and the run config already do. Only types are checked;
ranges, null fields and in-type values behave as before.
2026-10-09 15:21:45 +02:00
Niko1221 fbc60ee157 logit_bias: leave the contributor's raw receipts out of the tree, point the doc at the reproduction script 2026-10-09 15:21:38 +02:00
midagedev 5804094b2f serve: a content part the server does not read is a 400 on chat and messages, as on responses
A chat "file" or "input_audio" part, and a messages "document" block
(also inside a tool_result), were dropped without a word: the request got
a 200 and the model answered about a file it never saw. _check_parts
refuses them while the request is converted, naming the type and where
it sits, with /v1/responses' wording. Text, images, thinking, tool_use
and tool_result read as before; redacted_thinking is still dropped; a
chat "refusal" part reads as its text, as on /v1/responses.
2026-10-09 15:21:32 +02:00
David 18e7487e52 docs: demonstrate Hello token bias with measured before and after requests 2026-10-09 15:21:30 +02:00
David dcc6f756cc fix(api): apply request logit biases in CUDA and HIP sampling 2026-10-09 15:21:29 +02:00
Niko1221 52f548f45a server: death_note scans the last 256 KB for the watchdog line, so a long verify trace cannot hide it (#1705) 2026-10-09 15:20:31 +02:00
CC-David-CC c81f99849e fix: defer pipelined one-token commits to the host verdict 2026-10-09 15:20:28 +02:00
ruibeikaa dc9b219944 prefill: let native-layout packs take STRATA_MMQ_RESIDENT_SORT_NE
The row-count sort of a fully resident layer's experts skipped native-layout packs, though slot/src/off are
built the same way for both layouts. 4x GP100 (sm_60): 1.2K prompt 7.27 -> 5.32 s, 35K 63.3 -> 54.1 s, the
same bits. The switch stays opt-in.
2026-10-09 15:20:05 +02:00
sergiywith d0336684e5 kv-grow: kvg_trim uploads the residency table with res_put, which waits for the copy
kvg_trim refilled the slots the K/V gave back and then uploaded host_res with a plain cudaMemcpy from pageable memory.
That returns once the table is staged, before its DMA lands, and nothing after it waited for stream 0: the next
request's session_zero syncs main_stream only, and a prompt part of --short-read tokens or fewer goes straight to
ver.run, whose graph reads hits_.d_res on the verifier's cudaStreamNonBlocking stream. res_put (#550) is the upload
every other residency change uses: the same copy, then cudaStreamSynchronize(nullptr).

kvg_ensure's upload needs nothing: qsa_kv_elastic_grow ends in cudaDeviceSynchronize before anything reads the table.
Only --kv-grow (opt-in) runs kvg_trim, so the default path is unchanged.
2026-10-09 15:20:05 +02:00
nekomario28 14b9dd2e52 serve: keep-alive while a tool call's held-back argument is generated (#1666)
With stream_tools an array or object argument is held until it is whole,
so a large one (a measured 66 KB edit_file call) left the stream silent
for minutes while tokens were generated - a client's idle timeout then
ended a healthy request (Forge's is 120 s).

The token loop now tracks when anything last went out and sends the
engine's own keep-alive ping after KEEPALIVE_S (10 s) of silence - the
same ping the quiet-engine heartbeat sends, so clients already ignore it.
The reporter's reproduction measured a 27.8 s longest gap on a 7 KB
array (minutes on the 66 KB one); with this change it is 10.0 s and the
call arrives intact.

Change and live measurements from the issue; adds the ping-cadence unit
test (a patched clock over a held-back <tool_call>).
2026-10-09 15:19:48 +02:00
Niko1221andjkuepker 3a55dfd253 HIP: STRATA_HEAD_MIX_MULTI and STRATA_ONE_TOKEN_COMMIT as opt-ins (=1), default stays off (#1479)
Patch from the issue by jkuepker.

Co-authored-by: jkuepker <[email protected]>
2026-10-09 15:19:04 +02:00
Niko1221 00b14af827 sycl: #1710 stays opt-in: STRATA_SYCL_A770_FAST=1 gates the fast router, the paired payload stores and the paired mapped read; plain payload stores (i915, stepped window) are never paired
The default router, publish and copy_or_zero kernels are the f-142 ones. The probes set the variable (and
STRATA_DOORBELL_CHECK=1 for publish_res, which copies x only on a miss otherwise).
2026-10-09 15:18:03 +02:00
duncanmcqueen 174c74c07f sycl: add experimental A770 router and mapped-transfer tuning
Port the fork's fast router with a bracketed reciprocal and serial fallback. Add bitwise comparisons against the existing router, including ties, NaN tails and the forced serial path. On the A770's 256-expert/top-10 shape, measured host submission plus execution fell from 395 to 119 us per call.

Pair activation payload stores and mapped result reads into 64-bit uncached transfers, preserving the checksum and unaligned/odd-tail fallback. Register router and payload probes with CTest. The two probes, elementwise_parity and router_top10_parity passed on the A770.

Add the fork's opt-in host-clock stage profiler for further investigation. Profiling remains experimental: enabled runs encountered a payload-arrival timeout, and review flagged concurrent atomic host writes versus non-atomic device clock reads as a synchronization concern. The profiler is not needed for normal inference.

With CPU-computed misses, cache borrowing and an 8.98 GiB VRAM cache, unprofiled Coder IQ1_M runs generated 128 tokens at 11.9-13.5 tok/s and returned final arithmetic answer 41. These runs retained 128K context capacity but used short prompts; the 20 tok/s goal has not been achieved.
2026-10-09 15:17:50 +02:00
midagedev e85567b617 generate, mtp: near the context's end the spec window and its drafts stay inside it
The admission checks prompt + max_new <= max_context, but the spec loop
opened every window at S_mtp rows, so a request that fits ended with
"ran out of context" (rc 2) once p + S passed the end, and printed
nothing. The window now takes only the rows the context still holds.
MtpDrafter::draft wrote draft j at cell p + a + j for every step, past
max_cells near the end, where the page table has no entry; it now drafts
only the cells that exist. Far from the end nothing changes.
2026-10-09 15:17:33 +02:00
saikiran-rs 730700126b verify: initialize remote expert helper state in both batch paths 2026-10-09 15:17:33 +02:00
nekomario28 092fc3c5c6 setup: pass CC/CXX/CUDAHOSTCXX to the CUDA build's cmake (#1645)
CUDA 12 refuses a GCC newer than 14, and the docs already tell the user
to run CXX=g++-14 ./setup.sh - but the environment only reaches CMake on
a build folder's FIRST configure, so on a retry (or the encoder's own
folder) the flag never landed: the reporter's run died at the last step.

host_compiler_defs() maps them onto -DCMAKE_C_COMPILER /
-DCMAKE_CXX_COMPILER / -DCMAKE_CUDA_HOST_COMPILER for the engine and the
CUDA image encoder; CUDAHOSTCXX defaults to CXX, as CMake's does.  A
C++-only build (the CPU encoder) is not given the CUDA one.
2026-10-09 15:17:13 +02:00
noon-at-cgn 2eafe7f90d docs: the adaptive tier in batch windows - measured on 2x RTX 3080 20 GB, tier on, blocking, frozen and off 2026-10-09 15:16:22 +02:00
Andy Slater c2d008bc80 sycl: stepped verify window - CPU experts served between graph segments, cached host buffers and plain payload stores
STRATA_VERIFY_STEPPED=1 (Arc A-series): the doorbell window is captured as graph segments ending at each layer's
publish; the host serves the CPU experts between segments, pipelined with the plan copy + VRAM groups on a helper
thread pinned to the host thread's SMT sibling. No kernel then reads a host store while it runs and no host reads a
kernel store while it runs, so the window's mapped buffers are ordinary host USM and the publishes use plain stores
(STRATA_VERIFY_HOST_UNCACHED=1 keeps both uncached). A770, Coder IQ1_M, 8K prompt: GPU wait per window ~70 -> ~43 ms,
decode 22.8-25.0 -> 31.8-32.5 tok/s; 32K 27.2 tok/s.
2026-10-09 15:16:22 +02:00
noon-at-cgn b5d520313a batch-groups: --adapt-async 1 also keeps the 0.1.41 auto default at one group (said once)
The pipelined slot groups never run the adaptive tier, so --adapt-async 1 with --batch on a layer split and no
--batch-groups resolves auto to one group, like --batch-mtp (batch_groups.hpp, batch_groups_test); --batch-groups G
or auto given on the command line is honoured and turns --adapt-async off through the existing gate.
2026-10-09 15:16:22 +02:00
noon-at-cgn 495dc43690 batch: tick the asynchronous adaptive tier (--adapt-async 1) between batch windows; --adapt-min-gain
batch_step advances the async round one step per window (never between a prompt's chunks), commands drain the round in
flight before they run, a round with nothing to swap ends at its first step, and --adapt-min-gain sets the bar a swap
must clear (default 1.5, as before) for both tiers. --adapt-async is no longer refused with --batch slots (still with
--batch-groups).
2026-10-09 15:16:22 +02:00
noon-at-cgn e989c67194 batch: the adaptive VRAM expert tier keeps adapting in batch windows; per-stage timing in the strata batch line
batch_step now runs adapt() every --adapt-every windows on a thread beside the commit and the drafts, lands the swaps
(apply_pending) before the window ends, and pauses while a prompt holds a loan of cache slots. The timing line adds the
adaptive tier's rounds/swaps and wait, each stage's GPU-reach wait and pool time, and the VRAM/PCIe/CPU entry split.
2026-10-09 15:16:22 +02:00
noon-at-cgn de71d7bef4 docs: batch MTP on a layer split - measured on 2x RTX 3080 20 GB (0.1.41 build and the earlier 0.1.40.3 runs), with what was not measured 2026-10-09 15:16:16 +02:00
noon-at-cgn f90bec6f44 batch-mtp: not with helper expert caches on a layer split (not run); docs say how it meets --batch-groups
The 0.1.41 default --batch-groups auto, the pipelined path's lack of slot drafts and the helper-cache refusal are
written down in docs/BATCHING.md (the limits list and the --batch-mtp paragraph).
2026-10-09 15:16:16 +02:00
noon-at-cgn cb6c549c8a batch-groups: the 0.1.41 auto default gives way to --batch-mtp (one group, said once)
--batch-groups auto became the default for a layer split with --batch >= 2, but the --batch-mtp gate ran before
the groups were resolved: it saw 1 group, kept batch-MTP on, and the groups were then raised to 2 - pipelined groups
do not run the slots' MTP drafts (#1413). The resolution is now a pure helper (include/strata/program/batch_groups.hpp,
CPU test src/program/batch_groups_test.cpp): with --batch-mtp and no --batch-groups the default is one group and
the log says why; --batch-groups N (N > 1) or --batch-groups auto given on the command line pipelines the groups and
turns --batch-mtp off, as before.
2026-10-09 15:16:16 +02:00
noon-at-cgn 70fc0ba46d batch-mtp: log the accepted proposals in the batch summary 2026-10-09 15:16:16 +02:00
noon-at-cgn 48f2c47d54 batch-mtp: work with a layer split (slot drafters on the last stage's GPU, grouped slot rows through every stage)
- generate.cpp: --batch-mtp accepted with a layer split on separate GPUs (refused with --batch-groups); slot drafters
  load from the last stage's slot sessions under the last stage's device guard and bind to its weights/head; their
  residual buffers, row copy (async on the drafter's stream) and draft-KV copies run on the drafter's device; every
  stage verifier gets the 8-row window size and the batch graph limit; slot-drafter logits are not charged to CUDA0.
- verify.cpp: stage_batch's layout rules move to include/strata/core/batch_rows.hpp (CPU test batch_rows_test); grouped
  rows are allowed on a stage that hands rows on, the hand-off bound is kVerifyMaxT rows.
- docs/BATCHING.md, --batch-mtp help text.
2026-10-09 15:16:16 +02:00
sergiywith c396dc9c39 setup: the page-file warning advises a fixed size, and says why when Windows is set to grow the file
Step 1 warns below 4 GB of page file (issue #60) and advised "Set it to System managed". In #60 that did not help:
"System managed" and 4096-32768 MB still ran out of commit, and a fixed 64 GB worked - a page file Windows grows on
demand may not grow in time while WDDM charges the card's memory to the commit.

- The advice is a fixed size: Custom size, the same initial and maximum size, 65536 MB (PAGE_FILE_FIXED_MB).
- page_file_setting() reads the Virtual memory setting (PagingFiles; "?:" is the drive in ExistingPageFiles). When a
  file is system-managed or its initial size is below its maximum, the warning says that it grows on demand and that
  a growing page file may not keep up (#60).
- page_file_gb() still counts only what is there for sure: the commit limit minus RAM, as before, or a file's
  configured initial size when that is larger (within its drive's free space). Never the maximum or what a
  system-managed file may grow to. The 4 GB threshold is unchanged.
- docs/AI_SETUP.md and docs/DETAILS.md give the same advice.
- tools/test_setup_page_file.py: the parsing, the reading, the advice, and step 1 under --check on the golden
  harness's 32 GB PC (registry, drives and GlobalMemoryStatusEx mocked; runs on Linux too).
2026-10-09 15:16:03 +02:00
SuperLee 52c743d550 mcp: let the swift family take IQ3_S too; docs
The MCP server's FALLBACK_MODELS carries its own copy of the model
table, so #1688's setup.py fix leaves the MCP install path rejecting
--family swift --model IQ3_S. Drop the families gate there as well,
flip the validation test, and update the three doc lines that say
IQ3_S is original-model-only.

Measured on an RTX 3090 pair (engine 0.1.40.3, Swift IQ3_S, 200K
context, MTP spec on): 91-137 tok/s decode, expert cache hit rate
96-99%, KV streaming 98.6-99.6% VRAM hits.
2026-10-09 15:15:53 +02:00
nekomario28 614053a4d7 setup: let the swift family take IQ3_S (#1651)
UkisAI published a Swift 1.5 IQ3_S tier on 2026-10-08; the
"Swift 1.5 has no IQ3_S" restriction is stale.  Both shards match the
family's file pattern exactly (names/sizes/SHA-256 verified against the
Hub listing), the shard count and the shared BF16 mmproj are unchanged,
so it takes the same path the other Swift tiers already use.
2026-10-09 15:15:53 +02:00
Andy Slater 0e583707a5 sycl: grouped XMX passes the decoder thread that writes [o, o+8) for the run-of-8 IQ decoders
The IQ2/IQ3/IQ4_NL/IQ1_M decoders now write values [8 tid, 8 tid + 8) of a superblock (coalesced runs, as Q2_0
already did); iq_xmx_grouped still passed the IQ4_XS-style (sub-run, 32-block) index for them, so the B tile held
the wrong values (xmx_group_parity relerr 7-17 on types 18/20/21/22).  All six types back to ~1e-6.
2026-10-09 15:15:25 +02:00
Andy Slater ce60cc373f sycl/A770 prefill: grouped XMX expert GEMM (opt-in STRATA_PF_XMX=1/2)
- xmx_moe: joint_matrix FP16 GEMM over a group of up to 16 experts per launch (grf 256).
  Standalone vs oneMKL on uneven routing: down 1.75-6x, gate/up 0.8-3x (oneMKL kept above
  STRATA_PF_XMX_GU_MAX=250 tokens/expert).
- STRATA_PF_XMX=2: iq_xmx_grouped decodes IQ3_XXS/IQ3_S/IQ2_S/IQ4_XS gate/up and IQ4_NL/Q2_0
  down rows in-kernel (no FP16 staging); xmx_group_parity: rel err < 2e-6 vs dequant+oneMKL.
- End to end (8K/32K prompt, A770): +2-5% only; default off.
2026-10-09 15:15:25 +02:00
W1nge 538ef3aa5b prefill gemm: run a failing FP16 shape on a fixed cuBLAS algorithm; the default one fails once a second device's context exists (issue #1650) 2026-10-09 15:15:06 +02:00
Cass 553b0b788c pipeline-windows: overlap three or more layer-split stages; HIP drafter scratch race
--pipeline-windows 2 now runs on any layer split with two or more stages. Every
stage but the last is a front stage: the guessed window follows the verified
one through them one card behind (an odd verifier and an odd hand-off on every
boundary, a pair of GDN snapshots on every front stage, commits and the undo
per stage), and only the last stage waits for the verdict. The pipelined prompt
read (--pipeline-windows 1) and --adapt-async stay two-stage.

HIP: the draft layer's per-row gr_read used the last stage's session scratch
(ss.block.gr), which that stage's verifier also uses in lm_head_mix. A chain
launched at a verdict and the next window's last stage can run on the card at
once after a guess that held, and the window's head then read a clobbered mix.
The draft layer now carves its own GrWorkspace.

Measured on 4x RX 7900 XT (Flash-Next GSQ-RCO IQ3_S, split 12,24,36, every
expert in VRAM, greedy, reasoning off): decode 59.4 -> 67.6 tok/s at 4K and
51.8 -> 65.6 at 32K. Greedy text identical to the serial loop on four prompts
with STRATA_IQ_MT_MIN=1 --pcie-frac 0 --adapt-every 0, also with every guess
forced wrong. Debug knob STRATA_PIPELINE_SPEC_DEPTH. Two-stage path not re-run
on two cards.
2026-10-09 15:14:37 +02:00
Alex Sadleir 7e940789e6 Clean up comments in generate.cpp
Removed comments regarding memory allocation issues and synchronization behavior.
2026-10-09 15:14:19 +02:00
maxious 4340476438 sycl: pin the PCIe probe's source buffer
probe_pcie_h2d_gbps: a pageable 256 MiB source made the copy engine fault
inside the driver's bounce path on a layer split (755x bcs VM_NOT_FOUND at
the fixed VA 0xffffc001ffa10000, L4 = the region never mapped for that VM,
intermittent on fresh boots, wedging the whole load in both the 0.1.40
port and 0.1.41). Pin the source (sycl::malloc_host) and free it with the
matching sycl::free. Validated: two consecutive loads reached the window
without the wedge.

All the split-fault instrumentation (guard watch, stage stamps and the
STRATA_STAGE_WAIT serial gate, terminate dumps, coredump grabbers) lives
on scratch/split-fault-instruments.
2026-10-09 15:14:19 +02:00
Katsumi Takeuchi c98902a657 sycl: MtpDrafter's queue defaults to nullptr, so a run without --mtp no longer crashes at exit
dpct turned `cudaStream_t cs_ = nullptr` into `&dpct::get_in_order_queue()`: a drafter that never loads keeps it, and
its destructor destroys the device's default queue (exit 139, sometimes UR_RESULT_ERROR_UNINITIALIZED, after the output).
Arc Pro B70 at fb58e0d without --mtp: 3 of 3 runs crashed before, 0 of 3 after; output unchanged with and without --mtp.
2026-10-09 15:14:19 +02:00
Niko1221 04dbe56145 CPU prefill share: skip the default when host memory is short, and never fail a chat on its buffers
0.1.41 report (RTX 5090, tight RAM): chats failed and the engine restarted until STRATA_PREFILL_CPU_SHARE=0.
- share_rules: default_share_memory_verdict. The default is not armed when the RAM this process can get
  (cgroup-aware) is below the share's buffers + 3 GiB, when the Windows commit left is below buffers + 4 GiB,
  or when the experts it would read are file pages the free RAM cannot keep. One log line says why and that
  STRATA_PREFILL_CPU_SHARE=auto turns it on anyway. An explicit setting is kept and only warns.
- prefill: the share's two pinned buffers are taken once at their largest (an expert takes at most MAXT
  tokens' rows); a failed allocation ends the share for the run instead of failing the request.
- unit test, docs/DETAILS.md.
2026-10-09 15:10:32 +02:00
Niko1221 65d97b0ab6 memory_guard_test: build on Windows (process.h getpid instead of unistd.h) 2026-10-09 15:00:53 +02:00
Niko1221 abbd0ea9aa sycl prefill: bytes_needed* take the src flag like the shared header (merge of the multi-GPU stream) 2026-10-09 15:00:53 +02:00
Niko1221 76790f277a test_slots: compare the relative save dir through realpath (8.3 temp dir names on Windows) 2026-10-09 14:47:49 +02:00
Niko1221 3d247dd49c Cap a thinking budget at max_tokens less an answer reserve, so the reply is not cut off while still thinking (#984) 2026-10-09 14:47:32 +02:00
Niko1221 bce19db445 Progress line shows the thinking budget, not max_tokens, while one applies (#984) 2026-10-09 14:47:31 +02:00
Niko1221 5a6ef1d768 Merge branch 'f-142-amd' into f-142
# Conflicts:
#	serve/server.py
2026-10-09 14:47:31 +02:00
Niko1221 3fb23b146a Merge branch 'f-142-intel' into f-142 2026-10-09 14:47:08 +02:00
Niko1221 43b105a753 Merge branch 'f-142-mgpu' into f-142
# Conflicts:
#	src/program/generate.cpp
2026-10-09 14:47:08 +02:00
Niko1221 e85f56209c Merge branch 'f-142-engine2' into f-142
# Conflicts:
#	src/core/conversation_memory.cpp
2026-10-09 14:46:54 +02:00
Niko1221 760007e311 Merge branch 'f-142-carry' into f-142
# Conflicts:
#	src/program/generate.cpp
2026-10-09 14:46:36 +02:00
Niko1221 f5aa356f14 Merge branch 'f-142-small' into f-142 2026-10-09 14:46:18 +02:00
Niko1221 2dc6eeae42 Merge branch 'f-1411' into f-142 2026-10-09 14:46:18 +02:00
Niko1221 b70bc033b5 sycl: plain payload stores on the i915 driver (the uncached hint is for xe); STRATA_DOORBELL_PLAIN overrides 2026-10-09 14:08:15 +02:00
Niko1221 f583670cab sycl: Alchemist own-buffers prompt path only on cards with >= 12 GiB; the payload copy stays conditional unless STRATA_DOORBELL_CHECK=1 (A750 A/B, PR #1602) 2026-10-09 13:09:03 +02:00
Niko1221 68d66b117f memory_guard_test: a .. component in the cgroup path falls back without a cap 2026-10-09 12:38:17 +02:00
Niko1221 b0531c8dad memory_guard_test: memory.current "max" without a cap falls back too 2026-10-09 12:37:43 +02:00
Niko1221 c9680eeff6 memory guard: unusable cgroup files without --memory-limit-mib fall back to MemAvailable with one warning; with the flag the sample stays unknown 2026-10-09 12:37:05 +02:00
Niko1221 399a75c8ad tools: batch_groups_cache_test.py reads the engine log only 2026-10-09 11:51:43 +02:00
Niko1221 da6f23f8e9 ple: print the direct-read probe's rate when it is fine 2026-10-09 11:21:33 +02:00
Niko1221 3157c78935 sycl: the doorbell payload checksum wait is opt-in (STRATA_DOORBELL_CHECK=1): on an Arc A750 it cost 17% of decode (#1606, PR #1602) 2026-10-09 11:15:36 +02:00
Niko1221 5025d63a22 sycl: STRATA_SYCL_JIT_STATELESS option for the JIT build's 64-bit-address flag (default ON, as in #1602) 2026-10-09 11:15:35 +02:00
Niko1221 7609cc18a1 arch_defaults gfx12: drop STRATA_HCD_EXACT (inert without the gfx1151 tuning table, and noisy); five prompt switches, measured +0.8/+1.6/+1.8% at 4K/16K/32K on an R9700 (IQ3_S) 2026-10-09 10:51:41 +02:00
Niko1221 31ffa153c6 tools: session_pressure_test.py and batch_groups_cache_test.py 2026-10-09 10:30:16 +02:00
Niko1221 22a893d0c6 Version 0.1.41.1: project version and the setup minimum engine 2026-10-09 10:24:42 +02:00
Niko1221 8bf203f56b STRATA_ROUTE_RESIDENT: per-GPU counters owned by the verifier (#1578)
The counter block was one process-wide cudaMalloc on whichever device was current at the first Verifier::init, unreachable from
the other stage's kernels on a layer split without P2P. Each verifier now takes its own device's block in init (outside graph
capture), keeps the pointer, and a launch never picks by the host thread's device; a failed allocation runs without counters.
The exit report prints one block per GPU when there are several (single GPU: the same lines as before). The reporter's rare
fault with --pipeline-windows 2 after this fix is not reproduced here (needs two GPUs).

STRATA_ROUTE_RESIDENT_MTP=1 (experimental, opt-in, changes the output): the MTP draft's router gets the same residency bias,
with the last layer's residency row as its table (single GPU).
2026-10-09 10:11:10 +02:00
Niko1221 56354964d8 Stage buffers: fence the lent-slot refill copies too (#1237)
The refills of the slots a prompt borrowed (and the VRAM-resize refill) queue hundreds of cudaMemcpyAsync out of blob_stable()
pointers, which for a GGUF source are the recycled stage buffers. With STRATA_STAGE_PIN=1 the 257th assembly overwrote a
buffer whose copy had not run: IQ3_XXS lowmem (budget 12) decoded token id 0 after a 4096-token prompt. Each queued copy now
records the fence of its buffer. exact_pp 4096 and 20000 tokens with STRATA_STAGE_PIN=1: EXACT.
2026-10-09 10:11:10 +02:00
Niko1221 0650f31670 tools: cancel_park_test.py, a cancelled request must not lose the conversation it restored (#1620) 2026-10-09 10:01:59 +02:00
Niko1221 caba015bff serve: a parallel request is never neither running nor queued (#1603)
A busy slot flag with no owner kept every later request waiting for a slot nothing
would free, and a request waiting for the fifo (a load, an image encode, /v1/vram)
was counted nowhere. Now: slots have owners (claims / BDONE waits) and an ownerless
busy flag is freed after 3 s; the request waiting for a slot is counted
(admitting) and, past the stall time, named in the log; a parallel request on a
loaded engine no longer takes the fifo, and one that waits for it stays queued.
Tests with the fake engine.
2026-10-09 03:31:47 +02:00
Niko1221 5728828a84 Windows HIP: a bare --prefill auto stops at 6144; commit-limit check at serve start (#1630, #1607)
On an RX 7900 XTX the first long prompt at 8192-token chunks charged ~70 GB of
commit and the engine died (0xC0000409); 7500 and below were flat. A bare auto
now stops at 6144 on Windows HIP builds. A chunk the user names stays, with a
warning from 8192 up.

Windows: the serve start prints the commit left and warns when the planned host
allocations plus a 4 GiB floor do not fit; the conversation cache's admission
reads min(available RAM, available commit).
2026-10-09 03:31:47 +02:00
Niko1221 eaaa237e85 CPU prefill share: not armed by default when the cache holds every expert (#1595)
With every expert resident nothing streams, so the share has nothing to take and
only moves the staged-chunk limit (+4.8% at 1,000 tokens on a V100). An explicit
STRATA_PREFILL_CPU_SHARE is honoured as before; the default is unchanged where
experts are not all resident.
2026-10-09 03:31:47 +02:00
Niko1221 1c6c82ef4f layer split auto: avoid a one- or two-layer stage (#1616)
A stage with one layer holds a handful of expert slots (a 12 GB 3060: 512), and the
prompt chunk of every card in the pipeline is clamped to what that cache can lend.
When the pick has such a stage, search again among placements that give every
stage at least three layers; keep the new pick and print both and why. With none
startable the first pick stays. Identical cards pick balanced stages and never
reach the second search.
2026-10-09 03:31:47 +02:00
Niko1221 316097d2d9 sycl: recognise a Battlemage / Alchemist card by the PCI id in a name like 'Intel(R) Graphics [0xe223]' 2026-10-09 03:31:16 +02:00
Niko1221 793ca909c1 iq_multi_parity: an independent mmvq reference for the grouped expert kernels (the old-vs-new comparison was the same kernels by default) 2026-10-09 03:31:16 +02:00
noon-at-cgn 1aa4198804 docs: replace the evidence placeholder in the memory guard section with the measured result 2026-10-09 03:28:31 +02:00
noon-at-cgn daa765524d memory guard: parking and session save/restore read the RAM the engine really has (--memory-limit-mib)
available_host_bytes() = min(MemAvailable, the room under every cgroup limit above the engine, the operator's cap).
Parking and session save/restore read MemAvailable alone: in a container whose real limit sits on a parent cgroup it
cannot see, MemAvailable is stale (about 25 GiB shown when about 1 GiB was left) and the engine was OOM-killed.

conversation_available_memory() is replaced by available_host_bytes() / sample_host_memory() (the SYCL port's
generate.cpp too). The cgroup term covers memory.max and memory.high (v2) and memory.limit_in_bytes (v1) of the
process's group and every visible ancestor, with clean inactive file cache credited; an unparsable cgroup file makes the
sample unknown (fail closed). --memory-limit-mib / STRATA_MEMORY_LIMIT_MIB names the total RAM the container or cgroup
may use. Refusals say how much was available and from which source; one start-up line reports the limit.
tests/core/memory_guard_test.cpp runs on fixture directories (CPU only). Docs: DETAILS.md.
2026-10-09 03:28:31 +02:00
tuandat3019 bbb74cfca4 serve: wait for measured RAM before retrying reclaimed SAVE 2026-10-09 03:27:49 +02:00
tuandat3019 73ed39ae8e serve: optionally reclaim host caches before SAVE admission 2026-10-09 03:27:49 +02:00
Niko1221 8824e217c4 serve: a request cancelled between two prompt windows keeps the conversation it restored as the live one (#1620) 2026-10-09 03:27:49 +02:00
Niko1221 152292b633 tests: the AVX2 warning test of the docker entrypoint 2026-10-09 03:21:46 +02:00
Hulk 71eaee17b9 spec: re-probe stale full chains when shorter chains pay
A profitable shorter chained lookup returned before the stale full-size check, so the full chain could stay excluded for good
although the policy re-probes stale sizes in the other selection paths (#1252). Check a confident stale full size before
returning the profitable shorter chain. The 300-round policy, the EMA, the header and plain lookup are unchanged; a regression
test covers the bounded re-probe spacing and the MTP confidence gate.
2026-10-09 03:18:07 +02:00
sergiywith a26fcbcdba prefill: bytes_needed sizes the MoE buffers for the layout init picks (with or without an expert source)
0.1.39 took this PR's first part with #583 (the xn / grs count, STRATA_RING_BYTES). What is left: bytes_needed
counted the fused layout's smaller MoE buffers for every caller (fused_layout(T, true)), while init takes them only
when it gets an ExpertSource (fused_layout(T, m.src != nullptr)). A prompt path without one was lent fewer bytes than
its unfused buffers take. bytes_needed, bytes_needed_no_ring and (0.1.40) bytes_needed_owned - STRATA_OWNED_PRICE=exact's
sizing - now take the same src the callers pass to init.
2026-10-09 03:17:42 +02:00
Niko1221 8bb250d846 Layer split auto: warn on a near tie between two different splits on a mixed pair (#1428)
When the best split and a different one (boundaries at least 3 layers away, startable) are within 4% predicted decode window
(STRATA_SPLIT_NEAR_TIE_PCT, 0 = off) and the cards differ in speed or link, print the other K and --layer-split K. The model
prices decode only, not the prompt. Print only: the choice is unchanged.
2026-10-09 03:17:32 +02:00
Niko1221 cec7e2b1f6 --batch on a layer split: a BYIELD is taken even when no slot is decoding
A prompt read on a layer split with no slot decoding was one run, so the BYIELD of the batch_interleave_test step 4 was
dropped at the end of the read; the long prompt kept its slot and the next BGEN failed with 'ERR BGEN: no such free slot'
(pre-existing since 0.1.40.4). That read now goes in pieces of STRATA_BATCH_YIELD_CHUNKS chunks (default 8, 0 = one run), the
same chunks a single run reads, with the yield check between pieces. batch_read_piece holds the arithmetic; batch_read_test covers it.
2026-10-09 03:17:31 +02:00
Niko1221 9f5504d8a1 tests: test_ready_timeout is skipped on Windows, where its .cmd wrapper keeps the pipe open after the kill 2026-10-09 03:17:23 +02:00
Niko1221 f71c5307c6 Stage buffers: fence a pinned buffer's queued copy before the buffer is recycled (#1237)
A page-locked stage buffer is the source of the cache fill's cudaMemcpyAsync, which the DMA reads after the call has
returned; a pageable one is staged by the driver before it returns. The pool recycles a buffer after kStageAge layers or 256
assemblies, so a 4096-token prompt read (up to ~500 distinct experts per layer) could overwrite a buffer whose copy had not run
yet: the corrupted IQ3_S decode of #1237. note_async_read(blob, stream) records an event per pinned buffer after each queued
copy (hit-path fill, peer fill, remote-expert swap); claim_stage waits for it before it hands the buffer out again.
Pageable buffers and every other source are unchanged. STRATA_STAGE_PIN stays opt-in.
2026-10-09 03:17:17 +02:00
Niko1221 8d276c1997 Data-parallel replicas (opt-in): N engines on their own card groups behind one server, requests spread least-loaded and a conversation routed back to its replica; replicas in the config, --replicas in the server and setup 2026-10-09 03:15:34 +02:00
Niko1221 438019e36a hip: gfx1152 (Radeon 860M / 840M) in the unvalidated architecture list, like gfx1150 (#1625) 2026-10-09 03:15:13 +02:00
Niko1221 df29e3b2e6 prefill: guard std::atomic::wait/notify_all with __cpp_lib_atomic_wait so a GCC 10 build spins as before #1057 (#1488) 2026-10-09 03:15:13 +02:00
Niko1221 0ff45cffb2 hip: the Q2_0 signed-zero fix also on gfx1200/gfx1201 (ROCm 7.10), where hip_q2_zero failed (#1474) 2026-10-09 03:15:13 +02:00
Niko1221 3d6793a408 tests: expert_cache_segmented_test skips on a HIP build, where --vram-elastic is CUDA-only (#1475) 2026-10-09 03:15:12 +02:00
Niko1221 9d64b93a0f docker: warn, not stop, on a host CPU without AVX2 (#1584) 2026-10-09 03:15:12 +02:00
Niko1221 224bb6f899 setup: an engine update compiles for the cards the installed models use, not the biggest one (#1485) 2026-10-09 03:15:12 +02:00
Niko1221 598df11158 setup: --gpus when starting an installed model on a PC with AMD cards and no NVIDIA ones names the AMD cards (#1594) 2026-10-09 03:15:12 +02:00
BlueKingMuch 0d613031a9 serve: a </think> the reasoning quotes stays reasoning instead of ending it (#537)
The reasoning ended at the first </think> in the output, also when the model only
quotes the tag while it thinks (`</think>`, "</think>\n\n2" from a test). The rest
of the thinking then came back as the answer, and a tool call quoted in it could
be run.

A </think> right after a quote or backtick and followed by anything but a line
break is now reasoning text; the end of the thinking is followed by a line break
and ends it as before. Until the character after such a tag arrives, the tag is
held like a partial one, so streamed and whole outputs agree. If the turn ends
right after it, it was the end of the thinking: a forced tool call still opens
there, and the #1053 retry is not triggered, as before.
2026-10-09 03:15:12 +02:00
William Charbonneau Perrier 624070c5df Conversation cache: --conversation-cache-min-tokens parks only conversations of at least N tokens
Parking happens on a switch and every parked conversation costs one of
--conversation-cache-slots whatever its length.  A client that interleaves
short side requests (an agent's tool-result follow-ups, often under 1k tokens)
with one long conversation therefore spends the cache on the side requests and,
once it is full, evicts the long one oldest-first - the reuse it wanted.

Measured on an RTX 4070 SUPER / 32 GB / Q2_0, --conversation-cache-slots 2,
one 19,198-token conversation interleaved with three ~700-token requests, then
the same conversation again:

  min 0      skip nothing, side requests park, L is evicted
             re-send L: 0 reused + 19198 read in 41,059 ms   (wall 41.4 s)
  min 12288  skip parking (703 tokens, below the 12288 minimum) x3
             re-send L: 19193 reused + 5 read in 135 ms      (wall 0.3 s, 304x)

The default 0 keeps the previous behaviour, so nothing changes without the flag:
wants() answers for the length alone and the gate stays enabled() && wants(),
exactly as the existing byte-budget and RAM-floor skips are.  The check runs
before make_room(), so a skipped park neither evicts nor stores anything, and it
returns the same non-error path those skips take.

Tests: conversation_cache_test covers the inclusive boundary, 0, a negative that
never reaches the CLI, and that a disabled cache still answers for length; both
faults were reintroduced by hand to confirm the checks fail on them.
2026-10-09 03:15:12 +02:00
Niko1221 875d6a922a serve: a request waiting for the single turn sends keep-alives, and gives its place up when cancelled (#1619) 2026-10-09 03:15:12 +02:00
Niko1221 30017336ed ple: when the default --ple-io direct measures slow at start (cold random-row probe), use mmap; an explicit direct only warns (#1425 #1549 #1629) 2026-10-09 03:14:18 +02:00
Niko1221 6c9bbc8147 sycl: spin bound for a B-series card found from the device (Windows/OpenCL has no sysfs driver): 20,000 reads, 2,000,000 when drafting without --mtp (#1473 #1397) 2026-10-09 03:14:18 +02:00
Alex Gorevski 7d771bb028 Group a launcher's allowed CPUs by physical core and split them between independent instances (serve/cpu_affinity.py, from #1436) 2026-10-09 03:10:24 +02:00
jkuepker a257e0198e docs: gfx12 (R9700) native WMMA prompt experts: switch, measurements; gfx12 in the comments
AMD_HIP.md, RDNA4 speed switches: STRATA_PF_FUSED=1 now runs the native
IQ packs' prompt experts on the int8-WMMA kernels on gfx1200 / gfx1201.
Says that it is opt-in and why (it rounds differently, so it is not an
arch default), that STRATA_PF_FUSED_NATIVE=0 turns the native path off,
the start-up line it prints, the R9700 measurements (prompt time, kernel
vs MMQ, first-token KL against the FP16 path, retrieval checks), and
what was not run (gfx1200, Windows, K-quant packs).

DETAILS.md: the STRATA_PF_FUSED=1 sentence says where the native kernels
run on HIP.  CMakeLists.txt and gfx_arch.hpp: the comments that said the
native prompt-expert kernels are gfx11 only now include gfx12 (comments
only, no build change).
2026-10-09 03:06:58 +02:00
jkuepker 7969a6ec93 HIP gfx12: native int8-WMMA prompt experts on gfx1200/gfx1201 (opt-in STRATA_PF_FUSED=1, #1277)
The native IQ / K-quant prompt-expert kernels (moe_fused_iq.cu,
native_w11_kernel) were gfx11-only; on RDNA4 a pack with
STRATA_PF_FUSED=1 silently kept the MMQ path.  They now compile and run
on gfx1200 / gfx1201.  Still opt-in: STRATA_PF_FUSED=1 stays off by
default (it changes rounding, so it is not an arch_defaults switch), and
STRATA_PF_FUSED_NATIVE=0 still turns the native kernels off.

Compile gating
- STRATA_NAT_W12 for __gfx1200__ / __gfx1201__, STRATA_NAT_WMMA for
  either family.  The kernel calls the _gfx12 spelling of the iu8 WMMA
  builtin (the un-suffixed one does not compile there).  Any other
  target keeps the trapping body.

Kernel (same arithmetic, other lane layout)
- gfx12 WMMA fragments are 8 int8 per lane and the two lane halves hold
  different k (8*hi .. 8*hi+7), not a replica as on gfx11: B and A are
  loaded as uint2 at the matching offsets, and every 16-value k step is
  still one WMMA, so the per-16 scales of the IQ2_S / IQ2_XS paths are
  untouched.
- A lane's 8 accumulator rows are consecutive (8*hi + i), so a feature's
  gate and up are in the same lane: SwiGLU needs no __shfl_xor, and the
  down tile is stored as two float4 per lane.  NW_DROW(i, hi) gives the
  row of element i for the per-row scales.

Host side
- gfx_arch_is_gfx12_wmma(): exact gfx1200 / gfx1201 match, never a
  prefix, like the gfx11 helper.
- dev_info() takes the gfx11 list or gfx12, and also checks that the
  kernel the build loaded is the real one: the trap stub (a
  gfx12-generic or SPIR-V build on a listed card) declares no LDS and
  still passes hipFuncGetAttributes and the occupancy query.
- native_supported() reads the STRATA_PF_FUSED=1 opt-in itself:
  fused::available() / requested() belong to the Q2_0 pack's
  expert_w11_kernel, which is not ported and stays false on gfx12.  Off
  gfx12 the result is what it was.  prefill.cpp fused_ring() follows:
  a native pack asks native_supported() alone (it implied enabled()
  before), so on gfx12 it gets the fused buffer layout and ring.
- Grid factor per format: gfx12 holds 3 blocks per WGP for IQ2_S / IQ2_XS
  gate/up (LDS and VGPRs), 4 for the rest.  The occupancy API cannot say
  (it models 64 KB of LDS per multiprocessor), so it is a small table;
  STRATA_PF_OCC_W=N overrides it.  gfx11 keeps 4.
- experts_native() refuses a dm that is not 16-byte aligned on gfx12
  (the float4 stores); the prompt path's buffers always are.
- moe_fused.cu compares the gfx11 list with the helper instead of the
  "gfx11" prefix (no change on any listed part).

Tests (prefill_fused_iq_test)
- Runs on gfx12 (the skip gate also asks native_supported()), covers the
  other four pairs of the Coder IQ1_M pack, S20_E sets the expert count,
  and the real-shape check of fused against MMQ is now rel RMS <= 1e-3
  and worst row <= 2e-2 instead of rel RMS <= 0.05.

Measured on a Radeon AI PRO R9700 (gfx1201), Coder IQ1_M pack:
- parity: all 10 IQ pairs plus Q4_K / Q5_K pass, rel RMS <= 9.1e-5.
- kernels vs MMQ, 48 layers: 1.48x / 1.20x / 1.14x at 4K / 16K / 32K.
- prompt speed in the engine (server-reported, alternating starts per
  arm): 4K +12% to +19% depending on the start, 16K +8.3%, 32K +8.5%.
- first-token KL against the FP16 prompt path is 1.10x MMQ's (48 prompts
  of 4K), top-1 agreement 48/48; 4/4 long-context retrieval checks.
Not run on gfx11 or CUDA hardware here.
2026-10-09 03:06:58 +02:00
noon-at-cgn 425fc48a82 docs: replace the evidence placeholder in the split MTP-batch paragraph with the measured result 2026-10-09 03:06:46 +02:00
CYoung83 5fde38470a gguf_reader: open a tensor-less shard that ends before its aligned data start
unsloth/Qwen3.8-Flash-Next-GGUF UD-Q5_K_XL shard 1 holds metadata only and
is 10,946,618 bytes; its 32-byte-aligned data start is 10,946,624, so the
engine refused it with 'GGUF: data section starts past EOF'. llama.cpp's
gguf-py reads the same file (0 tensors, data_offset 10,946,624).

A file with tensors is still refused. A file without them gets data_start
clamped to the file size, so file_size() - data_start() stays 0 in the
payload checks downstream.
2026-10-09 03:06:46 +02:00
Niko1221 dd106e7de5 arch_defaults: gfx1200/gfx1201 get the six exact prompt switches of the gfx1151 table (decode half stays off); STRATA_GFX12_DEFAULTS=0 opts out (#1478) 2026-10-09 03:06:46 +02:00
noon-at-cgn a22a0e8c46 split: batched draft-layer prompt K/V on the last stage (STRATA_SPLIT_MTP_BATCH=1, opt-in)
On one GPU the prompt path computes the draft layer's K/V for each chunk in batches (Prefill::draft_kv, E-9). A layer
split always ran the drafter's own per-group pass instead, because the drafter sits on the last stage. With
STRATA_SPLIT_MTP_BATCH=1 the last stage's prompt path runs E-9 there. Default off: the default path is unchanged.

The drafter's K/V come from Q8_1 x Q8_0 MMQ instead of its own mmvq, so its drafts can differ; the target's tokens are
decided by the verify window.

Prefill::draft_kv says why it declined (optional out-parameter, also in the SYCL port's prefill.cpp), and the split
logs once what the variable did. Which path runs is a pure header (include/strata/program/draft_kv_plan.hpp) with a
CPU test. tools/split_mtp_batch_parity.py compares the greedy texts of the two arms on a live server. Docs: MULTI_GPU.md.
2026-10-09 03:06:46 +02:00
CYoung83 de21371b8a gguf_reader_test: a tensor-less file that ends before its aligned data start
Two synthetic cases: no tensors and the file ends at its header (should
open; data_start() clamped to the file size), and one tensor with the same
truncation (still refused). The first fails on 0.1.40.3: unsloth's
UD-Q5_K_XL shard 1 is that case.
2026-10-09 03:06:46 +02:00
noon-at-cgn 12019dc4a3 docs: replace the evidence placeholder in the thread-placement section with the measured result 2026-10-09 03:06:46 +02:00
noon-at-cgn 91f1e85dd1 --aux-cpus: put the engine's threads that are neither pool workers nor the host on spare CPUs (Linux, off by default)
Header-only placement (include/strata/platform/aux_cpus.hpp): the spare CPU set (the host core's SMT siblings, then
CPUs of physical cores with no worker; never a CPU that shares a core with a worker), the list parser, threads the
engine creates pin themselves, threads somebody else created (the CUDA driver's) are moved by a sweep over
/proc/self/task after the pool exists and at every request; pool workers and the host thread are never moved.
--aux-cpus off|auto|LIST / STRATA_AUX_CPUS; per request strata_tune aux_cpus=0|1 (serve/server.py).
Default off: the hooks do nothing. tests/platform/aux_cpus_test covers the derivation and live threads (CPU only).
2026-10-09 03:06:46 +02:00
noon-at-cgn f47349002c docs: replace the evidence placeholder in the lend-sizing paragraph with the measured result 2026-10-09 03:06:43 +02:00
noon-at-cgn d3fb3e6f75 split: decide STRATA_SPLIT_RING before sizing the lend regions; log borrowed vs RAM-kept slots
Builds on #1190. The layer split sized its lend regions (the RAM copies of the experts the prompt path borrows) with
Prefill::bytes_needed before the split's ring override was applied (Prefill::set_ring_override says it must be set
before any bytes_needed), so with STRATA_SPLIT_RING=384 the prompt path borrowed 5396 slots and the copy kept 4662.
The override is now decided first (decide_split_ring); include/strata/program/split_ring.hpp holds the pure helpers
(split_ring_slots, split_lend_slots - the scan's own tail-slot arithmetic, now one function for the region and the
borrow), src/program/split_ring_test.cpp covers them on the CPU. A start line reports borrowed vs kept slots and warns
when kept < borrowed. Docs: MULTI_GPU.md.
2026-10-09 03:06:43 +02:00
Evan 51d55a9ab4 generate: size every part's lend region for a layer split by the serve scan's own arithmetic (auto ceiling or the explicit chunk, the 128-slot floor, the auto percentage cap), #340's own-buffer rule deciding which parts lend nothing - and hand them to the resident copy before it pins 2026-10-09 03:06:39 +02:00
Evan aea9dc3ed8 resident split: pin_cache_complement keeps each stage's lend region in the RAM copy too (stage_lend_regions, append_stage_lend_regions) - the experts the prompt path borrows under a split streamed from the mapped files through page faults (3.7 GiB read for one 73K prompt, 1.5 s of a 3.9 s chunk on host staging, 2026-10-06 rig); one budget caps complement and regions, experts past it keep the file fallback 2026-10-09 03:06:39 +02:00
Vadim Shevchenko 3afbe5f7c2 setup: UD-Q4_K_XL / UD-IQ4_XS keep their RAM budget on a layer split (#642)
Engine 0.1.40 (fe9c10ca, #848) keeps the resident RAM copy on a layer split -
every stage's cache left out of it, the rest ranked by the whole expert
profile - and --resident-budget-gib runs through the same code
(pin_cache_complement), so the old engine refusal of the budget on a split is
gone. setup.py's split_mmap() got the matching exemption (43c8cfa9), but the
budget's three paths still assumed the old engine (#498):

- split_budget(): a start of a budget config on several GPUs deleted
  --resident-budget-gib (and asked for ~135 GB of RAM below that);
- offer_together(): a budget config was offered both cards only with ~135 GB
  of RAM, and "on both it has no budget";
- unsloth_together() / q4_split at install: --gpus, or "2" when asked, dropped
  the budget, and an explicit --resident-budget-gib kept one GPU.

From RESIDENT_SPLIT_ENGINE the budget is kept in all three, at any RAM. The
recommendations stay as they were (the golden configs are unchanged): one GPU
by default at install, "n" by default when a start offers both cards, --yes
alone one GPU; the split is taken only when asked for.

Tests: the #498 tests run with MIN_ENGINE pinned before RESIDENT_SPLIT_ENGINE
(as test_setup_risk's StartOnSeveralGpus does for #642), three new ones run the
kept budget; tools/test_setup_*.py on Linux: 421 tests, the 2 failures of
test_setup_engine_hash are the same without this change (no ready-made 0.1.41
engine to download in the test box).

Measured with engine 0.1.41 on an RTX 3090 (x16) + RTX 3080 Ti (x4), 91 GB of
RAM, UD-Q4_K_XL, budget 64 GiB: the auto split gives the x4 card layers 0-1
(678 slots) and the 3090 layers 2-47 (4,182 slots); 57.6 GiB of experts in RAM,
no file-tier reads per request; decode 45.0 / 45.1 tok/s (two runs) against
38.4 on the 3090 alone, a 20K prompt in ~18 s on both. Answers checked by hand.
2026-10-09 03:06:38 +02:00
Destrayon 1decc04efe setup: skip a cmake/ninja on PATH that does not run, for the one next to this Python
A pip launcher whose Python was removed stays on PATH ('ninja --version'
fails) and stopped every build there, though setup had just installed a
working copy in .venv.
2026-10-09 03:06:34 +02:00
BonifacioCalindoro 573642fc4c Fix PowerShell 5.1 command shown by UPDATE.bat
UPDATE.bat currently prints a command using `&&` for users to copy and
run manually. `&&` is not supported by Windows PowerShell 5.1, which
is still included with Windows 11.

Print the two Git commands separately instead.
2026-10-09 03:06:33 +02:00
nekomario28 9aba7baa28 sycl: keep the null-stream sentinel a sentinel in Verifier (#1555)
dpct migrated cudaStream_t ext_stream_ = nullptr to a pointer to the
in-order queue, evaluated at construction on whichever device is current.
Two places then misread it: init() compared ext_stream_ against the
*init-time* device's in-order queue, so a stage built on one device and
init'ed on another adopted the constructing device's queue
(cross-context submit failures, UR_RESULT_ERROR_DEVICE_LOST); teardown's
'cs_ != ext_stream_' ownership test drifted the same way.

Restore the CUDA contract: nullptr means 'no stream until set_stream()',
matching include/strata/core/verify.hpp:319 and src/core/verify.cpp:604.
2026-10-09 03:06:31 +02:00
duncanmcqueen 0b063ce804 sycl: fix the GEMM staging scratch lifetime and unregister freed allocations
Follow-up to the >4 GiB GEMM staging.

- gemm_stage freed its scratch as soon as a larger operand arrived, but the
  in-order queue is asynchronous: a previous staged GEMM could still read the
  buffer.  Wait for the queue before the free.
- The staging used dpct::get_in_order_queue() while the GEMM runs on the
  descriptor's queue (strata::q_of(stream_)); pass the descriptor's queue so
  the copy is ordered before the GEMM for a non-default stream, and so two
  Gemm objects on different queues do not share the thread_local scratch.
- Staging that could not allocate fell silently back to the direct (wrong)
  GEMM; it now says so.
- device_alloc_map had no unregister, so a freed cache base left a stale
  entry that device_offset_end could mis-attribute after the VA was reused.
  Add device_alloc_unregister and call it where the expert cache frees base_.
2026-10-09 03:06:31 +02:00
duncanmcqueen edff903d7a sycl: setup picks the Intel card by PCI id and configures Alchemist
setup reads the Intel cards from sysfs but to_sycl() decided the expert
mode from the driver alone, so a card's device id was thrown away and an
A-series on xe (the A770) could not be told from a Battlemage.  Carry the
PCI device id through intel_gpus() into to_sycl(), and treat every
Alchemist id (A770/A750/A580/A380/A310/A770M) as an A-series: it has no
FP64 hardware, so its config carries the driver's FP64 emulation (a kernel
on the sampled path declares double and is refused without it).

On i915 (the A750) the experts still load into a CPU-computed RAM arena
(--mmap-experts + STRATA_VERIFY_NO_HOST=0) - a single pinned host
allocation past ~3 GiB fails on that die.  On xe (the A770) the A-series
now takes the ordinary streamed path: the mirror is built in chunks (so
the >3 GiB limit is not hit) and the build passes
-ze-opt-greater-than-4GB-buffer-required, so the prompt path is correct
past the cache's 4 GiB.  tools/test_setup_sycl.py passes.
2026-10-09 03:06:31 +02:00
duncanmcqueen defadd6e0e sycl: build Alchemist kernels for allocations past 4 GiB
On an Arc A770 every prompt answered "!!!!" (token 0). The experts in the
last 0.46 GiB of the 4.46 GiB VRAM expert cache dequantized to NaN in the
prompt path. The slots' bytes were right (a memcpy read back equal to the
GGUF, and a fresh copy dequantized clean); only kernels reading them in place
got wrong bytes. In a JIT build IGC addresses kernel pointer arguments as a
32-bit offset from the allocation's base.

The JIT (non-AOT) build now passes -ze-opt-greater-than-4GB-buffer-required
to the spir64 backend, in the same argument as the divide/sqrt option (CMake
drops a repeated -Xsycl-target-backend=spir64). Measured on the A770, Coder
IQ1_M, --stream-experts: prompts of 73-542 tokens have 0 non-finite values
under STRATA_DBG_NAN=1, and the 542-token prompt answers right. The AOT build
is unchanged. Speed cost not measured.
2026-10-09 03:06:31 +02:00
duncanmcqueen 56efb70939 sycl: stage oneMKL GEMM operands that lie more than 4 GiB into their allocation
oneMKL's GEMM reads the last, partial tile of an operand from the wrong
place when that operand lies more than 4 GiB into its device allocation
(measured on an Arc A770, oneMKL 2026.1: a BF16 B past 4 GiB gave its last
21 columns wrong, none below 4 GiB). The prompt path's GEMMs meet this:
their buffers come from the top of the multi-GiB expert cache / prefill
region. This is the leading explanation for the A770's garbage on prompts
longer than a few dozen tokens - it repros with every expert
device-resident, so it is not the CPU handshake.

Register every big device allocation (malloc_device_guarded, and the
segmented expert cache's reserved VA) in a small registry, add
device_offset_end(ptr, bytes), and in Gemm::bf16 / Gemm::f16 copy either
operand into a scratch buffer of the queue's own (a fresh allocation, so
at offset 0) when its end lies past 4 GiB, then run the GEMM on the staged
operand. STRATA_SYCL_GEMM_NO_STAGE=1 is the negative control.

Not validated end to end: the run that showed the garbage was an
all-resident mirror, which no longer fits in this machine's RAM, and the
runnable --mmap-experts config crashes on this A770 with or without the
staging (exit 139, UR_RESULT_ERROR_DEVICE_LOST - verified a separate,
pre-existing fault with the negative control), so the staging has not yet
been shown to change an answer.
2026-10-09 03:06:31 +02:00
duncanmcqueen 18ee03e55c sycl: add the doorbell payload checksum
The decode handshake publishes a payload (the layer's activations, the
routed expert ids and weights) and then rings a sequence number the host
polls. The fences alone do not order the ring after the payload stores:
measured on an Arc A770, 14 of 2,832 rings in one run became visible
before the last activation store, so the CPU expert pool sometimes
computed on a partly stale payload. This is the fix the port's own
comments call for (elementwise.hpp: "the host can observe the ring and
then read a payload that has not landed").

The publish kernels now write, next to the sequence number, the ring it
belongs to and a checksum of the payload (the wrapping sum of every 32-bit
word mixed with its position, murmur3's finalizer - a plain sum missed a
reordering, [3,7] -> [7,3], of the expert ids) in a per-ring slot modulo 4,
and the payload is stored with the uncached write hint (a plain store to
host USM reaches the host only when the kernel ends on xe). The host waits
for the whole payload (doorbell_wait_payload) before the pool reads it, in
session_loop and both verify-window loops; the checksum is accepted in
either publish shape (with or without the routing weights), so a variant's
choice need not be plumbed to the reader. The sequence word grows from 1 to
9 mapped words (layer.cpp, verify.cpp) and the ring slot is reset with it.

Note this is a robustness fix, not the cure for the A770's garbage on
longer prompts: that one repros with every expert device-resident (no CPU
handshake) and points at oneMKL's >4 GiB operand bug, which needs the BLAS
staging the CUDA/HIP shims have.
2026-10-09 03:06:31 +02:00
duncanmcqueen 0d9d8541f4 sycl: keep Alchemist's prompt path on its own buffers
At a long context this port borrows expert-cache slots for the prompt
path. Those buffers are carved from the top of the ~10 GiB expert-cache
allocation, and oneMKL's BF16 GEMM reads a wrong tile when an operand lies
more than 4 GiB into its allocation (docs/INTEL.md). The borrowed operands
hit that, so from the first full prefill chunk the router read garbage.

Measured on an Arc A770: a 236-token prompt failed with "prefill: routed
id out of range", and a 452-token one segfaulted the engine (exit 139),
on both the mirrored and mmap configurations. With the prompt path on its
own buffers (its own allocation, reserved exactly by owned_prefill_mib)
and an automatic chunk capped at 2048 tokens, 452 / 1,748 / 3,476-token
prefill chunks ran to completion with no prefill failure (prompt reads
22 (cold) / 676 / 835 tok/s). Whether the *answers* of that configuration
are correct is a separate, still-open issue: the base port has no doorbell
payload checksum and its longer-prompt path garbles on some inputs, with
or without borrowing.

Scoped to Alchemist (PCI device ids, the same table setup_intel.py uses):
Battlemage's long prompts were measured correct with borrowing, so it
keeps the old default. --prefill-borrow still borrows, for testing a
future BLAS staging fix (the fix that lets borrowing return, as the
CUDA/HIP shims already stage).
2026-10-09 03:06:31 +02:00
duncanmcqueen e02bd9955e sycl: chunk the expert mirror and raise memlock in the wrapper
Two Alchemist (Arc A-series) fixes for the streamed-expert mirror:

- gguf_expert_source: a single sycl::malloc_host past ~3 GiB fails on DG2,
  so the mirror (thousands of experts, tens of GiB) could not be built on
  an A770 at all. It is now allocated in <=2 GiB chunks; the per-(layer,
  expert) address, the layer's first blob and close() use the chunk
  pointers. A cleanup guard frees every chunk on an allocation failure,
  a failed read, or any exception; the read phase is unchanged.
- strata-sycl.sh: docker's default memlock is 8 MiB, far below what pinning
  the mirror or the resident arena needs; pass --ulimit memlock=-1.

Measured on an Arc A770: the Coder IQ1_M (8,474 experts missing VRAM,
16.15 GiB) mirrors and the engine starts; before, it refused with
"8474 experts are neither in VRAM nor mirrored".
2026-10-09 03:06:31 +02:00
duncanmcqueen 9c790e3dde sycl: publish the doorbell with the uncached write hint
The decode handshake publishes a ring sequence the host polls while the
window graph still runs. On an Arc (xe) a system-scope atomic store, even
with a release fence, is served from the GPU's cache and reaches the host
only when the kernel ends, so a host polling mid-window never sees the
ring. That is why the verify window had to wait for the whole graph
(STRATA_VERIFY_NO_HOST) and every expert had to be device-resident or in
the pinned mirror.

STRATA_DOORBELL_ATOMIC_LOAD already fixes the load side the same way; this
does the store side with the Intel uncached L1+L3 write hint. The release
fence stays (ordering only: a later payload store follows the ring).

Measured with sycl/probe/doorbell_store.cpp on an Arc A770: the host sees
the store at 230 ms with the kernel running to 2030 ms; with the old atomic
store it sees it at 2038 ms, exactly when the kernel ends (exit code 3).
-DSTRATA_DOORBELL_ATOMIC_STORE restores the atomic store.

This is a prerequisite for running with the CPU expert pool on xe (the
per-layer handshake); it does not by itself fix longer-prompt prefill.
2026-10-09 03:06:31 +02:00
Evan 7a7c568b9a resident split: a test that the RAM copy keeps each stage's lend region (red: FileExpertSource has no stage_lend_regions, and no append_stage_lend_regions) 2026-10-09 03:06:15 +02:00
Chen Cheng 8e9d3d0a19 serve: the hardware sampler keeps running where psutil finds no disk counters
psutil.disk_io_counters() returns None when it finds no disk (Modal's gVisor
has no /proc/diskstats; psutil's docs say Windows can need `diskperf -y`).
_disk() then raised AttributeError at the first sample and the sampler thread
ended: /metrics' hardware stayed empty, the Monitor tab had nothing to show,
and the frozen-engine check's gpu_busy() read a GPU load of 0. It now returns
no disk reading and the other readings go on.
2026-10-09 03:04:22 +02:00
InB4DevOps 2a91a14414 serve: compact streamed line fragments into blocks 2026-10-09 03:04:22 +02:00
InB4DevOps ac1df2c416 tokenizer: initialize caches once per instance 2026-10-09 03:04:21 +02:00
InB4DevOps b2b94e3f6f tokenizer: sweep protected spans in match order 2026-10-09 03:04:21 +02:00
Hulk 8c6b1cc5b5 Limit body framing fix to incomplete chunked trailers 2026-10-09 03:04:21 +02:00
Hulk d467b3d2bb Reject incomplete HTTP request body framing 2026-10-09 03:04:21 +02:00
Hulk 2bf1d07c05 fix(serve): make context overflow errors recognizable 2026-10-09 03:04:21 +02:00
nekomario28 32a2439de0 serve: answer the request that hit the READY timeout (#1527)
The READY-read timeout from #1317 kills the engine, but the request that
was loading it ended with no response: the RuntimeError from __init__
matched none of the request path's handlers, and on a lazy first start
restart()'s finally crashed on slot_cv (created only after READY), so a
misleading 400 masked the failure instead.

- raise EngineDied (still RuntimeError) for the start failure, so the
  request ends 503 'did not report READY ...; the next request restarts
  it' on the load, restart and /v1/load paths alike
- skip the slot_cv wakeup when a lazy first start never created it

serve/test_ready_timeout.py covers both paths with silent fake engines:
the lazy load (unfixed: 400) and a restart that never reports READY
(unfixed: connection closed with no answer).
2026-10-09 03:04:14 +02:00
nekomario28 efe3bfcf87 serve: keep the given card order when the reorder starves the last stage (#1576)
#1352 orders an auto layer split faster-card-last because the last stage
runs the head, the draft layer and the verify. On a VRAM-asymmetric pair
that rule can put the heaviest stage on the card least able to hold it:
the reporter's 12 GB 4070 Ti ended up with 31 layers + head + verify and
a 3 GiB expert cache (decode ~58 -> ~44 tok/s, the 'card this small'
warning on).

Skip the reorder when it would put the last stage on a card with less
total VRAM than the one the config placed there (NVML / amdgpu sysfs;
unreadable cards keep the reorder).  scores/vrams stay injectable for
tests.
2026-10-09 03:03:14 +02:00
Niko1221 fb58e0dbc8 Stage buffers: pinning is opt-in (STRATA_STAGE_PIN=1); on by default it corrupted IQ3_S decode after a long prompt in the release gate 2026-10-08 12:25:39 +02:00
Niko1221 982b8cf515 stage pin: no cudaErrorNotSupported (it is not in the HIP compat layer); same behaviour 2026-10-08 10:17:21 +02:00
Niko1221 69342e8e6c Merge main (v0.1.40.4) into f-141
# Conflicts:
#	CMakeLists.txt
#	setup.py
2026-10-08 10:07:12 +02:00
Niko1221 459bf63537 tests: drop the duplicate test_pipeline_windows_wins left by the merge; the hotfix-tag test no longer assumes a four-number MIN_ENGINE 2026-10-08 10:03:44 +02:00
Niko1221 0a45d54b2f Version 0.1.41: MIN_ENGINE 0.1.41 2026-10-08 09:59:53 +02:00
Niko1221 9b5e9ab13a 0.1.41 merge cleanups: bench key placeholder, B65 README author text, calibration time text, fixups.py regenerates the run-time spin bound 2026-10-08 09:58:59 +02:00
Niko1221 e5c5739ae0 Merge f-141-research: STRATA_DBG_FEAT and ROUTE_OVERRIDE debug hooks (env-gated) 2026-10-08 09:57:02 +02:00
Niko1221 ed13c9a10f Merge f-141-routeres: route-resident experts (opt-in, experimental) 2026-10-08 09:56:56 +02:00
Niko1221 b0bc4a70f4 Merge f-141-conc: --batch-groups auto for layer-split batches, #857 fix, listen backlog
# Conflicts:
#	src/program/generate.cpp
2026-10-08 09:56:56 +02:00
Niko1221 4aca10c5a0 Merge f-141-mgpu: no-P2P path (opt-in), #794, #793, peer knobs 2026-10-08 09:56:29 +02:00
Niko1221 a00d889383 Merge f-141-amdintel: AMD and Intel work, watchdog file-tier tolerance, ports
# Conflicts:
#	bench/results/COMMUNITY.md
2026-10-08 09:56:29 +02:00
Niko1221 ef80713032 Merge f-141-fixb: Python, docs and bench fixes and ports
# Conflicts:
#	setup.py
2026-10-08 09:56:13 +02:00
Niko1221 e50c5946c7 Merge f-141-fixa: Pascal decode regression (#1469), clamps, SYCL multi-card and spin bound 2026-10-08 09:55:58 +02:00
Niko1221 c27998f5ac Merge f-141-cuda: foresight, GDN chunked, per-arch tables, stage pin, CUDA bug fixes
# Conflicts:
#	docs/DETAILS.md
2026-10-08 09:55:41 +02:00
Niko1221 073e2fe28d Merge f-141-quick: CPU prefill share, file tier batched reads, tokenizer cache, server hardening, small fixes 2026-10-08 09:55:28 +02:00
Niko1221 5df35dcb47 Experimental STRATA_ROUTE_RESIDENT: residency-biased routing in verify windows (opt-in, changes the output) 2026-10-08 07:54:09 +02:00
Niko1221 c34dd57143 serve: STRATA_HTTP_BACKLOG, test with 60 simultaneous connections 2026-10-08 07:32:43 +02:00
Niko1221 6674a0065f Version 0.1.40.4: MIN_ENGINE 0.1.40.4; Pascal sm_61 decode back to 0.1.40 speed (#1469) 2026-10-08 07:23:15 +02:00
Niko1221 ca001df571 batch: a layer split with --batch pipelines one group per stage by default (--batch-groups 1 opts out); server listen backlog 256 (30-40 simultaneous clients got connection resets) 2026-10-08 07:10:59 +02:00
Niko1221 55dee8ed84 prompt buffers (#1454): keep counting the bytes bo shares with emb unless STRATA_EMB_REUSE_ACCOUNT=1
The VRAM reuse stays, but bytes_needed_impl counts bo again by default, so the auto chunk and the borrowed cache slots
are those of 0.1.40.3 and the prompt residuals stay byte-identical (on the RTX 3060 the planner otherwise picked a
6656-token chunk instead of 6400). STRATA_EMB_REUSE_ACCOUNT=1 lets the planner use the saved bytes (+3.9% prompt there);
documented in docs/DETAILS.md.
2026-10-08 04:44:49 +02:00
Niko1221 41a5a5488d SYCL expert cache (#1440): a one-GPU run keeps the free-memory figure it had
The previous change skipped the DRM-fdinfo subtraction whenever the driver reported usage. On a single Arc Pro B70 that
made the expert cache 12168 slots instead of 11338 (23.09 vs 21.52 GiB), so a one-GPU run was not unchanged. The
subtraction is now skipped only when the process sees more than one GPU (where it double-counts the other card) and the
driver does not report the whole card as free.
2026-10-08 03:49:18 +02:00
Niko1221 a79e7225af SYCL doorbell spin bound chosen per device at run time (#1397)
The bound of the device's spins on host flags was a build constant: 20,000 reads for a bmg AOT build, 2,000,000
for anything else. A B70 built without AOT (SPIR-V, JIT: the Windows/OpenCL build, or a Linux build with no AOT
target) got the A750-sized bound. The issue reports a 37% drop in decode between 0.1.40.2 and 0.1.40.3. On a Linux B70 JIT build the two bounds decode
alike (--spec 2, 3 pairs: 19.4 vs 19.5 tok/s), so this makes the bound independent of the AOT flag but is not shown to be that drop's cause.

spin_max(queue) decides per device: 20,000 under the xe driver (the B-series on Linux), 2,000,000 elsewhere (the i915
A-series needs it for the host's per-layer expert work; a Windows/OpenCL B-series build measured faster with the long bound).
STRATA_SPIN_MAX=<reads> overrides it at run time; -DSTRATA_SYCL_SPIN_MAX=<reads> still fixes one bound for the
whole build. The three waiting kernels take the bound as an argument.
2026-10-08 03:40:09 +02:00
Niko1221andcrobe201 a85347811a SYCL layer split (#1440): the host mirror keeps to the first stage's layers; the fdinfo free-memory fallback only where the driver reports the whole card free
- generate.cpp: with a layer split the host mirror walked the later stages' layers too (#1054, fixed in #1111 but
  lost in the re-migration): 20 GiB of pinned RAM for experts the other card holds, or one 39 GiB pinned allocation
  that fails. Both loops stop at the first stage's last layer.
- expert_cache.cpp: device_free_bytes() subtracted the DRM bytes of every card of the process from the card it asked
  about, so on a layer split the second card was charged the first one's cache and its expert cache would not open.
  The fallback is for drivers that report the whole card as free (Arc A750/i915); it now runs only then.

Reported by crobe201 with both diffs; MaineTim confirmed the second on 2x B60.

Co-authored-by: crobe201 <[email protected]>
2026-10-08 03:07:50 +02:00
W1nge ccd657a296 cuda: retain HC norm products in existing scratch 2026-10-08 03:07:20 +02:00
W1nge 199fd1d5d8 prefill: reuse embedding storage for half outputs 2026-10-08 03:07:20 +02:00
Niko1221andMatthew Hines e372d4ffd9 q8_1 emit sites (#1448): clamp the scale and quantize against it, as the other producers do
The three sites fixed by the previous commit stored a clamped (d, sum) but still
quantized against the unclamped d, so an overflowing block stored int8 values
that no longer matched its stored scale. They now use q8_1_finite(d) and
q8_1_quant like native_quantize_q8_1_kernel, the swiglu quantizer and the CPU
pool. A block whose amax is finite and below 8.3M is stored bit for bit as
before.

Co-authored-by: Matthew Hines <[email protected]>
2026-10-08 03:02:42 +02:00
Matthew Hines 7a0e30235c #606: clamp the three unclamped q8_1 scale/sum emit sites via q8_1_ds
Three kernels still emit the (d, sum) pair of a q8_1 activation block
with a raw make_half2(d, sum), bypassing the finite helper the rest of
the tree uses (q8_1_finite.hpp, from 19de7f4 / f6601bf):

  fused_gr.cu   gr_q8_tail                 - the fused gate-reduce tail (QFUSE)
  verify_kernels.cu  gdn_q8_1_store        - the batched GDN verify store (QFUSE)
  iq_kernels.cu  s26_swiglu_q8_1_kernel   - the S26 swiglu path

A block whose amax overflows (or that reads a non-finite input) emits
d = inf/NaN; fp16 makes the pair NaN (or the pathological (0,0)). Nothing
downstream tests for it: a NaN scale poisons the fp16 dot of the quantized
activation and every sum downstream of it, and a fully non-finite logit
row ends at the sampler's all-NaN fallback - vocabulary id 0, the '!'
face of the degeneration reported in #606. Same defect class already
fixed at the other emit sites; these three were missed.

q8_1_ds() clamps both halves into the fp16-finite range (finite d,
non-negative finite sum - the sum is a mean of |x|), producing for the
GPU paths the same bits the CPU pool and the clamped GPU sites produce
for the same block. A clamp at the producer, content-blind: nothing
downstream of it is model-text-aware.

Criteria and the wider carried-state RCA: docs/Q8_1_FINITE_DS.md and
docs/DEGENERATION_RCA.md.
2026-10-08 03:02:28 +02:00
Niko1221 fbb362425c Pascal decode: keep __restrict__ and drop the PDL prefetch below sm_70 (#1469, #1470)
2e4ddf6 (the #904 port) removed __restrict__ from the activation pointers of
native_quantize_q8_1_kernel, bf16_f32_mmvf_multi_kernel and
native_mmvq_multi_kernel on every CUDA architecture, and turned on the weight
prefetch before pdl_wait(). Both only matter for PDL (sm_90+). On sm_61 the
missing restrict loses the read-only data path (LDG.CI) and decode halves
(36.4 -> 17.7 tok/s on a Tesla P40, 32-36 -> 17.7-18.6 on a GTX 1080 + 1070).

STRATA_PDL_RESTRICT is __restrict__ and kPdlPrefetch false again for
__CUDA_ARCH__ < 700 in the device pass. sm_70 and newer compile to the same
SASS as before (cuobjdump -sass of native_mmvq.cu and native_bf16.cu at sm_70,
86, 90 and 120 is identical); the host pass is unchanged.

Reported and bisected by paulhothersall and lineape.
2026-10-08 02:58:45 +02:00
Niko1221 c788e2aa72 Pascal decode: keep __restrict__ and drop the PDL prefetch below sm_70 (#1469, #1470)
2e4ddf6 (the #904 port) removed __restrict__ from the activation pointers of
native_quantize_q8_1_kernel, bf16_f32_mmvf_multi_kernel and
native_mmvq_multi_kernel on every CUDA architecture, and turned on the weight
prefetch before pdl_wait(). Both only matter for PDL (sm_90+). On sm_61 the
missing restrict loses the read-only data path (LDG.CI) and decode halves
(36.4 -> 17.7 tok/s on a Tesla P40, 32-36 -> 17.7-18.6 on a GTX 1080 + 1070).

STRATA_PDL_RESTRICT is __restrict__ and kPdlPrefetch false again for
__CUDA_ARCH__ < 700 in the device pass. sm_70 and newer compile to the same
SASS as before (cuobjdump -sass of native_mmvq.cu and native_bf16.cu at sm_70,
86, 90 and 120 is identical); the host pass is unchanged.

Reported and bisected by paulhothersall and lineape.
2026-10-08 02:58:44 +02:00
Niko1221 6c51a295b4 research: opt-in prefill hooks STRATA_DBG_FEAT (routing + hidden-state dump) and STRATA_DBG_ROUTE_OVERRIDE (not default-path) 2026-10-08 02:50:46 +02:00
Niko1221 9386089d80 docs: Lock pages in memory takes effect at the next logon (#1412) 2026-10-08 02:42:07 +02:00
paulhothersall 53c9baff07 docs: a short Contributing section in AGENTS.md and a test-requests list 2026-10-08 02:37:03 +02:00
Niko1221 5343e878a1 mtp: drop the duplicate dhead_type_ line from #1329 (f-141 already copies it); the batch-mtp test recipe stays 2026-10-08 02:36:53 +02:00
Ethan d1e93c02bf sweep: the round line prints that round's draw, not the median through it
Found while running it against a live engine: the line said 0.35 as 48.7 on round 3 while the three draws were 57.0, 48.7 and 35.5, because it printed the running median. Reading a sweep's drift is the whole point of the line, so it has to be the draw. The medians and the ranges are still in the line below it.
2026-10-08 02:36:32 +02:00
Ethan d1990da1a6 calibrate: the sweep visits every value in every round instead of once
Phase 1 measures each PCIe share once and takes the maximum, with the three prompts of that rate all at the one
setting.  Neighbours cannot be ordered that way: on this box 0.75 through 0.95 come out at 72.7, 69.0, 71.0 and
71.9, which is 0.2% apart, while the same settings moved 8-28% between engine starts.  A value that was worth a
real gain can lose its single sample and never be measured again, because phase 1's winner is the share phase 2
then sweeps spec_min_p at, and the confirm step only puts that one resulting pair against the default.

sweep() measures every value SWEEP_ROUNDS times inside the one engine start, one round visiting all of them and
every other round in reverse order, and the winners come from the medians.  Drift inside a sweep then lands on
every value the same number of times, and one bad sample cannot decide.  The same function runs phase 2, and the
confirm step uses the same round count instead of its own.

Cost: the sweep multiplies by SWEEP_ROUNDS, 3 by default.  A rate is three prompts of 128 tokens, so on this box
(six values, 95-100 tok/s) phase 1 goes from about 40 s to about 120 s, on a run that already loads the model more
than once.  A machine whose two leaders stay inside each other's noise can raise the constant.

Not in this change: phase 4 needs a restart per worker count and stays sequential, so a count is still taken from
one measurement each.

Tests: python -m unittest tools.test_calibrate (the stand-in engine is a pure function of the tune keys, so the
medians and the winners are the same as before); tools.test_setup_config and tools.test_setup_golden
2026-10-08 02:36:32 +02:00
Ethan fe0df23120 calibrate: the PCIe share sweep reaches 1.0, where no expert goes to the CPU pool
PCIE_FRACS stops at 0.75, so setup --calibrate never measures the value that takes the CPU pool out of the decode
window completely. 1.0 is a separate operating point rather than 0.75 carried further: the share is applied as
m = (nmiss * pcie_num) >> 8, so at any value below 1.0 at least one miss still goes to the CPU pool, and only at
1.0 does every non-resident expert come over PCIe. A CPU expert sits on the layer's critical path while the GPU
waits for it, and on a slow CPU that is the whole difference.

On a Core Ultra 9 290HX Plus with an RTX 5090 Laptop on PCIe 5.0 x16, 0.55 gives 51.1 tok/s against 71.3 at 1.0,
with cpu_exp falling from 1.06 to 0.00 per layer-window and the window time falling by the 11.3 ms of PCIe waiting
plus the CPU's expert time. A sequential sweep puts 1.0 at 78.8 against 72.7 for 0.75 and 51.5 for 0.35.

0.9 is added beside 1.0 so a machine where 1.0 is too much gets an interior point. pick() keeps the default unless a
candidate beats it by MIN_GAIN, so this cannot make a calibration worse. The cost is one rate per value, about 8 s
here.

Tests: python -m unittest tools.test_calibrate; tools.test_setup_config and tools.test_setup_golden
2026-10-08 02:36:23 +02:00
zetlaw e0f6da57f5 Fix shared MTP draft head type for batch slots 2026-10-08 02:36:23 +02:00
Alekperov Fuad 0d994dfb9c setup: link the engine against the toolkit whose nvcc builds it (CUDAToolkit_ROOT)
With a second toolkit beside the distribution's one, CMake compiled with the chosen nvcc but took cudart and cuBLAS from /usr (Ubuntu 24.04, STRATA_NVCC=/opt/cuda-13.0/bin/nvcc: the engine needed libcudart.so.12 while built with the 13.0 headers). Pass CUDAToolkit_ROOT for the engine and the CUDA image encoder; the distribution's own /usr/bin/nvcc keeps CMake's search as before.
2026-10-08 02:36:22 +02:00
MIDHAT NASHAR bee3b1947c test: make checkpoint byte fixtures valid under MSVC 2026-10-08 02:35:37 +02:00
phoenixclyde a732a19b6f hip/gfx906: the tree does not build (q6_k MMQ instance and cudaEventBlockingSync)
Two gfx906 build failures on v0.1.40.2. Both are one-line gates that know
about the wave32 HIP backend but not about STRATA_HIP_GFX906 -- the same
class as #808 and #1083. Neither is fixed on main (e8ca9afd03).

1) src/prefill/prefill.cpp:399 builds an event with
   cudaEventDisableTiming | cudaEventBlockingSync, but the gfx906 compat
   header include/strata/platform/hip_compat/strata_hip.h maps only
   cudaEventDisableTiming and cudaEventDefault. The wave32 header
   include/strata/hip_compat/cuda_runtime.h:39 has the mapping.
   -> add #define cudaEventBlockingSync hipEventBlockingSync there.

   A diff of the whole set of cuda* defines between the two headers lists
   one other name the gfx906 header lacks, cudaDevAttrIntegrated, and that
   one is already gated upstream (#if defined(__linux__) &&
   !defined(STRATA_HIP_GFX906) in src/core/expert_cache.cpp:31), so it is
   deliberately not touched here.

2) Link error: undefined symbol
   void mul_mat_q_case<(ggml_type)14>(ggml_backend_cuda_context&, mmq_args const&, ihipStream_t*)
   (14 = GGML_TYPE_Q6_K).
   src/prefill/moe_mmq.cu:219 calls it under
   #if defined(__HIPCC__) || defined(STRATA_Q6K_EXPERTS), so any HIP
   compilation references it; mmq.cuh declares the case as an extern
   template, so the definition must come from
   template-instances/mmq-instance-q6_k.cu. The CUDA branch of
   CMakeLists.txt adds that file only under STRATA_Q6K_EXPERTS.
   STRATA_HIP_GFX906=ON forces STRATA_ENABLE_CUDA=ON, so gfx906 takes the
   CUDA branch while its .cu files are compiled as HIP.
   -> widen the gate: STRATA_Q6K_EXPERTS OR STRATA_HIP_GFX906.
   This adds one MMQ instance to the static library. It does not enable
   STRATA_Q6K_EXPERTS (the grouped Q6_K expert kernels that allocate extra
   VRAM), and STRATA_HIP_GFX906 is only ever ON in the gfx906 build, so
   CUDA and wave32 builds are untouched.

Verified on a single Instinct MI50 32 GB (gfx906, wave64), ROCm/HIP 7.14,
-DSTRATA_HIP_GFX906=ON -DCMAKE_HIP_ARCHITECTURES=gfx906
-DSTRATA_MMQ_KQUANTS=ON:

- the engine builds and serves UD-IQ4_XS (Qwen3.8-Flash-Next) end to end:
  /v1/models, chat completions, tool calls, MTP drafts accepted;
- nm -C shows mul_mat_q_case<(ggml_type)14> in the binary (13 instances:
  7 8 12 13 14 16 17 18 20 21 22 23 42);
- change 2 is load-bearing: removing mmq-instance-q6_k.cu.o from
  libstrata_mmq.a and relinking with the same link command line fails with
  exactly the undefined symbol above (the archive holds one U reference
  and that one W definition), while the untouched archive relinks cleanly.
2026-10-08 02:35:37 +02:00
Niko1221 9bf626ff67 bench: COMMUNITY.md rows for the ten community folders ported from #1429 #1432 #1433 #1443 #1452 #1459 #1460 #1462 #1467 2026-10-08 02:34:58 +02:00
Dmitry f4e2c89c55 bench: community report - RTX 4090, Flash-Next IQ3_XXS at 204800, engine 0.1.40.3
Two variants of the same sweep (code-explanation prompts and Russian prose) on
one PC and one server config, engine 0.1.40.3 built from source for sm_89.
Each folder carries the 0.1.40.2 run, the 0.1.40 baseline and an
identical-binary control run so the noise band is in the report.

Result: no measurable change from 0.1.40.2 on this machine.
2026-10-08 02:34:06 +02:00
wolffahrer baa55adc4c bench: RTX 5090 Laptop GPU follow-up for 0.1.40.2 and 0.1.40.3
Same laptop, model files, configuration and scripts as the 0.1.40.1
entry (2026-10-06-community-rtx-5090-laptop, #1263); only the Strata
version changed. Raw data for 0.1.40.3, medians for 0.1.40.2 in the
README. Decode and prompt throughput unchanged within run-to-run
variation, all needles found, coding check 10/10 on 0.1.40.3. A 0/10
coding check on 0.1.40.2 is traced to a greedy reasoning loop that
occurs in 0.1.40.1 as well (re-runs documented). Results only, no
engine changes.
2026-10-08 02:34:05 +02:00
xWinIcex 654aa2edbe Add community benchmark: RTX 5080 in Docker Desktop on Windows (WSL2)
Coder IQ1_M, one RTX 5080 16 GB, the repository's own Dockerfile and entrypoint
on a Windows host (Docker Desktop 4.84.0 / WSL2), two context limits on the same
loaded model: 131,072 and 65,536.

- Speed: three runs at each of 4,096 / 8,192 / 16,384 / 32,768 / 65,536 /
  130,560 prompt tokens (128K limit) and at 4,096 / 8,192 / 16,384 / 32,768 /
  65,024 (64K limit), greedy, reasoning off, 256-token output cap, zero reused
  prompt tokens in all 33 runs.
- Correctness: tools/needle_bench.py 9 of 9 (32k/64k/128k) at the 128K limit and
  6 of 6 at the 64K limit, plus a vision check with the encoder on the CPU.
- Provenance: all three model files SHA-256-verified against the repository's
  published LFS oids at the pinned revision; MTP manifest and inventory included.
- Environment: Docker Desktop's default WSL2 memory cap makes setup.py read ~31 GB
  of a 64 GB host and silently select the low-RAM mode; the report documents the
  .wslconfig change used instead. Also documents the 8-token headroom a request
  needs (prompt + max_tokens + 8 <= max_context) and the engine's own VRAM warning
  at a 128K context on a 16 GB card.

Results-only; no engine changes.
2026-10-08 02:34:04 +02:00
saikiran-rs 244f113c96 bench: mixed AMD helper layout at1M, long-output and quality results 2026-10-08 02:34:02 +02:00
aleesposito85 69391129fe bench: community report, Tesla V100 32 GB + P100 16 GB expert-helper (mixed Volta/Pascal), Flash-Next IQ3_XXS (#1079) 2026-10-08 02:34:01 +02:00
rvannos 47e9552be7 Add benchmark results for 2x RTX 4000 configuration
Added detailed benchmark results for dual NVIDIA Quadro RTX 4000 setup, including performance metrics, hardware specifications, and observations.
2026-10-08 02:34:01 +02:00
Sophie Andreikina df53228251 community bench: RTX 5090 + RTX PRO 4000 Blackwell, UD-Q4_K_XL helper cache, engine 0.1.40.3 2026-10-08 02:33:57 +02:00
timnevits 6fff95b8a6 bench: Arc Pro B65 Gen4 results on v0.1.40.2 2026-10-08 02:33:56 +02:00
shrisha e905bf9946 bench: 2x TITAN RTX (sm_75) at Strata v0.1.40.3, IQ3_S, 262K
Nine files following docs/COMMUNITY_BENCHMARKS.md. Engine v0.1.40.3
(tag d5ea713) built from source for sm_75, IQ3_S at the model native
262,144-token window, with --vision and --reasoning-budget-tokens 12000.

Three runs each at 4,096 / 32,768 / 128,000 prompt tokens: decode
70.9 / 77.3 / 71.1 tok/s, prompt 868.9 / 1548.9 / 1626.9 tok/s, medians
in the index and full ranges in the report. needle_bench 6/6 at 32k and
128k. Records that NVLink is present but unused by the engine, so a bridge
on consumer boards is not worth buying.
2026-10-08 02:33:55 +02:00
Alex GorevskiandCopilot 86b283e1eb docs: record RTX 8000 existing runtime switch measurements
Document opt-in architecture limits, prompt/decode trade-offs, numeric caveats and negative tuning results without changing kernels or defaults.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: a7c2bcbb-4264-4eb2-879d-c71a90285b0b
2026-10-08 02:33:19 +02:00
Lech Najdek f4ce5ac07f OLDER_GPUS.md: fill the P100 row with the 2x P100 IQ3_S measurements (#1157) 2026-10-08 02:33:16 +02:00
Niko1221 37338cadf1 setup: warn on a non-Default GPU compute mode; vision encoder start error names the busy/unavailable cause (#1445) 2026-10-08 02:31:56 +02:00
Niko1221andadambenhassen 54d26589ca setup: --remote-expert-opt only beside a helper cache; docs: --pipeline-windows and --adapt-async combine (#1447)
The flag acts only on --expert-cache-device1..3; a plain layer split got a no-op flag that the docs listed as a reason
for the pipeline and the async tier to switch off. Built on f-141-quick's #1352 pipeline-windows rule (same hunk).
MULTI_GPU.md: the two combine (only STRATA_PIPELINE_ADAPT_ASYNC=0 turns async off); adds the 2x3090 table.

Co-authored-by: adambenhassen <[email protected]>
2026-10-08 02:28:41 +02:00
Ron Forrester dd667967b9 serve: a tool call written <function= NAME> is the call NAME
The function name was everything between `<function=` and `>`, untrimmed. A model that writes a space after the `=`
(seen from Qwen3.8-Flash-Next IQ2_XS in Zed's agent panel) got:

- inside the thinking: the name " capture" matched no declared tool, so the #804 rescue kept the call as reasoning
  text; the reply ended with finish_reason "stop" and no tool_calls, and the agent stopped mid-task;
- after the thinking: a call named " capture", which the client does not know, its arguments typed without the
  tool's schema.

The name is now stripped where the call is parsed (parse_tool_call), where the #804 rescue checks it against the
declared tools, and where a finished call looks up its schema (_finish_call). A name with surrounding whitespace is
never meaningful, so no text becomes a call that was not one: the #804/#1058 rules still decide whether a call
inside the thinking is an act.

serve/test_tool_name_space.py: both places, streamed and not, fed in 3-character pieces; fails 4/4 before, passes
after. test_reasoning_rescue, test_reasoning_tools and test_frontend unchanged and passing.
2026-10-08 02:25:52 +02:00
Niko1221 c85b7c87b9 sm_70 (PR 1401): the Volta table, expert mode 8 and the fast norm/up are opt-in with STRATA_SM70_TABLE=1 until confirmed on a V100; default sm_70 behaviour as in 0.1.40.3 2026-10-08 01:24:59 +02:00
Niko1221 162ce64eff prefill: the CPU share is on by default on CUDA, one GPU, no batch slots, chunks below 1024 tokens (STRATA_PREFILL_CPU_SHARE=0 turns it off); start line; docs 2026-10-08 01:06:02 +02:00
Niko1221 eb98c622e4 docs: measured numbers for STRATA_STAGE_PIN, STRATA_ADAPT_LAG, STRATA_GDN_CHUNKED 2026-10-08 01:04:50 +02:00
Niko1221 fc8444367d batch pipeline: a pipelined slot is a cache again, so a request left alone goes back to the solo path (#857) 2026-10-08 00:27:40 +02:00
Niko1221 5aee72b107 serve: the frozen-engine check does not end an engine whose GPU is busy (telemetry reading) 2026-10-08 00:27:08 +02:00
Niko1221 d2e3f1c9dd Stage buffers: pinned by default while the host has 3 GiB of RAM to spare (STRATA_STAGE_PIN=0 off, =1 always); docs: the 0.1.41 opt-in switches; DETAILS: several API keys, the frozen-engine check 2026-10-08 00:01:29 +02:00
Niko1221 421e163a30 serve: STRATA_DUMP_FIRST_LOGITS also writes each request's first-window logits (<path>.<n>), to compare prompt paths by KL 2026-10-07 22:55:42 +02:00
Niko1221 21d4165b16 tests: --batch-groups auto follows the engine's INFO batch_groups 2026-10-07 22:25:11 +02:00
Niko1221 886bc47742 docs: --batch-groups auto in BATCHING.md 2026-10-07 22:23:40 +02:00
Niko1221 aa19d0d395 docs: stager profile for a warm GGUF read in place (#1353) 2026-10-07 21:37:19 +02:00
Niko1221 b2c5b22131 docs/INTEL.md: the A750 sampled path needs no FP64 emulation (measured) 2026-10-07 21:24:46 +02:00
Niko1221 ff3bda728d fix: newline in the batch-groups auto log line 2026-10-07 21:21:44 +02:00
Niko1221 67fba9c1fc serve: card order on AMD/Linux too (KFD topology: SIMDs x clock) 2026-10-07 21:19:17 +02:00
Niko1221 786b3ca016 serve: card order on AMD/Linux too (KFD topology: SIMDs x clock) 2026-10-07 21:19:07 +02:00
adonizm 0b030b96fc prefill: batched unbuffered expert reads in the stager; close the experts.bin view once reads are unbuffered
NTFS serves unbuffered reads of a mapped file one at a time (WD SN580: 2 MiB reads at QD32
2.35 GB/s mapped vs 3.57 GB/s unmapped). With the file tier unbuffered (--resident-budget-gib
or STRATA_UNBUFFERED_LOAD=1) the mapped view is only a fallback after startup, so it is closed
(STRATA_KEEP_MAPPING=1 keeps it). The prompt path's stager claims runs of consecutive jobs and
reads them with one read_direct batch (STRATA_STAGER_BATCH, default 8 for file-backed blobs).

RTX 5070 Ti, 32 GB RAM, IQ3_XXS, 47.6K prompt, --resident-budget-gib 18 --prefill auto:32768:
1260 -> 1370 tok/s (batch 8) -> 1669 tok/s (view closed).

Ported from #833 onto the rewritten main. Two fixes on the way: unmapped_ is declared on every
platform (mapped_blob reads it outside the Windows block, so Linux did not compile), and
copy_blobs asks resident_blob, which follows the exchange rotation (STRATA_EXCHANGE_ROTATE).
2026-10-07 21:18:06 +02:00
Niko1221 d9621d1e23 serve: a request with no free slot gives the control lines back while it waits (long, long, short under parallel: 2 deadlocked: the slot a yielded read held needed the lines); regression test 2026-10-07 21:15:34 +02:00
Niko1221 039f9e9018 serve: malformed-shape bodies get a 400 with the traceback logged, a body-size cap (256 MiB, STRATA_MAX_BODY_MIB) and a bad Content-Length are answered before the read; engine warns on --batch-groups with --batch-mtp (#1413) 2026-10-07 21:08:03 +02:00
Niko1221 1bf6344cb3 Concurrency stage 1: --batch-groups auto (one pipeline group per GPU stage), INFO batch_groups; tools/serve_load.py load generator 2026-10-07 21:07:59 +02:00
Niko1221 f346dc6b0c tokenizer: remember the ids of repeated pre-tokenizer pieces (a resent 89K-token agent prompt encodes in 0.07 s instead of 0.32 s; same ids) 2026-10-07 21:05:33 +02:00
Niko1221 dfa26c19c9 tests: qsa_select_bench accuracy floor 1e-5 of the score scale (the fast scorer sits at 1e-6 against FP64; warp kernel 7e-8) 2026-10-07 21:02:45 +02:00
Niko1221 bad9a8f22e serve tests: a stopped server stops its hardware sampler (21 samplers leaked after test_responses skewed test_parallel's timing) 2026-10-07 21:00:39 +02:00
Niko1221 533667d06d fix the escape in the watchdog IO note (#1407) 2026-10-07 20:58:08 +02:00
Niko1221 37fa9f7817 sycl: the sampler's launchers no longer require FP64 (dpct left aspect::fp64 checks; samp_acc_t is float by default), so a sampled token no longer needs Intel's FP64 emulation on an A750 2026-10-07 20:56:08 +02:00
Niko1221 125f6ff05f native_mmvq, mmvq_il_parity, generate: fix newline escapes in messages 2026-10-07 20:50:30 +02:00
Niko1221 b7dd070d96 strata --version; build warnings: weights.cpp index scanf formats, qsa_layer unused variables and one-line statements, ignored cudaFreeHost results 2026-10-07 20:47:36 +02:00
Niko1221 ef362cc4ab One prefetch design (#1348): the router look-ahead feeds the Foresight swap space (ForesightSwap::predict, STRATA_FS_AHEAD), slot state under a mutex 2026-10-07 20:47:21 +02:00
Niko1221 b9aa4558a4 setup: checksum verified line, .previous keeps its BUILD.json; update scripts drop the backup branch when the move fails 2026-10-07 20:46:11 +02:00
Niko1221 d7f039992a sycl: expert dequant stores coalesced (lane tid writes the tid-th 16-byte run), bench dequant_bench; IQ3_S 200 -> 423 GB/s of writes on the B70, output bit-identical 2026-10-07 20:43:21 +02:00
NoxestNoz b04507c12f bench/results: V100 decode kernels (GPU work per verify window 22.8 -> 21.2 ms); docs/NVIDIA_V100.md: the decode side; tools/ab_engine.py 2026-10-07 20:42:39 +02:00
NoxestNoz aaa323fec5 fused_gr: Volta runs gfx906's latency-hidden norm/up (STRATA_GR_FAST), which now also writes the QFUSE q8_1 tail
The fast kernels build for CUDA; sm_70 defaults to them, other CUDA cards keep
the plain ones.  gr_up_fast_kernel ends with gr_q8_tail like gr_up_multi_kernel,
so STRATA_QFUSE gets its q8_1 image from it too.  In the engine (staged read)
only the up projection changes: hc-read0's up 1.08 -> 0.96 ms per window on a
V100-SXM2.  fused_gr_bench bitwise, gr_multi_parity OK.
2026-10-07 20:42:39 +02:00
NoxestNoz e509ba5e3d native_mmvq: the interleaved 2-4 column kernel on Volta, with a rows table read off a V100
Read off mmvq_il_parity --bench on a V100-SXM2-32GB with the table's own rule
(every measured shape of a class at least 3% faster, else native_mmvq).  At 3
columns: the Q5_K head 1094 -> 821 us, Q6_K 12288 rows 68.9 -> 47.9 us, Q4_K
10240 rows 43.9 -> 32.5 us.  Bitwise native_mmvq's output (mmvq_il_parity OK).
2026-10-07 20:42:39 +02:00
NoxestNoz f6330507db iq_kernels: Volta runs the routed experts in gfx906's expert mode 8 (bitwise the CUDA layout)
The AMD grouped-expert layouts (STRATA_EXP_MODE) now build for CUDA too.  sm_70
defaults to mode 8 - the codebook grid and the group's q8_1 activations in
shared memory, SwiGLU + q8_1 fused into the gate/up epilogue - every other CUDA
card keeps mode 0.  On CUDA, IQ2_XXS gate/up joins the shared-memory kernels,
a format without one falls back to the CUDA layout (not R2), and a strided call
(the verify window's PCIe call) keeps the CUDA layout.

V100-SXM2, setup's IQ2_XS, a verify window's VRAM call (28 groups): IQ2_S
gate/up layers 180 -> 150 us at 3 tokens, IQ2_XXS 170 -> 134 us; bitwise mode
0's output on all 48 layers (native_expert_bench), native_grouped_parity 0
failures in modes 0/2/7/8.
2026-10-07 20:41:10 +02:00
Niko1221 51d1ef4b8a Stage buffers: STRATA_STAGE_PIN switch (default decided by the A/B) 2026-10-07 20:40:45 +02:00
Zhong Uncle 475912c62a perf(expert): the stage buffers the cache fill copies from, pinned with cudaHostAlloc
A staged blob (the GGUF role slices assembled into one buffer) is handed to
ExpertCache::fill_slot's cudaMemcpyAsync as-is.  From a pageable buffer the
driver stages every copy through an internal bounce buffer - an extra copy at
a fraction of the transfer rate, on the calling thread - so the admit path,
the profile prefill and the lent-slot refills all pay it per expert.  Allocate
the pool's buffers with cudaHostAlloc instead, falling back to pageable for a
buffer when the driver refuses (pinned memory is scarce on the small-RAM
machines this path serves).
2026-10-07 20:40:45 +02:00
Niko1221 311c983964 serve: a config with a vision section but no --vision in its args starts the engine with images (#1322) 2026-10-07 20:39:21 +02:00
architectds ed6a02cdb3 docs: the CPU share up to 3,072-token chunks, from mapped experts, on a split - and what it measured
DETAILS.md's STRATA_PREFILL_CPU_SHARE section: chunks below 3,072 tokens are staged when it is on
(STRATA_PREFILL_CPU_SHARE_MAX=1024 keeps the old limit), the experts the CPU may take (the arena, the
page-locked copy, the mapped experts.bin's pages), the layer split, and a table measured on an RTX 5070 Ti
alone and an RTX 3060 + RTX 5070 Ti split (PCIe 3.0 x8, DDR4-3200, --mmap-experts): 512-token prompts
-49% / -41%, 3,000-token -22% / -24%; a recorded 100K-token coding conversation on the two cards 137.4 ->
130.6 s.
2026-10-07 20:39:12 +02:00
architectds 92bce20e4b prefill: the CPU share up to 3,072-token chunks, from mapped experts too, and on a layer split's stages
STRATA_PREFILL_CPU_SHARE handed the CPU pool its share of a chunk below 1,024 tokens only, only experts in
page-locked RAM, and only on one GPU.  With it set:

- the chunks staged after their routing (the only ones the share applies to) go up to
  STRATA_PREFILL_CPU_SHARE_MAX tokens (default 3072, at least 1024) instead of 1024; Prefill::arm_cpu_share
  sets the limit before any loan is sized, and init sets it too;
- an expert whose blob the CPU reads from RAM without assembling it (the arena's unpinned part, the mapped
  experts.bin's pages) can go to the CPU, not only a page-locked one; the blob pointers are taken when the
  experts are chosen, and one without a stable blob stays on the GPU;
- on a layer split every stage has the pool (serve, no --batch), and one stage at a time takes it for a
  chunk while the other reads its chunk on the GPU alone.

Unset, nothing changes.
2026-10-07 20:39:00 +02:00
sergiywith f2a3b946e9 prefill: STRATA_PREFILL_CPU_SHARE=auto times layers with and without the share and shares only while that is faster (#1282)
auto balanced the CPU's time per expert against the GPU's per streamed expert, both measured with the share on, so
nothing compared a layer with and without it.  On jase100k's RX 7900 GRE + Ryzen 7 5700X3D the host's time per
streamed expert rose 107 -> 200 us with auto (0.47): the streaming side saved nothing (0.53 x 1.87 = 0.99) and the
CPU's work came on top, -4.5%.  Now every eligible layer is timed with CUDA events from before its routing sync to its
combine, per non-resident expert; adjacent layers alternate no share / the measured share until three ratios are in,
then auto shares while the median of the last five is below 1, with one layer in 29 in the other arm.  Layers with a
one-time cost and the first layer are left out.  STRATA_DBG_CPU_GATE=1 prints each measurement.

RTX 5090 + 9950X3D, IQ2_XS, 250 / 600 / 900 tokens against off: DMA streaming -9.8/-9.9/-10.4% -> -8.8/-9.1/-10.5%;
an emulated host-copied stream -18.0/-16.7/-14.4% -> -16.1/-15.2/-13.0%; one core + one worker (where the old auto
lost up to +11.5%) +1.4..+2.4% -> +2.0..+2.3%, the cost of the first measurements.  Unset: byte-identical.
2026-10-07 20:38:56 +02:00
Niko1221 804dc7c326 setup: no --remote-expert-opt beside --pipeline-windows (the first turns the second off); docs: card order (#1352) 2026-10-07 20:38:27 +02:00
Niko1221 ae083aebbf calibrate: a later engine start that fails drops that candidate and keeps the earlier measurements (#1337) 2026-10-07 20:37:56 +02:00
Niko1221 be8db787c2 serve: card order for an auto layer split, faster card last (SMs x clock), gpu_order as_given opts out (#1352) 2026-10-07 20:37:35 +02:00
Niko1221 f60a12d407 conversation_cache_test: fix check call 2026-10-07 20:37:20 +02:00
Niko1221 f2e07afbf1 serve: the #29 watchdog waits (up to 10x, STRATA_WATCHDOG_IO_S) while the OS is still reading the file tier (#1407) 2026-10-07 20:37:15 +02:00
Niko1221 9b4336a225 conversation_cache_test: evict_oldest with pinned entries 2026-10-07 20:37:15 +02:00
alanthinker 940011dad9 serve: a full conversation cache evicts to pass the physical-RAM gate instead of dropping the snapshot
The parking path has two gates in series: make_room() balances the cache's own budget, then conversation_memory_admit() checks the host's free RAM against the snapshot plus --conversation-cache-min-free-mib. The second was final: a failure dropped the snapshot even though the cache held conversations whose RAM it could have, and the next request read its whole prompt again (270k tokens, 96 s, measured by the reporter in #1347).

Evict the least recently active parked conversations until the gate admits the snapshot, bounded by the slot count; entries that hold a pinned shared prefix are not evicted. The failure line says how many were evicted and how many remain.

Reported with the fix by alanthinker in #1347.
2026-10-07 20:37:05 +02:00
Niko1221 d7932123e1 serve: #1317 part 2 quick watchdog for a frozen engine (90 s silent, no CPU or disk work; STRATA_ENGINE_STALL_S); #1344 several API keys (comma list or config list) 2026-10-07 20:36:40 +02:00
Niko1221 f23913a0ac tests: expert_multi_test skip message had a literal newline in its string 2026-10-07 20:33:49 +02:00
Niko1221 f780961c44 fix the escape in the lend-coverage warning (#1389) 2026-10-07 20:32:56 +02:00
Go Kudo 61a76de08d hip: hipBLASLt tuning table for gfx1150 (Radeon 890M, ROCm 7.14.1 / hipBLASLt 100401)
The 16 dense GEMM geometries a Qwen3.8-Flash-Next prompt logs on a Radeon 890M (gfx1150, Strix Point) at
T = 64 .. 16384, 144 rows, calibrated with tools/hip/tune_hipblaslt.cpp against AMD's TheRock ROCm 7.14.1 for
gfx1150 (hipBLASLt 1.4.1 cd957402). Two passes back to back; each row keeps the solution with the lowest median of
all six repetitions (a single repetition stalled now and then, and the tool's mean-of-three picks differed in 26 rows
between the passes).

Every row beats plain hipBLAS, 1.55-22.6x per GEMM. End to end on GSQ-RCO IQ3_XXS, three interleaved rounds: prompts
read 1.5-1.7x faster (3.6K tokens 124 -> 216 tok/s, 7K 130 -> 226 tok/s), decode unchanged; hip_prefill_hipblaslt_gemm
passes with it and a server run logged no fallbacks. Setup does not install for gfx1150, so the table is set by hand
(STRATA_HIPBLASLT_TUNING). Method and data: bench/results/2026-10-07-community-gfx1150.
2026-10-07 20:32:04 +02:00
Chen Cheng a0181802b0 generate: say that CUDA_LAUNCH_BLOCKING=1 hangs the verify windows (#1341 #964)
A verify window's graph waits on the GPU for flags the host raises once
cudaGraphLaunch has returned. A blocking launch returns only when the
graph has finished, so with CUDA_LAUNCH_BLOCKING=1 the first window after
a prompt never ends and the watchdog stops the engine, whatever the prompt
length, GPU count or --pipeline-windows. The engine now says so at start,
and the watchdog's line names it. Nothing changes without the variable.
2026-10-07 20:31:54 +02:00
Niko1221 2d6c6ae5ce MMVQ_IL rows table per architecture: kIlArch hook, STRATA_MMVQ_IL_ROWS override, mmvq_il_parity --emit-table, the sm_89 method 2026-10-07 20:31:41 +02:00
Niko1221 83a7dbc0f1 prefill: the stager takes the RAM profile when the GGUF experts' pages are in the page cache (#1353) 2026-10-07 20:29:45 +02:00
Niko1221andblange48 2c7c02ab72 serve: with pipelined groups, a request fills a hole below a busy slot of its group first (#793)
A pipelined group's window spans its slots up to its last busy one, and the engine keeps no pipelined slot as a
conversation cache, so the least-recently-used pick left leading holes ([_ B]) that cost a whole row each.  pick_slot
now fills such a hole first, then follows the spreading order.  The engine reports INFO batch_groups so the server
knows the groups really run pipelined.  serve/test_slot_alloc.py covers the allocator.

Co-authored-by: blange48 <[email protected]>
2026-10-07 20:29:09 +02:00
shy 0391e478ce split: the auto placement sees each stage's PCIe link
The miss cost (190 ms per unit of routed mass, fitted on x16 links) is one
constant for every stage, so --layer-split auto cannot see an asymmetric
pair: on 2x 4090 D with CUDA1 on a physical x1 link (1.6 GB/s measured) it
gives that card 26 layers (K=22) and reads a 29K prompt at ~350 tok/s.

Probe each stage's link inside the search (probe_pcie_h2d_gbps; the
stage-setup probes are skipped when --pcie-frac is given, and the search
needs the reading either way) and scale that stage's miss term by
clamp(20 GB/s / bw, 1, 64).  20 GB/s is pcie_frac_for_gbps' x16 reference;
the lower clamp keeps a symmetric machine on the exact formula the sweep's
K was fitted with (every scale 1.0 -> the objective is unchanged).

With it, auto picks the fit-in-cache boundary on that machine (K=38: the
narrow card's cache holds all of its layers' pairs, zero streaming there)
and reads the same prompt at ~1410 tok/s, ~4x.

The 190 ms mixes the decode CPU-pool cost with the prompt link cost;
scaling the whole per-stage term is the first-order fix (splitting the
constant would need a fresh fit on both a narrow and a wide link).
2026-10-07 20:28:02 +02:00
Niko1221 85a108a751 gdn_rec_parity: same STRATA_GDN_CHUNKED=2 SM-count bypass as the engine copy 2026-10-07 20:27:55 +02:00
Andy 0c7eb37d1b Foresight swap: slots copied in but never used again become reusable once landed (they leaked as 'loading') 2026-10-07 20:26:47 +02:00
Andy 44f650f972 Foresight swap: STRATA_FS_VERIFY test mode (read every landed slot back, compare with its source) 2026-10-07 20:26:47 +02:00
Andy 14f9e4c50b Foresight swap space: per-layer VRAM slots refilled on their own stream with recently missed experts, served as GPU groups at the pool's existing per-layer decision (STRATA_FS_SLOTS, off by default) 2026-10-07 20:26:47 +02:00
Niko1221 b92a44bfc7 peer: STRATA_PEER_NO_P2P=1 test knob (pretend the pair has no P2P) 2026-10-07 20:25:14 +02:00
Niko1221 f0880b3251 STRATA_GDN_CHUNKED=2: skip the 128-SM test so the chunked recurrence can be measured on smaller cards 2026-10-07 20:25:11 +02:00
Jim Routh 8d6e8ca3fc docs: STRATA_PREFILL_STREAM_MIN=128 in the gfx1151 fast configuration
STRATA_PF_FUSED=1 reaches only chunks of stream_all_min() tokens or more (1,024, measured on a discrete card); a
smaller read runs its experts through MMQ. On Strix Halo every expert is resident and MMQ is ~3x slower on a
~1,000-token read, the size of an agent's turn: 441 / 855-token reads 3.1 / 4.0 s -> 0.9 / 1.2 s, a 12-turn agent
replay 3.4 -> 2.1 s per turn (C2T8, --no-prefill-borrow). It changes bits (the fused path's output, which reads of
1,024+ tokens already get), so section 5: teacher-forced KL against today's path 0.016 on reads under 1,024, the same
as fused vs MMQ on longer reads (0.019); reads of 1,024+ unchanged (KL 0).
2026-10-07 20:25:01 +02:00
alexdns1 94f9d169af hip: gfx1151 hipBLASLt tuning table (hipBLASLt 1.4.0)
Setup installs the ROCm 7.14.0a20260608 wheels for gfx1151 since 0.1.40.2 (#1267); their hipBLASLt is 1.4.0
(100400), which neither gfx1151 table matched, so setup's engine read the prompt through plain hipBLAS.
tune_hipblaslt --workspace-mib 32 on the 100401 table's 90 shapes, on a Ryzen AI Max+ 395 (Radeon 8060S, gfx1151,
128 GB, Fedora 44, kernel 7.2.8) with those wheels: 3.1-14.1x faster than plain hipBLAS per shape (median 6.2x).
hip_prefill_hipblaslt_gemm and hip_prefill_hcd_exact_parity pass with it (fallbacks=0); HIP ctest 61 of 62
(ple_parity needs the Q2_0 fixture). setup picks it up with no code change.
2026-10-07 20:25:01 +02:00
Niko1221 14d9d2e1a5 Warn when the prompt path's lend coverage is short; document STRATA_RESIDENT_HEADROOM_GIB and the long-prompt method (#1389)
Below N of N lendable slots the uncovered experts are read from the pack during a long prompt (reporter: 18% uncovered = -26% prompt speed on gfx1100). A WARNING line now says so, and docs/AMD_HIP.md documents the headroom knob and the levers from the report. No behaviour change otherwise.
2026-10-07 20:25:01 +02:00
sergiywith 0f78032fdc DeltaNet recurrence: STRATA_GDN_CHUNKED=1 - the prompt's recurrence in 32-token chunks (opt-in, 1.8x faster, other bits)
The gated delta rule in the WY form flash-linear-attention uses, all FP32: gdn_chunk_prep_kernel forms k.k / q.k and
T = (I + A)^-1 for every chunk of a 2048-token super-block at once; gdn_chunk_scan_kernel (128 blocks, a key head x
16 value columns each) walks the chunks with its state slice in registers.  Every exponent <= 0 (the gates are
negative).  The scratch (25.6 MB) is allocated once per device: freed and reallocated per call it cost more host time
than the kernels saved on short prompts.  sm_80+, >= 128 SMs, enough shared memory, chunks of >= 128 tokens; else
gdn_rec_kh_kernel.  Unset: unchanged.

gdn_rec_parity: against gdn_rec_kh_kernel <= 1.13e-6 (T = 1..4099, 8192, 32768, a non-zero starting state); against
an FP64 recurrence <= 1.05e-6 (the sequential kernel <= 3.2e-7) - FP32 rounding; 199 restarts of 7 shapes bit for
bit (NaN-filled scratch between).  RTX 5090: 1.81x the recurrence at 2K-32K; IQ2_XS: the recurrence phase of a 32K
prompt 520-532 -> 306-334 ms (-210 ms, -3.7%), 8K -2.1..-3.6%; first-token KL 0.0016-0.0152, 1.15-1.83x a harmless
control arm (STRATA_PROMPT_ATTN_V1), the same top token.
2026-10-07 20:24:14 +02:00
Thig 59ea77e5b3 prefill: the no-P2P hand-off copies on a stream of their own
The MoE input and the routing weights go to host memory on a stream beside the primary's compute stream, so its
own experts start at once instead of behind the copies over its link (mixed_h and w are rewritten only after this
layer's combine, which waits for the peer).  STRATA_PF_PEER_XSTREAM=0: the compute stream.  With
STRATA_PF_PEER_RING=160, prompt tok/s: 100K 1,854 -> 1,918; 8K and 3.4K unchanged.
2026-10-07 20:24:05 +02:00
ThigandEddoursul da0024fed1 prefill: the peer's per-token sums in one launch per group instead of one per expert
Each group's tokens and their row lists (in the experts' order) are built on the host from the rows' pairs and
uploaded once per layer; peer_gather_add is eddoursul/Strata's moe_gather_add with the weight looked up through
the row's pair, so the sums are bitwise those of the per-expert launches.  STRATA_PF_PEER_GATHER=0: per expert.
Prompt tok/s against the per-expert launches: 3.4K 825 -> 832, 8K 1,468 -> 1,500, 100K 1,821 -> 1,858.

Co-Authored-By: Eddoursul <[email protected]>
2026-10-07 20:24:05 +02:00
ThigandEddoursul 550159fc59 prefill: the no-P2P sums mode's two transfers in FP16
The MoE input goes out as the FFN input's existing FP16 image (mixed_h) and the per-token sums come back as
FP16, saturated at +-65504 (eddoursul/Strata f8de703's FP16 input, 2acfac4's sums_to_f16): half the bytes over
the primary's link both ways.  STRATA_PF_PEER_F16=0: FP32.

Peer stream 0.6, prompt tok/s: 3.4K 805 -> 831, 8K 1,284 -> 1,495, 100K 1,578 -> 1,855 (the share off: 1,712).
The 8K and 3.4K answers stay byte-identical to the share off.

Co-Authored-By: Eddoursul <[email protected]>
2026-10-07 20:24:05 +02:00
ThigandEddoursul f4bfedc5ce prefill: the no-P2P peer share sends each token's weighted sum back instead of its rows
The design is eddoursul/Strata's second-GPU prompt path (f8de703, 2acfac4, e8ade66): the second GPU adds its
experts' rows into each token's weighted sum and the first GPU's combine adds that to its own experts' rows.
Over the host route the peer scatter-adds its rows into per-token sums (peer_scatter_add, after the fork's
moe_scatter_add: one launch per expert in a fixed order, so the sums are repeatable) and sends T x N floats
back instead of rows_peer x N (up to K = 10 times more); moe_combine_peer adds them to the primary's rows.  The
2a row buffers are not allocated (3.4 GB of pinned RAM at 32K-token chunks).  STRATA_PF_PEER_SUMS=0: rows.

Same setup, peer stream 0.6, prompt tok/s against the share off: 3.4K 332 -> 805, 8K 762 -> 1,284, 100K
1,712 -> 1,578.  The 8K and 3.4K answers are byte-identical to the share off (greedy, 256 / 3,221 tokens).

Co-Authored-By: Eddoursul <[email protected]>
2026-10-07 20:24:05 +02:00
Thig 81af1da0b1 prefill: --peer-device's prompt share without P2P, through the layer split helper's mapped host route
set_peer refused a peer that cannot access the primary directly, so on GPU pairs without P2P (most GeForce
pairs) the prompt path stayed on the primary GPU.  Without P2P the share now takes the route the layer split's
helper already uses: the activations and the result rows through mapped pinned host buffers read by copy
kernels, with the compact group buffers (the layer-sized path copies over P2P only).  STRATA_PF_PEER_HOST=0
restores the refusal.

RTX 3090 (PCIe 4.0 x4) + RTX 3080 Ti (x16), no P2P, UD-Q4_K_XL, --prefill auto:32768, peer stream 0.35 / 1.0,
prompt tok/s against the share off: 3.4K 332 -> 492 / 551, 8K 763 -> 907 / 776, 100K 1,713 -> 1,072 / 734.
The peer's rows coming back over the primary's x4 link cost more than they save on long prompts; the next
commit sends sums instead.
2026-10-07 20:24:05 +02:00
Niko1221 344ea1cd4b tools: check_isa_guard flags std:: instantiations only inside the wide-ISA objects 2026-10-07 20:23:53 +02:00
Niko1221 f02c244628 tests: expert_multi_test skips on a CPU without the AVX-512 kernel's features 2026-10-07 20:23:53 +02:00
Niko1221 90dd021a70 tests: AVX2 dispatch never enters wider code (#795)
- tools/check_isa_guard.py disassembles the CPU kernels (objdump + nm on the Linux kernel library, capstone + the /MAP
  file of a linked binary on Windows) and fails when a function holding AVX-512 / AVX-VNNI-class instructions is
  not a kernel of a wide-ISA file: a weak or COMDAT copy (the linker may pick it for every caller), a std:: /
  template instantiation, a probe or helper that every CPU runs, or code in another object.  MSVC's own
  `__isa_available` vectorizer guard is accepted.  ctest isa_guard_check runs it; exit 77 (skip) when the tools
  are missing.
- -DSTRATA_SDE=<sde> adds sde_{hsw,adl}_* tests: the CPU parity tests on an emulated Haswell (AVX2 only) and Alder
  Lake (AVX-VNNI, no AVX-512); SDE stops at the first instruction the chip lacks.
- tools/test_check_isa_guard.py: unit tests of the policy.
2026-10-07 20:23:53 +02:00
Niko1221 680d499291 cpu: keep the AVX-512 probe and the "supported?" helpers out of the wide-ISA files (#795)
expert.cpp and iq_avx512.cpp are compiled for AVX-512, iq_avx2.cpp and kq_avx2.cpp for AVX2, and a file compiled
that way may use the wide ISA anywhere in its code.  Code that every CPU runs was still defined in them:

- cpu_features, CpuFeatures::reason, cpu_require_expert_support (the function that finds out whether the CPU has
  AVX-512) and expert_set_oracle_q8_0 / expert_oracle_q8_0_enabled (the first call of every generate, on any CPU)
  were in expert.cpp;
- iq512_supported, iq256_supported and kq256_supported (a comparison native_gu_rows asks before it has checked
  what the CPU can run) were in the kernel files; iq512_supported was called on every AVX2-only CPU for every
  gate/up call.

They now live in expert_layout.cpp and native_expert.cpp, which are built for the baseline.  No kernel changes;
the compiled code of the moved functions is identical, so default output is byte-identical.
2026-10-07 20:23:53 +02:00
Niko1221 d5ea713374 Version 0.1.40.3: MIN_ENGINE 0.1.40.3; Intel Arc setup text says supported (B70, A750) instead of 'no Intel card here' 2026-10-07 18:20:34 +02:00
Niko1221 34ff00bdd0 Merge branch 'f-hf-small' into f-1402 2026-10-07 18:16:39 +02:00
Niko1221 6d83a1ace3 Merge branch 'f-hf-intel' into f-1402 2026-10-07 18:16:39 +02:00
Niko1221 8aeefca2d8 Merge branch 'f-hf-misc' into f-1402 2026-10-07 18:16:38 +02:00
Niko1221 88d090bbef Merge branch 'f-hf-amd' into f-1402 2026-10-07 18:16:38 +02:00
Niko1221 41016e3221 Merge branch 'f-hf-nv' into f-1402 2026-10-07 18:16:38 +02:00
Niko1221 3581f853a6 sycl/tools/build.sh: executable in git (docker run ... sycl/tools/build.sh failed with Permission denied) 2026-10-07 18:16:10 +02:00
Niko1221 c5db5ea803 docs: Intel install path from zero (Docker, ./setup.sh --backend sycl), the missing How to build it section, the Arc A-series path 2026-10-07 18:15:57 +02:00
Niko1221 c454a4bddd sycl setup and serve: Intel wording in setup's output, an explicit --vram-reserve-mib is kept, the Arc A-series (i915) gets a config that starts (no --stream-experts, --ple-io ram, 300 MiB reserve, English draft vocabulary, NO_HOST off, FP64 emulation keys), strata-sycl.sh forwards STRATA_/ONEAPI_/UR_/IGC_/SYCL_/ZES_ variables, the start narrator no longer warns about a slow PC when experts are streamed 2026-10-07 18:15:57 +02:00
Niko1221 013015934a sycl engine: the device waits long enough for the host on an A-series card (first request after a start gave token 0 or crashed the CPU pool), STRATA_VERIFY_NO_HOST=0 means off, version follows the project, no CUDA-runtime warning, no FIXME placeholder in the verify lines; build.sh is executable and takes SPIN_MAX 2026-10-07 18:15:56 +02:00
Niko1221 c13ddd62d5 web: send an assistant turn with no answer text back in the history (#1392); docs: STRATA_STAGER_SLEEP numbers (#1101)
Reported-by: demetree
2026-10-07 18:05:29 +02:00
Niko1221 5600305998 docker: keep a config link that points into /data/config, add CONFIG, print the config used (#1244) 2026-10-07 18:04:02 +02:00
Niko1221 4895f63a61 #1376: the Windows auto-cache floor applies to HIP builds only; CUDA on Windows keeps its sizing (no change measured there) 2026-10-07 17:35:33 +02:00
Niko1221 d7c5dd0fc5 dashboard: say why a Windows AMD card shows no load or VRAM instead of an empty tile or 'not readable (NVML)' (#1380) 2026-10-07 17:34:36 +02:00
Niko1221 044c59fee6 Windows: an auto expert cache on a card of 16 GiB or more keeps 2560 MiB free after the cache (#1376)
RX 7900 XT 20 GB, IQ3_S, Windows HIP, auto cache, default reserve 700: 0.1.40 ended with 4,255 slots (8.12 GiB) and 2,170 MiB free, 0.1.40.2
with 5,512 slots (10.48 GiB) and 323 MiB free; WDDM paged and decode fell from 51.7 to 21-25 tok/s. With --vram-reserve-mib 2600: 4,299 slots
(8.20 GiB), 2,224 MiB free, long prompt 1,015.8 -> 1,355.9 tok/s (+33.5%), decode 24.3 -> 55.5 tok/s (+128%), back inside 0.1.40's ranges.
A reserve given on the command line is kept; a smaller card keeps its own sizing. The cache size changes no output bit.
2026-10-07 17:34:36 +02:00
Niko1221 4b3ff7a1b4 Windows HIP: setup and the package put the whole dependency closure of amdhip64_7.dll and amd_comgr.dll beside strata.exe, and the engine says why the bundled runtime did not load (#461)
amdhip64_7.dll imports rocm_kpack.dll, which imports the MSVC runtime. Only the two runtime DLLs were copied beside the exe, so a
full-path load of the bundled one failed with error 126, Windows fell back to System32's copy, and big prompts died with
hipErrorInvalidDeviceFunction (RX 7800 XT, gfx1101). The closure is now read from the PE import tables of the files in the zip
(tools/hip/dll_closure.py, no pefile), by setup.hip_runtime_beside_exe and tools/hip/package_windows.py. On the 0.1.40.2 HIP zip it is
amdhip64_7, amd_comgr, rocm_kpack, msvcp140, vcruntime140, vcruntime140_1. The engine, when the loaded runtime is not the one beside it,
also tries LoadLibraryEx on the bundled one and prints GetLastError. Diagnostic only. Not tested on a Windows AMD card.
2026-10-07 17:34:36 +02:00
Niko1221 4dc544eda7 tokenizer: _bpe reads self once instead of per symbol pair, and a threads-vs-serial encode test (#1385; the 'int has no attribute ranks' fault could not be reproduced and looks like an interpreter bug) 2026-10-07 17:21:48 +02:00
Niko1221 668072aa49 Revert "Verify window: the interleaved q8_1 projections (STRATA_MMVQ_IL) are opt-in again"
This reverts commit f7610d2c32.
2026-10-07 17:18:45 +02:00
Niko1221 ca016f230d sycl mtp: the same n_expert == 512 and k == 10 guard on the per-token native router call
Reported-by: rwkeyes (#1357)
2026-10-07 16:16:11 +02:00
Niko1221 6c0fca38ee mtp: guard the drafter's per-token native router call with n_expert == 512 and k == 10
The native router reads 512 logits a row; the window path two lines above already tested n_expert == 512 and K == 10, the
per-token fallback tested only native_router_enabled(), so a model with fewer experts read past each row. It now takes the
generic router_top10 like its siblings. native_multi_parity checks the generic router on 256-wide rows.

Reported-by: rwkeyes (#1357)
2026-10-07 16:16:11 +02:00
Niko1221 821651a44e engine: fix the newline in the kfd permission message 2026-10-07 16:15:55 +02:00
Niko1221 9993c62209 setup: arch-exact iGPU text (gfx1103 is not Strix Halo), warn on a missing render group or /dev/kfd access, engine names it instead of blaming another program; troubleshooting entry for Windows AMD driver resets 2026-10-07 16:14:03 +02:00
Niko1221 f7610d2c32 Verify window: the interleaved q8_1 projections (STRATA_MMVQ_IL) are opt-in again
0.1.40.2 turned fork F4 on by default after single-GPU measurements (+4.2% on an sm_86, +1.3 to +2.2% on an sm_120). A
two-GPU Windows layer split (RTX 4080 SUPER + RTX 5070 Ti) was reported 15% slower with it on (#1373). The outputs are
bitwise the same either way, so only speed changes. STRATA_MMVQ_IL=1 turns it on; docs/DETAILS.md says why.
2026-10-07 16:13:19 +02:00
Niko1221 e8ca9afd03 setup: engine versions compare all four numbers and MIN_ENGINE is 0.1.40.2, so an installed 0.1.40 engine is replaced by the new one 2026-10-07 13:22:57 +02:00
Niko1221 b8617c4b63 Merge branch 'f-141-pf' into f-1402 2026-10-07 12:40:07 +02:00
Niko1221 8f70a52cfa Merge branch 'f-141-intel' into f-1402 2026-10-07 12:35:33 +02:00
Niko1221 3838a40a0c sycl: the sampler's top_p/temperature tail accumulates in float (FP64 is emulated on Arc Alchemist); B70 IQ3_S findings in docs/INTEL.md
samp_acc_t is float by default (-DSTRATA_SYCL_SAMPLER_FP64=1 keeps the reference's double). On an Arc A750 one row of the
tail cost 451 us in double and 20 us in float; a sampled decode ran at 7.62 -> 8.10 tok/s (medians of 5 interleaved
pairs, runs 7.1-9.2). sampler_parity: 0 failures on the A750 (JIT build) and on the B70; both cards' 10-prompt gates pass
10 of 10, and the B70 gate's sampled cases pick the same tokens as before. Of the kernels that use double, only the
sampler runs on a native pack (the router, combine, gate and norm kernels there are float).

Probes: sycl/probe/fp64_cost.cpp (FP64 against float in the small reduction kernels), gemm_vary.cpp.
docs/INTEL.md: the aliased-pages bug, NO_HOST, the prompt-chunking numbers, draft-length sweep, FP64 and XMX findings;
docs/INTEL_ARC.md: a row for the B70/A750 runs.
2026-10-07 12:22:27 +02:00
Niko1221 f1ed65b92e docs: STRATA_PREFILL_CPU_SHARE (opt-in) with the measured prompt times and its output difference 2026-10-07 11:51:33 +02:00
Niko1221 4322e2411f prefill: STRATA_DBG_NAN does not count the CPU-share rows of GU and H (the GPU never writes them)
With STRATA_PREFILL_CPU_SHARE on, the last rows_cpu rows of GU and H hold whatever the buffer held before; the check read
them and reported them as non-finite (about 1 in 256 garbage floats), with Dm and the layer output clean.
2026-10-07 11:51:33 +02:00
Niko1221 667f2ecadf prefill: the CPU-share thread takes no ExpertSource call (blobs taken on the prefill thread, blob_stable)
blob() bumps a plain reads_ counter and, for a file source, its mapped/staged paths; calling it from the share's
thread raced with the prefill thread's own blob_stable()/pinned() calls (the streamed experts). The blobs of the CPU's
experts are now taken before the thread starts, so the thread reads only the arena bytes, the activations and the
grouping tables, none of which the prefill thread writes until the thread is joined.
2026-10-07 11:51:33 +02:00
sergiywith 978d355852 prefill: STRATA_PREFILL_CPU_SHARE=auto hands a small chunk's least-routed experts to the idle CPU pool
A chunk below stream_all_min() (1,024 tokens: an agent's tool result, a test's
output) streams every routed expert outside VRAM over PCIe while the decode CPU
pool sits idle and the arena holds the same experts in RAM.  Opt-in,
STRATA_PREFILL_CPU_SHARE=auto|x:
- the non-resident experts routed by at most MAXT of the chunk's tokens,
  fewest first, run on the pool on a thread while the GPU streams and runs the
  rest; their unweighted rows go to Dm's tail (as a peer GPU's do) and the
  combine weights them as any other row;
- auto: the share measured, each layer's CPU time per expert and the GPU's per
  streamed expert (CUDA events around its expert work, read after the next
  layer's routing sync), and the next layers hand the CPU g / (c + g), where
  both end together - a small CPU takes a small share;
- Q2_0 layers take the pool's Q2_0 kernels' activations (ActQ, as decode's),
  i-quant layers the native vec_dot type.

Serve without batch slots and generate, one GPU.  Unset (the default): no
expert moves, byte-identical to v0.1.39 (IQ2_XS 600 and 2,100 tokens, fixed
cache, logits and 32 tokens).

RTX 5090 + 9950X3D, 600-token prompts, ms (off / auto):
- UD-Q4_K_XL: 1,548 / 1,369, 1,528 / 1,296 (share 0.65; KL between 0.006);
- Q2_0: 472 / 445, 491 / 448;
- IQ2_XS: 493 / 481, 502 / 460, 527 / 529; 250 tokens 424 / 355, 402 / 382,
  379 / 367 (KL to STRATA_PREFILL_MMQ=0 0.0053 off, 0.0080 auto);
- IQ2_XS on 2 AVX2 workers (emulated): 512 / 487 (share 0.49).
2026-10-07 11:51:33 +02:00
Niko1221 9476ef1ade Merge branch 'f-1402-dec' into f-1402 2026-10-07 11:43:30 +02:00
Niko1221 960de8bb3d Engine SHA-256: warn and install when no digest is available, STRATA_SKIP_SHA256=1; docs note for STRATA_SH_STREAM (#1218 #1272) 2026-10-07 11:42:58 +02:00
Demetree Kallergis cf5fa3af91 Check the ready-made engine against GitHub's SHA-256 before installing it (#1218) 2026-10-07 11:42:03 +02:00
Niko1221 6fd12038b1 sycl: no short first prompt chunk while part of the experts is in the host mirror; setup asks a 24 GB+ card for 4096-token chunks
Every prompt chunk streams the experts the VRAM cache does not hold, over PCIe, and a 4K prompt touches nearly all 512
experts of a layer. The short first chunk (STRATA_PREFILL_FIRST, 256 tokens, there to start the GPU while PLE rows are
read) therefore cost a whole extra pass of that stream whenever the cache holds only part of the model. Its default is
now 256 only when the cache holds every expert, else 0 (an explicit STRATA_PREFILL_FIRST still decides).

Arc Pro B70 32 GB, IQ3_S (11.5k of 24.6k experts in VRAM, the rest in the pinned mirror at 13 GB/s), a 4,095-token
prompt, MTP on, interleaved runs, medians of 3: 2048-token chunks 618 -> 730 tok/s, 4096-token chunks 784 -> 980.
sycl/setup_intel.py now writes --prefill 4096 for a card with 24 GB or more (it costs ~700 expert cache slots, about 3%
of decode on the 200-token story).
2026-10-07 11:31:11 +02:00
Niko1221 c4c3221c97 Version 0.1.40.2 2026-10-07 11:29:31 +02:00
Niko1221 57747c1aa9 Merge branch 'f-1402-s' into f-1402
# Conflicts:
#	serve/server.py
2026-10-07 11:27:21 +02:00
Niko1221 2b87574b30 serve: STRATA_ENGINE_READY_S / STRATA_VISION_READY_S / STRATA_VISION_ENCODE_S override the #1317 read timeouts (0 waits for ever, as before) 2026-10-07 11:26:48 +02:00
Niko1221 78f6174474 sycl: guard big device allocations against aliased pages on the xe driver (Arc Pro B70)
An Arc Pro B70 (xe) returned the 22 GiB expert-cache arena with two of its 2 MiB pages mapped onto the same memory:
a write to the page at +2 MiB showed at +1022 MiB, so cache slots there held other experts' bytes and the prompt
came out NaN at the first layer that routed to one of them (layer 4, expert 249). Which pages alias depends on the
allocation history (the context size, the MTP window), so it comes and goes between runs.

strata::arena_alias_check tags every 64 KiB of a fresh range and reads the tags back in a second kernel;
strata::malloc_device_guarded allocates again behind a growing spacer until the range is clean (and warns if it
never is). It guards the expert cache arena and every device allocation of 32 MiB or more (session arena, weights,
MTP, verify, head, prefill). STRATA_ARENA_ALIAS_CHECK=0 skips it. Two debug aids: STRATA_VERIFY_ALL_SLOTS=1 (with
STRATA_VERIFY_FIND=1, STRATA_VERIFY_DUMP=dir) reads every filled slot back and compares it with the GGUF, and the
probes sycl/probe/arena_rw.cpp and alias_scan.cpp.
2026-10-07 10:53:07 +02:00
Niko1221 ec7f6a0275 serve: a restart clears max_context before the new engine process is visible, and close() does not join an output thread a restart never started 2026-10-07 10:35:58 +02:00
Niko1221 5e4de5c738 docs: STRATA_SPEC_GUMBEL on an RTX 3060: acceptance up, decode within noise
12 interleaved pairs at temperature 1.0 with MTP, default / coupled / coupled + Gumbel: accepted drafts 62.1 / 66.9 / 68.0%, median decode 43.4 / 43.3 / 42.8 tok/s.
2026-10-07 10:15:44 +02:00
Niko1221andbtc1000w 02be84a09c serve: time out the untimed reads of the engine's READY line and the image encoder's pipe (#1317)
A blocking read of the engine's READY line or of the vision encoder's pipe that
never returned held the request turn for good: the engine sat idle in its command
loop, so no watchdog could fire and every later request queued behind it (the lost
step class of #481). The READY read now has a 900 s timeout, the encoder's READY
and ENC reads 300 s each. A process that says nothing for that long is killed, the
read raises, and the next request starts a fresh one. Slow loads and slow encodes
stay far inside the limits.

Only the read timeouts of the reporter's patch are taken. The watchdog that ends
an engine after 90 s without output and CPU progress is left out for 0.1.41.

Tests: a silent engine, a silent encoder at READY and at ENC, and an in-time answer.

Reported-by: btc1000w
Co-authored-by: btc1000w <[email protected]>
2026-10-07 10:15:25 +02:00
Niko1221 e9e1757a7f hip: one-line start hint for STRATA_HIP_WMMA=1 on gfx12 with --kv int8
A hint only: it never enables the kernel and says the output bits change. Silent when the variable is set to anything. Follows the R9700 numbers of PR #1318 (+31% prompt at 8.8K).
2026-10-07 09:09:40 +02:00
l33tm4st3r 3512797dba docs(AMD_HIP): what a gfx1201 hipBLASLt 1.4.1 table measured on my R9700
I calibrated a table for hipBLASLt 1.4.1 with tune_hipblaslt over the 32 geometries of
gfx1201-hipblaslt-100500.txt (R9700, system ROCm 10.1.0, hipBLASLt 1.4.1): 1.15x geometric
mean per GEMM over hipBLAS, but only 1,606 -> 1,613 tok/s median prompt over three matched
trials (a 1.008x geometric mean) and decode unchanged, so within noise - which is what the
R9700 paragraph above already reported, now from a second machine. The cold first prompt of
each start is left out of those numbers.

I dropped two rows from it: bf16 N=10240 K=320 ldy=10240 at T=4096 and at T=8192. hipBLAS
won T=8192 in five separate runs (0.609-0.619 ms against 0.648-0.677 ms for the best of 16
candidates), and dropping only the T=8192 row would send that call to the T=4096 row rather
than back to hipBLAS, because closest() takes the nearest T bucket.

Two calibration traps I ran into are recorded too: the first case of a fresh tune_hipblaslt
process times its hipBLAS baseline while the clocks are still ramping, and the first fresh
prompt after a model load is not a measurement (949 vs 1,423 tok/s in the two arms there).
2026-10-07 09:07:39 +02:00
l33tm4st3r bc34b89e50 docs(AMD_HIP): STRATA_HIP_WMMA on my R9700: +27% prompt, decode unchanged
On my R9700 (engine 0.1.40, IQ3_S at 431072 ctx, --kv int8, the hipBLASLt 1.4.1 table of
the previous commit, GPU idle): fresh prompts of 4,210 and 8,830 tokens, three matched
trials each and the cold first prompt of each start left out - 1,571 -> 2,064 and
1,568 -> 2,065 tok/s at 8,830 tokens (+31.4 and +31.7%) and 1,699 -> 1,990 at 4,210
(+17.1%), medians 1,571 -> 2,064, a 1.27x geometric mean over those three - with decode
unchanged (89.8 -> 86.9 tok/s). hip_prompt_attn_wmma passes on this card.

STRATA_SELECT_WMMA=1 on top of it changed nothing at 4-9K prompts (2,026 tok/s), which is
consistent with its +1.5% being a 16K/262K-ctx number.
2026-10-07 09:07:39 +02:00
Leon Strong a45783c7f9 hip: the gfx1100 100401 table covers the small-T expert GEMM (prompt +31.9%)
The shipped gfx1100-hipblaslt-100401.txt calibrates "the 26 dense GEMM
geometries of the shipped gfx1100 table", all at T=4096/8192. A prompt on
Qwen3.8-Flash-Next reads a whole other geometry through the prompt path, and
it is issued once per expert group at whatever T the group has - so it never
matches a row:

  STRATA_HIPBLASLT_VERBOSE=1, GSQ-RCO IQ2_XS, 192000 ctx, 3151-token prompt
    hipBLASLt tuning enabled (26 rows, gfx1100, version 100401)
    642 fallbacks, all f16 N=1280 K=2560 ldy=1280, over 642 distinct
    T values from 1 to 1873

That is the small-T expert GEMM, and it is nearly every launch in the prompt.
TuningTable::closest() matches on (n, k, ldy) and takes the nearest t_bucket,
so the rows below are a spread of buckets over the small-T range rather than
one row per T; bf16 10240 2560 10240 (the hyper-connection projection, one
fallback) is covered the same way.

tools/hip/gfx1100-hipblaslt-100401.txt: 17 rows added with tune_hipblaslt
from this tree, against the engine's own 32 MiB workspace (gemm.cu:441), on
the same RX 7900 XTX and packaged ROCm 10.0.0 the existing rows were
calibrated on. No existing row is changed. 26 rows -> 43.

Same binary, same config, the table as the only difference, 3 interleaved
pairs at a 3151-token prompt:

  691.6 -> 940.7 tok/s  (+36.0%)
  712.3 -> 943.2 tok/s  (+32.4%)
  727.7 -> 928.7 tok/s  (+27.6%)
  mean 710.5 -> 937.5 tok/s (+31.9%)

Decode unchanged (58.6 -> 60.6 tok/s, inside this box's 2.3% run drift), and
STRATA_HIPBLASLT_VERBOSE=1 reports fallbacks=0 with the 43 rows, which the
26-row table does not.
2026-10-07 09:07:39 +02:00
Jim Routh 2088bd45f5 docs: coupled drafts with Gumbel-max picks (STRATA_SPEC_GUMBEL), measured on Strix Halo 2026-10-07 09:05:11 +02:00
Jim Routh 6381df34f6 sampler: Gumbel-max coupled drafts (STRATA_SPEC_GUMBEL=1, opt-in)
The pick becomes argmax_i p_i / E_i with E_i = -log(u_i) and u_i a hash of (seed, counter, token id), an exact
sample of p.  The noise a token gets does not depend on which other tokens survived the draft's or the target's
cut, so a coupled draft (STRATA_SPEC_COUPLED=1) agrees with the target on the tokens they share.  Off by default:
nothing changes without the variable.  sampler_parity: kernel vs host reference under three chains, frequencies
vs softmax, the flag is read and greedy is unchanged.
2026-10-07 09:05:11 +02:00
Niko1221 29897d780f docs: DETAILS links VRAM_ELASTIC.md from the vram_elastic paragraph 2026-10-07 04:17:28 +02:00
Niko1221 7dbd8c09fc Merge branch 'f-1402-d1' into f-1402 2026-10-07 04:15:44 +02:00
Niko1221 bd5b1a2789 tests: pdl_parity runs with STRATA_DF_PDL=1 (PDL is opt-in; without it the test's forced PDL capture reads as unsupported) 2026-10-07 04:15:44 +02:00
Niko1221 2a8cdeddda prefill: the Stager's sleeping waits (#1057) are the default on Linux only; Windows keeps the yield spin (5070 8K Q2_0 prompt -1.2% with them). STRATA_STAGER_SLEEP=1/0 forces either 2026-10-07 04:08:11 +02:00
Niko1221andFrancesco Albano 2e4ddf6e63 verify window: PDL on sm_90+, mixer graph branches, batched Q4_0 query path, PLE post-ops in one pass (all opt-in)
Ported from #904 onto the 0.1.40 verify window. Every switch is OFF by default, so
default output and the default launch sequence are unchanged:

- STRATA_DF_PDL=1: programmatic dependent launch (pdl.hpp) for the window's
  quantizations and dense projections (multi-column MMVQ, multi-row BF16 GEMV);
  sm_90+ only, CUDA 12.3+; HIP and older cards keep ordinary launches.
- STRATA_DF_PLE=1: the window's hyper-connection write and PLE post-ops in one
  pass, which also writes the commit's history snapshots (native_gr_post_multi
  with strides, native_ple_postops_batch_snap).
- STRATA_DF_QB_Q4=1: a Q4_0 KV cache takes the batched QSA query path too.
- STRATA_DF_BRANCH=1: the mixer's side branches inside the window graph.
  Left opt-in because the author saw a stall on Linux with NVML queries.

The q/gate split now uses copy_rows_strided (a kernel) instead of a
cudaMemcpy2DAsync so a PDL chain stays kernel to kernel; the bytes are the same.

Left out: STRATA_DF_KVAPP (kv_append_q4_step_multi). 0.1.40 already appends a
window's K/V cells in one launch (kv_append_q4_steps, #783 PR-d).

Merged by hand with the 0.1.40 forms of the same code: the exact-T BF16
multi-token kernel, the PAIR/TS MMVQ variants, QFUSE (q8_attn), the fused
norm+rope and the batched residual write.

Co-authored-by: Francesco Albano <[email protected]>

(PR #904)
2026-10-07 04:08:11 +02:00
Niko1221andStefanos Anastasiou d6850f31ee cpu: IQ3_S and IQ2_S AVX2 decode without per-index shift/mask/or
The high index bits of a 32-value half are spread one per byte by a table and
punpcklbw puts them next to the low index bytes, so each grid lookup is a
16-bit load plus the table load. The sign vector is one broadcast load plus one
pshufb, and the (2s+1) scale vectors are table loads. Arithmetic is untouched
(same words in the same lanes). The decode is ported onto the 0.1.40 layout of
iq_avx2.cpp: the gathered IQ3_S/IQ2_S forms (Fmt32<121>, <122>) and the VNNI
rows are left as they are.

Co-authored-by: Stefanos Anastasiou <[email protected]>

(PR #930)
2026-10-07 04:07:51 +02:00
Chromadera 818ec1c6e9 expert cache: --expert-cache-per-layer sizes each layer's slots to that layer's own blob (native packs)
Opt-in (only with --expert-cache-per-layer). The same VRAM holds more experts
(7,008 -> 9,312 slots at equal VRAM per the author). --expert-cache N stays the
budget of N largest blobs; shrink_to keeps whole per-layer lists so a blob never
lands in another layer's range. The free-VRAM reading is device_free_bytes(), as
in the shared-cache branch. The PR's png is left out.

(PR #934)
2026-10-07 04:07:51 +02:00
Unmaple 6b1e303f16 cpu: --pool-tasks N sets the row tasks per batched expert phase
Opt-in (default 0 keeps three tasks per participating thread, so default
output and timing are unchanged). Adds pool_tasks_test, which compares the
split against the default partition bitwise for 0..4096 tasks.

(PR #949)
2026-10-07 04:07:51 +02:00
uncle daddy bc089d5d97 prefill: let Stager threads sleep instead of yield-spinning
The prefill Stager waited by spinning: a yield loop on `issued`, and
cudaEventSynchronize on events created without cudaEventBlockingSync
(which spins under the default scheduler). With --mmap-experts there
are 32 stagers. On Linux they inherit the host thread's single-core
pin and starve it: on a DGX Spark (GB10) prompts were read ~10x slower
(IQ3_S 8K 91 -> 1,222 tok/s, 30K 88 -> 1,388 tok/s with this change).
Unpinned instead, the 32 spinners took every core for the whole prompt
(process CPU ~1,800%); with the waits sleeping it is ~100%.

- ring wait: std::atomic::wait, with notify_all in issued_one() and
  finish()
- dma_done events: cudaEventDisableTiming | cudaEventBlockingSync
- HIP shim: cudaEventBlockingSync -> hipEventBlockingSync (untested)

Decode speed and output tokens are unchanged.
(PR #1057)
2026-10-07 04:07:42 +02:00
Zhong Uncle 49c50f49f7 tools: bench_eviction replays --dump-routing traces against candidate eviction policies
ExpertCache::admit never evicts because eviction policy is a measured
question (R4.1's LFU-decay vs LRU sweep).  This is the harness for that
measurement: a policy's hit rate depends only on the order (layer,
expert) pairs were routed, which --dump-routing records, so replaying a
trace through simulated slot pools needs no GPU.  fill-only (the
engine's current behaviour), lru, lfu-decay and an oracle (Belady's
MIN, the upper bound); global or R4.2g per-layer scope matching
ExpertCache::layer_slot_range; any slot count; optional profile prefill;
per-window rates, so a topic shift shows up as a per-window dip.

tools/test_bench_eviction.py runs without a GPU (14 cases): each
policy's victim choice, the per-layer quota arithmetic, the windows
split, and profile prefill order.
(PR #991)
2026-10-07 04:07:42 +02:00
Niko1221 d19c07650b Merge branch 'f-1402-r' into f-1402 2026-10-07 04:05:58 +02:00
Niko1221 c954f5bd1e Merge branch 'f-1402-p' into f-1402 2026-10-07 04:05:58 +02:00
Niko1221 1be32e9c7b Merge branch 'f-1402-d2' into f-1402 2026-10-07 04:05:01 +02:00
Niko1221 881328bfb2 Merge branch 'f-1402-k' into f-1402 2026-10-07 04:02:23 +02:00
Niko1221andEddoursul ffac97f74f Verify window: 2-4 token dense projections read an interleaved copy of the q8_1 activations (fork F4)
A one-int-a-column interleaved copy of the window's q8_1 blocks lets a warp decode each weight block once and apply it
to every token with the single-column dot's own expression (the same *_q8_dot_impl calls), walking the chunks of the
exact multi-column layout and adding the four virtual warps' sums in warp order, so every output is bitwise the
multi-column kernel's. IQ4_XS, Q4_K, Q5_K and Q6_K projections (and the head) of 2-4 tokens use it; the copy is made
from xq_ by one small kernel, so the fused HC-read producer works too. STRATA_MMVQ_IL=0 keeps native_mmvq's kernels.
CUDA only (HIP keeps its kernels). mmvq_il_parity checks every case bitwise for rows 1/2/4 and the table; --bench times it.

Ported by hand from Eddoursul's fork (738e166, b67a291), with the IQ3_S and Q8_0 parts and the 5-8 column widths left out.

Co-authored-by: Eddoursul <[email protected]>
2026-10-07 04:00:38 +02:00
Niko1221 5adacbc4e1 Merge branch 'f-1402-v' into f-1402 2026-10-07 03:59:01 +02:00
Niko1221 8aa07e5ef0 docs: TROUBLESHOOTING for the update after the history rewrite (#1276), the unbuffered file tier (#1194) and the page-locked copy (#1250) 2026-10-07 03:57:39 +02:00
Niko1221 5738d38d50 resident RAM mode: give the model files' cached pages back before page-locking the expert copy, and when the free RAM still does not cover it take the pages in steps and page-lock what the RAM allows, with a warning, instead of being OOM-killed (#1250) 2026-10-07 03:57:39 +02:00
Niko1221 805088d5b6 file tier on Linux: unbuffered reads only when the cache cannot hold a twentieth of the experts it would read (at least 1.5 GiB); the rest read through it (#1194, #1116, #1085) 2026-10-07 03:57:38 +02:00
Niko1221 58b1ea221a docker: with REINSTALL=0 the config on the /data volume replaces a regular file left in /opt/strata (#1244) 2026-10-07 03:57:38 +02:00
Niko1221 1341e01a94 setup: the sm_120 compiler warning covers CUDA 13.2.0 and 13.2.1 only; 13.2.2 (nvcc build 86) is accepted (#968); tests for it and for the per-family ROCm pins 2026-10-07 03:57:38 +02:00
Niko1221 10f27e6e74 setup: the ROCm wheel is chosen per GPU family from what its TheRock index offers (a pinned version, else the newest of the same line or major, with a warning); gfx103X-all takes 7.13.0a20260515 and gfx1151 7.14.0a20260608 (#1103, #1267) 2026-10-07 03:57:38 +02:00
Niko1221andelanonimo832 495ba07679 UPDATE.bat / update.sh: a clone on the pre-2026-10-06 history is moved to origin/main (old commits kept in pre-cleanup-backup) when no tracked file is edited; otherwise the two commands are printed (#1276)
Co-authored-by: elanonimo832 <[email protected]>
2026-10-07 03:57:38 +02:00
Niko1221 6d69cd5032 Merge branch 'f-1402-u' into f-1402 2026-10-07 03:36:42 +02:00
Niko1221 56ae98dde9 --kv-grow: STRATA_KV_GROW_HOLD=1 keeps the K/V as long as a parked conversation (#1128)
kvg_trim took only the short request's cell count, so every short request in
a long conversation's session shrank the K/V to 8192 cells and gave the
expert slots back, and the conversation's next turn grew it again and moved
the experts again (a parked conversation is restored through kvg_ensure).
With STRATA_KV_GROW_HOLD=1 the trim keeps the longest parked conversation's
cells (+256) while it is parked.  Opt-in: the default trims to the request
alone as in 0.1.40, so the K/V gives back its VRAM as before.  Output is
unchanged; only --kv-grow is affected.
2026-10-07 03:34:33 +02:00
Niko1221 53623e56f3 Merge branch 'f-1402-w' into f-1402 2026-10-07 03:09:28 +02:00
xjc10 ddef182709 bench: community report, 2x RX 6900 XT (gfx1030), Ryzen 5 5600X, IQ3_S, 131K (PR #1270)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:56 +02:00
wolffahrer f012ae3001 bench: community report, RTX 5090 Laptop 24 GB, Windows 11, see README (PR #1263)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:54 +02:00
ManfredCh a58c4f3b5d bench: community report, RTX 5090 Laptop 24 GB, UD-IQ4_XS (first NVIDIA measurement) (PR #1245)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:53 +02:00
KalAbaddon ca2618b920 bench: community report, RTX 5080, Ryzen 9 9900X3D, Coder IQ1_M and others (PR #1243)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:53 +02:00
qni-live 93690a8d53 bench: community report, 2x RTX PRO 4500 Blackwell, Swift IQ3_XXS (PR #1226)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:49 +02:00
cezartolvai f44c540365 bench: community report, 2x RTX 2080 Ti (R9), see README (PR #1225)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:48 +02:00
lucapug 27e2e6f6ec bench: community report, RTX 3090 eGPU, 64 GB RAM, IQ2_XS vs IQ3_XXS (PR #1199)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:48 +02:00
apollo-mg 6024082a33 bench: community report, 4x Tesla P100 16 GB (CUDA 12 engine), IQ3_XXS (PR #1192)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:47 +02:00
nytasuk-commits b67da13702 bench: community report, RX 7900 XTX, Windows 11, IQ3_S (PR #1173)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:47 +02:00
CC-David-CC c819f4a89d bench: community report, RTX PRO 6000, Q8, --prefill 8192 (PR #1171)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:46 +02:00
Dmitry-B d79510700c bench: community report, RTX 4090, IQ3_S 140K / IQ3_XXS 200K opt-ins and tool-call hotfix (PR #1158)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:46 +02:00
lechlna000 08e1363a7e bench: community report, 2x Tesla P100 16 GB, Flash-Next IQ3_S, 128K (PR #1157)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:45 +02:00
jase100k c1daa7b1fb bench: community report, 2x RX 7900 GRE (gfx1100), layer split, #848 / #854 (PR #1140)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:44 +02:00
CC-David-CC 8ca475c223 bench: community report, RTX PRO 6000, Q4 / Q8 (MTP and ngram runs) (PR #1137)
Ported from the community PR with the author kept. Raw dumps and logs were trimmed (TRIMMED.md in the folder lists them); the README, summaries and configs are unchanged. Indexed in bench/results/COMMUNITY.md.
2026-10-07 03:04:42 +02:00
Niko1221andgopinath87607 e6ca0a04ba layer split: with the stage trim, auto prices a later stage's weights per candidate range, projections and cudaMalloc granules included (#1238, #880)
With --layer-split auto and STRATA_STAGE_TRIM=1 a later stage's weights load after the search, and the search priced them
as the full arena with no projections, so a small card was over-credited: P40 24 GB + RTX 3070 8 GB picked K=2 (prefill
107 / 112 tok/s at 4K / 32K) where K=16 gives 343 / 654 in the report. The carve is now priced for each candidate range:
the trimmed arena (WeightTable::pool_bytes) plus NativeDense::weight_bytes_for, a walk of load over the GGUF headers that
rounds each matrix to cudaMalloc's 2 MiB granule (+0.8% on the iq3_s pack, high on purpose). After each late stage loads, the
bytes priced are compared with the VRAM it really took and a warning says when they drift.

Only the late-weights arm changes (auto with the trim asked, opt-in); every other run has its weights loaded before the
search, carve_bytes is 0 there, and placements do not move. Ported from 572b607 of #880 onto this tree's loader (layer ranges
as arguments, STRATA_Q8_PACKED, STRATA_HC_Q8 untouched); not run on a mixed pair here.

Co-authored-by: gopinath87607 <[email protected]>
2026-10-07 03:02:56 +02:00
Niko1221 0499f8f7be engine: a Windows HIP engine says when the runtime in use is not the bundled one beside it (#1261)
The report shows the bundled amdhip64_7.dll next to strata.exe and the System32 copy (a different build) in use, with the
prompt GEMMs failing. The import is bound before main, so the engine cannot swap it; it now prints a WARNING naming the
bundled file when the loaded one is another, so the log carries the fact. Nothing else changes. The fix itself (loading the
bundled runtime by full path, or a delay-loaded import) needs a Windows AMD machine to test and is not in this commit.
2026-10-07 03:02:56 +02:00
Niko1221 0f931943c8 engine: a line before each verify-window capture says the free VRAM, and a Windows start with little room says what to try (#1275)
On Windows an engine with 313 MiB of VRAM free after a 15,104-token borrow exited silently inside the verify-window
capture. The cause is not reproduced here (no Windows AMD card), so this only leaves a trace and a hint, and changes
nothing: the capture prints how much VRAM is free before it starts, and a Windows start with less than 512 MiB free
and --prefill auto names --prefill auto:8192 (the reporter's workaround) and --vram-reserve-mib. The chunk choice is
untouched.
2026-10-07 03:02:56 +02:00
Niko1221andzeriyoshi c0c932cf9d cmake: gfx1150 (Radeon 890M, Strix Point) is an unvalidated HIP target, with a docs row (#1217)
The build stopped with a FATAL_ERROR because gfx1150 was in none of the architecture lists, although the device code
(the WMMA guards, sudot4) already covers it. It is now in the unvalidated list: the build warns and goes on. The
report's machine (96 GB, TheRock 7.14.1, IQ3_XXS: decode 17-19 tok/s, prompt about 125-135 tok/s) is in docs/AMD_HIP.md.
Setup's own AMD path is unchanged.

Co-authored-by: zeriyoshi <[email protected]>
2026-10-07 03:02:56 +02:00
Niko1221andcrazyaimachine 6111382bec vision: a JPEG with an EXIF Orientation other than 1 is turned upright before the encoder reads it (#1229)
Phones save portrait photos as landscape JPEGs with an Orientation tag, and the encoder's decoder ignores it, so the
model saw them sideways. Such a JPEG now takes the Pillow route (exif_transpose, then PNG); JPEGs without the tag, PNG,
BMP and GIF still pass through byte for byte, and without Pillow nothing changes. Other formats that carry the tag
are turned too.

Co-authored-by: crazyaimachine <[email protected]>
2026-10-07 03:02:56 +02:00
Niko1221andcrazyaimachine e21dc577a8 batch: with --batch-groups a second request goes to the other pipeline group, and idle slots no longer cost rows in a group's window (#1249)
On a layer split with --batch-groups, two concurrent requests ran at about half speed: the slot choice could put both in
one group, and a group's window carried a row for every slot, active or not. Slot choice now prefers the group with the
fewest busy slots (a held prefix of 512 tokens or more still wins), and a window covers the slots up to its last active
one. Without --batch-groups the slot choice is exactly as before.

Co-authored-by: crazyaimachine <[email protected]>
2026-10-07 03:02:56 +02:00
Niko1221andfukc-gihtub 09e5ab4f6e generate: --layer-split auto does not choose a split whose prompt path cannot start, and says so when none can (#1094, #1160)
The search priced placements by decode time only, so at 512K context it could pick a split where a stage could neither
lend a 512-token prompt chunk nor afford its own buffers, and the engine ran out of VRAM at start while an explicit
--layer-split worked. A placement is now taken only if every stage can lend the last-resort chunk or afford its own
buffers (the serve path's own tests, priced from the same figures); the tie-break for balanced stages stays. A
start-time probe on the live free figures says which card is short.

Changed from the PR: nothing is refused. If no placement can start on paper, the search runs again without the gate
(the choice is then what it was before) and prints what is short and how to make room; the probe only warns.

Co-authored-by: fukc-gihtub <[email protected]>
2026-10-07 03:02:56 +02:00
Chen Cheng 3bcfd8f303 verify: a window graph that finds no VRAM frees older batch slot layouts' graphs instead of failing (#1185)
Every layout of active slots keeps its own graph pair, and with the expert cache sized to the reserve the next window
graph can find no VRAM left; the engine then exited (#997, `parallel` 4 to 8). cudaGraphInstantiate that runs out of
memory now evicts the least recently used batch layouts, other than the one being captured, until it fits; they are
captured again when needed. The start prints how many were freed. Nothing changes while everything fits.
2026-10-07 03:02:56 +02:00
Gabe Ortiz 5bc479d99a serve: opt-in recovery of tool calls the model writes next to the template's form, "tool_call_recovery": true (#1172)
With the key on, a call of a declared tool is the call when the model writes the tool's name as a parameter tag,
a JSON object inside <tool_call>, or a bare <function=NAME> at the start of a line; two calls in one <tool_call>
are two calls. Anything else stays text. Without the key every reply is exactly as before. On the author's corpus of
1,462 agent turns: 92.9% read as intended before, 98.3% with the key. Re-files #966.
2026-10-07 03:02:56 +02:00
Niko1221 725022a011 serve: /v1/responses accepts a JSON text.format together with tools (#782)
Codex's review request sends tools, an additional_tools item and text.format json_schema at once, and got a 400.
Now a tool call passes through as it does without the format (words written before the call go out as a message
ahead of it), and the schema is checked on the turn that ends in text, as json_schema works without tools. With
tools the system directive says the schema is for the final answer only. The chat route's rule is unchanged.
Tests in serve/test_responses.py reproduce the request shape: a call, a valid text answer, and (with jsonschema
installed) an answer that breaks the schema.
2026-10-07 03:02:56 +02:00
hulkbig 423f5893d7 serve: poll the cancel event every 0.5 s while the engine is quiet, so a client that leaves during a long prompt read stops it (#1182)
The wait for the engine's next line was 10 s, so a disconnect during a quiet prefill reached the engine only at the
next heartbeat. Both waits now poll the cancel event every 0.5 s; the HTTP heartbeat stays at 10 s, and an expired
silence deadline is enforced before another heartbeat. serve/test_quiet_cancel.py drives a fake engine that says
nothing until STOP. This is the Python half of #962; the engine half (stop between prompt chunks) is open.
2026-10-07 03:02:56 +02:00
elanonimo832 41e21f5013 engine: document --spec and --spec-min-p in --help (#1198) 2026-10-07 03:02:56 +02:00
elanonimo832 9bbcf5b8e5 docs: --calibrate loads the model more than once, and starts it when it ends (#1197) 2026-10-07 03:02:56 +02:00
xjc10 1db5d255ad tests: hip_prefill_wmma_gemm_parity skips on a card without matrix cores instead of failing (#1148)
RDNA2 and older and gfx906 have no WMMA; the kernel declines every shape there. The test now exits 77, which CMake
reports as a skip.
2026-10-07 03:02:56 +02:00
Avicennasis e01cbf0d3e strata_pack.py build --force: unmake the old pack before writing, so a build that stops part-way is an unfinished build, not a pack (#1176)
--force now removes the old pack's markers (index.txt, manifest.json) before the build writes a byte. A forced build
that stops part-way leaves experts.bin without a manifest, which the next build or setup makes again, instead of the
old markers over partly new experts.
2026-10-07 03:02:55 +02:00
Avicennasis 20ebd1ebf4 serve: a tool's "parameters" / "input_schema" that is not an object is a 400 naming it, not an AttributeError on its first call (#1175)
A string or list as a tool's schema passed the tools check and raised later, in parse_tool_call and the stream parser,
after the 200 and part of the reply. It is now a 400 naming the tool and the field. A missing or null schema still
passes.
2026-10-07 03:02:55 +02:00
Niko1221 48d2e6cabc spec: DraftPolicy measures a lookup window size again when its cost is stale (#1252)
A window size's cost was updated only when that size was chosen, and the re-probe covered only sizes seen fewer than
three times. A size priced high during one slow stretch (a prompt's cache refill) could stay priced out for the life
of the process. A size not measured for 300 rounds is now tried again when a confident lookup could use it, one
round per stretch. Output is unchanged (the policy only picks which drafts to verify); draft_policy_test gains a case
that fails without the change.
2026-10-07 03:02:55 +02:00
rkcth ac52c2c754 mtp: HIP does not create the draft's unused shared-expert branch stream and two events (#1240)
HIP keeps the shared expert on the draft's own stream; only CUDA branches. The stream and events were created and
never used, and the report's R9700 lost prompt speed after the first request.
2026-10-07 03:02:55 +02:00
Niko1221 3b499f174d prefill: the gfx11 fused expert kernel waits after its weight write, and the VGPR-capped variant is off on clang 22 (#1180)
STRATA_PF_FUSED=1 on gfx1100 gave prompts that answered only "!". Two causes, from the report:
- the stage loop wrote the next weight buffer with no barrier after it; one __syncthreads() now follows the write.
- the waves_per_eu(8) variant of native_w11_kernel computes wrong results with clang 22 (ROCm 7.10): there the cap is 1
  (NW_LB; -DSTRATA_W_LB=N overrides it). Earlier compilers keep 8.
Default output is unchanged (the path is opt-in on gfx11).
2026-10-07 03:02:55 +02:00
Niko1221 3449041bb6 Merge branch 'f-1402-y' into f-1402 2026-10-07 02:56:13 +02:00
Niko1221 73e01fa54d sycl: on the xe driver without --stream-experts, --pcie-frac defaults to 0 (and an explicit value gets a warning)
An Arc Pro B70 (xe) page-faults when a kernel reads ordinary pageable host memory (devcoredump: ccs0, FaultType 0,
faulted address in the CPU's mmap range, then Timedout job and device lost). Without --stream-experts the experts
outside the VRAM cache sit in a pageable arena that the PCIe share hands to the GPU. The default share is 0 there
(the CPU computes those misses) with a one-line warning naming --stream-experts, as setup configures it. An explicit
--pcie-frac is kept and warned about. i915 (Arc Alchemist), CUDA and HIP are unchanged.
strata::intel_gpu_driver() reads the driver from sysfs; verify.cpp's i915 test uses it.
The mirror table (host-mirror pointers for the device plan) is filled only from pinned USM aliases under
--stream-experts, so it cannot hand out pageable pointers.
2026-10-07 02:52:41 +02:00
Niko1221 5af104b515 Merge branch 'f-1402-y' into f-1402 2026-10-07 02:42:06 +02:00
Niko1221 26609953a5 tools/batch_test.py: --long-tokens N reads the last prompt from N tokens of filler beside the earlier slots' windows (a prompt loan under --batch) 2026-10-07 02:33:42 +02:00
Niko1221 0a22c46747 verify: a batch's GDN output is quantized to q8_1 under STRATA_QFUSE too (#1139)
STRATA_QFUSE=1 (the gfx1151 arch default) has gdn_step_norm_multi write the
out-projection's q8_1 image itself and skips native_quantize_q8_1.  The batched
recurrence (batch_rec_) calls gdn_step_norm_multi once per slot with no q8_1
destination, so with --batch the projection read stale bytes and every
concurrent answer was corrupt.  The batched case now quantizes as without
QFUSE; the fused image holds the same bytes (gr_parity), so batched output
equals solo.  Single-request paths are unchanged.
2026-10-07 02:33:42 +02:00
wOvANandwin10ogod 7ef55e1959 verify batch: service the doorbell graph per-window (ar_on), not per-process (#1118)
A stage that was 100% resident at init takes the doorbell graph while
a prompt loan/shrink/swap is in flight (ar_off_), and that graph waits
on host doorbells the skipped service never raises: one GPU spins at
100% while the host sits in cudaStreamSynchronize, until the watchdog
fires with the previous stage's stale label. Key the all-resident
service skip in run_slot_rows, batch_poll and service off ar_on(),
as run() already does.

PR #1102 (win10ogod) is the same fix for run_slot_rows and batch_poll;
this one also covers service().

Co-authored-by: win10ogod <[email protected]>
2026-10-07 02:33:42 +02:00
Benjamin Larsson 35435e3d26 verify: the batched K/V append writes a streamed K8V4 state's host copy (#1188, #1169)
#783's batched append of a verify window's cells passed no host copy for the
hybrid K8V4 format (int8 and q4_0 pass st.host, and the per-token path passes
K8V4's two halves).  With --kv-resident, a block that was not resident when its
cells were appended came back from the host copy without them, so the tokens a
window wrote were lost: free generation degenerated into repetition (#1188,
#1135) and parked conversations lost their decode-written turns (#1169).  The
batched path now passes the same host halves as the per-token path.
2026-10-07 02:33:42 +02:00
Niko1221 0ef3be82b9 Merge branch 'f-1402-io' into f-1402 2026-10-07 02:29:34 +02:00
Niko1221 b56d6e3a78 sycl: fix the newline escape in the fdinfo warning (it did not compile) 2026-10-07 02:29:12 +02:00
Niko1221 76e1cd2eef sycl: the draft steps wait for the GPU, not 1024 spins (MTP acceptance 4% -> 62%)
wait_step and the chain wait polled ext_oneapi_empty() through DPCT_CHECK_ERROR, which is always 0, so the
loop left after 1024 spins and read draft tokens the GPU had not written (-1). Same migration bug as the
cudaStreamQuery fix in verify.cpp, missed in mtp.cpp. IQ3_XXS on the A750: 61 of 99 drafts accepted on the
story prompt (the 5070 gets 106 of 166); copy_'s readiness test in verify.cpp had the same flaw.
STRATA_DBG_DRAFT=1 prints each window's drafts and the target's tokens.
2026-10-07 02:25:48 +02:00
Niko1221 9370ea41f9 sycl: drain the other queues before each verify window on an i915 card; STRATA_RING_TIMEOUT_S
On an Arc A750 (i915) a verify window launched behind other GPU work in flight (the previous window's commit,
a prefill) often never started: no GPU breadcrumb, the 20 s ring timeout, the engine ends. queues_wait_and_throw
before the launch removes it (9 of 9 three-request runs pass, almost all failed before). Default on for i915,
off elsewhere (STRATA_WINDOW_SYNC=1/0/2 overrides). STRATA_RING_TIMEOUT_S raises the 20 s doorbell timeout.
2026-10-07 02:25:48 +02:00
Niko1221 93b129ad61 sycl: size from the DRM fdinfo when the driver reports the whole card free (i915); STRATA_CHUNKED_FILL=0 toggle
Level Zero's free-memory query returns the constant total on an Arc A750 (i915); the expert cache and draft head
were sized from the whole card and the first launch failed with out-of-host-memory. device_free_bytes now lowers
the figure by what this process holds (drm-total-local0 / drm-total-vram0 in /proc/self/fdinfo) and says so once.
2026-10-07 02:25:48 +02:00
Niko1221 94013054ba sycl: verify_slot names the first bytes that differ 2026-10-07 02:25:36 +02:00
Niko1221 614678f8e7 sycl: native_router_top10_multi launches the single-token route<512> per token block
Same bits as upstream's one-warp-per-token route_multi (native_multi_parity compares them), and the
same launch shape as the 256-expert multi router.
2026-10-07 02:25:36 +02:00
Niko1221 0d4e82c6d8 sycl: host memory a kernel polls comes from zeMemAllocHost BIAS_UNCACHED; null-allocation checks
On the Arc A750 a kernel never saw the host's doorbell and result-row stores to ordinary host USM
(sycl/probe/doorbell.cpp: only the uncached allocation completes the handshake), so every ring wait
hit its bound and the window read stale rows: NaN logits. verify's mapped staging and the pool's
y_miss rows now use host_malloc_polled (sycl_queue.hpp; needs libze-dev's header, else sycl::malloc_host;
STRATA_HOST_UNCACHED=0 turns it off). IQ3_XXS on the A750 now answers 12*12 = 144.

Also: the expert cache and the MTP draft head check Level Zero's null return for a too-large
allocation (and an Arc A750 needs UR_L0_ENABLE_RELAXED_ALLOCATION_LIMITS=1 for a single allocation over
4 GiB); gather_rows no longer asks for a cooperative launch it does not use; the expert-cache
zeroing in generate.cpp is chunked too. Based on the probe in PR #423 (maxfridbe).
2026-10-07 02:25:36 +02:00
Niko1221 6d1cb3a657 sycl: fill big device arenas with chunked kernels, not one queue.memset
One memset of the 25 GiB expert-cache arena runs on the blitter engine and times out on the
Arc Pro B70 (xe: engine memory CAT error, GT reset). big_fill_zero zeroes in 256 MiB kernel
launches; used for the expert cache, the MTP and verify arenas and the KV reset.
2026-10-07 02:25:36 +02:00
Niko1221 02a64abf15 io prefetch: warn (never force) on an AMD iGPU with the adaptive tier and on the staging variant; docs note with the measured table 2026-10-07 02:25:35 +02:00
Niko1221 23a38a0bb7 sycl: re-migrate the port to 0.1.40.1 (dpct 2025.3, 3-way merge per docs/INTEL.md)
The sycl/ copies were at 0.1.39 and did not build against the 0.1.40.x shared
headers (load_experts_gguf ready argument, fused_gr_read_multi bool, Gemm::bf16/native ldx,
13 symbols missing at link). Re-migrated the 5ffcce43 and 0.1.40.1 trees with migrate.sh,
merged with merge_upstream.py, hand-resolved the conflicts (the port's tuned expert and
MMVQ kernels kept; verify, mtp, generate adopt 0.1.40.1 with the port's queue handling).
Stubs: vmm (no driver VMM on Level Zero), --remote-expert-opt. Dropped upstream's
opt-in AMD S26/S27 grouped-expert kernels and STRATA_QFUSE/HC_Q8 reads on SYCL.
build.sh and migrate.sh source setvars.sh outside set -u (they exited silently).
2026-10-07 02:25:35 +02:00
Niko1221 aa659c9eb9 io prefetch: the experts.bin tier fills the page cache from the I/O threads and the kernels read the mapping (no staging, nothing waits); cheaper residency probe
The first version staged every uncached blob and made the layer wait for all of them before any expert computed, which
lost the overlap the kernels' own page faults had (-19% on a cold 16 GB lane).  Now prefetch() hands the layer's uncached
blobs to the I/O threads and returns; the kernels that reach a blob before it lands wait on its pages, the others
compute.  STRATA_IO_PF_STAGE=1 keeps the staging variant.  Residency is probed on four sampled pages, not the whole blob.
2026-10-07 02:21:11 +02:00
Niko1221 9745567e5f file tier: opt-in Linux I/O path (STRATA_IO_PREFETCH=1) and counters that separate drive reads from page-cache hits
The mapped file tier reads experts through page faults. With STRATA_IO_PREFETCH=1 (Linux, experts.bin or the GGUF
in place) a blob whose pages are not cached is read with one pread of the whole blob into a staging buffer, the
predicted experts of the next layer(s) are read ahead on I/O threads (STRATA_IO_PF_THREADS, STRATA_IO_PREFETCH_DEPTH,
STRATA_IO_PF_AHEAD=0 for demand reads only), and a cached blob is still handed out as the mapping. Same bytes, so the
same output; off by default.

Counters: each request now logs the OS's own view of the file tier (/proc/self/io read_bytes and major faults) beside
the engine's expert reads, and with STRATA_IO_STATS=1 or io prefetch a mincore split of cached and uncached bytes,
read-ahead used / unused / skipped, and the decode's wait in the fetches (--stats prints the same).

blob_stable(): a pointer that is not recycled, for the copies that run asynchronously (adaptive swaps, the GPU cache
fills, the prompt stager).
2026-10-07 02:21:11 +02:00
Niko1221 e2de691c7c bench: Strata vs llama.cpp v0.6.0 head-to-head script and results (3060, P100, 5070 one round) 2026-10-07 01:33:18 +02:00
Niko1221 cbd83b25a0 tests: expert_multi_test checks and times the pool's rows path (gate/up rows, re-quantization, down rows) against the whole-expert kernel 2026-10-07 00:24:19 +02:00
benoit lange a782177ee1 serve: GET /metrics in the Prometheus text format with vLLM's metric names (#793)
Asked with Accept: text/plain or ?format=prometheus; every other request keeps the JSON.
Latency histograms, a Grafana dashboard and scrape configs under docs/monitoring.
Only the Prometheus part of #793; the pipelined-window slot allocator needs a 2-GPU check and is left out.
2026-10-07 00:18:37 +02:00
Francesco Albano 5208b320dd cpu experts: AVX2 Q8_K activation quantizer for the native i-quant layers (#851)
Byte-identical to ggml's quantize_row_q8_K (q8k_quant_parity checks it, including the
avx-only run). STRATA_NO_Q8K_AVX2=1 keeps ggml's.
2026-10-07 00:16:07 +02:00
InB4DevOps 6f21c9d912 prefill: overlap routed expert uploads with the shared expert (#789)
Reads the routing ids first and queues the shared expert afterwards; staging
counts real transfers. STRATA_PREFILL_STREAM_AHEAD=0 restores the old schedule.
2026-10-07 00:15:26 +02:00
Niko1221andAnon 293da8d223 engine --gpu: set HIP_VISIBLE_DEVICES on AMD builds and warn when it overrides the environment (#852 follow-up)
Co-authored-by: Anon <[email protected]>
2026-10-07 00:15:26 +02:00
Anon f88e27b396 engine: --gpu selects the visible GPUs without environment variables (#852) 2026-10-07 00:15:26 +02:00
Francesco Albano 13eee452de pipeline-windows beside the asynchronous adaptive tier: copies queued from the decode loop at a card's gap (#905)
--adapt-async 1 no longer drops to the blocking tier with --pipeline-windows 2: the steps that overwrite
something a window in flight may read wait for those windows. STRATA_PIPELINE_ADAPT_ASYNC=0 keeps the
blocking tier there. Opt-in; default output unchanged.
2026-10-07 00:01:18 +02:00
Niko1221 08cdd8bf68 layer split auto: on a tie the balanced placement wins
When every placement holds the whole expert profile the predicted decode window is the same for all of them and the winner was floating-point noise: --trim-stage-weights on four GPUs chose layers 6/30/5/7 and read a 32K prompt at 1,750 tok/s, against 2,790 for 12/12/12/12 with the same decode speed and tokens (4x Radeon AI PRO R9700, IQ3_S). Among placements whose window time agrees to 1e-6 ms the smallest slowest stage is kept.
2026-10-07 00:01:18 +02:00
Niko1221 feccbfe32f R1/R3: tools/research_run.py, docs/RESEARCH_RUNS.md (measured numbers), DETAILS.md strata_prefix section 2026-10-07 00:01:12 +02:00
Niko1221 13d14a4944 R1: a pinned shared prefix for many suffix queries (opt-in pin=N / "strata_prefix")
The engine's GEN line takes pin=N: the first N prompt tokens are a shared read-only prefix (a long document many
short questions follow). The prompt is read in two parts at N and the checkpoint there is pinned: retention never
evicts it, a parked conversation holding it stays parked, the session file keeps it, and a later request that resumes
from it does not park the branch it leaves, so N queries keep one prefix. One pin at a time. Without pin= nothing
changes.

The server takes "strata_prefix" in a chat, messages or responses request: {"messages": K}, {"message": I,
"chars": N} or {"tokens": N}, resolved to the longest common start of the prompt's ids. A prefix that cannot be
marked is said in the log and ignored; a malformed field is a 400.

Tests: conv_cache_test (pinned eviction), conversation_cache_test (a pinned parked conversation is not evicted),
conversation_file_test (the pinned checkpoint is the one saved), serve.test_server.SharedPrefix.
2026-10-07 00:01:12 +02:00
Niko1221 0d2ecef720 docs: sampled drafting (STRATA_SPEC_COUPLED / STRATA_SPEC_PROB) and what was measured 2026-10-06 23:58:41 +02:00
Niko1221 589ccc4701 spec: STRATA_SPEC_PROB gates --spec-min-p on the drawn draft's probability by default (STRATA_SPEC_PROB_GATE=top for q's top probability) 2026-10-06 23:58:41 +02:00
Niko1221 33356f7289 spec: probabilistic draft acceptance for sampled MTP drafts (opt-in STRATA_SPEC_PROB=1)
Speculative rejection sampling for the MTP drafts of a sampled request. The draft layer samples
its draft from the chain's distribution q (its own Philox stream, counter | 1<<61) and writes
q's (id, probability) list; the verify head computes the target's post-chain p per row from the
split sampler's block lists (penalties, top_k, top_p, min_p, temperature; at most 64 entries)
and accepts draft d with min(1, p(d)/q(d)), else samples the residual max(0, p - q) with its
own uniform. Row t writes the draft on an accept and the residual sample (never d) on a reject,
so the host's accept loop and the commit are unchanged. Rows past the MTP drafts (a lookup
chain's tail) and suffix windows are point masses: accept with p(d), residual p without d - no
q never means q = 0 (the llama.cpp #29924 class). Greedy requests never reach the new kernels;
the nine existing sampler kernels keep identical SASS.

- spec_prob.hpp: the rule, host reference; sample_tokens_spec / spec_draft_sample kernels
- STRATA_SPEC_PROB_GATE=pick, STRATA_SPEC_PROB_DT=x, STRATA_SPEC_MIN_TEMP=x: tunables, off
- spec_prob_test (CPU, 2M trials per case, chi-square 99.9%, controls that must fail) and
  spec_verify_parity (GPU verifier vs host reference, token for token)
2026-10-06 23:58:41 +02:00
Niko1221 0ab663fc20 serve: STRATA_SPEC_DEPTH=1 prints the MTP drafts tested and accepted by depth for each request
The per-request totals (drafts accepted / offered) hide where a window ends. With
STRATA_SPEC_DEPTH=1 the serve loop counts, for the windows drafted by the MTP layer, how many
times draft 0, 1, 2 ... was tested (every earlier draft was kept) and how many of those were
kept, and prints one stderr line per request. Off by default; nothing else changes.
2026-10-06 23:58:41 +02:00
Niko1221 efe7839905 setup: CUDA 13.2's nvcc for an RTX 50 card (sm_120) is passed over for an older 13.x, or warned about (#892, #968)
Built on our RTX 5070 with nvcc 13.2.51 and 13.0.88 from the same source: with 13.2 the IQ3_S MMVQ product
(iq_multi_parity) and the IQ2_S / IQ3_S MMQ prompt path (prefill_fused_iq_test) are wrong, relative error 0.5 to 1.0; the
other formats are bitwise equal. setup now takes an installed 13.0 / 13.1 over 13.2 for sm_120 (STRATA_NVCC still wins) and
otherwise warns, saying what to install. Recommends, never refuses.
2026-10-06 16:35:09 +02:00
Niko1221 b540e994b8 prefill: STRATA_DBG_FORCE_SLOW_LAYER=1 exercises the fused-buffer backstop (#954)
With the fused layout (STRATA_PF_FUSED=1, a chunk of 1024 tokens or more) layer 0 is treated as one the fused path does not
take, so a prompt must stop with the backstop's error instead of overflowing the shrunk buffers. The check that
the guard works at a 16,384-token chunk without needing a pack that really has such a layer.
2026-10-06 16:35:09 +02:00
Niko1221 471303d9ab tools: stop_resend_test.py, the stop / edit / resend loop of #879 and #606 as a repeatable check
Streams an answer, stops reading, resends or edits the prompt after 0-0.4 s (or with another request already running)
and checks every answer for real text, not a repeated token. Run the engine with STRATA_DBG_NAN=1 beside it.
2026-10-06 16:35:09 +02:00
Niko1221 e155cf8fc4 engine: a big prompt chunk whose lent slots do not fit in RAM is warned about (#669, #765)
--prefill auto:32768 lends 6,000 cache slots to the prompt path; on 31 GB of RAM only 3,700 of their experts stay in
RAM and the rest are read from the SSD on every chunk (prompts ~3x slower than --prefill auto). The engine now says
so at start, with the counts. A warning, never a cap: with the RAM for them the big chunk is 21-35% faster.
2026-10-06 16:35:09 +02:00
Niko1221 5628e280fb docs: troubleshooting for #781 #831, #649, #961 and #654 2026-10-06 16:35:08 +02:00
Niko1221 e8b53d5d46 serve: a full card with an explicit --expert-cache says so, and what to do (#781, #831)
The LOW free-VRAM line at READY now also names the explicit --expert-cache (its slot count) and suggests a smaller
one or 'auto', which re-checks the free VRAM after the slots are written. Said, never changed.
2026-10-06 16:35:08 +02:00
Niko1221 dfc8f74dda verify: STRATA_DBG_GDN=1 checks the commit kernel's window count (#937)
gdn_step_commit_kernel reads n_keep from device memory. With STRATA_DBG_GDN=1 a count past the window's rows is
printed and the commit skipped, where a gfx1030 reporter sees a memory-aperture violation. A template parameter
keeps the default kernel unchanged.
2026-10-06 16:35:08 +02:00
Niko1221andKrypto-Whitehat 73d3983046 prefill: a backstop for the fused layout's MoE buffers - an error naming the layer, not a hang (#954, #958)
fused_layout() and fused_ring() already keep the shrunk GU/H/Xq buffers and the per-layer expert path in step. If
they ever drift (a layer that takes MMQ or the FP16 path at a chunk of stream_all_min() tokens or more while the
buffers are the fused ones), the prompt now stops with 'the fused layout's MoE buffers are too small for layer N'
instead of writing past the buffer: an illegal access, or a kernel that never returns and shows up as
'prefill mmq: iota' one prompt later. Same check in the SYCL copy.

Co-authored-by: Krypto-Whitehat <[email protected]>
2026-10-06 16:35:08 +02:00
Niko1221 0e773c8624 serve: the AMD engine's environment: a HIP_PATH that points nowhere is dropped, an unwritable TEMP is replaced (#654)
A deleted ROCm build named by HIP_PATH / HIP_DEVICE_LIB_PATH / LLVM_PATH, and a TEMP the comgr JIT cannot
create files in, both crash amdhip64 before the engine prints a line. Only a missing path and an unwritable
folder are touched; a healthy environment passes through unchanged.
2026-10-06 16:35:08 +02:00
Tanay 8e52aecab5 setup.sh: make the venv with uv on NixOS, or with STRATA_UV=1 (#662)
uv's own managed Python loads pip wheels' libraries where Nix's Python cannot. Changed from the PR as the plan asked: uv is used only on NixOS or when STRATA_UV=1 is set (no silent toolchain change on other systems), and the setup.py hunk is left out. Not run on NixOS here. (PR #662)
2026-10-06 15:57:39 +02:00
Niko1221 50ba742bcb serve: a reply that ends inside its thinking can be continued once with the thinking closed, opt-in (#1053)
"reasoning_close_retry": true in the config: when a reply ends on its end-of-turn token still inside <think>, with no answer and no tool call, the thinking is closed once and generation continues (the engine holds the prefix). Off by default; tested both ways and for a reply that already answered.
2026-10-06 15:57:39 +02:00
Fringe210 61d5efab45 Enable --ple-io ram on Windows via VirtualLock
PleTable's Mmap+lock path now calls strata::platform::lock_resident (working-set minimum + VirtualLock, chunked per GiB) instead of refusing to start. A lock failure still falls back to touching the pages with a warning, same as the POSIX path.

Changed as the plan asked: only a full VirtualLock counts (a partial one is unlocked again and said so), a warning when the table is more than half of the RAM, the usage text indent fixed. Compiled on Windows here (MSVC, CUDA 13.0); not run with --ple-io ram yet.

(PR #813)
2026-10-06 15:57:39 +02:00
Niko1221 20b5005c7f docs: setup remembers the data folder; how to choose another one (#1070)
A reinstall put the model on the old drive again because setup keeps the last data folder in its settings file. The troubleshooting table now says where, and that --data-dir, --models-dir, --gguf-dir or deleting the data_dir entry change it.
2026-10-06 15:57:39 +02:00
Niko1221 9d692d8d02 serve: a --resident-budget-gib above the free RAM is a warning at start (#1080)
An explicit budget bigger than the RAM the PC has free (or than its total less ~6 GiB) pushes experts to swap or the file tier; one report went from dead slow to fast by deleting the number. The server now says so at start and goes on (recommend, never force).
2026-10-06 15:57:39 +02:00
Niko1221 13c1fcb628 setup: Visual Studio 2026 is accepted for the build with CUDA 13.3 or newer (#985)
find_vcvars looked for versions [16.0,18.0) only, because CUDA 13.0-13.2 refuse a newer Visual Studio. The range now goes up to 19.0 when the toolkit found is 13.3 or newer (the reporter built with VS 18 and CUDA 13.3); older toolkits stay on 2019 and 2022.
2026-10-06 15:57:39 +02:00
moebear 0d73237632 serve: a Codex compaction can be rendered with its conversation's tools, opt-in (#924)
Codex's compaction request has tools: [], so its prompt shared almost nothing with the cached conversation and was read again from the start. With "codex_compaction_cache": true the tools of that conversation's last prompt are kept (one entry, keyed by session and thread) and used for the compaction prompt only; the parser and the response keep the request's own tools, and a request that would not fit with them is rendered as sent. Changed from the PR as the plan asked: off by default, the mismatch documented, ported on top of the opt-in #923 and tested over HTTP with a tool call emitted in the compaction. (PR #924)
2026-10-06 15:57:39 +02:00
moebear 2db8ae14af serve: Codex's thread-title turn can be answered without the engine, opt-in (#923)
Codex sends a thread-title request beside each user turn (another session, no tools); running it on the engine replaces the one prefix cache. With "codex_thread_titles": true in the config it is answered at once with the user's line cut to 36 characters (usage 0). Changed from the PR as the plan asked: off by default (the title is not model-written), documented, tested both ways. (PR #923)
2026-10-06 15:57:38 +02:00
Aníbal Muñoz Calero d125bef795 serve: lazy-load vision encoder with model
Rebased onto the 0.1.40.1 Vision (work_dir, #480 absolute paths). Added tests for the lazy encoder (nothing starts, restart starts it, a failed start is killed); the --lazy help no longer says text-only. A live smoke with a vision config and --lazy is still to run.

(PR #673)
2026-10-06 15:57:38 +02:00
Niko1221andupdalla-apshir f3019d00b3 serve: the endpoints keep reporting the context while the engine restarts (#351)
Cut down to reporting only, as the plan asked: max_context is 0 until READY, and /v1/status, /v1/models, /props, /health, /slots and /metrics told a client the model had no context for the whole load. They now use the last known context (reported_ctx). Requests are still checked in prepare() as before.

Co-authored-by: updalla-apshir <[email protected]>
2026-10-06 15:57:38 +02:00
liowald 3379596b38 serve: usage and timings across reasoning continuations keep the original request's input and sum every pass's work (#615)
A reasoning-budget continuation (and the reasoning-loop recovery pass) is another native generation, not another API request: each pass keeps its own DONE, the prompt reuse is the first pass's (the generated reasoning is not this request's cached input), and times, tokens and draft counts (offloaded included) are summed. Rebased onto the 0.1.40.1 Service.run. (PR #615)
2026-10-06 15:57:38 +02:00
Niko1221andGuillaume PUTIER ecdc028c73 serve: an empty api_key in the config is a warning at start, not silence (#569)
Cut down from the PR as the plan asked (recommend, never force): an empty --api-key or STRATA_API_KEY is already refused (#213) and a blank config key is refused with #956; an api_key that is present but empty now prints that the server has no key and starts. The setup-side refusal is left out (the final line already says no API key is set).

Co-authored-by: Guillaume PUTIER <[email protected]>
2026-10-06 15:57:38 +02:00
Akido 8d8e165148 tools/vision: portable by default; setup.py and the Dockerfile ask for native (#411 #412 #419)
The 0.1.32 image encoder was configured without -DSTRATA_PORTABLE, so the old OFF default built it native
(MSVC + FindSIMD: /arch:AVX512 on the build machine) and it stopped with 0xC000001D on AVX2-only CPUs.  A build
without flags is now the AVX2 baseline; the builds for the PC they run on (setup.py's two encoder builds, the
Dockerfile) pass -DSTRATA_PORTABLE=OFF and stay native.

- portable also forces GGML_AVX512*, GGML_AVX_VNNI and GGML_AMX_* OFF, so a -D cannot put AVX-512 back in
  (with the old file, -DSTRATA_PORTABLE=ON -DGGML_AVX512=ON gave 10522 zmm instructions; now 0)
- native restores a fresh native configure's GGML_OPENMP / GGML_AVX* when a folder was portable before
- the configure log says which of the two it is
- test: build_engine's encoder build (gpu and cpu) passes -DSTRATA_PORTABLE=OFF

Rebased by hand onto the 0.1.40 setup.py (the Windows MSVC environment of #881 kept). The build script of the ready-made engine zips is not in this repository: it must pass -DSTRATA_PORTABLE=ON for the encoder (the default was OFF until now).

(PR #427)
2026-10-06 15:57:38 +02:00
Joe-Huber 954cb2a2b6 chore(github): issue forms (bug, feature, question) and a pull request template (#401)
Short forms with the required fields, a link to the troubleshooting table, and the pull request template. Taken without CODEOWNERS and without the template's checklist (the changes the plan asked for); blank issues stay allowed.
2026-10-06 15:57:38 +02:00
Zac Chen 64de53b634 docs: the RX 7900 XTX IQ3_S rows in the speed tables (#319)
Prompt and output rates for the AMD RX 7900 XTX (gfx1100) beside the NVIDIA rows, with the measurement note. Only the DETAILS tables are taken: the PR's branch also deleted about 190 lines of current text, and the README tables are two fixed machines (RTX 5070 and RX 9070 XT).
2026-10-06 15:57:38 +02:00
1872183316 5ecc7fea17 setup: --inspect, a GGUF's real quantization and whether Strata runs it, from its headers only (#911)
tools/quantscope.py (from 1872183316/quantscope, MIT OR Apache-2.0) reads only the headers (a file, a folder, a URL, ms: or hf: repositories); tools/strata_inspect.py compares them with data/gguf_fingerprints.json, the files setup installs. Changed from the PR: the size menu is untouched (its bits per weight line would change the golden output; left out), and the untested verdict says that setup still refuses the GGUF names it lists as unsupported, so the two do not contradict each other. (PR #911)
2026-10-06 15:57:38 +02:00
Niko1221and1872183316 4f36b95557 setup: ModelScope as an explicit download source, --source modelscope (#908)
Setup never switches hosts by itself: auto is Hugging Face, and a download that fails says --source modelscope exists (no probe at start). With it, each file is checked against the SHA-256 ModelScope publishes (a wrong one is deleted; a file with no published hash is warned about), the MTP tensors against the pinned checkpoint's own hashes, and setup says that ModelScope serves current files, not our pinned revision. Changed from the PR: the automatic switch and its 10 s probe are gone.

Co-authored-by: 1872183316 <[email protected]>
2026-10-06 15:57:38 +02:00
1872183316 7e2864a3fc calibrate: measure the adaptive expert tier (--adapt-every / --adapt-swaps / --adapt-decay), with 512-token answers (#907)
A new step 5 tries two tier settings (every window, 80 or 160 swaps, decay 0.97) against the engine's own, after the worker count, one restart each; a setting is kept only when it beats the default by 3%. The tier follows a text over some windows, so this step measures 512-token answers. The author measured +7.9% on a DDR3 Xeon with an RTX 4060 Ti. Applied by a 3-way merge onto the 0.1.40 calibrate.py; the check that it keeps the engine default on a fast-RAM PC is still to run on a GPU.
2026-10-06 15:57:38 +02:00
Niko1221and1872183316 98222df476 setup: step 1 shows the GPU's PCIe link and says when it reads fewer lanes than the card has (#912)
The generation card and board both run, the lanes now, and the copy rate that implies for a slow link. Changed from the PR: the fewer-lanes note is worded as a hint (a card or laptop can narrow the link while idle), not a verdict.

Co-authored-by: 1872183316 <[email protected]>
2026-10-06 15:57:38 +02:00
Niko1221 af76b97bff setup: images are allowed with UD-Q4_K_XL, with an untested warning (#967)
The model is the same base model and the same image encoder as UD-IQ4_XS; a user ran it by hand (#967, #971). Setup asks about images for it and warns that we have not run this file with images (no smoke test here: the 111 GB file is not on this PC). MCP's plan and size table follow.
2026-10-06 15:57:38 +02:00
Niko1221 a681cac6a8 serve: /v1/responses accepts Codex's additional_tools input items (#782)
Codex v0.160 sends an input item of type additional_tools, which was refused with a 400. The item is no message; a tools array in it joins the request's own tools, and one without a usable array adds nothing. The item's exact shape is not documented here: only the tools-array form is read.
2026-10-06 15:57:38 +02:00
Guillaume PUTIER 832b2e4f34 gr_parity, ple_parity: wait for the default-stream setup before launching on the test stream
Both tests upload and memset on the legacy default stream, then launch on
a cudaStreamNonBlocking stream, which does not wait for it. cudaMemset is
asynchronous to the host and a pageable cudaMemcpy may return before the
DMA lands, so the kernels can read buffers still being written. A device
sync before each launch closes it, the same fix 0.1.36 made in the fused
tests. Not reproduced on an RTX 5090: 0 failures in 210 runs of gr_parity
before and after.

Only the first commit of the PR (the stream syncs); the second one (a rewrite of ple_parity without the fixtures) is not taken. The same syncs for the HIP tests were not added (no AMD test machine). (PR #568)
2026-10-06 15:57:38 +02:00
Klaus Friedel b8aca3e6fb serve: no automatic --layer-split when the args use --peer-device
With several GPUs in the config's "gpu" the server appended --layer-split auto, which the engine refuses beside
--peer-device (#531), so the peer tier could not be started through the server at all:

    strata generate: --peer-device cannot be combined with --layer-split (use one or the other)

The split (and --split-skip-if-fits) is now added only when --peer-device is not among the args; the start line
names the peer tier instead of a layer split. Tests in serve/test_server.py (PeerDevice), docs/SECOND_GPU.md says
how to configure it.

Ported by hand onto the 0.1.40 engine_args (layer_split_value); the start banner now says the peer tier is in use when the split is not.

(PR #665)
2026-10-06 15:57:38 +02:00
1314521gjy f2158a5cf5 docs: the expert cache size is a budget, and an over-sized one pages on Windows
--expert-cache N is N times the largest blob, compared only with the free VRAM
read before the slots are written; the check that runs once they are written is
auto-only. Under WDDM the free figure can be about a gigabyte high and an
over-committed allocation goes to system memory instead of failing, so only the
speed shows it. Measured at 1,048,576 on a 32 GB 4080 SUPER: 11,631 slots with
0 MiB free ran 13.7 tok/s, 11,178 slots (auto) with 217 MiB free ran 102-122,
and raising --vram-reserve-mib moves the free figure because it is deducted
before the cache is sized.

Also records that the GPU process Shared/Dedicated counters did not separate the
slow run from the fast ones on that machine, and adds a Troubleshooting row.

Read against generate.cpp: the second size check after the slots are written is auto-only (the loop breaks on !auto_cache), and the LOW line is printed at 256 MiB.

(PR #799)
2026-10-06 15:57:38 +02:00
christopherrobertbrooks-tech eefed6aa5e docs: running Strata behind llama-swap
The entry (serve/server.py with llama-swap's port, checkEndpoint /health, healthCheckTimeout), the DNS-rebinding
'Host ... is not allowed' trap and its three fixes, /upstream/<model>/ for Strata's own pages, and a note on the

Read against the 0.1.40 server: allowed_hosts and STRATA_ALLOWED_HOSTS, /health, the effort handling and the 403 text match.

(PR #829)
2026-10-06 15:57:38 +02:00
shefowl e7ec6b1abb strata-vision: flash attention off on the CPU only when ggml has AVX-512
ggml's tiled CPU flash-attention kernel needs the head size (72 in this
encoder) to be a multiple of the vector width: 8 floats with AVX2, 16 with
AVX-512. With AVX-512, which a build from source gets on a CPU that has it,
ggml falls back to a kernel that is several times slower and accumulates in
FP16. The release builds are AVX2, where flash attention is the faster one,
so auto stays the default there.

1024x1024 picture, Ryzen 7 7700X, 8 threads, F16 mmproj: AVX2 8.3-9.9 s
with it, 14.0-15.2 s without; AVX-512 43.5 s with it (26% off the FP32
output), 13.3 s without. --gpu keeps auto, and an explicit --flash-attn
still wins.

Rebased onto the 0.1.40 strata_vision.cpp (the --min-tokens usage line). Only a source build on an AVX-512 CPU changes; the release builds (AVX2) do not. The Zen4 timing is not run here.

(PR #660)
2026-10-06 15:57:38 +02:00
mad9home 3e14442811 docker: GGUF_DIR, RESIDENT_BUDGET_GIB and KV_STREAMING env vars; document the stop timeout
- GGUF_DIR passes setup's --gguf-dir, so a container can use model files the
  host already has instead of downloading them again (#252/#444 cover the
  native setup only)
- RESIDENT_BUDGET_GIB and KV_STREAMING pass --resident-budget-gib and
  --kv-streaming; unset keeps setup's own defaults
- INSTALL.md: a mounted GGUF folder must be writable on the first start
  (the Unsloth SHA-256 result is kept as <shard>.done next to each shard)
- INSTALL.md: releasing tens of GB of page-locked RAM on SIGTERM takes longer
  than Docker's 10 s default; without --stop-timeout the container is killed
  (exit 137)

The INSTALL.md paragraph was added by hand (the PR's numbered list no longer exists); KV_STREAMING maps to setup's current --kv-streaming auto|on|off.

(PR #491)
2026-10-06 15:57:38 +02:00
Yannick Loth ebd9c81c98 native head: report the cudaHostAlloc error, not just "cannot pin N MiB"
The bare message cost a full debugging session: on Linux the failure was a
missing libcuda.so.1 (a host driver library the nix loader cannot see) in a
launch environment, not memory pressure.  cudaGetErrorString names the real
cause, e.g. "CUDA driver version is insufficient for CUDA runtime version".

Ported by hand onto the 0.1.40 native_head.cpp (the PR's hunk context had moved).

(PR #476)
2026-10-06 15:57:38 +02:00
Niko1221 3c6456a2e6 setup: every finish mark is written with a warning on a read-only folder (#570)
The PR guarded the mark of a whole shard; mark() itself now warns and goes on, so the SHA-256 result of a verified Unsloth shard and the shared-shard link on a --gguf-dir share do not stop setup either. The step is repeated on the next run.
2026-10-06 15:57:38 +02:00
Guillaume PUTIER 762e58b5b2 setup: a read-only --gguf-dir no longer stops setup when marking checked shards
A whole shard without its .done mark gets one written next to it. On a
read-only --gguf-dir (a network share) that write raised OSError and setup
stopped. It now warns that the mark cannot be written and goes on.

(PR #570)
2026-10-06 15:57:38 +02:00
jollyroger1480 ccfdc2ebef native dense: run the architecture guard only on the first shard of a split
A splitter may copy general.architecture into EVERY shard of a split while the
qwen4exp.* hyperparameters live only in shard 1 (seen with the Huihui
abliterated re-split of the UD-Q4_K_XL GGUF: general.architecture in shards
2-4, no qwen4exp.* there). NativeDense::load then ran check_architecture on
shard 2 and failed with 'missing qwen4exp.block_count'.

Run the full guard only when the shard is the first of the split (no split.no,
or split.no == 0); later shards take the existing split-metadata consistency
check whether or not they carry the architecture key. Verified end to end:
with this change the engine loads the re-split GGUF (previously: refused),
pack built with iq_pack.py --compat-bf16, coherent greedy output.

(PR #648)
2026-10-06 15:57:38 +02:00
Léon Avic Simmons 536db01af9 expert profile: refuse a header version other than 1, naming both numbers and the file
read_expert_profile read the header's version field and then ignored it
((void) version), so a profile of a later format would have been read as a
version 1 layout: the fields after the version are only known to be
n_layers, n_expert, slots, n_ranked in a version 1 file.  Refuse any other
version right after the magic, before the geometry check, with the version
found, the version this engine reads, and the file path in the message.

tests/core/expert_profile_version_test.cpp (registered like
expert_profile_save_test, CPU only): a version 1 file loads; the same bytes
with the version field at 2 or 0 are refused naming the version and the
file; a version 2 file of another geometry is refused by its version, not
its geometry; back at 1 it loads again.

(PR #632)
2026-10-06 15:57:38 +02:00
eadra 638ab2dd5e tests: prefill_mmq_kquant_test waits for its uploads before the kernels
The inputs and the NaN sentinel go up with cudaMemcpy / cudaMemset on the legacy stream, and the kernels run on a
cudaStreamNonBlocking stream, which does not wait for it: a pageable copy or the memset can land after the kernels
started. On an RTX 4090 (sm_89, 128 SMs) that failed the screen ("unwritten or non-finite output") in 3 of 3 runs,
in a different product each time; with the sync before the kernels, 5 of 5 runs passed. The same fix as the fused
tests' (prefill_fused_moe_test.cpp, prefill_fused_iq_test.cpp).

(PR #532)
2026-10-06 15:57:38 +02:00
CurtisThe b9fdc1cdce serve: a batch request continued in another leg reports its own prompt's reuse, and input_tokens is never 0 (#1013)
A request stopped on the solo path and continued in a slot is sent again as its prompt plus what it generated, and the engine holds all of that: the usage split counted the continuation. The first read's reuse is kept, and Anthropic's input_tokens is at least 1 (Claude Code took 0 as not given and counted the prompt twice: Prompt is too long at about half the context). Rebased onto the 0.1.40.1 server and the stop_sequence field.
2026-10-06 15:57:38 +02:00
Niko1221 adc87ab7c9 mcp: the install plan accepts vision=cpu with the AMD backend on Linux (#990)
strata_install refused every vision value but no with backend=hip, although setup's own --vision cpu reads images on the CPU beside the HIP engine. It now follows setup.hip_vision: cpu on Linux is planned, a GPU encoder (yes, gpu) and cpu on Windows get a message that says what works. The Windows AMD engine in MCP's hardware detection is left as it was (it still reports AMD cards on Linux only).
2026-10-06 15:57:38 +02:00
BGonnermann 4bc3b4ef45 serve: an API key with spaces or characters outside ASCII matches; a blank key is refused at start (#956)
The configured key loses the spaces and line ends a header cannot carry; a key outside ASCII matches as UTF-8 and as Latin-1 (constant-time). Server side only: the web app part of the PR (the UTF-8 header and the key-rejected note) is left out.
2026-10-06 15:57:38 +02:00
elanonimo832 2de5fa0a84 serve: the thinking budget can be shared with other apps through POST /settings (#1035)
reasoning_budget_tokens joins the shared Chat settings (0 means no budget, a whole number of tokens otherwise) and is read after the shared and config defaults are merged, as max_tokens is. Rebased onto the 0.1.40.1 Service.run (force argument).
2026-10-06 15:57:38 +02:00
elanonimo832 e2d9a4bd7f serve: a POST /settings body without "defaults" is a 400 and no longer clears every shared setting (#1031)
req.get("defaults") is None both for an absent key and for an explicit null, and null means clients use their own settings again. A body the endpoint does not recognise cleared them all. {"defaults": null} still clears.
2026-10-06 15:57:38 +02:00
Niko1221andZerschranzer 0b576686f6 serve: /slots reports n_prompt_tokens, and one entry per slot with --batch (#562)
A context meter that divides n_prompt_tokens by n_ctx read 0 % against Strata. The key is the running request's prompt size, kept after it ends. Added to the PR: with --batch /slots lists every slot (the view /metrics has) instead of one entry.

Co-authored-by: Zerschranzer <[email protected]>
2026-10-06 15:57:37 +02:00
midagedev f69ce694e1 serve: /props total_slots is the engine's batch slots (#1004)
With "parallel": N the engine runs N requests at once, and /v1/status already says so. /props still said total_slots 1, so llama.cpp clients that read it refused or serialized concurrent runs. It now reports the same count, at least 1.
2026-10-06 15:57:37 +02:00
Niko1221andIvan Tuzhilkin 111d62ae96 serve: /props reports chat_template_caps so Zed offers tools; a failing template probe never stops the start (#986, #987)
The caps (supports_tools, supports_tool_calls, supports_parallel_tool_calls, supports_system_role, supports_preserve_reasoning) are rendered once at template load. Changed from the PR: every probe error is swallowed (logged at debug) and turns that feature off, so a custom template that fails in an odd way cannot crash the start.

Co-authored-by: Ivan Tuzhilkin <[email protected]>
2026-10-06 15:57:37 +02:00
Niko1221andOscar Rojas be8c29185c serve: decode chunked request bodies on every route that reads one, with a size cap (#893, #894)
A POST behind a relay that sends Transfer-Encoding: chunked had no Content-Length and read as empty (400 No messages provided). _body(), the control-body read and the unread-body drain now decode it (it wins over a Content-Length, as RFC 9112 says). A chunked body is capped at 256 MiB (413; the announced size is checked before a chunk is read) and a malformed one is a 400. Content-Length requests are unchanged. Left out of the PR: nothing; added: the cap, the other body readers, tests.

Co-authored-by: Oscar Rojas <[email protected]>
2026-10-06 15:57:37 +02:00
Niko1221 47aafa9eb5 serve: a request with more than 64 unique images keeps its own encoded files (#1072)
The vision cache unlinked the oldest .sve on every insert past 64, including a file the same request had not read yet (FileNotFoundError). Vision.encode_all() protects the request's earlier files; the cache shrinks back on the next insert. A cache hit now counts as a recent use.
2026-10-06 15:57:37 +02:00
Niko1221 ef6c18a14a serve: a request the engine refuses with ERR answers at once, not after a 300 s wait for a DONE (#1059)
The solo and admission paths read the engine's ERR line, then sent STOP and drained for a DONE that never comes (it holds the control lines meanwhile). The ERR now ends the exchange.
2026-10-06 15:57:37 +02:00
Niko1221 43badeda1f bench: index the three AMD / Intel community folders in bench/results/COMMUNITY.md, list the 6900 XT trim
Rows for #927 (2x RX 6900 XT), #917 (Radeon 8060S, gfx1151) and #955 (Arc Pro B65) in the new COMMUNITY.md index, with notes on what each
build was; the bullets I had added to docs/COMMUNITY_BENCHMARKS.md are removed so the index lives in one place. TRIMMED.md lists the
seven telemetry files left out of the 6900 XT folder.
2026-10-06 14:59:04 +02:00
Niko1221 58b6f5d045 docs: gfx1031 reports (#915, #1027, #1078) and the Windows zip note in AMD_HIP.md
The gfx1031 entry now lists the RX 6700 XT run (#1027) and the RX 6800M run on Windows (#915, #1078), and says the Windows zip
needs gfx1031 in the STRATA_HIP_ARCHS build list, which the default list has from 0.1.40.2 on.
2026-10-06 14:59:04 +02:00
Niko1221 56d77a983f docs: Pascal has community numbers now (P40, P100, V100 + P100); a GCC 12.3 build note (#1028, #1069, #1079, #1074)
NVIDIA_V100.md said sm_60 was not measured. It now points to the two-P40 layer split (#1028), the P100 alone and with a helper card
(#1069) and the V100 + P100 pair on 0.1.40 (#1079), each one reporter's run. OLDER_GPUS.md notes the extra host flag a GCC 12.3 +
nvcc build needed in #1074 (one report).
2026-10-06 14:59:04 +02:00
Niko1221andTim Nevits 98d1c8d4f3 bench: community report, Intel Arc Pro B65 32 GB (SYCL), IQ2_XS (PR #955)
Three fresh engine launches, five fixed tasks at 512 and 7,000 prompt tokens, 640 output tokens: decode medians 40.4-41.3
and 39.8-40.9 tok/s, reference hashes recorded. The six local patches the report built with are kept as data; patches 3, 5 and 6
are ported as source changes in this release, the others were already in 0.1.40. Index line added to COMMUNITY_BENCHMARKS.md.

Co-authored-by: Tim Nevits <[email protected]>
2026-10-06 14:59:04 +02:00
mini 16fd58b8c4 bench: community report, Radeon 8060S (gfx1151) on a Ryzen AI Max+ 395 (PR #917)
Interactive Q2_0 and UD-IQ4_XS chats against llama.cpp Vulkan, then controlled A-E runs of the gfx1151 hipBLASLt tuning table
(250.9 to 528.9 prompt tok/s), dense MMQ (+0.2%) and the shared-expert decode stream (+2.5%, 8 requests). The build is
0.1.39 plus then-unmerged PRs (#895, #820, a tuning table adapted from #755), not the 0.1.40 release; the index line in
COMMUNITY_BENCHMARKS.md says so and notes that 0.1.40 turned the shared-expert stream off on HIP (#826).
2026-10-06 14:59:04 +02:00
Niko1221andxjc10 cc52598ab5 bench: community report, 2x RX 6900 XT (gfx1030), seven configurations (#927)
One card, a layer split and the expert-helper mode, Strata 0.1.39 stock and with then-unmerged #835, #849 and #854, original
Flash-Next IQ3_S at 131,072 context; three runs at 4K, 32K and 128K prompt tokens and six recall checks per configuration.
Index line added to COMMUNITY_BENCHMARKS.md with a note that #835 is opt-in in 0.1.40, so the second set is not the
release default. The one-second telemetry.jsonl files (5 MB) are left out, and the README says so.

Co-authored-by: xjc10 <[email protected]>
2026-10-06 14:59:04 +02:00
Niko1221 958f61f548 hip windows: build_windows.bat's default arch list includes gfx1031 (RX 6700 XT / 6800M)
The ready-made Windows HIP zip had no gfx1031 code, so an RX 6700 XT or 6800M user had to build from source with
STRATA_HIP_ARCHS=gfx1031 (#915, PR #1078; #1027 ran a 6700 XT as gfx1030). setup already knows gfx1031 (AMD_ARCHS, the
wheels index, the PCI id) and the arch gate builds it. This only adds it to the default list the zip is built from.

Prepared, not decided: building the release zip with this list makes it larger (one more arch of rocBLAS kernels and
engine code), and the owner decides whether the 0.1.40.2 zip carries it.
2026-10-06 14:59:04 +02:00
Niko1221andmini f5ab356abd hip gfx1151: keep the sign of Q2_0's negative zero; warn when the auto cache outgrows an APU's host memory (PR #895)
Two pieces of PR #895 that 0.1.40 did not have; the rest of it is in 0.1.40 (gfx1151 in the arch gate, sudot4, setup
and the Strix Halo defaults) or is not taken (the ROCm pin bump for every AMD arch).

- dq_q2_0: the compiler on gfx1151 (ROCm 7.13) folds the half path's negative scale times +0 to +0, as HIP 5.7 did on
  gfx1012. The sign-preserving path now covers gfx1151 too. Changes bits on gfx1151 only (the same value the FP32 and CPU
  paths give); nothing else is touched.
- Linux HIP, --expert-cache auto: an APU's "VRAM" is system RAM, and the device-free figure counts the whole GPU-addressable
  pool, so a cache sized from it can ask the OOM killer for nearly all RAM. The PR capped the cache at the host's free
  memory less 4 GiB; here it only prints a warning with both numbers and leaves the cache as asked
  (recommend, never force). --expert-cache N sets it by hand.

Compile check only on HIP; the run needs a gfx1151 box.

Co-authored-by: mini <[email protected]>
2026-10-06 14:59:04 +02:00
Niko1221andStormace 10f44a98d5 tests/hip: prefill_mmq_parity initializes its output sentinel on the product's stream
A plain hipMemset runs on the null stream, which a non-blocking stream does not wait for; on a Radeon 8060S (gfx1151,
Windows) the memset landed after the MMQ kernel and left only the sentinel. hipMemsetAsync on the same stream orders it.
Taken from PR #919's Windows gfx1151 port; its other pieces are not taken here (the Windows carve-out accounting needs
a Windows gfx1151 run).

Co-authored-by: Stormace <[email protected]>
2026-10-06 14:59:04 +02:00
ada22464d3 docs: AMD verify timeouts under host-memory reclaim, one experimental section (PRs #750 and #920)
Merges two community reports of the same mechanism (GPU queues suspended while the kernel reclaims KFD userptr pages)
into one short section in AMD_HIP.md and one line in TROUBLESHOOTING.md: HSA_USERPTR_FOR_PAGED_MEM=0 (two R9700, ROCm
7.2) and GPU_PINNED_MIN_XFER_SIZE=1048576 with --mmap-experts (RX 6800), each labelled as one machine and not
confirmed, with the MemoryMax versus MemoryHigh note. Condensed from the two PRs.

Co-authored-by: zengyishou <[email protected]>
Co-authored-by: YamineRL <[email protected]>
2026-10-06 14:59:04 +02:00
Niko1221 4372e3a828 setup: accept gfx1102 (RX 7600 / 7600 XT) as an unvalidated AMD card (#938, #942)
setup listed the RX 7600 XT as "not supported" although the arch gate and the Windows zip already build it, and one
RX 7600 XT ran the engine (Swift IQ3_XXS, 146 requests up to 51K context, about 43 tok/s decode, 250-300 tok/s prompt,
unvalidated; docs/AMD_HIP.md has the report. The two setup tests that expected "not supported" now expect it listed.
2026-10-06 14:59:03 +02:00
Niko1221andTim Nevits 55c3178730 sycl: three shutdown and safety fixes from the Arc Pro B65 report (PR #955, patches 3, 5 and 6)
- conversation_snapshot_test: the pinned host buffers are freed with sycl::free (they come from USM, not malloc).
- STRATA_VERIFY_NO_HOST with experts that are neither in VRAM nor mirrored now refuses to start, instead of a warning
  followed by a generation with no safe host fallback.
- On QUIT the direct-access USM host mirror is released (gguf_src.close()) before the process leaves; a failure to
  release is reported and exits 1.

From the Arc Pro B65 bench report (timnevits), where they are shipped as patch files; the other patches there were
already in 0.1.40. SYCL compile check only.

Co-authored-by: Tim Nevits <[email protected]>
2026-10-06 14:58:47 +02:00
Niko1221andTxgom2 811eeaa32b sycl: the native router's synchronization is a subgroup barrier (hang on Arc A770)
Only subgroup row zero of the 32x8 block reaches the router's synchronization in route(); the other seven rows return
before it. A work-group barrier there is divergent, and on an Arc A770 the kernel hung. The surviving row now meets a
subgroup barrier. Also recorded in sycl/tools/fixups.py so a regenerated tree keeps it.

Salvaged from PR #797 (the rest of that draft is not taken: its build fixes were already in 0.1.40). An A770 on i915
reported decode only with this change (23.0 tok/s with MTP, 8.65 without). Needs an Arc owner to test on the current
tree; SYCL compile check only.

Co-authored-by: Txgom2 <[email protected]>
2026-10-06 14:58:47 +02:00
Niko1221andNathan Bradley 3e05230b1d hip: gfx1151 hipBLASLt tuning table (hipBLASLt 1.5.0)
tune_hipblaslt on the gfx1201-100500 table's 32 shapes, on a Ryzen AI Max+ 395 (Radeon 8060S, gfx1151) with ROCm
10.2.0a20260930 (whl-next, hipBLASLt 1.5.0 efafc1c1). 3.9-15x faster than plain hipBLAS per shape; prefill with
STRATA_HIPBLASLT_VERBOSE=1: launches=3138 fallbacks=0.

Only the table is taken from PR #944. Its gfx11 WMMA attention and scorer duplicate the kernels already in 0.1.40, and
its "gfx11" prefix gate would enable gfx1103 and gfx1152, which 0.1.40 leaves out on purpose. To be re-measured on the
gfx1151 box.

Co-authored-by: Nathan Bradley <[email protected]>
2026-10-06 14:58:47 +02:00
joeyjoe02 557c2b67c1 sycl: read doorbell flags with an uncached L1+L3 hint
On an Arc Pro B60 a system-scope atomic load of host USM is still served
from the GPU's cache, so a device spin never sees the host's store and
runs to kSpinMax every time. Read the flag through an annotated_ptr with
an uncached L1+L3 read_hint after an acquire fence instead. Bounded
ping-pong: 263 ms -> 2.4 us per round trip; per-layer waits on a B60
with experts in the host mirror: 8.7-10.5 -> 19.3 tok/s decode.
-DSTRATA_DOORBELL_ATOMIC_LOAD keeps the old load.
2026-10-06 14:58:47 +02:00
Niko1221andkonijiwa110 65ce329917 qsa_select: FP32 tiled block scores below sm_80 (opt-in: STRATA_SELECT_SIMT=1)
Based on konijiwa110's PR #742. Below sm_80 there is no TF32 tensor-core scorer, so the prompt path scores the QSA
blocks on the warp kernel: 5 shuffles per 4 products, ~1 TFLOPS on an RTX 2080 Ti, 24% of a 128K prompt. With
STRATA_SELECT_SIMT=1, qsa_block_scores_tc runs an FP32 tiled GEMM there (rows = query x indexer head, K = 128 through
shared memory, 2 queries x 4 heads x 4 blocks per thread, relu per head summed in registers), ~6.9 TFLOPS by the
author's measurement (128K prompt 1,016 to 1,242 tok/s, 248K 715 to 952 on one 2080 Ti).

The tiled kernel sums in another order than the warp kernel, so its scores are not bitwise equal. The PR made it the
default below sm_80; here it is opt-in, so default output is unchanged until an interleaved A/B of at least 10 pairs
supports a default. The tail block keeps the warp kernel's arithmetic. STRATA_SELECT_OLD=1 still forces the warp kernel.

qsa_select_bench is now also a CUDA test: at 32K, and at 131K with STRATA_QSA_WARP=1 and STRATA_SELECT_SIMT=1 so any
card runs the FP32 tiled scorer (FP64 gate, selections compared with the warp kernel's).

Co-authored-by: konijiwa110 <[email protected]>
2026-10-06 14:58:47 +02:00
xjc10 6126f8dbeb HIP gfx103x: PR #540's attention kernel with 8 cells per step and DPP lane exchanges (bit-exact)
On HIP the default attention kernel's score loop is bound by the LDS pipe on gfx1030: its 12 x 5 __shfl_xor_sync per
cell compile to ds_bpermute_b32 and share that pipe with the shared-memory q reads.  attn_chunk_kernel_pre75 (PR #540,
bit for bit the default kernel) is now built for HIP wave32 (not the gfx906 backend) and run on gfx103x by default:
- NC_ template parameter, cells per step: CUDA 2 as before, HIP 8 (on gfx1030 every step up in NC was faster although
  occupancy fell from NC 1: the q reads bound it, not occupancy)
- DPP_ template parameter: the butterfly's lane exchanges (reduce12, the softmax max / sum) through DPP row_xmask within
  a row and v_permlanex16 across rows instead of ds_bpermute; same values, same pairing order, unchanged bit for bit
- pre75_attn() on HIP: gfx103x per device; STRATA_ATTN_PRE75=0 off, =1 also another wave32 card
CUDA is unchanged: the non-experimental build does not build the kernel, and the template defaults equal the old
constants with shfl_x<false> = __shfl_xor_sync.

RX 6900 XT, IQ3_S, prompt tok/s at 9.4K / 34.7K / 105.8K: 441 / 467 / 460 -> 459 / 494 / 488; with #835's FP16 GEMMs
746 / 917 / 928 -> 801 / 1,022 / 1,041.  Teacher-forced log-probabilities against the default kernel: KL 0, argmax 100%.

bench, docs: the pre75 attention kernel on RX 6900 XT - measurements, exactness, decode A/B; OLDER_GPUS.md notes

bench/results/2026-10-04-rdna2-pre75-attention: the rig, the compiler output (registers, occupancy, ds_bpermute counts)
that motivated NC 8 and DPP, prompt speed on this branch (+4.2 / +5.7 / +6.1% at 9.4K / 34.7K / 105.8K tokens), the
step-by-step NC / DPP series on the FP16 prompt path, the teacher-forced exactness check (KL 0 over 1,578 positions),
a decode A/B (0.3-0.6 ms shorter windows, two rounds), the limits, and the configuration used.

docs/OLDER_GPUS.md: the RX 6800 / 6900 row says this kernel is the default on gfx1030 and names the switch; the A/B
switches line says what STRATA_ATTN_PRE75 does there.
2026-10-06 14:58:47 +02:00
Eli Elgaev 0ff5b4145b qsa_prompt_attn: Volta m8n8k4 kernel - hi and lo halves in separate accumulator chains
Follow-up to #600 (fks's mma.m8n8k4 prompt attention for sm_70).

The kernel splits q (for q.k) and p (for p.v) into FP16 hi + lo halves
and runs both products into the same accumulator. Volta's tensor cores
don't keep full FP32 precision while they accumulate, so once the hi
products are in the accumulator most of the much smaller lo
contribution gets truncated away, and the split buys less than it
should. Giving the lo products their own chain and adding the two in
FP32 at the end fixes that.

qsa_prompt_attn_parity, one V100-SXM2-16GB, 2,048 queries, error vs the
FP64 reference (the FP32 kernel in brackets):

  ctx 8,192    5.88e-6 -> 3.02e-6   (2.14e-6)   11.94 -> 12.20 ms
  ctx 32,768   5.08e-6 -> 2.33e-6   (2.37e-6)   12.20 -> 12.38 ms
  ctx 131,072  5.35e-6 -> 2.75e-6   (1.81e-6)   12.34 -> 12.51 ms

Registers unchanged (166 / 226 / 163 for modes 0 / 1 / 3), no spills.
docs/NVIDIA_V100.md updated for the new error level.
2026-10-06 14:58:47 +02:00
willowmerestudios 3641d2f8dd tools: sm60_dp4a_check (STRATA_DP4A vs byte reference, bit-exact + timing) 2026-10-06 14:58:47 +02:00
Niko1221andAcckiyGerman e874e654fc hip: accept gfx1010 and gfx1011 (RDNA1) in the arch gate
gfx1010 (Navi 10) is measured on an RX 5700 XT: 8 GB card, Coder IQ1_M, 32K
context, prompt 115 / 144 / 149 tok/s and decode 18.0 / 20.3 / 23.3 tok/s at 4K /
16K / 30K prompt tokens, 6 of 6 needle checks at 8K and 30K, 61 of 61 ctest on the
card. Nothing beyond the arch list is needed: the portable scalar dp4a that main
falls to measured 162.7 / 24.3 against 158.3 / 24.0 with the __gfx1012__ SDWA
control (#442) - inside the run-to-run spread - and 16 of 16 HIP tests pass on it.

gfx1011 (Navi 12) is untested on hardware: same RDNA1 ISA, builds with 0 errors on
ROCm 7.14's gfx101X-dgpu wheels.

Two box facts, for whoever picks this up: hipBLASLt 1.4.0 refuses all 26 of the
engine's GEMM shapes on gfx1010 (heuristic_count = 0), so there is no tuning table
and prompts run on plain hipBLAS; and in a PC that also has an AMD iGPU, ROCm's HSA
runtime segfaults in GpuAgent::InitDma while enumerating the pair - below HIP, so
HIP_VISIBLE_DEVICES cannot reach it - and the engine must start with
ROCR_VISIBLE_DEVICES naming the one KFD node (rocminfo for the index).

Co-authored-by: AcckiyGerman <[email protected]>
2026-10-06 14:58:47 +02:00
BlueKingMuch 0af54b6f56 HIP: a failed first configure no longer leaves the kernels at -O0
CMake caches CMAKE_HIP_FLAGS_<CONFIG> while it enables HIP, from the
values its platform files set once the compiler is identified. When
HIP cannot compile CMake's identification source, that configure
caches them empty and then stops; every later configure of the same
build folder keeps the cached values, so the build that finally works
compiles every HIP source without -O, i.e. at -O0, while C and C++ get
-O3 -DNDEBUG. setup configures build-hip every time, so one failed
attempt is enough.

That happened on an RX 6800 (Windows 11, HIP SDK 7.2, CMake 4.0.1):
the first attempt failed on Visual Studio 2026's <cmath>, the next one
passed with the include fix, and the engine (0.1.30 with the Windows
build of #247, --expert-cache 2048) decoded at 0.24-0.27 tok/s with
7.1-7.5 s per decode window. STRATA_VERIFY_PROFILE put the time in
the GPU's own stages (per window: q8+qkv GEMVs 1468 ms and out-proj
849 ms in the DeltaNet layers, the LM head 540 ms); the waits for flags
the CPU raises were 41 ms of its 8.4 s per window. With the flags back:
59-62 ms per window, 28.1-28.3 tok/s.

After enable_language(HIP), hip_backend.cmake gives each empty
per-configuration HIP flag the value CMake computed for the compiler
(CMAKE_HIP_FLAGS_<CONFIG>_INIT), which is what a fresh build folder
gets. Flags with a value, from CMake or the command line, are left
alone; one set empty on purpose is filled too.

Reproduced on Windows (HIP SDK 7.2, CMake 4.0.1) with a first
configure that failed on --rocm-path flags split at the space in
C:/Program Files, and on Linux (CMake 3.28, TheRock's ROCm 7.10 clang)
with one that failed on a --rocm-device-lib-path that does not exist.
After a working second configure in the same folder,
CMAKE_HIP_FLAGS_RELEASE stays empty and native_mmvq.cu compiles
without -O; with this change it gets -O3 -DNDEBUG. A fresh folder gets
-O3 -DNDEBUG either way.

setup.py's engine fingerprint now covers cmake/ as well, so a change
there alone (this one) recompiles a locally built engine on its next
start; that includes NVIDIA engines compiled here.
2026-10-06 14:58:39 +02:00
Niko1221 2497a05c26 hip: opt-in gfx1103 (Radeon 780M iGPU): CMake accepts it, STRATA_EXPERIMENTAL_GFX1103=1 makes setup treat it as a unified-memory AMD card 2026-10-06 14:47:02 +02:00
imanu 569c094521 fix(mtp): --batch-mtp exits on the first admission with a draft-vocab head
The batch slots' drafters share the draft-vocab head but never copied its ggml type; it stayed -1 and the first
admission of --batch-mtp exited with "unsupported native MMVQ GGML type".
2026-10-06 14:17:54 +02:00
Niko1221andzhouxihong1 b4182a8d50 vmm: fall back to cudaGetDriverEntryPoint below CUDA 12.5 (#1071)
cudaGetDriverEntryPointByVersion arrived in CUDA 12.5; the CUDA 12.2-12.4 engine build stopped in vmm.cpp. Same
fallback expert_cache.cpp already carries, and the sticky error of a failed lookup is cleared.

Co-authored-by: zhouxihong1 <[email protected]>
2026-10-06 14:17:54 +02:00
xxDoman f236dbb85c gfx906: the 0.1.40 tree does not build on HIP/gfx906 (three compile errors)
The STRATA_HIP_GFX906 build defines STRATA_HIP_GFX906, not STRATA_USE_HIP, so three
sites that gate on STRATA_USE_HIP still take the CUDA branch and fail to compile:

1. fused_gr.cu: a bare 'return;' in the bool fused_gr_read_multi -> 'return false;'.
2. mtp.cpp: the MTP stream-priority block (cudaDeviceGetStreamPriorityRange) is gated
   on !defined(STRATA_HIP_GFX906) too.
3. vmm.cpp: the <cuda.h> include and the CUDA driver VMM code get the same gate.

All three are inside #if blocks that only the gfx906 build reaches.
2026-10-06 14:17:54 +02:00
aleesposito85 ac11069a97 fix(build): qualify size_t in vmm.hpp (GCC 12 rejects the unqualified name)
vmm.hpp only includes <cstdint> and <vector>, so the unqualified size_t is not
declared on toolchains that do not expose ::size_t through those headers
(GCC 12.2, Debian 12). Qualify it as std::size_t, as expert_cache.hpp does.
2026-10-06 14:17:54 +02:00
Niko1221 7b7f1a57c4 bench: add bench/results/COMMUNITY.md, an index of the community bench folders 2026-10-06 13:19:05 +02:00
Hugua700 1be67e8949 bench: RX 6800M (gfx1031) community report - self-built HIP engine on Windows (#1078)
Bench folder ported from PR #1078 by Hugua700, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:05 +02:00
fioruccione d6c444efdb bench: community report, 2x RTX 4060 Ti 16 GB + Threadripper PRO 3975WX, UD-IQ4_XS at 131K, layer split (#1062)
Bench folder ported from PR #1062 by fioruccione, authorship kept.
Trimmed for 0.1.40.2: 7 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:05 +02:00
sarge18 3f4d44ece2 Community benchmark: 2x Tesla P40 22 GB (Pascal, experimental CUDA 12), single card and layer split (#1028)
Bench folder ported from PR #1028 by sarge18, authorship kept.
Trimmed for 0.1.40.2: 8 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:05 +02:00
GPF cfb4a33495 bench: community report, RX 6700 XT 12 GB (gfx1031), Ryzen 7 5700G, I… (#1027)
Bench folder ported from PR #1027 by GPF, authorship kept.
Trimmed for 0.1.40.2: 6 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:05 +02:00
avarakin 60982e6ad6 Bench: RTX 5060 Ti 16 GB on Ryzen 9 7945HX, IQ3_S (#1016)
Bench folder ported from PR #1016 by avarakin, authorship kept.
Trimmed for 0.1.40.2: 21 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:05 +02:00
lfontanez 09fadfee61 Community benchmark: RTX 4070 Ti SUPER 16 GiB (Shin-BlackMamba, upstream 6f32ec07) (#995)
Bench folder ported from PR #995 by lfontanez, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: bench/results/2026-09-29-speed-0126/README.md, bench/results/2026-09-29-speed-0126/matrix.json, docs/COMMUNITY_BENCHMARKS.md, tools/integrate_speed_0126_shin_blackmamba.py.
2026-10-06 13:19:05 +02:00
Zauberio 18bce49fed bench: community report, RTX 5070 Ti + Ryzen 7 9800X3D on Windows 11, IQ3_S, 5K to 164K prompt tokens (#832)
Bench folder ported from PR #832 by Zauberio, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:05 +02:00
gravitomagnetic f129082f08 bench: community report — RTX 5090, IQ3_S at 1M context (YaRN), agent-style workload (#466)
Bench folder ported from PR #466 by gravitomagnetic, authorship kept.
Trimmed for 0.1.40.2: 1 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:05 +02:00
obiscr 3be9f44cda bench: community report, RTX 5070 Ti + Ryzen 9 5900X, 64 GB DDR4-3600 (IQ2_XS / IQ3_XXS / IQ3_S, six-arm A/B) (#993)
Bench folder ported from PR #993 by obiscr, authorship kept.
Trimmed for 0.1.40.2: 1 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
1872183316 ffa7a90b74 bench: community report, RTX 4060 Ti + Xeon E5-2673 v3 (DDR3, PCIe 3.0 x8), Q2_0 and IQ3_S, 0.1.39 path vs #706 + #764 + a busier tier (#913)
Bench folder ported from PR #913 by 1872183316, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
Mitsuasa513 2752f62687 Community benchmark: Tesla V100-SXM2-32GB on Windows (CUDA 12.6 / MSVC) (#902)
Bench folder ported from PR #902 by Mitsuasa513, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
T-Crypt efa1ea2f91 bench: community report, Flash-Next IQ2_XS vs a dense 27B as coding agents (RTX 4090 + 32 GB RAM) (#882)
Bench folder ported from PR #882 by T-Crypt, authorship kept.
Trimmed for 0.1.40.2: 1 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
christopherrobertbrooks-tech abc802e528 bench: community report, Tesla V100 32 GB + RTX 4070 layer split with 16 GB of RAM (IQ2_XS) (#850)
Bench folder ported from PR #850 by christopherrobertbrooks-tech, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
T-Crypt 2cf6f8a6d7 bench: community report, RTX 4090 + 32 GB RAM at 512K context (IQ2_XS) (#834)
Bench folder ported from PR #834 by T-Crypt, authorship kept.
Trimmed for 0.1.40.2: 4 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
christopherrobertbrooks-tech 3366409f68 bench: community report, Tesla V100 32 GB with 16 GB of RAM (Coder IQ1_M) (#823)
Bench folder ported from PR #823 by christopherrobertbrooks-tech, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
did-technomancer 0b65d593c3 bench: community results for an RX 6800 on Windows (0.1.39 against 0.1.38) (#815)
Bench folder ported from PR #815 by did-technomancer, authorship kept.
Trimmed for 0.1.40.2: 3 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
1314521gjy 1ce2c0ed35 Community benchmark: Flash-Next IQ3_S on an RTX 4080 SUPER (Windows), with the expert-pool worker sweep (#780)
Bench folder ported from PR #780 by 1314521gjy, authorship kept.
Trimmed for 0.1.40.2: 2 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
30crows 239a1fb45c bench: community results for an RTX 4090 Laptop GPU (IQ3_S, 128K and 256K) (#757)
Bench folder ported from PR #757 by 30crows, authorship kept.
Trimmed for 0.1.40.2: 4 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
xyzzing 8d6ae0cd83 Community benchmark: RX 7900 XTX 24 GB — ROCm 7.1.1 vs ROCm 10.2 nightly, serve-path decode (Qwen3.8-Flash-Next IQ3_S) (#745)
Bench folder ported from PR #745 by xyzzing, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
jase100k 7b52a0b62b Community benchmark: RX 7900 GRE (gfx1100, 16 GB), three Flash-Next quants (#740)
Bench folder ported from PR #740 by jase100k, authorship kept.
Trimmed for 0.1.40.2: 3 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
zimuhuan-code eb4e0c4b4f Community benchmark: 2× RTX 3060 12 GB, Qwen3.8-Flash-Next IQ3_S (three prompt sizes, 3 runs each) (#721)
Bench folder ported from PR #721 by zimuhuan-code, authorship kept.
Trimmed for 0.1.40.2: 8 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
pertain99 c9a554bc34 bench: community report, RTX 3090 Ti + 2x Xeon E5-2699 v3 (HP Z840), IQ3_S at 262K (#674)
Bench folder ported from PR #674 by pertain99, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
Dmitry-B fe63d57a8d bench: community report — RTX 4090, Ryzen 9 7950X, Flash-Next IQ3_S (#624)
Bench folder ported from PR #624 by Dmitry-B, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:04 +02:00
HarukiOtaku 34ee687744 bench: Windows 11 + Radeon AI PRO R9700 (gfx1201), IQ2_XS, 4K to 248K prompt tokens (#618)
Bench folder ported from PR #618 by HarukiOtaku, authorship kept.
Trimmed for 0.1.40.2: 2 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
lijieming 8dd3c4e5a5 bench: add community RTX 4000 Ada 2-GPU vs 3-GPU layer-split results (#521)
Bench folder ported from PR #521 by lijieming, authorship kept.
Trimmed for 0.1.40.2: 2 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
Efs-O 99b9311c1f bench: community results, 2x RTX 5060 Ti 16 GB (PCIe gen3), Strata 0.1.34 (#483)
Bench folder ported from PR #483 by Efs-O, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
Ambolio 263c99b96d bench: community report, RTX 5090 + Ryzen 9 9950X3D, IQ3_S at the full 262K window; prefill auto:32768 A/B; conversation-cache 32 GiB demo (#440)
Bench folder ported from PR #440 by Ambolio, authorship kept.
Trimmed for 0.1.40.2: 4 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
qni-live c69872aa23 bench: community entry, 2x RTX PRO 4500, Swift IQ3_XXS, engine 0.1.30 and 0.1.36 (#418)
Bench folder ported from PR #418 by qni-live, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
pipeob0 822b9857c0 bench: IQ4_XS on a 64 GB PC - pinned arena vs experts.bin mmap vs the RAM budget (#416)
Bench folder ported from PR #416 by pipeob0, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
rwkeyes c888e4a993 bench(gfx1100): 0.1.31 vs 0.1.30 prefill/decode, curated entry + raw trials (#404)
Bench folder ported from PR #404 by rwkeyes, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:04 +02:00
steve8697 75b0554906 Add an RTX 5090 + Ryzen 7 9700X IQ3_S benchmark on engine 0.1.39 (#811)
Bench folder ported from PR #811 by steve8697, authorship kept.
Trimmed for 0.1.40.2: 27 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:03 +02:00
steve8697 c9fc157744 Add an RTX 5090 + Ryzen 7 9700X IQ3_XXS benchmark on engine 0.1.39 (#777)
Bench folder ported from PR #777 by steve8697, authorship kept.
Trimmed for 0.1.40.2: 21 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:03 +02:00
xxDoman c97ad41eb0 bench: community MI50 (gfx906) — Strata 0.1.38 speed and recall (#758)
Bench folder ported from PR #758 by xxDoman, authorship kept.
Trimmed for 0.1.40.2: 21 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:03 +02:00
CYoung83 f546be7e3f Bench: RTX 5090 / 9950X3D (IQ3_S, three-pass A/B) + RTX 2080 Ti / 9900KF (IQ3_XXS, Turing) (#698)
Bench folder ported from PR #698 by CYoung83, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:03 +02:00
Yasei-no-otoko cc86ad90de bench: report Coder IQ1_M on Threadripper 3990X and RX 6900 XT (#628)
Bench folder ported from PR #628 by Yasei-no-otoko, authorship kept.
Trimmed for 0.1.40.2: 30 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/AMD_HIP.md.
2026-10-06 13:19:03 +02:00
R6DJO 9aa0f3e5df Community benchmark: 2x RTX 5060 Ti (16 GB), Xeon E5-2690 v4, IQ3_XXS, engine 0.1.35 (#508)
Bench folder ported from PR #508 by R6DJO, authorship kept.
Trimmed for 0.1.40.2: 40 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:03 +02:00
brenoperucchi 2a7db04226 bench: community report, RTX 5090 + Ryzen 9 5950X (AVX2), UD-Q4_K_XL / IQ3_S / Swift IQ3_XXS on engines 0.1.31-0.1.39 (#433)
Bench folder ported from PR #433 by brenoperucchi, authorship kept.
Trimmed for 0.1.40.2: 2 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:03 +02:00
mlfather 8c15b38be7 bench: community report, RTX PRO 4500 x1/x2 + RTX PRO 4000, Threadripper PRO 3975WX, engine 0.1.31 (#417)
Bench folder ported from PR #417 by mlfather, authorship kept.
Trimmed for 0.1.40.2: 162 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
2026-10-06 13:19:03 +02:00
noahark afee5009d8 docs: community benchmark row - Tesla V100-PCIE-32GB (sm_70 source build, before/after calibration) (#707)
Bench folder ported from PR #707 by noahark, authorship kept.
Trimmed for 0.1.40.2: 0 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:03 +02:00
homeofe aebd0f34d7 bench: community results, RX 9070 XT 16 GB on Windows 11, ready-made AMD engine 0.1.35, IQ3_S (#499)
Bench folder ported from PR #499 by homeofe, authorship kept.
Trimmed for 0.1.40.2: 2 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:03 +02:00
homeofe 707fd49eb4 bench: community results, RTX 2080 Ti 11 GB + Threadripper 3960X, IQ3_S at 262K with KV streaming (#469)
Bench folder ported from PR #469 by homeofe, authorship kept.
Trimmed for 0.1.40.2: 4 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:03 +02:00
shrisha108 219960ef34 bench: community report, 2x TITAN RTX (sm_75), IQ3_S at 262K (#389)
Bench folder ported from PR #389 by shrisha108, authorship kept.
Trimmed for 0.1.40.2: 1 raw dumps/logs/binaries left out (listed in TRIMMED.md of each folder).
Not ported: docs/COMMUNITY_BENCHMARKS.md.
2026-10-06 13:19:03 +02:00
Niko1221 4211e3b338 docs: VRAM_ELASTIC.md (what POST /v1/vram answers, #1034); MULTI_GPU.md: Pascal/Volta cards admitted when named (#1029) 2026-10-06 13:13:50 +02:00
Niko1221 82f46a8c8f serve: a tool call inside a code fence or inline code in the visible answer is text (#1058)
The live smoke showed the same quoted-example problem in the answer: a model that wrote the call format in a
fenced example after </think> got a real tool call. The content parser now uses the fence / inline-code
tracking of the reasoning rescue: an opener inside a ``` or ~~~ fence (tracked across stream chunks) or inside
inline code stays text, the decision depending only on the text before it, so streamed and whole outputs agree.
Openers at the top level fire as before, including one after a sentence on the same line (none of the 37
corpus specimens needed that, but the old tests do and nothing argues against it). A closed call at the end of
an answer cut by max tokens stays a call.

Tests: quoted calls stay text and real ones fire at every chunk split; OpenAI (whole and streamed), Anthropic
and Responses over HTTP.
2026-10-06 11:25:45 +02:00
Niko1221 b6c2e01589 serve: a request that meets a restart in progress gets an EngineDied, not an AttributeError (#1012)
Found in the live smoke: an old request that learns its engine died while restart() has already taken the
process (self.proc is None) failed in death_note() with an AttributeError, and its client got no reply at
all. death_note(), alive() and _send() now read the process once and treat None as a dead engine.

Test: after close() the death note is a sentence, alive() is False and a send raises EngineDied.
2026-10-06 10:36:01 +02:00
Niko1221 3a711ae90b serve: a --batch request whose engine died during its prompt read ends at once instead of after 300 s (#1012)
Found in the live smoke of #1012: a long prompt being admitted when the engine is killed read its own
end-of-engine line (EngineDied), and its cleanup then drained the control lines for up to 300 s for an
end line that had been read already; the client got its 503 only after that. The cleanup now skips the
STOP and the drain when the engine process the request was sent to is gone (the server counts its engine
processes in gen, and a request notes the one it has the control lines of), and the drain polls once a
second and gives up as soon as that engine is gone.

Tests: a long read killed alone, and with short requests arriving meanwhile (it gives way and is admitted):
every request ends in a 503 within seconds, and the next request restarts the engine.
2026-10-06 10:22:06 +02:00
Niko1221 511b646783 setup: a four-part hotfix version cannot break the version code
Versions are read as their first three numbers everywhere; the one place that was not (the version compiled
into an engine binary, `engine=0.1.40.1`) now accepts an optional fourth number. A test pins that a hotfix
tag keeps finding the engine zips: the checkout's v<project version> release first, the latest release second.
2026-10-06 10:03:35 +02:00
Niko1221 57a69eb48f serve: a restart keeps the admission state, so waiting requests no longer hang or fail with ValueError (#1012)
StrataEngine.restart() runs __init__ again, which made a new condition variable, control lock, wait counter
and wait list while requests were still waiting on the old ones: they hung for good, or failed with
`list.remove(x): x not in list`. The admission state is now made once and kept; restart() wakes the waiters
when it ends. A request that waits for the control lines now ends with a clean EngineDied (503, "not sent")
when the engine is gone and nobody is starting it, keeps waiting while a restart is under way, and goes on
with the new engine when it is up. The lock is checked again after it is taken, a slot that frees itself
after a restart keeps the lists of its own process, and the wait counters cannot go below zero.

Tests (fake batch engine behind the real StrataEngine and Service): waiters during a restart are served by
the new engine (2 and 4 slots, same condition variable and lock), get a clean 503 when the engine stays down,
and when the restart itself fails; the next request restarts and is answered; the solo engine is unchanged.
2026-10-06 10:03:35 +02:00
Niko1221 38357f1858 serve: a tool call inside the thinking is a call only when it was an act (#804, #1058)
The #804 rescue turned any declared <tool_call> in the reasoning into a real call, so a call that was only
quoted (in a code fence, mid-sentence, in inline code, followed by more prose) ran, and so did one in a reply
cut by max tokens (47Hunter47's `rm -rf build` case). Now it must start a line (after optional whitespace),
be outside a code fence (``` or ~~~, tracked across chunks) and inline code, close with </tool_call>, and have
only whitespace or more calls after it before the end of the turn or </think>. Such calls wait until then,
and become reasoning text again when the turn ends by length, cancel or error. The decision is the same
streamed or whole. Declared calls beside an undeclared wrapper are still delivered, the wrapper stays
reasoning.

The server tells the parser how the turn ended, logs how many calls were read or kept as text, and counts
the read ones in the metrics totals (tool_calls_from_reasoning).

Tests: 37 recorded and constructed specimens (PR #525's corpus and 47Hunter47's 12 live strandings; 16 act,
21 do not) at chunk sizes 1, 2, 3, 7, random and whole; fence and opener split at every position; OpenAI
(whole and streamed), Anthropic and Responses over HTTP.
2026-10-06 10:03:35 +02:00
Chen Cheng 5b5455982f serve: an engine that exits after an unread ERR line says why (#997 #890)
With "parallel": N, a batch window that fails prints its reason as an ERR
line on stdout and the engine exits with code 1. At that point every
request reads its own slot's lines, so nobody reads that ERR line, and the
server's note fell back to the log's last "strata" line (a stats line in
#997), or to "the usual cause is running out of RAM" without a log.

The pump now keeps an ERR line that was the engine's last stdout line, and
death_note gives it first. test_parallel's fake engine can fail its first
window over two slots: the note names the error.

(cherry picked from commit 9c5066303e)
2026-10-06 09:45:16 +02:00
2309 changed files with 466347 additions and 4424 deletions

No files matched your search

+37
View File
@@ -0,0 +1,37 @@
name: Bug report
description: Something in Strata doesn't work as documented
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
value: |
Thanks for the report.
Please search the [open issues](https://github.com/Niko1221/Strata/issues?q=is%3Aissue+state%3Aopen) first — this may already be tracked.
- type: textarea
id: what
attributes:
label: What happened
description: What you did, what you got, and what you expected instead. Paste the exact error text if there is one. If there's a set of steps or commands that reproduces it, put them here too.
validations:
required: true
- type: input
id: hardware
attributes:
label: Strata version, GPU, OS
description: The engine version is on the About tab, or in `GET /v1/status` under `engine`.
placeholder: "0.1.31 · RTX 5070 12GB · Windows 11"
validations:
required: true
- type: textarea
id: log
attributes:
label: Engine log
description: Drag in `strata-<model>.log` from your Strata folder. Drag and drop works directly into this box.
validations:
required: false
- type: markdown
value: |
A couple of extras that help a lot:
- Check the [troubleshooting table](https://github.com/Niko1221/Strata/blob/main/docs/DETAILS.md#troubleshooting) first, many reports are already solved there.
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Troubleshooting and known issues
url: https://github.com/Niko1221/Strata/blob/main/docs/DETAILS.md#troubleshooting
about: Symptom-to-fix table for crashes, out of memory, hangs, and port problems.
@@ -0,0 +1,18 @@
name: Feature request
description: Suggest something new or an improvement
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
value: |
Explain what you want in plain language.
- type: textarea
id: idea
attributes:
label: What do you want to do?
description: Describe the goal rather than the implementation. If there's a workaround you use today, mention it.
validations:
required: true
- type: markdown
value: |
All of this counts: model and architecture support, the server and API, the web app, multi-GPU and layer split, images and vision, MCP servers, setup and installation, and performance.
+22
View File
@@ -0,0 +1,22 @@
name: Question
description: Ask how to use Strata, or share a result
title: "Question: "
labels: ["question"]
body:
- type: markdown
value: |
Not sure how to do something, or want to share numbers from your machine? Ask here.
- type: textarea
id: question
attributes:
label: Your question
description: What you're trying to do. Include your version, GPU, and OS if it affects the answer.
validations:
required: true
- type: textarea
id: numbers
attributes:
label: Results (optional)
description: Only if you ran a benchmark or measured speed. Model, quant, context, and the tok/s you get.
validations:
required: false
+12
View File
@@ -0,0 +1,12 @@
## Title
<!-- If Applicable, reference the GitHub issue -->
Issue: Resolves #
## Summary
<!-- Quick Summary of changes -->
## What changed
<!-- Specifics on files changed, and what changes were made there -->
## Extra Notes
<!-- Any extra notes, delete if there are none -->
+13
View File
@@ -21,3 +21,16 @@ offers the same steps as tools.
`tools/test_setup_amd.py`, `tools/test_setup_choices.py`).
- Keep the docs' style: plain words, measured numbers with what they were measured on, no claims without a
measurement.
## Contributing a change or report
- Search the open issues and pull requests first, and add to a thread that already covers your point.
- Open an issue with the form that fits (bug report, feature request or question).
- One change per pull request. Say what it changes and what it leaves alone.
- A new feature is opt-in, and the default path stays byte-identical to the last release. Say how you checked.
- Build every backend a file touches (CUDA, HIP, SYCL) before asking for review.
- Change a default only where you measured it faster, and show the numbers with what they were measured on.
- A report from hardware the maintainers do not have is welcome. Follow
[docs/COMMUNITY_BENCHMARKS.md](docs/COMMUNITY_BENCHMARKS.md), compare against a same-day run of the build you are
testing, and say what you did not test.
- Open test requests and the hardware that is wanted are listed in [docs/TEST_REQUESTS.md](docs/TEST_REQUESTS.md).
+16
View File
@@ -0,0 +1,16 @@
@echo off
rem Benchmark this Strata install the way it is used and write a report you can share (docs/COMMUNITY_BENCHMARKS.md).
rem It starts the model with your settings (or uses the one already running), takes about 10 minutes and changes nothing.
rem Options: --clients 4 (several requests at once), --long (a 32K prompt), --compare <older results folder>.
setlocal
title Strata - benchmark
cd /d "%~dp0"
if not exist ".venv\Scripts\python.exe" (
echo Strata is not set up here yet: run START-HERE.bat first.
pause
exit /b 1
)
".venv\Scripts\python.exe" tools\strata_bench.py %*
set "RC=%ERRORLEVEL%"
pause
exit /b %RC%
+221 -6
View File
@@ -8,7 +8,7 @@
# below refuses anything older: silently building for a pre-Ampere arch would produce a binary that runs
# and is wrong about its own performance, which is the worst of both outcomes.
cmake_minimum_required(VERSION 3.24)
project(strata VERSION 0.1.40 LANGUAGES CXX)
project(strata VERSION 0.1.43 LANGUAGES CXX)
# EXPERIMENTAL: the Intel Arc engine, the SYCL port in sycl/ (PR #423; icpx + oneMKL, docs/INTEL_ARC.md). It is its
# own project that takes the unported sources from src/; with this on, nothing below is configured.
option(STRATA_ENABLE_SYCL "EXPERIMENTAL: build the Intel Arc (SYCL) engine from sycl/ - docs/INTEL_ARC.md" OFF)
@@ -114,6 +114,9 @@ if(STRATA_BUILD_CONVERSATION_TESTS)
add_executable(conversation_file_test src/core/conversation_file_test.cpp src/core/conversation_file.cpp)
target_include_directories(conversation_file_test PRIVATE include)
add_test(NAME conversation_file_test COMMAND conversation_file_test)
add_executable(session_save_reclaim_test src/core/session_save_reclaim_test.cpp src/core/conversation_file.cpp)
target_include_directories(session_save_reclaim_test PRIVATE include)
add_test(NAME session_save_reclaim_test COMMAND session_save_reclaim_test)
endif()
if(STRATA_HIP_GFX906)
@@ -236,6 +239,9 @@ target_link_libraries(strata-dequant PRIVATE strata_artifact)
# this file is now nothing but the CLI entry point, and its inherited guard spells that as a "selftest"
target_compile_definitions(strata-dequant PRIVATE STRATA_DEQUANT_SELFTEST)
if(STRATA_BUILD_TESTS)
add_executable(logit_bias_test src/core/logit_bias_test.cpp)
target_include_directories(logit_bias_test PRIVATE include)
add_test(NAME logit_bias_test COMMAND logit_bias_test)
# the reader refuses a duplicate tensor name at open, naming it (a synthetic GGUF: no model, no GPU)
add_executable(gguf_reader_test src/artifact/gguf_reader_test.cpp)
target_link_libraries(gguf_reader_test PRIVATE strata_artifact)
@@ -244,12 +250,31 @@ if(STRATA_BUILD_TESTS)
add_executable(gguf_split_test tests/core/gguf_split_test.cpp)
target_link_libraries(gguf_split_test PRIVATE strata_artifact)
add_test(NAME gguf_split_test COMMAND gguf_split_test)
# how --batch cuts a prompt read into pieces (a layer split with no slot decoding must still reach a BYIELD)
add_executable(batch_read_test tests/program/batch_read_test.cpp)
target_include_directories(batch_read_test PRIVATE include)
add_test(NAME batch_read_test COMMAND batch_read_test)
# the hipBLASLt tuning header's architecture gate: RDNA4 siblings share solution IDs, RDNA3 siblings share
# theirs, cross-family pairs and version mismatches stay rejected (synthetic tables: no model, no GPU)
add_executable(tuning_header_arch_family_test tests/prefill/tuning_header_arch_family_test.cpp)
target_include_directories(tuning_header_arch_family_test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/prefill")
add_test(NAME tuning_header_arch_family_test COMMAND tuning_header_arch_family_test)
# the auto layer split's minimum stage size (#1616): header-only rule, no GPU
add_executable(split_rules_test tests/program/split_rules_test.cpp)
target_include_directories(split_rules_test PRIVATE include)
add_test(NAME split_rules_test COMMAND split_rules_test)
# the default CPU prefill share is not armed when every expert is resident (#1595): header-only rule, no GPU
add_executable(share_rules_test tests/prefill/share_rules_test.cpp)
target_include_directories(share_rules_test PRIVATE include)
add_test(NAME share_rules_test COMMAND share_rules_test)
# the Windows HIP auto prefill chunk cap (#1630) and the commit-limit verdict (#1607): header-only, no GPU
add_executable(host_limits_test tests/platform/host_limits_test.cpp)
target_include_directories(host_limits_test PRIVATE include)
add_test(NAME host_limits_test COMMAND host_limits_test)
endif()
# ---- strata_plan ----------------------------------------------------------
@@ -329,6 +354,7 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
src/kernels/cuda/native_gdn.cu
src/kernels/cuda/native_gr_postops.cu
src/kernels/cuda/native_router.cu
src/kernels/cuda/route_prior.cu
src/kernels/cuda/native_gdn_preprocess.cu
src/kernels/cuda/native_qsa.cu
src/kernels/cuda/native_rope.cu
@@ -378,6 +404,16 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
target_link_libraries(qsa_select_bench PRIVATE strata_kernels strata_hip_runtime)
add_test(NAME qsa_select_bench COMMAND qsa_select_bench 32768 256 3 262144)
endif()
# the same on CUDA: the tensor-core scorer (sm_80+) or the FP32 tiled one (below sm_80, and forced on any card by
# STRATA_QSA_WARP=1 in the second test) against the warp kernel and FP64, at 32K and 131K
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA)
add_executable(qsa_select_bench src/kernels/qsa_select_bench.cpp)
target_include_directories(qsa_select_bench PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_select_bench PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_select_bench COMMAND qsa_select_bench 32768 256 3 262144)
add_test(NAME qsa_select_bench_fp32_tiled COMMAND qsa_select_bench 131072 256 3 262144)
set_tests_properties(qsa_select_bench_fp32_tiled PROPERTIES ENVIRONMENT "STRATA_QSA_WARP=1;STRATA_SELECT_SIMT=1")
endif()
# plan v0.3 P6: llama.cpp's block layouts and codebook grids for the i-quant formats (MIT, third_party/ggml)
target_include_directories(strata_kernels PRIVATE third_party/ggml)
# Match the pinned llama.cpp CUDA arithmetic contract for this adapter only.
@@ -448,6 +484,15 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
set_tests_properties(exchange_storage_test PROPERTIES TIMEOUT 60)
endif()
# available_host_bytes(): MemAvailable, cgroup limits and the operator's --memory-limit-mib cap on fixture directories
# - no GPU, no CUDA
if(STRATA_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/memory_guard_test.cpp")
add_executable(memory_guard_test tests/core/memory_guard_test.cpp src/core/conversation_memory.cpp)
target_include_directories(memory_guard_test PRIVATE include)
add_test(NAME memory_guard_test COMMAND memory_guard_test)
set_tests_properties(memory_guard_test PROPERTIES TIMEOUT 60)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/file_expert_source_test.cpp")
enable_testing()
add_executable(file_expert_source_test tests/core/file_expert_source_test.cpp)
@@ -458,6 +503,21 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
target_link_libraries(vmm_test PRIVATE strata_engine)
add_test(NAME vmm_test COMMAND vmm_test)
set_tests_properties(vmm_test PROPERTIES TIMEOUT 60)
# The resident RAM mode with a layer split: the copy keeps each stage's lend region, and the experts the
# prompt path borrows are served from RAM (the file source reads none of them). The device calls are
# wrapped - conversation_transfer_test's pattern - and `pin = false` keeps the copy pageable, so this
# runs on a CPU-only PC.
if(NOT STRATA_ENABLE_HIP AND UNIX AND NOT APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_executable(split_lend_complement_test tests/core/split_lend_complement_test.cpp)
target_link_libraries(split_lend_complement_test PRIVATE strata_engine)
target_link_options(split_lend_complement_test PRIVATE
-Wl,--wrap=cudaMalloc -Wl,--wrap=cudaFree -Wl,--wrap=cudaMemset -Wl,--wrap=cudaMemcpy
-Wl,--wrap=cudaMemcpyAsync -Wl,--wrap=cudaStreamSynchronize -Wl,--wrap=cudaDeviceSynchronize
-Wl,--wrap=cudaGetLastError -Wl,--wrap=cudaGetDevice -Wl,--wrap=cudaSetDevice
-Wl,--wrap=cudaMemGetInfo -Wl,--wrap=cudaHostAlloc -Wl,--wrap=cudaFreeHost)
add_test(NAME split_lend_complement_test COMMAND split_lend_complement_test)
set_tests_properties(split_lend_complement_test PROPERTIES ENVIRONMENT "CUDA_VISIBLE_DEVICES=-1" TIMEOUT 120)
endif()
endif()
# #477: the learned expert profile (--expert-profile-save): its order and make_profile.py's file format (CPU only)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/expert_profile_save_test.cpp")
@@ -467,6 +527,14 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
add_test(NAME expert_profile_save_test COMMAND expert_profile_save_test)
set_tests_properties(expert_profile_save_test PROPERTIES TIMEOUT 60)
endif()
# the profile header's version is enforced: a version other than 1 is refused by its number (CPU only)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/expert_profile_version_test.cpp")
enable_testing()
add_executable(expert_profile_version_test tests/core/expert_profile_version_test.cpp)
target_link_libraries(expert_profile_version_test PRIVATE strata_engine)
add_test(NAME expert_profile_version_test COMMAND expert_profile_version_test)
set_tests_properties(expert_profile_version_test PROPERTIES TIMEOUT 60)
endif()
if(STRATA_BUILD_TESTS AND STRATA_NATIVE_EXPERTS)
# native_experts.txt v2-v4 (the real packs' copies and the per-role shard column) and the arena filled from
# synthetic GGUF shards at the real expert dimensions
@@ -502,7 +570,9 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
add_executable(conversation_transfer_test src/core/conversation_validation_test.cpp)
target_compile_definitions(conversation_transfer_test PRIVATE CONVERSATION_TEST_TRANSFERS)
target_link_options(conversation_transfer_test PRIVATE
-Wl,--wrap=cudaMemcpy -Wl,--wrap=cudaDeviceSynchronize -Wl,--wrap=cudaGetLastError)
-Wl,--wrap=cudaMemcpy -Wl,--wrap=cudaMemcpyAsync -Wl,--wrap=cudaStreamSynchronize
-Wl,--wrap=cudaGetDevice -Wl,--wrap=cudaStreamCreateWithFlags
-Wl,--wrap=cudaDeviceSynchronize -Wl,--wrap=cudaGetLastError)
target_link_libraries(conversation_transfer_test PRIVATE strata_engine)
add_test(NAME conversation_transfer_test COMMAND conversation_transfer_test)
set_tests_properties(conversation_transfer_test PROPERTIES ENVIRONMENT "CUDA_VISIBLE_DEVICES=-1" TIMEOUT 120)
@@ -594,6 +664,7 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
target_include_directories(hip_prefill_wmma_gemm_parity PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/prefill")
target_link_libraries(hip_prefill_wmma_gemm_parity PRIVATE strata_prefill strata_hip_runtime)
add_test(NAME hip_prefill_wmma_gemm_parity COMMAND hip_prefill_wmma_gemm_parity)
set_tests_properties(hip_prefill_wmma_gemm_parity PROPERTIES SKIP_RETURN_CODE 77) # no matrix cores: skipped
list(APPEND _strata_hip_tests hip_prefill_wmma_gemm_parity)
# S (STRATA_HCD_EXACT): the exact HC down kernel against hipBLASLt solution 1176 / 1177 (needs STRATA_HIPBLASLT_TUNING; exit 77 else)
add_executable(hip_prefill_hcd_exact_parity tests/hip/prefill_hcd_exact_parity.cpp)
@@ -702,6 +773,24 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
set_tests_properties(mmvq_multi_parity_cuda_layout PROPERTIES ENVIRONMENT "STRATA_MMVQ_WAVE=0")
endif()
endif()
if(STRATA_ENABLE_CUDA)
# fork F4: the 2-4 column dense projections from the interleaved q8_1 copy, bitwise vs native_mmvq (GPU, synthetic)
add_executable(mmvq_il_parity src/kernels/mmvq_il_parity.cpp)
target_include_directories(mmvq_il_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(mmvq_il_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME mmvq_il_parity COMMAND mmvq_il_parity)
endif()
# programmatic dependent launch (pdl.hpp) in a captured verify-shaped chain: bitwise vs ordinary launches over
# replays with poisoned buffers, and only kernel-to-kernel programmatic edges (GPU, synthetic, no model; the edges
# are only made on sm_90+, elsewhere it checks that there are none). NVIDIA only: PDL is a CUDA feature.
if(STRATA_ENABLE_CUDA AND NOT STRATA_HIP_GFX906)
add_executable(pdl_parity src/kernels/pdl_parity.cpp)
target_include_directories(pdl_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(pdl_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME pdl_parity COMMAND pdl_parity)
# PDL is opt-in (STRATA_DF_PDL); the test checks the opted-in path, whose PDL capture forces programmatic launches
set_tests_properties(pdl_parity PROPERTIES ENVIRONMENT "STRATA_DF_PDL=1")
endif()
# the IQ kernels that decode a weight part once for every column/entry, bitwise vs the per-column ones
# (GPU, synthetic, no model; --bench adds old/new timings)
add_executable(iq_multi_parity src/kernels/iq_multi_parity.cpp)
@@ -751,6 +840,9 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
add_executable(fused_gr_bench src/kernels/fused_gr_bench.cpp)
target_include_directories(fused_gr_bench PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(fused_gr_bench PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_executable(router_chain_bench src/kernels/router_chain_bench.cpp)
target_include_directories(router_chain_bench PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(router_chain_bench PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_executable(gr_parity src/kernels/gr_parity.cpp)
target_include_directories(gr_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(gr_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
@@ -771,6 +863,12 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
target_link_libraries(sampler_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME sampler_parity COMMAND sampler_parity --selftest)
set_tests_properties(sampler_parity PROPERTIES ENVIRONMENT "STRATA_OLD_SAMPLER=0;STRATA_SAMPLER_ONE_BLOCK=0")
# the GPU verifier of probabilistic draft acceptance against the host reference (STRATA_SPEC_PROB)
add_executable(spec_verify_parity src/kernels/spec_verify_parity.cpp)
target_include_directories(spec_verify_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(spec_verify_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME spec_verify_parity COMMAND spec_verify_parity)
set_tests_properties(spec_verify_parity PROPERTIES ENVIRONMENT "STRATA_OLD_SAMPLER=0;STRATA_SAMPLER_ONE_BLOCK=0")
# the same fixtures on the other two sampled paths: the one-block kernel and the 0.1.20 kernel
add_test(NAME sampler_parity_one_block COMMAND sampler_parity --selftest)
set_tests_properties(sampler_parity_one_block PROPERTIES ENVIRONMENT "STRATA_OLD_SAMPLER=0;STRATA_SAMPLER_ONE_BLOCK=1")
@@ -801,6 +899,14 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
target_link_libraries(route_window_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME route_window_parity COMMAND route_window_parity)
# STRATA_ROUTE_RESIDENT's swap: the warp-per-token kernel against a scalar reference of the same semantics,
# bitwise on the ids, the weights and the four counters (GPU, synthetic, no model)
add_executable(route_resident_parity src/kernels/route_resident_parity.cu)
target_include_directories(route_resident_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(route_resident_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME route_resident_parity COMMAND route_resident_parity)
set_tests_properties(route_resident_parity PROPERTIES TIMEOUT 120)
add_executable(router_top10_parity src/kernels/router_top10_parity.cpp)
target_include_directories(router_top10_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(router_top10_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
@@ -903,6 +1009,12 @@ if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
target_include_directories(kv_q4_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(kv_q4_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME kv_q4_parity COMMAND kv_q4_parity)
# the verify window's one-launch forms (hyper-connection write, PLE post-ops with the commit's
# history snapshots) vs the per-token launches they replace, bitwise (GPU, synthetic, no model)
add_executable(verify_batch_parity src/kernels/verify_batch_parity.cpp)
target_include_directories(verify_batch_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(verify_batch_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_test(NAME verify_batch_parity COMMAND verify_batch_parity)
add_executable(cvec_parity src/kernels/cvec_parity.cpp)
target_include_directories(cvec_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(cvec_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
@@ -1011,18 +1123,38 @@ if(STRATA_BUILD_TESTS OR STRATA_BUILD_CONVERSATION_TESTS)
add_executable(message_boundary_test src/program/message_boundary_test.cpp)
target_include_directories(message_boundary_test PRIVATE include)
add_test(NAME message_boundary_test COMMAND message_boundary_test)
# a layer split's ring override and the lend slots sized with it: the RAM copy must keep every borrowed slot (CPU)
add_executable(split_ring_test src/program/split_ring_test.cpp)
target_include_directories(split_ring_test PRIVATE include)
add_test(NAME split_ring_test COMMAND split_ring_test)
# the serve loop's conversation-cache retention policy (header-only, no library to link)
add_executable(conv_cache_test src/program/conv_cache_test.cpp)
target_include_directories(conv_cache_test PRIVATE include)
add_test(NAME conv_cache_test COMMAND conv_cache_test)
# which path computes the draft layer's prompt K/V, and STRATA_SPLIT_MTP_BATCH's parsing (header-only, CPU)
add_executable(draft_kv_plan_test src/program/draft_kv_plan_test.cpp)
target_include_directories(draft_kv_plan_test PRIVATE include)
add_test(NAME draft_kv_plan_test COMMAND draft_kv_plan_test)
# coupled draft sampling's counter and penalty-ring arithmetic (header-only, CPU)
add_executable(coupled_draft_test src/core/coupled_draft_test.cpp)
target_include_directories(coupled_draft_test PRIVATE include)
add_test(NAME coupled_draft_test COMMAND coupled_draft_test)
# probabilistic draft acceptance (STRATA_SPEC_PROB): rejection sampling preserves p - chi-square over 2M trials per case (CPU)
add_executable(spec_prob_test src/core/spec_prob_test.cpp)
target_include_directories(spec_prob_test PRIVATE include)
add_test(NAME spec_prob_test COMMAND spec_prob_test)
# the f32 -> bf16 rounding against ggml's, NaN included, over every f32 bit pattern (header-only, CPU)
add_executable(bf16_bits_test src/kernels/bf16_bits_test.cpp)
target_include_directories(bf16_bits_test PRIVATE include)
add_test(NAME bf16_bits_test COMMAND bf16_bits_test)
# the batch window's row layout (a slot's group of proposed rows, the layer split's hand-off capacity; header-only, CPU)
add_executable(batch_rows_test tests/core/batch_rows_test.cpp)
target_include_directories(batch_rows_test PRIVATE include)
add_test(NAME batch_rows_test COMMAND batch_rows_test)
# how many pipelined slot groups --batch runs with: the 0.1.41 default, and --batch-mtp keeping it serial (header-only, CPU)
add_executable(batch_groups_test src/program/batch_groups_test.cpp)
target_include_directories(batch_groups_test PRIVATE include)
add_test(NAME batch_groups_test COMMAND batch_groups_test)
endif()
add_library(strata_kernels_cpu STATIC src/kernels/cpu/expert.cpp src/kernels/cpu/pool.cpp
@@ -1134,7 +1266,7 @@ if(STRATA_NATIVE_EXPERTS)
# one-pass static linking has no cycle to resolve)
target_sources(strata_kernels_cpu PRIVATE src/kernels/cpu/native_expert.cpp src/kernels/cpu/iq_avx512.cpp
src/kernels/cpu/iq_avx2.cpp src/kernels/cpu/kq_avx2.cpp
src/kernels/cpu/kq_avx1.cpp)
src/kernels/cpu/kq_avx1.cpp src/kernels/cpu/q2_avx1.cpp src/kernels/cpu/iq_avx1.cpp)
target_include_directories(strata_kernels_cpu PRIVATE third_party/ggml)
if(MSVC)
set_source_files_properties(src/kernels/cpu/iq_avx512.cpp PROPERTIES COMPILE_OPTIONS "/arch:AVX512")
@@ -1142,12 +1274,26 @@ if(STRATA_NATIVE_EXPERTS)
set_source_files_properties(src/kernels/cpu/kq_avx2.cpp PROPERTIES COMPILE_OPTIONS "/arch:AVX2")
# the router lookahead's AVX1 dot (older CPUs; called behind cpu_avx1_ok): no FMA, no 256-bit integer ops
set_source_files_properties(src/kernels/cpu/kq_avx1.cpp PROPERTIES COMPILE_OPTIONS "/arch:AVX")
# the older CPUs' Q2_0 rows and activation quantizer (q2_avx1.cpp, issue #1699): 128-bit integer lanes,
# no FMA/F16C, its own #error guard; called only behind cpu_avx1_ok() && !cpu_avx2_ok()
set_source_files_properties(src/kernels/cpu/q2_avx1.cpp PROPERTIES COMPILE_OPTIONS "/arch:AVX")
# the older CPUs' i-quant expert rows (iq_avx1.hpp): 128-bit SSSE3/SSE2 integer, SSE2 float, no FMA/F16C
set_source_files_properties(src/kernels/cpu/iq_avx1.cpp PROPERTIES COMPILE_OPTIONS "/arch:AVX")
else()
set_source_files_properties(src/kernels/cpu/iq_avx512.cpp PROPERTIES COMPILE_OPTIONS
"-mavx512f;-mavx512bw;-mavx512vl;-mavx512dq;-mavx512vnni;-mavx512vbmi;-mf16c;-mfma")
set_source_files_properties(src/kernels/cpu/iq_avx2.cpp PROPERTIES COMPILE_OPTIONS "-mavx2;-mfma;-mf16c")
set_source_files_properties(src/kernels/cpu/kq_avx2.cpp PROPERTIES COMPILE_OPTIONS "-mavx2;-mfma;-mf16c")
set_source_files_properties(src/kernels/cpu/kq_avx1.cpp PROPERTIES COMPILE_OPTIONS "-mavx")
# the older CPUs' Q2_0 rows and activation quantizer (q2_avx1.cpp, issue #1699): SSE4.2 + AVX with
# AVX2/FMA/F16C explicitly off, so a -march=native build host cannot leak AVX2 code into the file that
# runs on a Sandy Bridge Xeon
set_source_files_properties(src/kernels/cpu/q2_avx1.cpp PROPERTIES COMPILE_OPTIONS
"-msse4.2;-mavx;-mno-avx2;-mno-fma;-mno-f16c")
# the older CPUs' i-quant expert rows (iq_avx1.hpp): SSE4.2 + AVX with AVX2/FMA/F16C explicitly off, so a
# -march=native build host cannot leak AVX2 code into the file that runs on a Sandy Bridge Xeon
set_source_files_properties(src/kernels/cpu/iq_avx1.cpp PROPERTIES COMPILE_OPTIONS
"-msse4.2;-mavx;-mno-avx2;-mno-fma;-mno-f16c")
endif()
set_property(SOURCE src/kernels/cpu/iq_avx2.cpp APPEND PROPERTY COMPILE_DEFINITIONS ${_strata_avxvnni})
if(STRATA_ISA_FLOOR STREQUAL "avx")
@@ -1167,6 +1313,10 @@ if(STRATA_NATIVE_EXPERTS)
add_executable(router_dot_parity src/kernels/cpu/router_dot_parity.cpp)
target_link_libraries(router_dot_parity PRIVATE strata_kernels_cpu)
if(STRATA_BUILD_TESTS)
add_executable(pool_tasks_test src/kernels/cpu/pool_tasks_test.cpp)
target_link_libraries(pool_tasks_test PRIVATE strata_kernels_cpu)
add_test(NAME pool_tasks_test COMMAND pool_tasks_test)
set_tests_properties(pool_tasks_test PROPERTIES TIMEOUT 180)
add_test(NAME router_dot_parity COMMAND router_dot_parity)
add_test(NAME router_dot_parity_avx COMMAND ${CMAKE_COMMAND} -E env STRATA_FORCE_ISA=avx $<TARGET_FILE:router_dot_parity>)
endif()
@@ -1178,6 +1328,21 @@ if(STRATA_NATIVE_EXPERTS)
add_test(NAME iq_avx2_parity COMMAND iq_avx2_parity)
add_test(NAME iq_avx2_parity_iq3s_mt1 COMMAND ${CMAKE_COMMAND} -E env STRATA_IQ3S_MT1=1 $<TARGET_FILE:iq_avx2_parity>)
endif()
# the older CPUs' AVX1 expert rows (iq_avx1.cpp) against ggml-cpu's dots and against their own one-token
# rows at every width; --bench: ms per expert, the weights streamed from DRAM. Runs on any AVX CPU:
# on an AVX2 machine it checks the older-CPU path as an AVX-only machine would run it.
add_executable(iq_avx1_parity src/kernels/cpu/iq_avx1_parity.cpp)
target_link_libraries(iq_avx1_parity PRIVATE strata_kernels_cpu)
if(STRATA_BUILD_TESTS)
add_test(NAME iq_avx1_parity COMMAND iq_avx1_parity)
endif()
# the AVX-2 Q8_K activation quantizer against ggml's, byte for byte (--bench: its time)
add_executable(q8k_quant_parity src/kernels/cpu/q8k_quant_parity.cpp)
target_link_libraries(q8k_quant_parity PRIVATE strata_kernels_cpu)
if(STRATA_BUILD_TESTS)
add_test(NAME q8k_quant_parity COMMAND q8k_quant_parity)
add_test(NAME q8k_quant_parity_avx COMMAND ${CMAKE_COMMAND} -E env STRATA_FORCE_ISA=avx $<TARGET_FILE:q8k_quant_parity>)
endif()
set(_strata_mmq_types q2_0 iq2_xxs iq2_xs iq2_s iq3_xxs iq3_s iq4_nl iq4_xs q8_0)
# #296: OrcaRouter Q4_K_S's Q5_0 experts (Q4_K and Q5_1 come with STRATA_MMQ_KQUANTS on CUDA, S5's branch)
if(STRATA_ORCA_Q4KS_MMQ)
@@ -1203,8 +1368,8 @@ if(STRATA_NATIVE_EXPERTS)
set(_strata_mmq_cuda_types ${_strata_mmq_types})
if(STRATA_MMQ_KQUANTS)
list(APPEND _strata_mmq_cuda_types q4_k q5_k q5_1)
if(STRATA_Q6K_EXPERTS) # UD-Q6_K_XL gate/up (docs/UNSLOTH_Q6.md)
list(APPEND _strata_mmq_cuda_types q6_k)
if(STRATA_Q6K_EXPERTS OR STRATA_HIP_GFX906) # UD-Q6_K_XL gate/up (docs/UNSLOTH_Q6.md),
list(APPEND _strata_mmq_cuda_types q6_k) # and gfx906: moe_mmq.cu calls it under __HIPCC__
endif()
endif()
foreach(_t IN LISTS _strata_mmq_cuda_types)
@@ -1216,7 +1381,11 @@ if(STRATA_NATIVE_EXPERTS)
if(MSVC)
target_compile_options(strata_mmq PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=/bigobj>)
endif()
# GGML's CUDA error reporting calls cuGetErrorString from the driver API.
target_link_libraries(strata_mmq PUBLIC ggml-base ${_strata_gpu_runtime_target})
if(TARGET CUDA::cuda_driver) # not in the HIP (gfx906) build, whose CUDA::cudart is a stand-in
target_link_libraries(strata_mmq PUBLIC CUDA::cuda_driver)
endif()
if(STRATA_MMQ_KQUANTS)
target_compile_definitions(strata_mmq PRIVATE STRATA_MMQ_KQUANTS=1)
endif()
@@ -1256,7 +1425,7 @@ if(STRATA_NATIVE_EXPERTS)
if(NOT EXISTS "${_strata_mmq}/common.cuh" OR NOT EXISTS "${_strata_mmq}/quantize.cu")
message(FATAL_ERROR "The selected GGML source is missing the ggml-cuda MMQ sources: ${_strata_mmq}")
endif()
# Aurora: moe_fused.cu's and moe_fused_iq.cu's gfx11 WMMA kernels for the prompt experts (opt-in STRATA_PF_FUSED=1)
# Aurora: moe_fused.cu's gfx11 and moe_fused_iq.cu's gfx11 / gfx12 WMMA kernels for the prompt experts (opt-in STRATA_PF_FUSED=1)
set(_strata_mmq_sources
src/prefill/moe_mmq.cu
src/prefill/moe_fused.cu
@@ -1390,6 +1559,7 @@ if(STRATA_BUILD_TESTS)
add_executable(expert_multi_test src/kernels/cpu/expert_multi_test.cpp)
target_link_libraries(expert_multi_test PRIVATE strata_kernels_cpu)
add_test(NAME expert_multi_test COMMAND expert_multi_test)
set_tests_properties(expert_multi_test PROPERTIES SKIP_RETURN_CODE 77) # no AVX-512: skipped
# PR #706: the opt-in AVX-2 Q2_0 bit-plane kernel (STRATA_Q2_BITPLANE=1) against the legacy one, bitwise alone/window
if(STRATA_BUILD_TESTS AND CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64|x64|X86")
@@ -1414,12 +1584,52 @@ if(STRATA_BUILD_TESTS)
target_link_libraries(pool_stress PRIVATE strata_kernels_cpu)
add_test(NAME pool_stress COMMAND pool_stress 20)
# #795: nothing an AVX2-only CPU can reach may hold AVX-512 / AVX-VNNI-class instructions. (1) the linked test binary
# (every CPU kernel file plus ggml-cpu; on Linux, the kernel library) is disassembled and any function with such instructions must be a kernel of
# a wide-ISA file, not a weak / COMDAT copy and not a probe or helper that every CPU runs; (2) with -DSTRATA_SDE=<sde>
# (Intel SDE) the CPU tests run on an emulated AVX2-only Haswell and on an Alder Lake (AVX-VNNI, no AVX-512): SDE stops
# at the first instruction the chip does not have.
if(TARGET iq_avx2_parity)
find_package(Python3 QUIET COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
if(MSVC)
target_link_options(iq_avx2_parity PRIVATE /MAP)
add_test(NAME isa_guard_check COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/check_isa_guard.py
$<TARGET_FILE:iq_avx2_parity> --map $<TARGET_FILE_DIR:iq_avx2_parity>/iq_avx2_parity.map)
else()
# the library, not the binary: a Linux source build compiles ggml-cpu for the build machine on purpose
add_test(NAME isa_guard_check COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/check_isa_guard.py
$<TARGET_FILE:strata_kernels_cpu>)
endif()
set_tests_properties(isa_guard_check PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 600)
endif()
endif()
set(STRATA_SDE "" CACHE FILEPATH "Intel SDE (sde / sde64): also run the CPU tests on an emulated AVX2-only and an AVX-VNNI-only CPU")
if(STRATA_SDE AND TARGET iq_avx2_parity)
foreach(_chip hsw adl)
foreach(_t iq_avx2_parity q8k_quant_parity router_dot_parity pool_tasks_test q2_bitplane_parity)
if(TARGET ${_t})
add_test(NAME sde_${_chip}_${_t} COMMAND ${STRATA_SDE} -${_chip} -- $<TARGET_FILE:${_t}>)
set_tests_properties(sde_${_chip}_${_t} PROPERTIES TIMEOUT 900)
endif()
endforeach()
if(TARGET q2_bitplane_parity)
set_tests_properties(sde_${_chip}_q2_bitplane_parity PROPERTIES ENVIRONMENT "STRATA_Q2_BITPLANE=1")
endif()
endforeach()
endif()
if(UNIX AND NOT APPLE)
add_executable(direct_file_async_test tests/platform/direct_file_async.cpp src/platform/direct_file.cpp)
target_include_directories(direct_file_async_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(direct_file_async_test PRIVATE Threads::Threads)
add_test(NAME direct_file_async_test COMMAND direct_file_async_test)
endif()
# --aux-cpus: the CPU-set derivation (pure) and the placement of live threads; header-only, no GPU
add_executable(aux_cpus_test tests/platform/aux_cpus_test.cpp)
target_include_directories(aux_cpus_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(aux_cpus_test PRIVATE Threads::Threads)
add_test(NAME aux_cpus_test COMMAND aux_cpus_test)
endif()
if(STRATA_BUILD_TESTS)
@@ -1441,3 +1651,8 @@ if(STRATA_HIP_GFX906)
endif()
set_source_files_properties(${_strata_cu} PROPERTIES LANGUAGE HIP)
endif()
if(STRATA_HIP_GFX906)
add_executable(gfx906_qsa_query_swizzle_probe bench/gfx906_qsa_query_swizzle_probe.cpp)
target_link_libraries(gfx906_qsa_query_swizzle_probe PRIVATE strata_kernels ${_strata_gpu_runtime_target})
endif()
+5 -3
View File
@@ -24,7 +24,8 @@
#
# Setup choices are env vars, read by docker-entrypoint.sh: FAMILY, MODEL, CONTEXT,
# VISION (no | yes | cpu), KV (int8 | q4_0 | k8v4), GPU (one card) or GPUS ("0,2"
# or "all", with LAYER_SPLIT), LOW_RAM (auto | on | off), HOST, PORT, API_KEY.
# or "all", with LAYER_SPLIT), LOW_RAM (auto | on | off), HOST, PORT, API_KEY,
# GGUF_DIR (GGUF files you already have), RESIDENT_BUDGET_GIB, KV_STREAMING.
#
# Only the model files, the prepared pack, the MTP layer and the install config
# live in the /data volume; the engine is part of the image. Strata loads 32-62 GB
@@ -64,7 +65,8 @@ RUN python3 -m venv .venv \
&& chmod +x setup.sh docker-entrypoint.sh
# llama.cpp at the pinned commit, then the engine and the image encoder, built
# exactly the way setup.py builds them. BUILD.json is what setup.py reads to
# exactly the way setup.py builds them: native code for the CPU that builds the
# image, so build it on the PC it runs on. BUILD.json is what setup.py reads to
# decide whether an engine is current: source=local with a matching src hash
# means the first start reuses it instead of recompiling.
RUN .venv/bin/python - <<'PYEOF'
@@ -82,7 +84,7 @@ setup.cmake_build(setup.ROOT, setup.ROOT / "build", "strata",
f"-DSTRATA_GGML_DIR={llama}"], None, "build-strata.bat")
if vision != "none":
setup.cmake_build(setup.ROOT / "tools" / "vision", setup.ROOT / "build-vision", "strata-vision",
[f"-DLLAMA_DIR={llama}", "-DSTRATA_VISION_CUDA=ON",
[f"-DLLAMA_DIR={llama}", "-DSTRATA_VISION_CUDA=ON", "-DSTRATA_PORTABLE=OFF",
f"-DCMAKE_CUDA_ARCHITECTURES={arch}", f"-DCMAKE_CUDA_COMPILER={nvcc}"], None, "build-vision.bat")
eng = setup.ROOT / "engine"
+115 -115
View File
@@ -3,28 +3,28 @@
[English](README.md) · [简体中文](README.zh-CN.md) · **日本語** · [Deutsch](README.de.md) · [Français](README.fr.md) · [Español](README.es.md) · [Português](README.pt-BR.md)
<p align="center"><b>1,250 億パラメータの AI モデルを、手元のゲーミング PC で動かす</b><br>
NVIDIA または AMD のグラフィックカード(12 GB 以上) · Windows または Linux · 無料のオープンソース</p>
NVIDIA または AMD のグラフィックカード(12 GB 以上) · Windows または Linux · 無料・オープンソース</p>
<p align="center"><a href="https://github.com/Niko1221/Strata/releases/download/v0.1.10/Pagoda.mp4"><img src="docs/media/pagoda-preview.webp" width="720" alt="Strata のモデルが書いたボクセルの五重塔の庭。ブラウザで動いている様子"></a><br>
<sub>ボクセルの五重塔の庭。1 回のプロンプトで作成し、RTX 5070 上の Strata(IQ3_S、128K コンテキスト)で実行 ·
<a href="https://github.com/Niko1221/Strata/releases/download/v0.1.10/Pagoda.mp4">動画全体(49 秒)</a></sub></p>
<p align="center"><a href="https://github.com/Niko1221/Strata/releases/download/v0.1.10/Pagoda.mp4"><img src="docs/media/pagoda-preview.webp" width="720" alt="Strata で生成した、五重塔のある庭。ボクセルで表現され、ブラウザーで動作している"></a><br>
<sub>五重塔のある庭をボクセルで表現。RTX 5070 で動かした Strata(IQ3_S、コンテキスト長 128K)に、1 回のプロンプトで作成を依頼 ·
<a href="https://github.com/Niko1221/Strata/releases/download/v0.1.10/Pagoda.mp4">動画を見る(全編 49 秒)</a></sub></p>
Strata は **[Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next)** を普通の PC で動かします。
これは大きくて賢い AI モデルで、ふつうはサーバーが必要です。チャットをしたり、コードを書いたり、画像を読んだりできます。
アプリやコーディングエージェントとも連携します。データが PC の外に出ることはありません。
Strata を使うと、**[Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next)** を一般的な PC で動かせます。
通常はサーバーを必要とする大規模で高性能な AI モデルで、チャットやコードの生成、画像の読み取りができます。
アプリやコーディングエージェントとも連携でき、データは PC の外に送信されません。
## どのくらい速い?
ごく普通のゲーミング PC 2 台で測りました。1 トークンは英単語のおよそ ¾ です。
一般的なゲーミング PC 2 台で速度を測定しました。英語の場合、1 トークンはおよそ ¾ 語に相当します。
- **回答の書き出し:** 短いチャットで返答が表示される速さです。毎秒 60 トークンあれば、読むより速く表示されます。
- **プロンプトの読み込み:** 送った内容を取り込む速さです(ここでは 32K トークンの文書、コード、またはチャット履歴)。
- **生成速度:** 短いチャットで回答を生成する速度です。毎秒 60 トークンなら、人が読むよりも速く文章が表示されます。
- **プロンプトの読み込み:** 入力した内容を読み込む速度です。ここでは、文書やコード、チャット履歴など 32K トークン分を読み込ませています。
<table>
<tr><th>NVIDIA: RTX 5070 (12 GB), Ryzen 5 7600, 64 GB RAM</th><th>AMD: RX 9070 XT (16 GB), Ryzen 9 3900X, 47 GB RAM</th></tr>
<tr><td>
| サイズ | 回答の書き出し | プロンプトの読み込み |
| モデルサイズ | 生成速度 | プロンプトの読み込み |
| --- | ---: | ---: |
| **Q2_0** | 94 tokens/s | 2,650 tokens/s |
| **IQ2_XS** | 79 tokens/s | 2,090 tokens/s |
@@ -34,7 +34,7 @@ Strata は **[Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next
</td><td>
| サイズ | 回答の書き出し | プロンプトの読み込み |
| モデルサイズ | 生成速度 | プロンプトの読み込み |
| --- | ---: | ---: |
| **Q2_0** | 60 tokens/s | 1,160 tokens/s |
| **IQ2_XS** | 52 tokens/s | 1,110 tokens/s |
@@ -43,169 +43,169 @@ Strata は **[Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next
</td></tr>
</table>
NVIDIA:Q2_0 はエンジン 0.1.36、ほかの行は 0.1.26 で測定(回答 4K、プロンプト 32K)。表の全体は
[DETAILS.md](docs/DETAILS.md#speed-measured) にあります。VRAM が多いカードほど速くなります。RTX 3090(24 GB)なら
毎秒およそ 100-140 トークンで書き出せるはずです。長いチャットやほかのカードについて:[モデルごとの速度](docs/MODELS.md#how-fast-is-each-size)、
[コミュニティの測定結果](docs/COMMUNITY_BENCHMARKS.md)。
NVIDIA の測定には、Q2_0 のみエンジン 0.1.36、それ以外は 0.1.26 を使用しています(回答 4K、プロンプト 32K)。測定結果の詳細は
[DETAILS.md](docs/DETAILS.md#speed-measured) をご覧ください。VRAM の容量が大きいカードほど高速で、RTX 3090(24 GB)なら
毎秒およそ 100-140 トークンの生成速度が見込めます。長いチャットや他のカードでの性能については、[モデルごとの速度](docs/MODELS.md#how-fast-is-each-size)や
[コミュニティの測定結果](docs/COMMUNITY_BENCHMARKS.md)を参照してください。
<p align="center"><a href="https://buymeacoffee.com/strataengine"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50"></a><br>
<sub>Strata は無料です。あなたの PC でうまく動いたら、コーヒー 1 杯の支援が開発を続ける力になります。</sub></p>
<sub>Strata は無料で使えます。お使いの PC で快適に動いたら、コーヒー 1 杯分のご支援が開発を続ける励みになります。</sub></p>
## 必要なもの
| | |
| --- | --- |
| **グラフィックカード** | **NVIDIA** GeForce RTX 20、30、40、50 シリーズ、または **AMD** Radeon RX 7900 XT / XTX、RX 7800 XT / 7700 XT、RX 9060 XT、RX 9070 / 9070 XT、Radeon AI PRO R9700、RX 6800 / 6900 シリーズ。**VRAM 12 GB 以上**が必要です。 |
| **RAM** | 32 GB 以上。RAM の量で[使えるモデル](#どのモデルを選べばいい)が決まります。64 GB あればすべてのサイズが動きます。 |
| **ディスク** | 空き容量が約 80 GB。できれば SSD を使ってください。初回の起動がずっと速くなります。 |
| **OS** | Windows 10 / 11 または Linux。NVIDIA または AMD の最新のグラフィックドライバー。 |
| **RAM** | 32 GB 以上。[利用できるモデル](#どのモデルを選べばいい)は RAM の容量によって異なります。64 GB あれば、どのサイズも利用できます。 |
| **ディスク** | 約 80 GB の空き容量。SSD を使うと初回の起動が大幅に速くなるため、おすすめです。 |
| **OS** | Windows 10 / 11 または Linux。NVIDIA または AMD の最新のグラフィックドライバーも必要です。 |
ほかに必要なものはすべてインストーラーが用意します。2 枚や 3 枚のカードでモデルを分担することもできます([マルチ GPU](docs/MULTI_GPU.md))。
その他の必要なソフトウェアは、インストーラーが用意します。2 枚または 3 枚のカードにモデルを分散させることもできます([マルチ GPU](docs/MULTI_GPU.md))。
試験的なサポート。コミュニティのメンバーが自分のマシンで書き、テストしたものです:
以下のハードウェアにも試験的に対応しています。いずれもコミュニティのメンバーが手元のマシンで実装・テストしたものです。
- **古いグラフィックカード**(Tesla P40 / V100、GTX 10、Radeon VII / MI50、RX 6700 XT、RX 5500 XT):[Older GPUs](docs/OLDER_GPUS.md)。
- **Intel Arc**(Linux でソースからビルド):[Intel Arc](docs/INTEL_ARC.md)。
- **AMD Ryzen AI Max(Strix Halo)**(Linux でソースからビルド):[Strix Halo](docs/STRIX_HALO.md)。
- **AVX2 のない古いプロセッサー**:動きますが、遅いです。[Older CPUs](docs/INSTALL.md#older-cpus-experimental)。
- **旧世代のグラフィックカード**(Tesla P40 / V100、GTX 10、Radeon VII / MI50、RX 6700 XT、RX 5500 XT):[旧世代 GPU の対応状況](docs/OLDER_GPUS.md)。
- **Intel Arc**(Linux でソースからビルド):[Intel Arc の対応状況](docs/INTEL_ARC.md)。
- **AMD Ryzen AI Max(Strix Halo)**(Linux でソースからビルド):[Strix Halo の対応状況](docs/STRIX_HALO.md)。
- **AVX2 非対応の旧世代 CPU**:動作しますが、速度は遅くなります。[旧世代 CPU の対応状況](docs/INSTALL.md#older-cpus-experimental)を参照してください。
全リスト:[docs/INSTALL.md](docs/INSTALL.md#what-you-need)。
必要な環境の一覧は、[docs/INSTALL.md](docs/INSTALL.md#what-you-need)をご覧ください。
## インストール
### AI にセットアップしてもらう
AI コーディングアシスタント(Claude Code、Cursor、Codex、GitHub Copilot など)を使っていますか? これを貼り付けてください:
Claude Code、Cursor、Codex、GitHub Copilot などの AI コーディングアシスタントを使っている場合は、次の文を貼り付けてください。
```text
Set up Strata on this PC for me: https://github.com/Niko1221/Strata - follow docs/AI_SETUP.md in that repository.
```
AI がグラフィックカード、RAM、ディスクを調べて、合うモデルを選びます。それからインストールして起動し、
アプリのつなぎ方を教えてくれます。AI ツールは Strata の [MCP サーバー](docs/MCP_SERVER.md) を通じて、
Strata のインストール、起動、停止もできます。
AI がグラフィックカード、RAM、ディスクを確認して、PC に合ったモデルを選びます。インストールと起動を行い、
アプリとの接続方法も案内します。また、Strata の [MCP サーバー](docs/MCP_SERVER.md) を使えば、
AI ツールから Strata のインストール・起動・停止を行えます。
### 自分でやる
[Strata をダウンロード](https://github.com/Niko1221/Strata/archive/refs/heads/main.zip)して展開します(または `git clone` します)。
**Windows:** **`START-HERE.bat`** をダブルクリック。**Linux:** Strata フォルダーで **`./setup.sh`** を実行します。
[Strata をダウンロード](https://github.com/Niko1221/Strata/archive/refs/heads/main.zip)して展開します(`git clone` でも構いません)。
**Windows:** **`START-HERE.bat`** をダブルクリックします。**Linux:** Strata フォルダーで **`./setup.sh`** を実行します。
手順は NVIDIA でも AMD でも同じです。インストーラーがカードを見つけて、それに合うエンジンを用意します。
いくつか質問があります:
NVIDIA でも AMD でも手順は同じです。インストーラーがカードを検出し、対応するエンジンを用意します。
続いて、次の項目を設定します。
- どのモデルを、どのサイズで使うか
- コンテキストをどのくらいにするか(モデルが覚えておけるテキストの量)
- 画像を読ませるかどうか
- 使用するモデルとサイズ
- コンテキストの長さ(モデルが覚えておけるテキストの量)
- 画像を読み取る機能を使うかどうか
毎回 Enter を押せば、おすすめの答えになります。そのあとモデル(約 70 GB)をダウンロードして起動します。
ダウンロードが止まったら、もう一度実行してください。続きから再開します。ブラウザーで Strata アプリが
`http://127.0.0.1:8080` に開きます。
各項目で Enter を押すと、推奨設定が選ばれます。設定が終わるとモデル(約 70 GB)をダウンロードし、起動します。
ダウンロードが途中で止まった場合は、もう一度実行すると続きから再開できます。起動が完了すると、ブラウザーで
`http://127.0.0.1:8080` が開き、Strata の画面が表示されます。
> **モデルの起動中、1-3 分ほど PC が重くなったり、反応しなくなったりすることがあります**(初回がいちばん長いです)。
> Strata は 35-55 GB を RAM に読み込み、その一部をグラフィックカード用に固定します。これは正常です。
> ウィンドウを閉じずに待ってください。Strata が何をしているかはウィンドウに表示されます。
> **モデルの起動中は、1-3 分ほど PC の動作が遅くなったり、応答しなくなったりすることがあります**(初回は特に時間がかかります)。
> Strata が 35-55 GB のデータを RAM に読み込み、その一部をグラフィックカード用に固定(ピン留め)しているためです。
> 正常な動作なので、ウィンドウに表示される処理状況を確認しながら、閉じずにお待ちください。
**次回からは** `START-HERE.bat`(または `./setup.sh`)をもう一度実行します。すぐに起動し、同じものを 2 回ダウンロードすることはありません。
モデルを止めるときはウィンドウを閉じます。`UPDATE.bat`(`./update.sh`)を使うと、Strata を起動せずに更新できます。
更新、Docker、複数のカード、ファイルの保存場所、すべてのオプションについて:[docs/INSTALL.md](docs/INSTALL.md)。
**次回からは** `START-HERE.bat`(または `./setup.sh`)を実行するだけですぐに起動できます。2 回目以降は、ダウンロード済みのファイルを再度取得する必要はありません。
モデルを停止するにはウィンドウを閉じてください。`UPDATE.bat`(`./update.sh`)を使うと、Strata を起動せずに更新できます。
更新方法や Docker、複数のカードの使い方、ファイルの保存先、各種オプションについては、[docs/INSTALL.md](docs/INSTALL.md)をご覧ください。
## どのモデルを選べばいい
インストーラーが RAM に合わせてひとつ勧めます。同じモデルにも、圧縮の強さが違う複数のサイズがあります。
小さいサイズほど速く、大きいサイズほど少し賢くなります。
インストーラーが RAM の容量に合ったモデルをおすすめします。同じモデルでも、圧縮率によって複数のサイズが用意されています。
サイズが小さいほど高速に動作し、大きいほど回答の品質が少し高くなります。
| RAM | 選ぶもの | 理由 |
| RAM | おすすめのモデル | 選ぶ理由 |
| --- | --- | --- |
| **32 GB** | **Coder** | 32 GB に収まり、コード向けに作られている(24 GB のカードなら Q2_0 と IQ2_XS も動く) |
| **48 GB** | **IQ2_XS**(または最速の Q2_0) | 大きいサイズは収まらない |
| **64 GB** | **IQ2_XS**(おすすめ)、または IQ3_XXS / IQ3_S | すべてのサイズが収まる。IQ3_S がいちばん賢く、いちばん遅い |
| **96 GB 以上** | **IQ3_S**、または Unsloth の UD-IQ4_XS(約 4-bit) | ほかのものを全部開いたままでも、最大のサイズが入る余裕がある |
| **32 GB** | **Coder** | 32 GB で動作するコーディング向けモデル(24 GB のカードがあれば Q2_0 と IQ2_XS も動作) |
| **48 GB** | **IQ2_XS**(速度優先なら Q2_0) | これより大きいサイズはメモリに収まらない |
| **64 GB** | **IQ2_XS**(おすすめ)、または IQ3_XXS / IQ3_S | どのサイズも利用可能。IQ3_S は回答の品質が最も高い一方、速度は最も遅い |
| **96 GB 以上** | **IQ3_S**、または Unsloth の UD-IQ4_XS(約 4-bit) | 他のアプリを開いたままでも、最大サイズのモデルを動かせるだけの余裕がある |
- **[Coder](docs/MODELS.md#coder):** エキスパートを半分取り除いたコーディング向け版です。フルモデルの SWE-bench Verified スコアの
91% に届き(作者による測定)、32 GB の RAM に収まります。コード以外では弱く、中国語などの CJK のテキストも苦手です(#438)。
そうした用途には、すべてのエキスパートを残している Q2_0、IQ2_XS、IQ3_S を選んでください。
- **[Swift 1.5](docs/MODELS.md#swift-15):** 答える前に考える時間がずっと短いファインチューン版です。
ほぼ同じ品質のまま、答えが早く返ってきます。
- **[Unsloth UD-IQ4_XS](docs/MODELS.md#unsloth-ud-iq4_xs):** Unsloth の約 4-bit 版で、品質は IQ3_S と UD-Q4_K_XL の間です。
ダウンロードは 94 GB。RAM が約 80 GB 未満だと、答えている間に Strata がその一部を SSD から読むので遅くなります(NVMe SSD が有効です)。
- **[Unsloth UD-Q4_K_XL](docs/MODELS.md#unsloth-ud-q4_k_xl-experimental)**(試験的):フルモデルにいちばん近いものです。
ただし答えている間、Strata はその大部分を SSD から読むので、64 GB の PC では毎秒 7-8.5 トークンしか書き出せません。
- **[OrcaRouter's Uncensored IQ3_XXS](docs/MODELS.md#orcarouter-uncensored-iq3_xxs):** 手動でセットアップします。
インストーラーのメニューにはありません。
- **[Coder](docs/MODELS.md#coder):** エキスパートを半分に減らしたコーディング向けモデルです。SWE-bench Verified ではフルモデルのスコアの
91% を達成しており(開発者による測定)、32 GB の RAM で動作します。ただし、コード以外の用途では性能が劣り、中国語などの CJK 言語の処理も苦手です(#438)。
こうした用途には、すべてのエキスパートを残した Q2_0、IQ2_XS、IQ3_S を選んでください。
- **[Swift 1.5](docs/MODELS.md#swift-15):** 回答前の思考時間が大幅に短くなるようファインチューニングしたモデルです。
回答の品質をほぼ保ったまま、より短い時間で答えが返ってきます。
- **[Unsloth UD-IQ4_XS](docs/MODELS.md#unsloth-ud-iq4_xs):** Unsloth による約 4-bit のモデルです。回答の品質は IQ3_S と UD-Q4_K_XL の中間です。
ダウンロード容量は 94 GB です。RAM が約 80 GB 未満の場合、回答の生成中にモデルの一部を SSD から読み込むため、速度が落ちます(NVMe SSD を使うと改善します)。
- **[Unsloth UD-Q4_K_XL](docs/MODELS.md#unsloth-ud-q4_k_xl-experimental)**(試験的):回答の品質がフルモデルに最も近いモデルです。
ただし、回答の生成中にモデルの大部分を SSD から読み込むため、RAM が 64 GB の PC では毎秒 7-8.5 トークン程度の速度になります。
- **[OrcaRouter's Uncensored IQ3_XXS](docs/MODELS.md#orcarouter-uncensored-iq3_xxs):** 手動でのセットアップが必要です。
インストーラーのメニューからは選べません。
サイズ、ダウンロード、どれがどこに収まるかについて:[docs/MODELS.md](docs/MODELS.md)。あとで別のモデルを追加するには
`SETUP.bat`(Linux:`./setup.sh --setup`)を実行します。
各サイズのダウンロード情報や必要なメモリ容量は、[docs/MODELS.md](docs/MODELS.md)をご覧ください。後から別のモデルを追加する場合は、
`SETUP.bat`(Linux:`./setup.sh --setup`)を実行してください。
## 使い方
<p align="center"><img src="docs/media/runpagoda.png" width="900" alt="Strata アプリの Monitor タブと、その横で動くコーディングエージェント"><br>
<sub>Strata アプリの <b>Monitor</b>(左)。コーディングエージェントが動画の五重塔の庭を書いているところ(右)</sub></p>
<p align="center"><img src="docs/media/runpagoda.png" width="900" alt="Strata の Monitor タブ(左)と、コードを生成するコーディングエージェント(右)"><br>
<sub>動画に登場する五重塔の庭を作成中の画面。左が Strata の <b>Monitor</b>、右がコードを生成しているコーディングエージェント</sub></p>
- **ブラウザーで:** `http://127.0.0.1:8080` を開きます。**Chat**、モデルと GPU/CPU/RAM の様子をリアルタイムで見る **Monitor**、
設定とアドレスが載った **About** があります。
- **アプリやコーディングエージェントから:** 「OpenAI-compatible」プロバイダーを追加し、ベース URL に
**`http://127.0.0.1:8080/v1`** を指定します。API キーとモデル名は何でもかまいません。
- Anthropic の API を使うアプリ:`http://127.0.0.1:8080/v1/messages`(Claude Code:
`ANTHROPIC_BASE_URL=http://127.0.0.1:8080`)。
- Codex CLI など OpenAI Responses API を使うアプリ:`/v1/responses`
- **ブラウザーで使う:** `http://127.0.0.1:8080` を開きます。チャット用の **Chat**、モデルや GPU/CPU/RAM の状態をリアルタイムで確認できる **Monitor**、
設定や接続先のアドレスを確認できる **About** の各タブがあります。
- **アプリやコーディングエージェントと連携する:** 「OpenAI-compatible」プロバイダーを追加し、ベース URL に
**`http://127.0.0.1:8080/v1`** を指定します。API キーとモデル名には任意の値を指定できます。
- Anthropic API を使うアプリ:`http://127.0.0.1:8080/v1/messages`(Claude Code では
`ANTHROPIC_BASE_URL=http://127.0.0.1:8080` を設定)。
- Codex CLI など、OpenAI Responses API を使うアプリ:`/v1/responses`
([設定方法](docs/DETAILS.md#the-responses-api-and-codex-cli))。
- **思考(Thinking):** チャットのメニュー、またはアプリの「reasoning effort」で **off、low、medium、high** を選びます。
off がいちばん速く、high は難しい質問に向いています。
- **画像:** セットアップで「Images?」に yes と答えます。そのあとチャットで **Picture** をクリックするか、アプリで画像を添付します。
AMD のカードは、Linux ではプロセッサーを使って画像を読みます。Windows ではまだ読めません。
- **スマートフォンや別の PC から:** `START-HERE.bat --setup --host 0.0.0.0 --api-key <secret>`。キーは必ず設定してください。
- **リクエストは 1 つずつ:** 初期設定では Strata は 1 つのリクエストに答え、ほかは待ちます。複数に同時に答えるには
`"parallel": 2` を設定します([BATCHING.md](docs/BATCHING.md))。12 GB のカードでは、それぞれの回答が遅くなります。
- **長いプロンプト:** Strata はチャットの最初のメッセージをすべて読みます。30,000 トークンあたり約 1 分かかります。
2 通目以降のメッセージは数秒で始まります。
- **思考(Thinking)の設定:** チャットのメニュー、または連携先アプリの「reasoning effort」で **off、low、medium、high** から選びます。
off が最も速く、難しい質問には high が向いています。
- **画像を読み取る:** セットアップ時に「Images?」で yes を選びます。その後、チャットの **Picture** ボタンをクリックするか、連携先のアプリで画像を添付してください。
AMD のカードを使う場合、Linux では CPU で画像を読み取ります。Windows ではまだ画像の読み取りに対応していません。
- **スマートフォンや別の PC から使う:** `START-HERE.bat --setup --host 0.0.0.0 --api-key <secret>` で起動します。API キーは必ず設定してください。
- **リクエストは 1 つずつ:** 標準設定では 1 つずつ処理し、それ以外のリクエストは順番を待ちます。同時に処理するには、
`"parallel": 2` を設定してください([BATCHING.md](docs/BATCHING.md))。12 GB のカードでは、リクエストごとの回答速度が落ちます。
- **長いプロンプトを送る:** チャットの最初のメッセージは全文を読み込むため、30,000 トークンあたり約 1 分かかります。
2 通目以降は数秒で応答が始まります。
詳しくは:[チャットの保存場所](docs/INSTALL.md#where-things-are-stored)、[API](docs/DETAILS.md#using-it)。
[チャットの保存場所](docs/INSTALL.md#where-things-are-stored)や[API の詳細](docs/DETAILS.md#using-it)も参照してください。
## うまくいかないとき
- **Strata の初回起動で PC が固まった。** モデルの読み込み中はよくあることです。ウィンドウを閉じずに待ってください。
10 分たっても固まったまま? PC を再起動し、ほかのプログラムを閉じてもう一度試すか、小さいサイズを選んでください。
- **ダウンロードやインストールの途中で止まった。** `START-HERE.bat`(または `./setup.sh`)をもう一度実行してください。
止まったところから続きます。
- **とても遅く、ディスクのランプが点滅し続ける。または「the engine stopped unexpectedly」と表示される。** PC の空き RAM が
足りません。ほかのプログラムを閉じるか(ブラウザーは多く使います)、小さいサイズ(Q2_0 または IQ2_XS)を選んでください。
- **ポート 8080 がすでに使われていると表示される。** Strata はもう動いています。そのウィンドウを探してください。
- **初回起動時に PC が応答しなくなる。** モデルの読み込み中は、一時的に応答しなくなることがあります。ウィンドウを閉じずにお待ちください。
10 分たっても応答しない場合は、PC を再起動して他のアプリを閉じてからやり直すか、モデルのサイズを小さくしてください。
- **ダウンロードやインストールが途中で止まる。** `START-HERE.bat`(または `./setup.sh`)をもう一度実行してください。
中断したところから再開できます。
- **動作が極端に遅く、ディスクのアクセスランプが点滅し続ける。または「the engine stopped unexpectedly」と表示される。** RAM の空き容量が不足しています。
ブラウザーなど、RAM を多く使う他のアプリを閉じるか、小さいモデル(Q2_0 または IQ2_XS)を選んでください。
- **ポート 8080 が使用中と表示される。** Strata がすでに起動しています。起動済みのウィンドウを探してください。
ほかの問題と解決方法:[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)。それでも解決しない場合は
[issue](https://github.com/Niko1221/Strata/issues) を作成し、Strata フォルダーにある `strata-<model>.log` を添付してください。
セキュリティの問題を見つけたら、非公開で報告してください:[SECURITY.md](SECURITY.md)。
その他の問題と対処方法は、[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)をご覧ください。解決しない場合は
[Issue](https://github.com/Niko1221/Strata/issues) を作成し、Strata フォルダー内の `strata-<model>.log` を添付してください。
セキュリティ上の問題は、[SECURITY.md](SECURITY.md)の手順に従って非公開で報告してください。
## しくみ
このようなモデルは、ふつう数百ギガバイトのグラフィックメモリを持つサーバーで動きます。あなたのグラフィックカードは
12-24 GB です。Strata は **PC 全体で作業を分担する** ことで、モデルを収めています。台所を思い浮かべてください。
いつも使う道具は調理台に置き、残りは食料庫にしまっておきます。
この規模のモデルは通常、数百ギガバイトの GPU メモリを搭載したサーバーで動かします。一方、手元のグラフィックカードのメモリは
12-24 GB 程度です。Strata は **PC 全体で処理を分担する** ことで、こうした環境でもモデルを動かせるようにしています。
台所で、よく使うものは調理台に置き、それ以外は食品庫にしまっておくような仕組みです。
<p align="center"><img src="docs/media/how-it-works.svg" width="860" alt="モデルの 24,576 個のエキスパート:よく使うものはグラフィックカードに、すべては RAM に、ルックアップテーブルは SSD に"></p>
<p align="center"><img src="docs/media/how-it-works.svg" width="860" alt="24,576 個のエキスパートのうち、よく使うものをグラフィックカードに配置。全エキスパートは RAM に、ルックアップテーブルは SSD に保持"></p>
- **モデルは 24,576 個の小さな専門家(「エキスパート」)のチームです。** 1 語ごとに必要なのはそのうち 10 個だけです。
- **グラフィックカード** は、よく使われる数千個のエキスパートを持ちます。**RAM** はすべてのエキスパートを持ち、
**プロセッサー** が残りを同時に処理します。**SSD** は大きなルックアップテーブルを持ちます。
- **モデルは「エキスパート」と呼ばれる 24,576 個の小さな処理単位から構成されています。** 1 語の生成に必要なのは、このうち 10 個だけです。
- **グラフィックカード** に、使用頻度の高い数千個のエキスパートを置きます。**RAM** にはすべてのエキスパートを保持し、
**CPU** が残りの処理を並行して担当します。**SSD** には大きなルックアップテーブルを保存します。
<p align="center"><img src="docs/media/guess-and-check.svg" width="860" alt="小さなヘルパーが次の単語を予想し、大きなモデルがまとめて確認して正しいものを残す"></p>
<p align="center"><img src="docs/media/guess-and-check.svg" width="860" alt="小さな補助モデルが次の数語を予測し、大きなモデルがまとめて検証。正しい予測だけを採用する"></p>
- **予想してから確認:** 小さなヘルパーが次の数語を予想します。大きなモデルがそれをまとめて確認します。
答えは同じまま、1.6-1.8x 早く返ってきます。
- **長いテキストは大きなまとまりで読みます**(一度に最大 8,192 トークン)。速さは毎秒 1,000 トークン以上です。
- **予測してから検証:** 小さな補助モデルが次の数語を予測し、大きなモデルがまとめて検証します。
回答を変えずに、生成速度を 1.6-1.8 倍に高められます。
- **長いテキストはまとめて読み込みます**(一度に最大 8,192 トークン)。読み込み速度は毎秒 1,000 トークンを超えます。
もっと詳しい説明:[docs/HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md)。各部分とその数値:
[詳細](docs/DETAILS.md#how-it-works)と[論文](docs/paper/Strata-Paper.pdf)。
仕組みについては、[docs/HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md)で詳しく説明しています。各処理の詳細や測定値は、
[技術資料](docs/DETAILS.md#how-it-works)と[論文](docs/paper/Strata-Paper.pdf)をご覧ください。
## クレジットとライセンス
モデルは Qwen チームの [Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next) です。
圧縮は [ISTA-DASLab](https://huggingface.co/ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF)、UkisAI(Swift 1.5)、
Unsloth によるものです。Strata は [llama.cpp / ggml](https://github.com/ggml-org/llama.cpp) の一部を使っています。すべてのクレジット:
[docs/HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md#credits)。Strata は [MIT License](LICENSE) のオープンソースです。一部のパーツと
すべてのモデルには、それぞれ独自のライセンスがあります([どれか](docs/HOW_IT_WORKS.md#license))。
使用しているモデルは、Qwen チームが開発した [Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next) です。
[ISTA-DASLab](https://huggingface.co/ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF)、UkisAI(Swift 1.5)、Unsloth が提供する圧縮版を利用しています。
Strata は [llama.cpp / ggml](https://github.com/ggml-org/llama.cpp) の一部も使用しています。クレジットの一覧は
[docs/HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md#credits)をご覧ください。Strata は [MIT License](LICENSE) で公開されていますが、一部の構成要素と
各モデルには、それぞれ別のライセンスが適用されます([ライセンスの詳細](docs/HOW_IT_WORKS.md#license))。
## Strata を支援する
Strata は無料のオープンソースです。役に立ったら、開発を支援していただけます:
Strata は無料で使えるオープンソースソフトウェアです。役に立ったら、開発へのご支援をお願いします。
<p align="center"><a href="https://buymeacoffee.com/strataengine"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50"></a></p>
+2
View File
@@ -109,6 +109,8 @@ download stops, run it again: it continues where it left off. Your browser opens
its window to stop the model. `UPDATE.bat` (`./update.sh`) updates Strata without starting it. Updating, Docker,
several cards, where the files go and every option: [docs/INSTALL.md](docs/INSTALL.md).
**Is it fast on your PC?** Run `BENCH.bat` (`./bench.sh`): about 5-10 minutes, it measures chat, 4K/16K prompts and an agent-style session with your own settings and writes a report to share ([how](docs/COMMUNITY_BENCHMARKS.md#run-the-benchmark)).
## Which model should I pick?
The installer recommends one for your RAM. The same model comes in several sizes, compressed more or less. Smaller
+51 -6
View File
@@ -16,12 +16,57 @@ rem All of it in one block: cmd reads a .bat file while it runs it, and the git
exit /b 1
)
echo Getting the newest Strata ^(git pull^) ...
git pull --ff-only || (
echo.
echo git pull did not succeed ^(the reason is above^): nothing was updated. Files you changed here can stop it:
echo "git status" lists them.
pause
exit /b 1
git pull --ff-only
if errorlevel 1 (
rem The history was rewritten on 2026-10-06, see #1276: a clone from before has no commit in common with origin/main.
set "REWR="
git merge-base HEAD origin/main >nul 2>nul
if errorlevel 1 set "REWR=1"
git rev-parse --is-shallow-repository 2>nul | findstr /x true >nul && set "REWR="
if defined REWR (
echo.
echo This clone is on the repository's old history ^(cleaned up on 2026-10-06^): it has no commit in common
echo with origin/main, so it cannot be updated by git pull.
set "DIRTY="
for /f "delims=" %%i in ('git status --porcelain --untracked-files^=no') do set "DIRTY=1"
if defined DIRTY (
echo You have local changes to tracked files here, so nothing was touched. To move to the new history
echo yourself ^(your untracked files, models and settings stay where they are^):
echo git branch pre-cleanup-backup
echo git stash push
echo git checkout -B main origin/main
echo then run UPDATE.bat again.
pause
exit /b 1
)
set "BK=pre-cleanup-backup"
git rev-parse --verify -q refs/heads/pre-cleanup-backup >nul 2>nul && set "BK=pre-cleanup-backup-%RANDOM%"
set "BR=main"
for /f "delims=" %%i in ('git symbolic-ref --short -q HEAD 2^>nul') do set "BR=%%i"
call git branch %%BK%% HEAD
if errorlevel 1 (
echo Could not make the backup branch: nothing was updated.
pause
exit /b 1
)
call git checkout -q -B %%BR%% origin/main
if errorlevel 1 (
call git branch -D %%BK%% >nul 2>nul
echo.
echo Could not move to the new history ^(the reason is above^): nothing was updated. By hand:
echo git checkout -B main origin/main
pause
exit /b 1
)
call echo Moved to the new history. Your old commits are kept in the branch %%BK%%.
echo Untracked files ^(models, settings, the engine^) were not touched.
) else (
echo.
echo git pull did not succeed ^(the reason is above^): nothing was updated. Files you changed here can stop it:
echo "git status" lists them.
pause
exit /b 1
)
)
) else (
echo This copy of Strata was not made with git, so it cannot fetch new files itself. Download the newest one:
Executable
+10
View File
@@ -0,0 +1,10 @@
#!/bin/sh
# Benchmark this Strata install the way it is used and write a report you can share (docs/COMMUNITY_BENCHMARKS.md).
# It starts the model with your settings (or uses the one already running), takes about 10 minutes and changes nothing.
# Options: --clients 4 (several requests at once), --long (a 32K prompt), --compare <older results folder>.
cd "$(dirname "$0")" || exit 1
if [ -x .venv/bin/python ]; then
exec .venv/bin/python tools/strata_bench.py "$@"
fi
echo "Strata is not set up here yet: run ./setup.sh first."
exit 1
+281
View File
@@ -0,0 +1,281 @@
#!/usr/bin/env python3
"""Strata vs llama.cpp head-to-head (stream 1402-h). Measurement only.
Both engines are driven through their OpenAI-compatible HTTP servers (/v1/chat/completions), with the same prompt
text, same sampler settings and the same GGUF. Server-side timings (llama.cpp's names, which Strata's server also
emits) give prompt tok/s and decode tok/s; time to first token is the server's prompt_ms (non-streaming request,
so TTFT = prompt processing time). Peak VRAM comes from nvidia-smi polling (minus the idle baseline), RAM from the
engine process tree (psutil RSS, which counts mmap'd file pages) and the system's used-memory delta.
python bench_vs_llama.py prompts --out DIR --tokenizer DIR --corpus FILE... # build 4K / 32K prompt files
python bench_vs_llama.py run --machine CFG.json --out DIR --rounds N [--configs a,b] [--cells ...]
python bench_vs_llama.py quality --machine CFG.json --out DIR [--configs a,b]
python bench_vs_llama.py summary --out DIR
CFG.json: {"gguf": ..., "strata_cfg": ..., "llama_server": ..., "configs": [{"name","kind":"llama|strata","args":[...]}]}
Interleaving: engines cannot be loaded together (12 GB card, 64 GB RAM), so one ROUND runs every config once
(start, warm-up, cells, stop), and rounds alternate the config order. Medians are taken over rounds.
The caller holds the machine's GPU lock. Processes are stopped by PID only.
"""
import argparse, json, os, statistics, subprocess, sys, threading, time, urllib.request
from pathlib import Path
import psutil
CELLS = [("4k", 4000, 2), ("32k", 32000, 1)] # (name, prompt tokens, reps per round)
TEMPS = [0.0, 0.7]
GEN = 256
PORT = 8190
def http(url, body=None, timeout=3600):
req = urllib.request.Request(url, data=json.dumps(body).encode() if body is not None else None,
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=timeout) as r:
return json.loads(r.read())
def vram_used_mib():
try:
out = subprocess.run(["nvidia-smi", "--query-gpu=memory.used", "--format=csv,noheader,nounits"],
capture_output=True, text=True, timeout=20).stdout.split()
return int(out[0])
except Exception:
return -1
class Sampler(threading.Thread):
def __init__(self, pid):
super().__init__(daemon=True)
self.pid, self.stop, self.vram, self.rss, self.sysused = pid, False, 0, 0, 0
def run(self):
while not self.stop:
self.vram = max(self.vram, vram_used_mib())
try:
p = psutil.Process(self.pid)
self.rss = max(self.rss, sum(c.memory_info().rss for c in [p] + p.children(recursive=True)))
except psutil.Error:
pass
self.sysused = max(self.sysused, psutil.virtual_memory().used)
time.sleep(1.0)
class Engine:
def __init__(self, machine, cfg):
self.m, self.cfg, self.proc, self.log = machine, cfg, None, None
def command(self):
c = self.cfg
if c["kind"] == "llama":
return [self.m["llama_server"], "-m", self.m["gguf"], "--port", str(PORT), "--host", "127.0.0.1",
"-np", "1", "--no-webui", "--jinja"] + c["args"]
scfg = json.load(open(self.m["strata_cfg"], encoding="utf-8-sig"))
scfg["port"] = PORT
scfg.pop("vision", None)
args = [a for a in scfg["args"]]
for k, v in c.get("override", {}).items(): # replace "--flag value" pairs
if k in args:
args[args.index(k) + 1] = v
else:
args += [k, v]
scfg["args"] = args + c["args"]
if self.m.get("strata_exe"):
scfg["exe"] = self.m["strata_exe"]
p = Path(self.out) / ("strata-cfg-%s.json" % c["name"])
p.write_text(json.dumps(scfg, indent=1))
return [sys.executable, str(Path(self.m["serve"]) / "server.py"), "--engine", "strata", "--config", str(p),
"--port", str(PORT)]
def start(self, out):
self.out = out = str(Path(out).resolve())
self.base_vram, self.base_used = vram_used_mib(), psutil.virtual_memory().used
cmd = self.command()
self.cmdline = " ".join(cmd)
self.log = open(Path(out) / ("server-%s-%d.log" % (self.cfg["name"], int(time.time()))), "w", encoding="utf-8")
env = dict(os.environ, PYTHONIOENCODING="utf-8")
self.proc = subprocess.Popen(cmd, stdout=self.log, stderr=subprocess.STDOUT, env=env,
cwd=self.m.get("cwd"))
self.t0 = time.time()
self.sampler = Sampler(self.proc.pid)
self.sampler.start()
health = "http://127.0.0.1:%d/health" % PORT
while time.time() - self.t0 < 1500:
if self.proc.poll() is not None:
raise RuntimeError("server exited rc=%s (see %s)" % (self.proc.returncode, self.log.name))
try:
r = http(health, timeout=5)
if r.get("status", "ok") == "ok":
break
except Exception:
pass
time.sleep(2)
else:
raise RuntimeError("server not ready in 1500 s")
self.load_s = time.time() - self.t0
def stop(self):
if self.proc:
self.sampler.stop = True
try:
p = psutil.Process(self.proc.pid)
kids = p.children(recursive=True)
for c in kids + [p]:
try:
c.terminate()
except psutil.Error:
pass
psutil.wait_procs(kids + [p], timeout=30)
for c in kids + [p]:
try:
if c.is_running():
c.kill()
except psutil.Error:
pass
except psutil.Error:
pass
self.log.close()
self.proc = None
time.sleep(5)
def chat(self, prompt, temp, max_tokens=GEN, seed=1):
body = {"model": "x", "messages": [{"role": "user", "content": prompt}], "max_tokens": max_tokens,
"temperature": temp, "top_k": 20, "top_p": 0.95, "seed": seed, "stream": False,
"cache_prompt": False, "chat_template_kwargs": {"enable_thinking": False}}
if temp == 0:
body.pop("top_p"), body.pop("top_k")
t = time.time()
r = http("http://127.0.0.1:%d/v1/chat/completions" % PORT, body)
wall = time.time() - t
tm = r.get("timings") or {}
u = r.get("usage") or {}
return {"wall_s": round(wall, 2), "prompt_n": tm.get("prompt_n", u.get("prompt_tokens")),
"prompt_ms": tm.get("prompt_ms"), "prompt_tps": tm.get("prompt_per_second"),
"pred_n": tm.get("predicted_n", u.get("completion_tokens")), "pred_tps": tm.get("predicted_per_second"),
"draft_n": tm.get("draft_n"), "draft_acc": tm.get("draft_n_accepted"),
"text": (r["choices"][0]["message"].get("content") or "")}
def cmd_prompts(a):
sys.path.insert(0, str(Path(a.repo) / "tools"))
import strata_tokenizer as ST
tp = Path(a.tokenizer)
vocab = json.loads((tp / "vocab.json").read_text(encoding="utf-8"))
toks = [None] * len(vocab)
for t, i in vocab.items():
toks[i] = t
tok = ST.Tokenizer(toks, (tp / "merges.txt").read_text(encoding="utf-8").split("\n"),
json.loads((tp / "token_type.json").read_text()))
corpus = "\n\n".join(Path(f).read_text(encoding="utf-8", errors="replace") for f in a.corpus)
print("corpus chars", len(corpus))
out = Path(a.out)
out.mkdir(parents=True, exist_ok=True)
allids = tok.encode(corpus)
print("corpus tokens", len(allids))
for name, n, _ in CELLS:
if len(allids) < n:
raise SystemExit("corpus too small for %s" % name)
(out / ("prompt-%s.txt" % name)).write_text(tok.decode(allids[:n]), encoding="utf-8")
print(name, n, "tokens")
def load_prompt(out, name):
return (Path(out) / ("prompt-%s.txt" % name)).read_text(encoding="utf-8")
def cmd_run(a):
m = json.load(open(a.machine))
out = Path(a.out)
(out / "raw").mkdir(parents=True, exist_ok=True)
cfgs = [c for c in m["configs"] if not a.configs or c["name"] in a.configs.split(",")]
cells = [c for c in CELLS if not a.cells or c[0] in a.cells.split(",")]
prompts = {c[0]: load_prompt(out, c[0]) for c in cells}
for rnd in range(a.start_round, a.start_round + a.rounds):
order = cfgs if rnd % 2 == 0 else cfgs[::-1]
for cfg in order:
eng = Engine(m, cfg)
rec_path = out / "raw" / ("%s.jsonl" % cfg["name"])
try:
eng.start(str(out))
eng.chat("Say hello.", 0.0, 16) # warm-up, discarded
eng.chat(prompts["4k"][:6000] if "4k" in prompts else "hi", 0.0, 32)
for cname, n, reps in cells:
for t in TEMPS:
for k in range(reps):
nonce = "[bench %s r%d %s t%.1f k%d %d]\n" % (cfg["name"], rnd, cname, t, k, time.time())
r = eng.chat(nonce + prompts[cname] + "\n\nSummarize the text above in three sentences.", t)
r.update(config=cfg["name"], round=rnd, cell=cname, temp=t, rep=k,
vram_peak_mib=eng.sampler.vram, vram_base_mib=eng.base_vram,
rss_peak_gib=round(eng.sampler.rss / 2**30, 2),
sys_used_delta_gib=round((eng.sampler.sysused - eng.base_used) / 2**30, 2),
load_s=round(eng.load_s, 1), cmd=eng.cmdline if (rnd == a.start_round and k == 0) else None)
r["text"] = r["text"][:300]
with open(rec_path, "a", encoding="utf-8") as f:
f.write(json.dumps(r) + "\n")
print(cfg["name"], rnd, cname, t, "pp %s tg %s draft %s/%s ttft %s" % (
r["prompt_tps"], r["pred_tps"], r["draft_acc"], r["draft_n"], r["prompt_ms"]), flush=True)
except Exception as e:
print("ERROR", cfg["name"], rnd, repr(e), flush=True)
with open(out / "raw" / "errors.log", "a") as f:
f.write("%s r%d %r\n" % (cfg["name"], rnd, e))
finally:
eng.stop()
QPROMPT = "Write a short story about a lighthouse keeper."
def cmd_quality(a):
m = json.load(open(a.machine))
out = Path(a.out)
(out / "raw").mkdir(parents=True, exist_ok=True)
cfgs = [c for c in m["configs"] if not a.configs or c["name"] in a.configs.split(",")]
res = {}
for cfg in cfgs:
eng = Engine(m, cfg)
try:
eng.start(str(out))
eng.chat("Say hello.", 0.0, 16)
for q in (QPROMPT, "Write a Python function that parses a CSV file into a list of dicts, with tests."):
r = eng.chat(q, 0.0, 200)
res.setdefault(cfg["name"], []).append(r["text"])
finally:
eng.stop()
(out / "raw" / "quality.json").write_text(json.dumps(res, indent=1), encoding="utf-8")
def cmd_summary(a):
out = Path(a.out)
rows = {}
for f in sorted((out / "raw").glob("*.jsonl")):
for line in open(f, encoding="utf-8"):
r = json.loads(line)
rows.setdefault((r["config"], r["cell"], r["temp"]), []).append(r)
med = lambda xs: statistics.median(xs) if xs else float("nan")
print("| config | ctx | temp | n | prompt tok/s | decode tok/s | TTFT s | draft acc | VRAM peak MiB (delta) | RSS GiB |")
print("|---|---|---|---|---|---|---|---|---|---|")
for (c, cell, t), rs in sorted(rows.items()):
pp = [r["prompt_tps"] for r in rs if r["prompt_tps"]]
tg = [r["pred_tps"] for r in rs if r["pred_tps"]]
tt = [r["prompt_ms"] / 1000 for r in rs if r["prompt_ms"]]
da = [r["draft_acc"] / r["draft_n"] for r in rs if r.get("draft_n")]
print("| %s | %s | %.1f | %d | %.1f | %.2f | %.2f | %s | %d (+%d) | %.1f |" % (
c, cell, t, len(rs), med(pp), med(tg), med(tt), ("%.2f" % med(da)) if da else "-",
max(r["vram_peak_mib"] for r in rs), max(r["vram_peak_mib"] - r["vram_base_mib"] for r in rs),
max(r["rss_peak_gib"] for r in rs)))
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("mode", choices=["prompts", "run", "quality", "summary"])
ap.add_argument("--out", required=True)
ap.add_argument("--machine")
ap.add_argument("--configs")
ap.add_argument("--cells")
ap.add_argument("--rounds", type=int, default=1)
ap.add_argument("--start-round", type=int, default=0)
ap.add_argument("--tokenizer")
ap.add_argument("--repo")
ap.add_argument("--corpus", nargs="*")
a = ap.parse_args()
{"prompts": cmd_prompts, "run": cmd_run, "quality": cmd_quality, "summary": cmd_summary}[a.mode](a)
+284
View File
@@ -0,0 +1,284 @@
// Standalone HIP INT8 QSA query-LDS-swizzle component probe.
// Adapted from the cell-probability probe; input generation and full-byte dump are unchanged.
// Build/link in the project with strata_kernels + hip::host (or its HIP runtime target).
// Usage: gfx906_qsa_query_swizzle_probe [nq=32] [ctx=65536] [reps=20] [output.bin] [--masked-pages]
// Output path may instead be supplied by STRATA_QSA_PROBE_OUT. It is required.
// Run candidate environment settings in SEPARATE processes: the dispatcher caches them.
// No prompt-attention entry point, architecture skip, model loading, or repository writes.
#include "strata/kernels/qsa.hpp"
#include "strata/kernels/qsa_decode_attn.hpp"
#include <hip/hip_runtime.h>
#include <algorithm>
#include <cerrno>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <limits>
#include <numeric>
#include <vector>
namespace k = strata::kernels;
namespace {
constexpr int64_t kBatch = 32;
constexpr size_t kGuard = 64;
constexpr uint64_t kSeed = 0x7161736150524f42ull;
[[noreturn]] void fail(const char* why) {
std::fprintf(stderr, "qsa_query_swizzle_probe: %s\n", why);
std::exit(2);
}
void ck(hipError_t e, const char* what) {
if (e != hipSuccess) {
std::fprintf(stderr, "%s: %s\n", what, hipGetErrorString(e));
std::exit(2);
}
}
#define HIP_CHECK(x) ck((x), #x)
// Fixed integer generator and exact binary fractions avoid library-dependent
// normal distributions and never inspect the candidate environment setting.
struct Rng {
uint64_t state = kSeed;
uint32_t next() {
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
return uint32_t((state * 2685821657736338717ull) >> 32);
}
size_t below(size_t n) { return size_t(next()) % n; }
template <class T> void shuffle(std::vector<T>& v) {
for (size_t n = v.size(); n > 1; --n) std::swap(v[n - 1], v[below(n)]);
}
};
template <class T> struct Device {
T* p = nullptr;
explicit Device(size_t n) { HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&p), n * sizeof(T))); }
explicit Device(const std::vector<T>& v) : Device(v.size()) {
HIP_CHECK(hipMemcpy(p, v.data(), v.size() * sizeof(T), hipMemcpyHostToDevice));
}
~Device() { if (p) (void) hipFree(p); }
Device(const Device&) = delete;
Device& operator=(const Device&) = delete;
};
uint64_t hash_bytes(uint64_t hash, const void* ptr, size_t bytes) {
const auto* p = static_cast<const unsigned char*>(ptr);
for (size_t i = 0; i < bytes; ++i) hash = (hash ^ p[i]) * 1099511628211ull;
return hash;
}
template <class T> void hash_vector(uint64_t& hash, const std::vector<T>& v) {
hash = hash_bytes(hash, v.data(), v.size() * sizeof(T));
}
int64_t positive(const char* text) {
errno = 0;
char* end = nullptr;
const long long n = std::strtoll(text, &end, 10);
if (errno || end == text || *end || n <= 0 || n > INT32_MAX)
fail("nq, ctx, and reps must be positive int32 values");
return int64_t(n);
}
const char* env(const char* name) {
const char* value = std::getenv(name);
return value ? value : "<unset>";
}
std::vector<float> read_checked(const Device<float>& storage, size_t count) {
std::vector<float> host(count + 2 * kGuard);
HIP_CHECK(hipMemcpy(host.data(), storage.p, host.size() * sizeof(float), hipMemcpyDeviceToHost));
for (size_t i = 0; i < host.size(); ++i) {
if (i >= kGuard && i < kGuard + count) {
if (!std::isfinite(host[i])) {
std::fprintf(stderr, "nonfinite or unwritten output at float %zu\n", i - kGuard);
fail("output check failed");
}
} else {
uint32_t bits;
std::memcpy(&bits, &host[i], sizeof(bits));
if (bits != UINT32_MAX) fail("output guard changed");
}
}
return std::vector<float>(host.begin() + kGuard, host.begin() + kGuard + count);
}
int run(int64_t nq, int64_t ctx, int reps, const char* output, bool masked) {
if (ctx < nq) fail("ctx must be >= nq (query positions are ctx-nq through ctx-1)");
const char* lane = std::getenv("STRATA_ATTN_LANECELL");
if (lane && lane[0] == '1') fail("unset STRATA_ATTN_LANECELL or set it to 0 for this probe");
static_assert(sizeof(float) == 4 && std::numeric_limits<float>::is_iec559,
"probe requires IEEE binary32 output");
const k::QsaShapes s = k::qsa_real_shapes();
// Match prefill.cpp: use the model's actual 4-cell page layout.
if (s.n_head != 24 || s.n_head_kv != 2 || s.head_dim != 256 || s.page_size != 4)
fail("unexpected model geometry");
const int64_t hd = s.head_dim, nh = s.n_head, nkvh = s.n_head_kv, ps = s.page_size;
const int64_t pages = (ctx + ps - 1) / ps, rows = pages * nkvh * ps;
const int64_t cap = k::qsa_selection_width(ctx, s);
const size_t count = size_t(nq) * size_t(nh * hd);
Rng rng;
std::vector<int8_t> kq(size_t(rows * hd)), vq(kq.size());
std::vector<uint16_t> ks(size_t(rows * (hd / 64))), vs(ks.size());
for (auto* v : {&kq, &vq})
for (auto& x : *v) x = int8_t(int(rng.next() % 255) - 127);
// Positive finite binary16 scales, directly encoded: [2^-8, 2^-5).
for (auto* v : {&ks, &vs})
for (auto& x : *v) x = uint16_t(0x1c00u + rng.next() % 0x0c00u);
std::vector<int32_t> table(size_t(pages), 0);
std::iota(table.begin(), table.end(), 0);
rng.shuffle(table);
const auto physical = table;
if (masked)
for (int64_t page = 1; page < pages; page += 3) table[size_t(page)] = -1;
// The decode kernel reads ids[c0+t] only for t<n_here, and returns before
// reading IDs for empty chunks. Unused row slots are nevertheless zeroed;
// a final 64-cell initialized guard also avoids an exact allocation end.
// cap remains the actual row stride; no per-row padding is passed as cap.
std::vector<int32_t> ids(size_t(nq * cap) + kGuard, 0);
std::vector<int32_t> steps(size_t(nq * k::kStepCount), 0);
std::vector<float> query(count);
for (auto& x : query) x = float(int(rng.next() % 4096) - 2048) * (1.0f / 1024.0f);
std::vector<int32_t> old_cells;
for (int64_t i = 0; i < nq; ++i) {
const int64_t pos = ctx - nq + i, nkv = pos + 1;
const int64_t width = k::qsa_selection_width(nkv, s);
k::qsa_step_fill(steps.data() + i * k::kStepCount, pos, s);
int32_t* sel = ids.data() + i * cap;
if (width == nkv) {
std::iota(sel, sel + width, 0);
} else {
// Same selection pattern as qsa_prompt_attn_parity: a recent
// 512-cell window plus a sorted older set drifting ~3% per query.
constexpr int64_t recent = 512;
const int64_t older = width - recent;
if (int64_t(old_cells.size()) != older) {
std::vector<int32_t> all(size_t(nkv - recent), 0);
std::iota(all.begin(), all.end(), 0);
rng.shuffle(all);
old_cells.assign(all.begin(), all.begin() + older);
} else {
for (int64_t r = 0; r < older / 32; ++r) {
const int32_t cell = int32_t(rng.below(size_t(nkv - recent)));
if (std::find(old_cells.begin(), old_cells.end(), cell) == old_cells.end())
old_cells[rng.below(size_t(older))] = cell;
}
}
std::copy(old_cells.begin(), old_cells.end(), sel);
std::iota(sel + older, sel + width, int32_t(nkv - recent));
std::sort(sel, sel + width);
}
bool valid_page = false;
for (int64_t c = 0; c < width; ++c) {
if (sel[c] < 0 || sel[c] >= nkv || (c && sel[c - 1] >= sel[c]))
fail("selection must be unique, sorted, and causal");
valid_page |= table[size_t(sel[c] / ps)] >= 0;
}
// Restore a selected logical page if necessary, never allow an
// all-masked query to make a vacuous zero-output equality check pass.
if (!valid_page) {
const size_t page = size_t(sel[0] / ps);
table[page] = physical[page];
}
}
size_t masked_selected = 0;
for (int64_t i = 0; i < nq; ++i)
for (int32_t c = 0; c < steps[size_t(i * k::kStepCount + k::kStepWidth)]; ++c)
masked_selected += table[size_t(ids[size_t(i * cap + c)] / ps)] < 0;
uint64_t input_hash = 14695981039346656037ull;
hash_vector(input_hash, kq); hash_vector(input_hash, vq);
hash_vector(input_hash, ks); hash_vector(input_hash, vs);
hash_vector(input_hash, table); hash_vector(input_hash, ids);
hash_vector(input_hash, steps); hash_vector(input_hash, query);
Device<int8_t> dk(kq), dv(vq);
Device<uint16_t> dks(ks), dvs(vs);
Device<int32_t> dtable(table), dids(ids), dsteps(steps);
Device<float> dq(query), dout(count + 2 * kGuard);
const uint64_t stride = k::qsa_decode_attn_scratch_floats(cap, s);
Device<float> scratch(size_t(std::min(kBatch, nq)) * size_t(stride));
k::QsaAttnPools pools;
pools.k_q = dk.p; pools.v_q = dv.p; pools.k_scale = dks.p; pools.v_scale = dvs.p;
pools.page_table = dtable.p;
HIP_CHECK(hipMemset(dout.p, 0xff, (count + 2 * kGuard) * sizeof(float)));
HIP_CHECK(hipMemset(scratch.p, 0xff, size_t(std::min(kBatch, nq)) * size_t(stride) * sizeof(float)));
auto launch = [&] {
for (int64_t t = 0; t < nq; t += kBatch)
k::qsa_decode_attn_batch(dq.p + t * nh * hd, pools, dids.p + t * cap,
dsteps.p + t * k::kStepCount, cap, s, scratch.p, dout.p + kGuard + t * nh * hd,
std::min(kBatch, nq - t), nullptr);
HIP_CHECK(hipGetLastError());
};
launch();
HIP_CHECK(hipDeviceSynchronize());
const auto first = read_checked(dout, count);
constexpr int warmups = 3;
for (int i = 0; i < warmups; ++i) launch();
HIP_CHECK(hipDeviceSynchronize());
hipEvent_t begin = nullptr, end = nullptr;
HIP_CHECK(hipEventCreate(&begin)); HIP_CHECK(hipEventCreate(&end));
std::vector<float> times(size_t(reps), 0.0f);
for (int i = 0; i < reps; ++i) {
// Excluded from event duration; the last timed invocation must also
// overwrite every logical output element instead of inheriting it.
HIP_CHECK(hipMemsetAsync(dout.p + kGuard, 0xff, count * sizeof(float), nullptr));
HIP_CHECK(hipEventRecord(begin, nullptr));
launch();
HIP_CHECK(hipEventRecord(end, nullptr));
HIP_CHECK(hipEventSynchronize(end));
HIP_CHECK(hipEventElapsedTime(&times[size_t(i)], begin, end));
}
const auto result = read_checked(dout, count);
if (std::memcmp(first.data(), result.data(), count * sizeof(float)) != 0)
fail("first and final invocation differ byte-for-byte within this process");
const uint64_t output_hash = hash_bytes(14695981039346656037ull, result.data(), count * sizeof(float));
std::FILE* file = std::fopen(output, "wb");
if (!file) fail("cannot open output file");
const size_t written = std::fwrite(result.data(), sizeof(float), count, file);
const int close_status = std::fclose(file);
if (written != count || close_status != 0) fail("cannot write complete output file");
std::sort(times.begin(), times.end());
const float median = reps % 2 ? times[size_t(reps / 2)]
: 0.5f * (times[size_t(reps / 2 - 1)] + times[size_t(reps / 2)]);
int device = 0;
hipDeviceProp_t prop{};
HIP_CHECK(hipGetDevice(&device)); HIP_CHECK(hipGetDeviceProperties(&prop, device));
std::printf("device=%s arch=%s nq=%lld ctx=%lld cap=%lld page_size=%lld batch=%lld reps=%d warmups=%d\n",
prop.name, prop.gcnArchName, (long long)nq, (long long)ctx, (long long)cap,
(long long)ps, (long long)kBatch, reps, warmups);
std::printf("QUERY_SWIZZLE=%s LANECELL=%s seed=%016llx masked_pages=%zu masked_selected=%zu\n",
env("STRATA_GFX906_ATTN_QUERY_SWIZZLE"), env("STRATA_ATTN_LANECELL"), (unsigned long long)kSeed,
size_t(std::count(table.begin(), table.end(), -1)), masked_selected);
std::printf("INPUT_FNV1A64=%016llx OUTPUT_FNV1A64=%016llx output_floats=%zu output_bytes=%zu\n",
(unsigned long long)input_hash, (unsigned long long)output_hash, count, count * sizeof(float));
std::printf("PASS finite_written_guards_and_repeat median_ms=%.6f min_ms=%.6f max_ms=%.6f us_per_query=%.6f output=%s\n",
median, times.front(), times.back(), double(median) * 1000.0 / double(nq), output);
HIP_CHECK(hipEventDestroy(begin)); HIP_CHECK(hipEventDestroy(end));
return 0;
}
} // namespace
int main(int argc, char** argv) {
try {
std::vector<const char*> args;
bool masked = false;
for (int i = 1; i < argc; ++i) {
if (std::strcmp(argv[i], "--masked-pages") == 0) masked = true;
else args.push_back(argv[i]);
}
if (args.size() > 4) fail("usage: probe [nq] [ctx] [reps] [output.bin] [--masked-pages]");
const int64_t nq = args.size() > 0 ? positive(args[0]) : 32;
const int64_t ctx = args.size() > 1 ? positive(args[1]) : 65536;
const int reps = args.size() > 2 ? int(positive(args[2])) : 20;
const char* output = args.size() > 3 ? args[3] : std::getenv("STRATA_QSA_PROBE_OUT");
if (!output || !*output) fail("pass output.bin as fourth positional argument or set STRATA_QSA_PROBE_OUT");
return run(nq, ctx, reps, output, masked);
} catch (const std::exception& e) {
std::fprintf(stderr, "qsa_query_swizzle_probe: %s\n", e.what());
return 2;
}
}
+47
View File
@@ -0,0 +1,47 @@
[
{"d":"code","p":"Write a Python function that merges two sorted lists into one sorted list without using sort(). Include type hints and a short docstring."},
{"d":"code","p":"Implement an LRU cache in Python using OrderedDict with get and put methods, and write three unit tests with pytest."},
{"d":"code","p":"Write a JavaScript function debounce(fn, wait) and explain how it differs from throttle, with a small usage example."},
{"d":"code","p":"Write a C++ class template for a fixed-capacity ring buffer with push, pop and size, using std::array. Mention thread-safety."},
{"d":"code","p":"Show a Rust function that reads a file line by line, counts word frequencies in a HashMap and prints the top 5 words."},
{"d":"code","p":"Write a Go HTTP handler that returns JSON {\"status\":\"ok\"} and a middleware that logs the request duration."},
{"d":"code","p":"Write a SQL query that returns the top 3 customers by total order value per country from tables customers(id,country) and orders(id,customer_id,amount)."},
{"d":"code","p":"Write a bash script that finds all files larger than 100 MB under /var/log, sorted by size, and prints them in human readable form."},
{"d":"code","p":"Explain this Python error and how to fix it:\nTraceback (most recent call last):\n File \"app.py\", line 12, in <module>\n print(data['users'][0]['name'])\nKeyError: 'users'"},
{"d":"code","p":"Refactor this function to be more readable:\ndef f(a):\n r=[]\n for i in range(len(a)):\n if a[i]%2==0:\n r.append(a[i]*a[i])\n return r"},
{"d":"code","p":"Write a regular expression that matches ISO 8601 dates like 2026-10-11 and explain each part."},
{"d":"code","p":"Write a TypeScript interface for a paginated API response and a generic fetch helper that validates the shape at runtime."},
{"d":"math","p":"A train leaves a station at 9:15 and travels at 80 km/h. A second train leaves the same station at 10:00 on the same track at 120 km/h. At what time does the second catch the first? Show the steps."},
{"d":"math","p":"Prove that the sum of the first n odd numbers is n squared, using induction."},
{"d":"math","p":"Solve the system: 3x + 2y = 16 and x - y = 2. Then check the solution."},
{"d":"math","p":"What is the probability of getting exactly two heads in five fair coin flips? Explain the formula you use."},
{"d":"reason","p":"Three friends, Ann, Ben and Cara, each own exactly one of a cat, a dog and a parrot. Ann does not own the dog. Ben owns neither the cat nor the parrot. Who owns what? Explain your reasoning."},
{"d":"reason","p":"I have a 3 liter jug and a 5 liter jug and unlimited water. How can I measure exactly 4 liters?"},
{"d":"chat","p":"I have a job interview tomorrow for a junior data analyst role and I am nervous. Can you give me practical advice?"},
{"d":"chat","p":"What are some good ways to learn a new language as an adult with only 30 minutes a day?"},
{"d":"chat","p":"Recommend a weekly meal prep plan for someone who wants high protein, vegetarian meals on a budget."},
{"d":"chat","p":"My laptop gets very hot and the fan is loud when I open many browser tabs. What can I check?"},
{"d":"creative","p":"Write a short story (about 300 words) about a lighthouse keeper who discovers that the light attracts something other than ships."},
{"d":"creative","p":"Write a poem about autumn rain in the style of a haiku sequence, five stanzas."},
{"d":"creative","p":"Describe a bustling night market in a fantasy city, using all five senses."},
{"d":"science","p":"Explain how a transformer neural network processes a sentence, from tokens to the next-token prediction, for a software engineer who has not studied machine learning."},
{"d":"science","p":"Why is the sky blue and sunsets red? Explain Rayleigh scattering without heavy math."},
{"d":"science","p":"Explain how CRISPR-Cas9 edits a gene, and name two ethical concerns."},
{"d":"history","p":"Summarize the causes and consequences of the fall of the Western Roman Empire in a few paragraphs."},
{"d":"history","p":"Compare the economic policies of the New Deal with those of the Reagan era."},
{"d":"law","p":"Explain the difference between a trademark, a copyright and a patent, with one everyday example of each."},
{"d":"business","p":"Draft a polite email to a customer whose order is delayed by two weeks, offering a 10 percent discount and a new delivery date."},
{"d":"business","p":"Write a one-page project proposal outline for migrating a small company's file server to a cloud storage service."},
{"d":"data","p":"Create a markdown table comparing PostgreSQL, MySQL and SQLite on concurrency, typical use cases, licensing and scaling."},
{"d":"data","p":"Return a JSON object describing a fictional book with fields title, author, year, genres (array) and a nested publisher object. Only output JSON."},
{"d":"tool","p":"You are an assistant with a tool get_weather(city, unit). The user asks: 'Do I need an umbrella in Hamburg tomorrow?' Write the tool call you would make as JSON and then explain what you would tell the user once you get the result."},
{"d":"summ","p":"Summarize in three bullet points: Photosynthesis converts light energy into chemical energy. In the light-dependent reactions, chlorophyll absorbs photons and splits water, releasing oxygen and producing ATP and NADPH. In the Calvin cycle, these carriers drive the fixation of carbon dioxide into sugars. The rate of photosynthesis depends on light intensity, carbon dioxide concentration and temperature."},
{"d":"fr","p":"Explique en français comment fonctionne un moteur à combustion à quatre temps, avec des phrases simples."},
{"d":"de","p":"Schreibe eine kurze, freundliche E-Mail auf Deutsch, in der du einen Termin beim Zahnarzt von Dienstag auf Donnerstag verschiebst."},
{"d":"es","p":"Dame una receta sencilla de tortilla española y explica los errores más comunes al hacerla."},
{"d":"zh","p":"请用中文解释什么是区块链,并举一个日常生活中的例子。"},
{"d":"ja","p":"日本語で、初心者向けにPythonのリスト内包表記を説明してください。"},
{"d":"ru","p":"Объясни простыми словами, чем отличается процесс от потока в операционной системе."},
{"d":"trivia","p":"Who wrote 'The Master and Margarita', and what is the novel about? Answer in a short paragraph."},
{"d":"essay","p":"Write a balanced essay of about 350 words on whether remote work is better than office work for software teams."}
]
@@ -29,12 +29,12 @@ def main():
from strata_tokenizer import Tokenizer
from serve.frontend import ChatTemplate, openai_to_messages
directory = args.pack / "tokenizer"
vocab = json.loads((directory / "vocab.json").read_text())
vocab = json.loads((directory / "vocab.json").read_text(encoding="utf-8"))
tokens = [None] * len(vocab)
for token, number in vocab.items():
tokens[number] = token
tokenizer = Tokenizer(tokens, (directory / "merges.txt").read_text().splitlines(),
json.loads((directory / "token_type.json").read_text()))
tokenizer = Tokenizer(tokens, (directory / "merges.txt").read_text(encoding="utf-8").splitlines(),
json.loads((directory / "token_type.json").read_text(encoding="utf-8")))
template = ChatTemplate(directory / "chat_template.jinja")
initial = get(args.url + "/v1/status")
(args.out / "initial-status.json").write_text(json.dumps(initial, indent=2) + "\n")
@@ -0,0 +1,29 @@
# Strata 0.1.31 vs 0.1.30 — RX 7900 XTX (gfx1100), 2026-10-01
Two runs of `tools/hip/bench_prefill.py` against an otherwise idle local server, one per release binary.
Sequential same-config runs roughly an hour apart, **not interleaved** — treat the numbers as indicative of
the throughput difference, not as a tightly controlled A/B.
- Host: Ryzen 7 7700X, RX 7900 XTX 24 GB, ROCm 7.1, gfx1100, model on a rotational drive.
- Pack: `coder-iq1_m` (Qwen3.8-Flash-Next-Coder) — identical for both arms, as are template and settings.
- Candidate binary: the **stock v0.1.31** release build, 17,652,664 B,
sha256 `17f60a01ff553593af813b2ded8b288179ee9285d8a270a10b90f6bb63e5de1d`.
- Source commit for the candidate: `9259cad4cfa3543cd3b8decab5962672b968c649` (tag `v0.1.31`).
- Throughput cap 128 tokens; this is a throughput measurement, **not** a coding-quality benchmark.
## Fresh-prefill trials (fresh > 512 tokens)
| arm | n | prefill tps (min / median / max) | decode tps (min – max) |
|---|---|---|---|
| 0.1.30 (control) | 4 | 225 / 545 / 700 | 16.0 – 34.2 |
| 0.1.31 (candidate) | 4 | 260 / 900 / 994 | 16.7 – 64.7 |
Consistent with the release notes: the warm prefill path and warm decode both improved, with the spread
between trials dominated by first-touch reads from the rotational drive rather than by the engine.
## Files
- `control-0.1.30.json`, `optimized-0.1.31.json` — verbatim `bench_prefill.py` output (all trials,
including warmup and follow-up, with usage and message text).
- `docs/benchmarks/2026-10-01-gfx1100-0.1.31.json` — the curated control/candidate entry in the
`docs/benchmarks/` shape, derived from the two files above by `strata-bench-contribute.py`.
@@ -0,0 +1,263 @@
[
{
"label": "0.1.30",
"kind": "warmup",
"trial": 0,
"wall_s": 1.5735834310180508,
"metrics": {
"prompt_tokens": 15.0,
"reused": 0.0,
"fresh": 15.0,
"prefill_ms": 1161.0,
"prefill_tps": 12.9,
"generated": 2.0,
"decode_ms": 410.0,
"decode_tps": 4.9
},
"usage": {
"prompt_tokens": 15,
"completion_tokens": 2,
"total_tokens": 17,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "READY"
}
},
{
"label": "0.1.30",
"kind": "fresh",
"trial": 1,
"wall_s": 26.722589478013106,
"metrics": {
"prompt_tokens": 4210.0,
"reused": 0.0,
"fresh": 4210.0,
"prefill_ms": 18722.0,
"prefill_tps": 224.9,
"generated": 128.0,
"decode_ms": 7993.0,
"decode_tps": 16.0
},
"usage": {
"prompt_tokens": 4210,
"completion_tokens": 128,
"total_tokens": 4338,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The source code defines a series of 140 exported functions, named `rule0` through `rule139`, each of which accepts a single numeric argument `x` and applies a conditional transformation based on a specific integer constant associated with the function's name. For any given function `ruleN`, if the input `x` is strictly equal to the integer `N`, the function returns `x + (N + 1)`; otherwise, it returns `x - N`. This pattern creates a set of specialized handlers where the \"true\" branch increments the input by one more than the rule's index, while the"
}
},
{
"label": "0.1.30",
"kind": "followup",
"trial": 1,
"wall_s": 9.036391863017343,
"metrics": {
"prompt_tokens": 4451.0,
"reused": 4338.0,
"fresh": 113.0,
"prefill_ms": 1734.0,
"prefill_tps": 65.2,
"generated": 128.0,
"decode_ms": 7293.0,
"decode_tps": 17.6
},
"usage": {
"prompt_tokens": 4451,
"completion_tokens": 128,
"total_tokens": 4579,
"prompt_tokens_details": {
"cached_tokens": 4338
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the strict integer equality check (`x === N`) within each function, which acts as a precise trigger for the \"increment\" branch while defaulting to a \"decrement\" branch for all other values. This creates a highly sensitive behavior where the output depends entirely on whether the input matches the function's specific index; for example, `rule0(0)` returns `1` (the increment path), whereas `rule0(1)` returns `1` (the decrement path), and `rule0(-1)` returns `-1` (the decrement path). The smallest useful regression test should verify this critical"
}
},
{
"label": "0.1.30",
"kind": "fresh",
"trial": 2,
"wall_s": 23.5992965750047,
"metrics": {
"prompt_tokens": 8830.0,
"reused": 0.0,
"fresh": 8830.0,
"prefill_ms": 16290.0,
"prefill_tps": 542.0,
"generated": 128.0,
"decode_ms": 7296.0,
"decode_tps": 17.5
},
"usage": {
"prompt_tokens": 8830,
"completion_tokens": 128,
"total_tokens": 8958,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The source code defines a series of 280 exported functions, named `rule0` through `rule279`, each accepting a single numeric argument `x`. Every function follows an identical logical structure: it checks if the input `x` is strictly equal to the integer value embedded in its function name (denoted as `n`); if this condition is true, the function returns `x + n + 1`, and if the condition is false, it returns `x - n`. Consequently, each function acts as a conditional modifier that either increments the input by one more than its index or decrements it by its"
}
},
{
"label": "0.1.30",
"kind": "followup",
"trial": 2,
"wall_s": 9.576413626025897,
"metrics": {
"prompt_tokens": 9071.0,
"reused": 8957.0,
"fresh": 114.0,
"prefill_ms": 2196.0,
"prefill_tps": 51.9,
"generated": 128.0,
"decode_ms": 7367.0,
"decode_tps": 17.4
},
"usage": {
"prompt_tokens": 9071,
"completion_tokens": 128,
"total_tokens": 9199,
"prompt_tokens_details": {
"cached_tokens": 8957
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the **exact integer equality check** (`x === n`), which acts as a strict gatekeeper for the \"increment\" branch. For any function `ruleN`, the input `x` must be the specific integer `N` to trigger the `x + N + 1` return path; any other value, including `N + 1`, `N - 1`, or non-integers, falls through to the `x - N` return path. This creates a highly sensitive behavior where a single unit change in input flips the output from a large positive jump to a large negative subtraction."
}
},
{
"label": "0.1.30",
"kind": "fresh",
"trial": 3,
"wall_s": 11.473167807038408,
"metrics": {
"prompt_tokens": 4210.0,
"reused": 0.0,
"fresh": 4210.0,
"prefill_ms": 7721.0,
"prefill_tps": 545.2,
"generated": 128.0,
"decode_ms": 3744.0,
"decode_tps": 34.2
},
"usage": {
"prompt_tokens": 4210,
"completion_tokens": 128,
"total_tokens": 4338,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The source code defines a series of 140 exported functions, named `rule0` through `rule139`, each of which accepts a single numeric argument `x` and applies a conditional logic based on a specific integer constant associated with the function's name. For any given function `ruleN`, if the input `x` is strictly equal to the integer `N`, the function returns `x + (N + 1)`; otherwise, it returns `x - N`. This means that for every function, there is exactly one input value that causes an increment by `N + 1`, while all other inputs"
}
},
{
"label": "0.1.30",
"kind": "followup",
"trial": 3,
"wall_s": 6.953374623961281,
"metrics": {
"prompt_tokens": 4451.0,
"reused": 4338.0,
"fresh": 113.0,
"prefill_ms": 867.0,
"prefill_tps": 130.3,
"generated": 128.0,
"decode_ms": 6078.0,
"decode_tps": 21.1
},
"usage": {
"prompt_tokens": 4451,
"completion_tokens": 128,
"total_tokens": 4579,
"prompt_tokens_details": {
"cached_tokens": 4338
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the **exact integer equality check** (`x === N`), which acts as a strict gate for the \"special\" branch (`x + N + 1`) versus the \"default\" branch (`x - N`). This behavior is highly sensitive to type coercion and precision: if `x` is a floating-point number that is mathematically equal to `N` but not strictly identical (e.g., due to floating-point errors), or if `x` is a string like `\"5\"` for `rule5`, the strict equality `===` will fail, causing the function to fall into the default"
}
},
{
"label": "0.1.30",
"kind": "fresh",
"trial": 4,
"wall_s": 16.981091229012236,
"metrics": {
"prompt_tokens": 8830.0,
"reused": 0.0,
"fresh": 8830.0,
"prefill_ms": 12624.0,
"prefill_tps": 699.5,
"generated": 128.0,
"decode_ms": 4343.0,
"decode_tps": 29.5
},
"usage": {
"prompt_tokens": 8830,
"completion_tokens": 128,
"total_tokens": 8958,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The source code defines a series of exported functions named `rule0` through `rule279`, each accepting a single numeric argument `x` and returning a numeric result based on a specific conditional logic. For any given function `ruleN`, the behavior is identical: if the input `x` is strictly equal to the integer `N` (the number embedded in the function name), the function returns `x + N + 1`; otherwise, it returns `x - N`. This pattern creates a set of 280 distinct functions where each function acts as a specialized transformation that increments the input by `N+1"
}
},
{
"label": "0.1.30",
"kind": "followup",
"trial": 4,
"wall_s": 6.207711412978824,
"metrics": {
"prompt_tokens": 9071.0,
"reused": 8823.0,
"fresh": 248.0,
"prefill_ms": 1062.0,
"prefill_tps": 233.6,
"generated": 128.0,
"decode_ms": 5132.0,
"decode_tps": 24.9
},
"usage": {
"prompt_tokens": 9071,
"completion_tokens": 128,
"total_tokens": 9199,
"prompt_tokens_details": {
"cached_tokens": 8823
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the **exact integer equality check** (`x === N`), which acts as the primary switch between the two arithmetic branches. For any function `ruleN`, the behavior changes drastically at the single point where `x` equals `N`. Specifically, if `x === N`, the result is `2N + 1` (since `x + N + 1` = `N + N + 1`), whereas for any other value, the result is `x - N`. This creates a discontinuity in the function's output curve.\n\nThe **smallest useful regression test** should"
}
}
]
@@ -0,0 +1,263 @@
[
{
"label": "0.1.31",
"kind": "warmup",
"trial": 0,
"wall_s": 0.8124384850380011,
"metrics": {
"prompt_tokens": 15.0,
"reused": 0.0,
"fresh": 15.0,
"prefill_ms": 552.0,
"prefill_tps": 27.2,
"generated": 2.0,
"decode_ms": 255.0,
"decode_tps": 7.8
},
"usage": {
"prompt_tokens": 15,
"completion_tokens": 2,
"total_tokens": 17,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "READY"
}
},
{
"label": "0.1.31",
"kind": "fresh",
"trial": 1,
"wall_s": 23.868840647977777,
"metrics": {
"prompt_tokens": 4210.0,
"reused": 0.0,
"fresh": 4210.0,
"prefill_ms": 16195.0,
"prefill_tps": 260.0,
"generated": 128.0,
"decode_ms": 7666.0,
"decode_tps": 16.7
},
"usage": {
"prompt_tokens": 4210,
"completion_tokens": 128,
"total_tokens": 4338,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The source code defines a series of 140 exported functions, named `rule0` through `rule139`, each of which accepts a single numeric argument `x` and applies a conditional transformation based on a specific integer constant associated with the function's name. For any given function `ruleN`, if the input `x` is strictly equal to the integer `N`, the function returns `x + (N + 1)`; otherwise, it returns `x - N`. This pattern creates a set of specialized arithmetic operations where the \"true\" branch adds an incrementally larger value (starting from 1 for `"
}
},
{
"label": "0.1.31",
"kind": "followup",
"trial": 1,
"wall_s": 8.811686859990004,
"metrics": {
"prompt_tokens": 4451.0,
"reused": 4203.0,
"fresh": 248.0,
"prefill_ms": 2051.0,
"prefill_tps": 120.9,
"generated": 128.0,
"decode_ms": 6752.0,
"decode_tps": 19.0
},
"usage": {
"prompt_tokens": 4451,
"completion_tokens": 128,
"total_tokens": 4579,
"prompt_tokens_details": {
"cached_tokens": 4203
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the **exact integer equality check** (`x === N`), which acts as a strict gate for the \"special\" branch. For any function `ruleN`, the behavior changes drastically only when `x` is exactly `N`. For example, in `rule0`, if `x` is `0`, it returns `1` (`0 + 1`); if `x` is `1`, it returns `1` (`1 - 0`). This creates a collision where different inputs can produce the same output, but more importantly, it highlights that the function is **not** a simple"
}
},
{
"label": "0.1.31",
"kind": "fresh",
"trial": 2,
"wall_s": 15.694618315959815,
"metrics": {
"prompt_tokens": 8830.0,
"reused": 0.0,
"fresh": 8830.0,
"prefill_ms": 11265.0,
"prefill_tps": 783.8,
"generated": 128.0,
"decode_ms": 4417.0,
"decode_tps": 29.0
},
"usage": {
"prompt_tokens": 8830,
"completion_tokens": 128,
"total_tokens": 8958,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "This source code defines a collection of 280 exported functions, named `rule0` through `rule279`, each of which accepts a single numeric argument `x` and implements a specific conditional arithmetic transformation based on a unique integer constant associated with its name. For any given function `ruleN`, the logic checks if the input `x` is strictly equal to the integer `N`; if this condition is true, the function returns `x + (N + 1)`, effectively adding the next consecutive integer to the input, whereas if the condition is false, it returns `x - N`, subtracting the function"
}
},
{
"label": "0.1.31",
"kind": "followup",
"trial": 2,
"wall_s": 6.075737391016446,
"metrics": {
"prompt_tokens": 9071.0,
"reused": 8957.0,
"fresh": 114.0,
"prefill_ms": 835.0,
"prefill_tps": 136.5,
"generated": 128.0,
"decode_ms": 5225.0,
"decode_tps": 24.5
},
"usage": {
"prompt_tokens": 9071,
"completion_tokens": 128,
"total_tokens": 9199,
"prompt_tokens_details": {
"cached_tokens": 8957
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the **exact integer equality check** (`x === N`), which acts as a strict gatekeeper for the \"special\" branch (`x + N + 1`). This is critical because it distinguishes the single \"trigger\" value from all other inputs, which fall into the \"default\" branch (`x - N`).\n\n**Key Boundary & Edge Cases:**\n\n1. **Integer Equality (`x === N`):**\n * **Trigger:** When `x` exactly equals the rule's index `N`, the function returns `N + (N + 1) = 2N"
}
},
{
"label": "0.1.31",
"kind": "fresh",
"trial": 3,
"wall_s": 6.66396439098753,
"metrics": {
"prompt_tokens": 4210.0,
"reused": 0.0,
"fresh": 4210.0,
"prefill_ms": 4679.0,
"prefill_tps": 899.7,
"generated": 128.0,
"decode_ms": 1978.0,
"decode_tps": 64.7
},
"usage": {
"prompt_tokens": 4210,
"completion_tokens": 128,
"total_tokens": 4338,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The source code defines a series of 140 exported functions, named `rule0` through `rule139`, each of which accepts a single numeric argument `x` and applies a conditional transformation based on a specific integer constant associated with the function's name. For any given function `ruleN`, if the input `x` is strictly equal to the integer `N`, the function returns `x + (N + 1)`; otherwise, it returns `x - N`. This pattern creates a set of specialized arithmetic operations where the \"true\" branch adds an incrementally larger value (starting from 1 for `"
}
},
{
"label": "0.1.31",
"kind": "followup",
"trial": 3,
"wall_s": 7.167138791992329,
"metrics": {
"prompt_tokens": 4451.0,
"reused": 4203.0,
"fresh": 248.0,
"prefill_ms": 900.0,
"prefill_tps": 275.6,
"generated": 128.0,
"decode_ms": 6259.0,
"decode_tps": 20.5
},
"usage": {
"prompt_tokens": 4451,
"completion_tokens": 128,
"total_tokens": 4579,
"prompt_tokens_details": {
"cached_tokens": 4203
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the strict integer equality check (`x === N`) within each function, which acts as a precise trigger for the \"true\" branch; for example, in `rule0`, the input `0` is the critical boundary where the behavior shifts from the default subtraction (`x - 0`, which is identity) to the special addition (`x + 1`), making `rule0(0)` return `1` while `rule0(1)` returns `1` (via `1 - 0`), creating a collision that highlights the fragility of this design. The smallest useful regression test would"
}
},
{
"label": "0.1.31",
"kind": "fresh",
"trial": 4,
"wall_s": 11.244427776022349,
"metrics": {
"prompt_tokens": 8830.0,
"reused": 0.0,
"fresh": 8830.0,
"prefill_ms": 8888.0,
"prefill_tps": 993.5,
"generated": 128.0,
"decode_ms": 2345.0,
"decode_tps": 54.6
},
"usage": {
"prompt_tokens": 8830,
"completion_tokens": 128,
"total_tokens": 8958,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The source code defines a series of 280 exported functions, named `rule0` through `rule279`, each of which accepts a single numeric argument `x` and implements a specific conditional arithmetic operation based on a unique integer constant associated with the function's name. For any given function `ruleN`, the logic checks if the input `x` is strictly equal to the integer `N`; if this condition is true, the function returns `x + (N + 1)`, effectively adding the successor of the rule index to the input, whereas if the condition is false, it returns `x - N`,"
}
},
{
"label": "0.1.31",
"kind": "followup",
"trial": 4,
"wall_s": 4.393163571017794,
"metrics": {
"prompt_tokens": 9071.0,
"reused": 8958.0,
"fresh": 113.0,
"prefill_ms": 721.0,
"prefill_tps": 156.8,
"generated": 128.0,
"decode_ms": 3659.0,
"decode_tps": 35.0
},
"usage": {
"prompt_tokens": 9071,
"completion_tokens": 128,
"total_tokens": 9199,
"prompt_tokens_details": {
"cached_tokens": 8958
}
},
"finish_reason": "length",
"message": {
"role": "assistant",
"content": "The most relevant boundary case is the **exact integer equality check** (`x === N`), which acts as a strict gatekeeper for the \"special\" branch (`x + N + 1`) versus the \"default\" branch (`x - N`). This creates a discontinuity in the function's behavior: for `ruleN`, the output jumps from `N - N = 0` (when `x = N - 1`) to `N + N + 1 = 2N + 1` (when `x = N`), and then back down to `N + 1 - N = 1"
}
}
]
@@ -0,0 +1,87 @@
# Community benchmark: 2x RTX PRO 4500 Blackwell, Swift IQ3_XXS
Measured 2026-10-01 on a Threadripper workstation with two 32 GB Blackwell workstation cards (SM 12.0), engine 0.1.30.
This is **not** the Flash-Next IQ2_XS pack from the first community entry: it is the Swift 1.5 variant at IQ3_XXS,
split over two GPUs. Numbers are not directly comparable to single-GPU entries.
## Hardware
| | |
| --- | --- |
| GPU | 2x NVIDIA RTX PRO 4500 Blackwell, 32 GB GDDR7 each (GB203, 200 W limit), no NVLink |
| PCIe | Gen 5 x16 to each card (read from `nvidia-smi` under load) |
| CPU | AMD Ryzen Threadripper 7960X, 24 cores / 48 threads |
| RAM | 64 GB DDR5 (61 GiB usable) |
| Board | Gigabyte TRX50 AERO D |
| Storage | Model on a 3.7 TB NVMe at PCIe 4.0 x4 (`/mnt/models`). Disk only matters at startup: during generation the server read 23 MiB for 400 tokens (experts are resident in RAM/VRAM, 100% expert cache hits). Cold start was not measured. |
## Software
- Ubuntu 26.04.1 LTS, kernel 7.0.0-31-generic
- NVIDIA driver 595.84, CUDA 13.3 toolkit libraries (`/usr/local/cuda-13.3`)
- Strata engine 0.1.30 (release download, not a git checkout; no commit hash)
- Served through `serve/server.py` on port 8080
## Model
- Repo: `ukisai/Swift-1.5-Qwen3.8-Flash-Next-GSQ-RCO-GGUF`
- Files: `Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf` (39.8 GB) (2-GPU split) and `...-00002-of-00002.gguf` (36.2 GB)
- Pack built by setup as `swift-iq3_xxs`, shipped expert profile, MTP runtime from `Strata-data/mtp/rt`
- Vision is loaded (`--vision`) but no images are sent
## Launch command (engine, as started by `serve/server.py`)
```
engine/strata --serve --pack Strata-data/packs/swift-iq3_xxs \
--native Swift-...-IQ3_XXS-00001-of-00002.gguf --ple-gguf Swift-...-IQ3_XXS-00001-of-00002.gguf \
--expert-profile data/expert-profile.bin --expert-cache auto --prefill auto \
--spec 5 --spec-min-p 0.5 --mtp Strata-data/mtp/rt \
--max-context 262144 --kv k8v4 --vision --vram-reserve-mib 700 --layer-split 25
```
The full serve settings file is `settings.json` in this folder (paths are ours, no credentials).
`--spec 5` is not the default (4). We swept N = 3, 4, 5, 6 first (see below) and kept 5. `--layer-split 25` (the
2-GPU split) and `--kv k8v4` are the values already in our running config; we did not tune them here.
## Method
`bench.py` in this folder, against the running server:
- chat completions, `temperature 0`, `max_tokens 256`, thinking left at the server default (the reasoning text counts
toward the 256 tokens)
- prompt = Strata's own source (2-GPU split) and docs, cut to the target length, plus a "explain and propose three refactorings" task
- a random nonce at the start of every prompt, so no prompt is reused from the cache (`reused` is 0 in `runs.json`)
- one warm-up run per length, then 3 measured runs
- speeds are read from the server log line (`prompt ... read in ... ms`, `... generated in ... ms`)
## Results (median, range of 3 runs)
| Length | Prompt tokens | Prompt tok/s | Output tok/s | Draft accept |
| --- | ---: | ---: | ---: | ---: |
| 1k | 1,023 | 903 (898-908) | 124.0 (122.1-124.5) | 69% |
| 4k | 4,233 | 1,428 (1,428-1,429) | 115.4 (113.8-119.4) | 70% |
| 32k | 30,793 | 2,454 (2,449-2,463) | 120.0 (110.6-120.7) | 68% |
| 128k | 120,605 | 2,785 (2,784-2,794) | 93.0 (90.2-98.4) | 55% |
Needle recall (`tools/needle_bench.py`, 32K (2-GPU split) and 128K at depths 10/50/90): **6 of 6 found** (`needles.json`).
## Draft length sweep (`--spec`)
Same bench idea, one server run per N, 3 runs per cell, output tok/s median:
| `--spec` | short code prompt | 4K | 32K |
| ---: | ---: | ---: | ---: |
| 3 | 114.8 | 112.1 | 117.2 |
| 4 | 122.2 | 110.7 | 118.6 |
| 5 | 132.4 | 113.9 | 121.3 |
| 6 | 116.2 | 112.1 | 121.0 |
The spread between runs is 3-5%, so N = 5 is at most a small gain (about +8% on the short prompt, +2% at 32K).
N = 6 lowers the accepted-draft share (67% against 74-76%). `sweep.py` (2-GPU split) and its raw output are in `spec-sweep/`.
## Limits
- Output speed moves several percent with the generated text; one prompt family only (code explanation)
- Single stream only, no concurrency test
- No comparison run against engine 0.1.32 yet
@@ -0,0 +1,42 @@
#!/usr/bin/env python3
"""Community benchmark: laufender Server (Port 8080), 3 Laeufe je Laenge, 256 Token, greedy, Prompt ohne Cache (Nonce vorn)."""
import json, re, time, random, pathlib, statistics, urllib.request, sys
ROOT = pathlib.Path.home() / "ai/Strata"
OUT = pathlib.Path(sys.argv[1]); OUT.mkdir(parents=True, exist_ok=True)
LOG = ROOT / "strata-swift-iq3_xxs.log"
MODEL = "swift-1.5-iq3_xxs"
files = []
for d, g in [("docs", "*.md"), ("src", "*.cpp"), ("src", "*.cu"), ("include", "*.hpp"), ("serve", "*.py"), ("tools", "*.py"), ("third_party/llama.cpp/docs", "*.md")]:
files += sorted((ROOT / d).rglob(g))
corp = "".join(f"\n### {f.name}\n" + f.read_text(errors="ignore") for f in files)
print("Korpus Zeichen:", len(corp), "Dateien:", len(files))
TASK = "\n\nExplain what this code does and propose three concrete refactorings with code."
def prompt(tok):
return "Here is source code:\n" + corp[: int(tok * 3.2)] + TASK
LENGTHS = {"1k": 1000, "4k": 4000, "32k": 32000, "128k": 125000}
def ask(text):
body = {"model": MODEL, "messages": [{"role": "user", "content": f"[{random.random()}]\n" + text}], "max_tokens": 256, "temperature": 0}
r = urllib.request.Request("http://127.0.0.1:8080/v1/chat/completions", json.dumps(body).encode(), {"Content-Type": "application/json"})
return json.load(urllib.request.urlopen(r, timeout=1800))
pat = re.compile(r"prompt (\d+) tokens = (\d+) reused \+ (\d+) read in (\d+) ms \(([\d.]+) tok/s\), (\d+) generated in (\d+) ms \(([\d.]+) tok/s\), drafts accepted (\d+) of (\d+)")
res = []
ask("Say hi.")
for name, tok in LENGTHS.items():
text = prompt(tok)
ask(text) # Aufwaermung
for i in range(3):
before = LOG.read_text()
ask(text); time.sleep(1)
m = pat.findall(LOG.read_text()[len(before):])
if not m: print("kein Logtreffer", name, i); continue
pt, reused, read, pms, ptps, gen, gms, gtps, acc, tot = m[-1]
res.append(dict(length=name, run=i + 1, prompt_tokens=int(pt), reused=int(reused), prompt_tok_s=float(ptps), generated=int(gen), output_tok_s=float(gtps), drafts_accepted=int(acc), drafts_total=int(tot)))
print(res[-1], flush=True)
json.dump(res, open(OUT / "runs.json", "w"), indent=1)
lines = ["| Length | Prompt tokens | Prompt tok/s (median, range) | Output tok/s (median, range) | Draft accept |", "| --- | ---: | ---: | ---: | ---: |"]
for name in LENGTHS:
r = [x for x in res if x["length"] == name]
if not r: continue
p = [x["prompt_tok_s"] for x in r]; o = [x["output_tok_s"] for x in r]
lines.append(f"| {name} | {r[0]['prompt_tokens']} | {statistics.median(p):.0f} ({min(p):.0f}-{max(p):.0f}) | {statistics.median(o):.1f} ({min(o):.1f}-{max(o):.1f}) | {sum(x['drafts_accepted'] for x in r) / max(1, sum(x['drafts_total'] for x in r)):.0%} |")
open(OUT / "table.md", "w").write("\n".join(lines) + "\n"); print("\n".join(lines))
@@ -0,0 +1,7 @@
32k depth 10%: FOUND (30,839 prompt tokens, 13 s)
32k depth 50%: FOUND (30,839 prompt tokens, 13 s)
32k depth 90%: FOUND (30,840 prompt tokens, 13 s)
128k depth 10%: FOUND (124,021 prompt tokens, 45 s)
128k depth 50%: FOUND (124,019 prompt tokens, 45 s)
128k depth 90%: FOUND (124,019 prompt tokens, 40 s)
6 of 6 found
@@ -0,0 +1,56 @@
[
{
"length": "32k",
"depth": 10,
"prompt_tokens": 30839,
"seconds": 12.9,
"found": true,
"word": "meadow-copper-504",
"answer": "meadow-copper-504"
},
{
"length": "32k",
"depth": 50,
"prompt_tokens": 30839,
"seconds": 12.9,
"found": true,
"word": "falcon-quartz-940",
"answer": "falcon-quartz-940"
},
{
"length": "32k",
"depth": 90,
"prompt_tokens": 30840,
"seconds": 12.9,
"found": true,
"word": "willow-cobalt-696",
"answer": "willow-cobalt-696"
},
{
"length": "128k",
"depth": 10,
"prompt_tokens": 124021,
"seconds": 44.9,
"found": true,
"word": "falcon-saffron-138",
"answer": "falcon-saffron-138"
},
{
"length": "128k",
"depth": 50,
"prompt_tokens": 124019,
"seconds": 45.2,
"found": true,
"word": "quartz-tundra-528",
"answer": "quartz-tundra-528"
},
{
"length": "128k",
"depth": 90,
"prompt_tokens": 124019,
"seconds": 39.8,
"found": true,
"word": "quartz-glacier-192",
"answer": "quartz-glacier-192"
}
]
@@ -0,0 +1,134 @@
[
{
"length": "1k",
"run": 1,
"prompt_tokens": 1023,
"reused": 0,
"prompt_tok_s": 907.9,
"generated": 256,
"output_tok_s": 124.0,
"drafts_accepted": 142,
"drafts_total": 204
},
{
"length": "1k",
"run": 2,
"prompt_tokens": 1023,
"reused": 0,
"prompt_tok_s": 897.8,
"generated": 256,
"output_tok_s": 122.1,
"drafts_accepted": 137,
"drafts_total": 197
},
{
"length": "1k",
"run": 3,
"prompt_tokens": 1023,
"reused": 0,
"prompt_tok_s": 902.6,
"generated": 256,
"output_tok_s": 124.5,
"drafts_accepted": 142,
"drafts_total": 207
},
{
"length": "4k",
"run": 1,
"prompt_tokens": 4233,
"reused": 0,
"prompt_tok_s": 1427.9,
"generated": 256,
"output_tok_s": 115.4,
"drafts_accepted": 126,
"drafts_total": 178
},
{
"length": "4k",
"run": 2,
"prompt_tokens": 4233,
"reused": 0,
"prompt_tok_s": 1428.8,
"generated": 256,
"output_tok_s": 113.8,
"drafts_accepted": 128,
"drafts_total": 197
},
{
"length": "4k",
"run": 3,
"prompt_tokens": 4234,
"reused": 0,
"prompt_tok_s": 1427.9,
"generated": 256,
"output_tok_s": 119.4,
"drafts_accepted": 132,
"drafts_total": 179
},
{
"length": "32k",
"run": 1,
"prompt_tokens": 30793,
"reused": 0,
"prompt_tok_s": 2463.0,
"generated": 256,
"output_tok_s": 120.0,
"drafts_accepted": 151,
"drafts_total": 218
},
{
"length": "32k",
"run": 2,
"prompt_tokens": 30792,
"reused": 0,
"prompt_tok_s": 2453.9,
"generated": 256,
"output_tok_s": 110.6,
"drafts_accepted": 129,
"drafts_total": 181
},
{
"length": "32k",
"run": 3,
"prompt_tokens": 30792,
"reused": 0,
"prompt_tok_s": 2448.7,
"generated": 256,
"output_tok_s": 120.7,
"drafts_accepted": 162,
"drafts_total": 248
},
{
"length": "128k",
"run": 1,
"prompt_tokens": 120605,
"reused": 0,
"prompt_tok_s": 2793.5,
"generated": 256,
"output_tok_s": 90.2,
"drafts_accepted": 116,
"drafts_total": 229
},
{
"length": "128k",
"run": 2,
"prompt_tokens": 120603,
"reused": 0,
"prompt_tok_s": 2785.1,
"generated": 256,
"output_tok_s": 98.4,
"drafts_accepted": 124,
"drafts_total": 195
},
{
"length": "128k",
"run": 3,
"prompt_tokens": 120605,
"reused": 0,
"prompt_tok_s": 2783.9,
"generated": 256,
"output_tok_s": 93.0,
"drafts_accepted": 122,
"drafts_total": 229
}
]
@@ -0,0 +1,53 @@
{
"exe": "/home/qni/ai/Strata/engine/strata",
"args": [
"--pack",
"/home/qni/ai/Strata-data/packs/swift-iq3_xxs",
"--native",
"/mnt/models/models/gguf/Swift-IQ3_XXS/Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"/mnt/models/models/gguf/Swift-IQ3_XXS/Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"/home/qni/ai/Strata/data/expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"5",
"--spec-min-p",
"0.5",
"--mtp",
"/home/qni/ai/Strata-data/mtp/rt",
"--max-context",
"262144",
"--kv",
"k8v4",
"--vision",
"--vram-reserve-mib",
"700"
],
"cwd": "/home/qni/ai/Strata",
"tokenizer": "/home/qni/ai/Strata-data/packs/swift-iq3_xxs/tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "/home/qni/ai/Strata/strata-swift-iq3_xxs.log",
"lib_dirs": [
"/usr/local/cuda-13.3/bin",
"/usr/local/cuda-13.3/lib64"
],
"port": 8080,
"gpu": [
0,
1
],
"gpus_asked": true,
"layer_split": "25",
"vision": {
"exe": "/home/qni/ai/Strata/engine/strata-vision",
"mmproj": "/home/qni/ai/Strata-data/models/mmproj-Swift-Qwen3.8-Flash-Next-BF16.gguf",
"model": "/mnt/models/models/gguf/Swift-IQ3_XXS/Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"gpu": true,
"max_tokens": 1024
},
"host": "0.0.0.0"
}
@@ -0,0 +1,50 @@
3 kurz-code 0 114.8 117/157
3 kurz-code 1 118.6 118/148
3 kurz-code 2 114.0 111/152
3 4k-code 0 110.5 107/142
3 4k-code 1 112.1 113/150
3 4k-code 2 115.1 114/141
3 32k-code 0 113.4 119/145
3 32k-code 1 122.4 134/161
3 32k-code 2 117.2 124/151
5 kurz-code 0 120.9 138/189
5 kurz-code 1 133.5 160/225
5 kurz-code 2 132.4 149/191
5 4k-code 0 113.9 131/197
5 4k-code 1 114.7 122/164
5 4k-code 2 109.5 129/215
5 32k-code 0 123.1 140/165
5 32k-code 1 120.5 144/191
5 32k-code 2 121.3 141/177
6 kurz-code 0 116.2 137/209
6 kurz-code 1 109.3 117/181
6 kurz-code 2 125.7 147/211
6 4k-code 0 112.1 128/200
6 4k-code 1 107.7 115/175
6 4k-code 2 115.8 126/166
6 32k-code 0 121.0 145/191
6 32k-code 1 117.8 135/168
6 32k-code 2 122.6 150/198
4 kurz-code 0 122.2 135/172
4 kurz-code 1 126.7 145/200
4 kurz-code 2 121.0 128/170
4 4k-code 0 106.9 114/171
4 4k-code 1 110.7 119/173
4 4k-code 2 121.4 136/175
4 32k-code 0 118.6 133/164
4 32k-code 1 115.5 129/169
4 32k-code 2 119.8 132/158
spec prompt median min-max accept
3 kurz-code 114.8 114-119 76%
3 4k-code 112.1 110-115 77%
3 32k-code 117.2 113-122 82%
5 kurz-code 132.4 121-134 74%
5 4k-code 113.9 110-115 66%
5 32k-code 121.3 120-123 80%
6 kurz-code 116.2 109-126 67%
6 4k-code 112.1 108-116 68%
6 32k-code 121.0 118-123 77%
4 kurz-code 122.2 121-127 75%
4 4k-code 110.7 107-121 71%
4 32k-code 118.6 116-120 80%
@@ -0,0 +1,362 @@
[
{
"spec": 3,
"prompt": "kurz-code",
"run": 0,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 114.8,
"accepted": 117,
"drafted": 157
},
{
"spec": 3,
"prompt": "kurz-code",
"run": 1,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 118.6,
"accepted": 118,
"drafted": 148
},
{
"spec": 3,
"prompt": "kurz-code",
"run": 2,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 114.0,
"accepted": 111,
"drafted": 152
},
{
"spec": 3,
"prompt": "4k-code",
"run": 0,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 110.5,
"accepted": 107,
"drafted": 142
},
{
"spec": 3,
"prompt": "4k-code",
"run": 1,
"prompt_tokens": 3961,
"generated": 256,
"tok_s": 112.1,
"accepted": 113,
"drafted": 150
},
{
"spec": 3,
"prompt": "4k-code",
"run": 2,
"prompt_tokens": 3959,
"generated": 256,
"tok_s": 115.1,
"accepted": 114,
"drafted": 141
},
{
"spec": 3,
"prompt": "32k-code",
"run": 0,
"prompt_tokens": 29702,
"generated": 256,
"tok_s": 113.4,
"accepted": 119,
"drafted": 145
},
{
"spec": 3,
"prompt": "32k-code",
"run": 1,
"prompt_tokens": 29704,
"generated": 256,
"tok_s": 122.4,
"accepted": 134,
"drafted": 161
},
{
"spec": 3,
"prompt": "32k-code",
"run": 2,
"prompt_tokens": 29702,
"generated": 256,
"tok_s": 117.2,
"accepted": 124,
"drafted": 151
},
{
"spec": 5,
"prompt": "kurz-code",
"run": 0,
"prompt_tokens": 99,
"generated": 256,
"tok_s": 120.9,
"accepted": 138,
"drafted": 189
},
{
"spec": 5,
"prompt": "kurz-code",
"run": 1,
"prompt_tokens": 97,
"generated": 256,
"tok_s": 133.5,
"accepted": 160,
"drafted": 225
},
{
"spec": 5,
"prompt": "kurz-code",
"run": 2,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 132.4,
"accepted": 149,
"drafted": 191
},
{
"spec": 5,
"prompt": "4k-code",
"run": 0,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 113.9,
"accepted": 131,
"drafted": 197
},
{
"spec": 5,
"prompt": "4k-code",
"run": 1,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 114.7,
"accepted": 122,
"drafted": 164
},
{
"spec": 5,
"prompt": "4k-code",
"run": 2,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 109.5,
"accepted": 129,
"drafted": 215
},
{
"spec": 5,
"prompt": "32k-code",
"run": 0,
"prompt_tokens": 29702,
"generated": 256,
"tok_s": 123.1,
"accepted": 140,
"drafted": 165
},
{
"spec": 5,
"prompt": "32k-code",
"run": 1,
"prompt_tokens": 29704,
"generated": 256,
"tok_s": 120.5,
"accepted": 144,
"drafted": 191
},
{
"spec": 5,
"prompt": "32k-code",
"run": 2,
"prompt_tokens": 29701,
"generated": 256,
"tok_s": 121.3,
"accepted": 141,
"drafted": 177
},
{
"spec": 6,
"prompt": "kurz-code",
"run": 0,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 116.2,
"accepted": 137,
"drafted": 209
},
{
"spec": 6,
"prompt": "kurz-code",
"run": 1,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 109.3,
"accepted": 117,
"drafted": 181
},
{
"spec": 6,
"prompt": "kurz-code",
"run": 2,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 125.7,
"accepted": 147,
"drafted": 211
},
{
"spec": 6,
"prompt": "4k-code",
"run": 0,
"prompt_tokens": 3959,
"generated": 256,
"tok_s": 112.1,
"accepted": 128,
"drafted": 200
},
{
"spec": 6,
"prompt": "4k-code",
"run": 1,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 107.7,
"accepted": 115,
"drafted": 175
},
{
"spec": 6,
"prompt": "4k-code",
"run": 2,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 115.8,
"accepted": 126,
"drafted": 166
},
{
"spec": 6,
"prompt": "32k-code",
"run": 0,
"prompt_tokens": 29702,
"generated": 256,
"tok_s": 121.0,
"accepted": 145,
"drafted": 191
},
{
"spec": 6,
"prompt": "32k-code",
"run": 1,
"prompt_tokens": 29701,
"generated": 256,
"tok_s": 117.8,
"accepted": 135,
"drafted": 168
},
{
"spec": 6,
"prompt": "32k-code",
"run": 2,
"prompt_tokens": 29701,
"generated": 256,
"tok_s": 122.6,
"accepted": 150,
"drafted": 198
},
{
"spec": 4,
"prompt": "kurz-code",
"run": 0,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 122.2,
"accepted": 135,
"drafted": 172
},
{
"spec": 4,
"prompt": "kurz-code",
"run": 1,
"prompt_tokens": 98,
"generated": 256,
"tok_s": 126.7,
"accepted": 145,
"drafted": 200
},
{
"spec": 4,
"prompt": "kurz-code",
"run": 2,
"prompt_tokens": 100,
"generated": 256,
"tok_s": 121.0,
"accepted": 128,
"drafted": 170
},
{
"spec": 4,
"prompt": "4k-code",
"run": 0,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 106.9,
"accepted": 114,
"drafted": 171
},
{
"spec": 4,
"prompt": "4k-code",
"run": 1,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 110.7,
"accepted": 119,
"drafted": 173
},
{
"spec": 4,
"prompt": "4k-code",
"run": 2,
"prompt_tokens": 3960,
"generated": 256,
"tok_s": 121.4,
"accepted": 136,
"drafted": 175
},
{
"spec": 4,
"prompt": "32k-code",
"run": 0,
"prompt_tokens": 29702,
"generated": 256,
"tok_s": 118.6,
"accepted": 133,
"drafted": 164
},
{
"spec": 4,
"prompt": "32k-code",
"run": 1,
"prompt_tokens": 29702,
"generated": 256,
"tok_s": 115.5,
"accepted": 129,
"drafted": 169
},
{
"spec": 4,
"prompt": "32k-code",
"run": 2,
"prompt_tokens": 29702,
"generated": 256,
"tok_s": 119.8,
"accepted": 132,
"drafted": 158
}
]
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Spec-Sweep fuer Strata: pro N den Server neu starten, 3 Laeufe je Prompt, 256 Token, greedy.
Liest tok/s aus dem Serverlog ("generated in X ms")."""
import json, os, re, signal, subprocess, sys, time, urllib.request, random, pathlib, statistics
ROOT = pathlib.Path.home() / "ai/Strata"
OUT = pathlib.Path.home() / "messungen/2026-10-01-strata-spec-sweep"
BASE = json.load(open(ROOT / "strata-swift-iq3_xxs.json.bak-20261001-vorsweep"))
NS = [int(x) for x in sys.argv[1].split(",")]
RUNS = 3
def corpus(tokens):
files = sorted((ROOT/"src").rglob("*.cpp")) + sorted((ROOT/"serve").glob("*.py")) + sorted((ROOT/"docs").glob("*.md"))
txt = ""
for f in files:
txt += f"\n### {f.name}\n" + f.read_text(errors="ignore")
if len(txt) > tokens * 3.2: break
return txt[: int(tokens * 3.2)]
PROMPTS = {
"kurz-code": ("Write a Python class LRUCache with get/put, type hints and unit tests using pytest.", 0),
"4k-code": ("Here is source code:\n" + corpus(4000) + "\n\nExplain what this code does and propose three concrete refactorings with code.", 4000),
"32k-code": ("Here is source code:\n" + corpus(32000) + "\n\nExplain what this code does and propose three concrete refactorings with code.", 32000),
}
def stop():
me = os.getpid()
for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGKILL):
for d in os.listdir("/proc"):
if d.isdigit() and int(d) != me:
try: cmd = open(f"/proc/{d}/cmdline").read().replace(chr(0), " ")
except OSError: continue
if "serve/server.py --engine strata" in cmd or "engine/strata" in cmd and "python3 sweep" not in cmd:
try: os.kill(int(d), sig)
except OSError: pass
time.sleep(8)
for _ in range(60):
port = subprocess.run("ss -ltn | grep -q ':8080 '", shell=True).returncode == 0
mem = max(int(x) for x in subprocess.run("nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits", shell=True, capture_output=True, text=True).stdout.split())
if not port and mem < 1000: return
time.sleep(3)
raise SystemExit("Port/GPU wurde nicht frei")
def start(n):
cfg = json.loads(json.dumps(BASE))
a = cfg["args"]; a[a.index("--spec")+1] = str(n)
cfg["log"] = str(OUT / f"server-spec{n}.log")
p = OUT / f"cfg-spec{n}.json"; json.dump(cfg, open(p, "w"), indent=1)
proc = subprocess.Popen(["setsid", str(ROOT/".venv/bin/python"), str(ROOT/"serve/server.py"), "--engine", "strata", "--config", str(p), "--port", "8080", "--open"],
cwd=ROOT, stdout=open(OUT/f"stdout-spec{n}.txt","w"), stderr=subprocess.STDOUT)
for _ in range(300):
time.sleep(3)
if proc.poll() is not None: raise SystemExit(f"Server fuer spec={n} sofort beendet, siehe stdout-spec{n}.txt")
try:
urllib.request.urlopen("http://127.0.0.1:8080/v1/models", timeout=3).read(); return cfg["log"]
except Exception: pass
raise SystemExit(f"Server fuer spec={n} nicht hochgekommen")
def ask(text, nonce):
body = {"model": BASE["model_name"], "messages": [{"role":"user","content": f"[{nonce}]\n"+text}],
"max_tokens": 256, "temperature": 0, "stream": False}
r = urllib.request.Request("http://127.0.0.1:8080/v1/chat/completions", json.dumps(body).encode(), {"Content-Type":"application/json"})
return json.load(urllib.request.urlopen(r, timeout=900))
res = []
for n in NS:
stop(); log = start(n)
for name, (text, _) in PROMPTS.items():
ask(text, f"warm{n}{name}{random.random()}") # Aufwaermung, zaehlt nicht
for i in range(RUNS):
before = open(log).read()
ask(text, f"r{n}{name}{i}{random.random()}")
time.sleep(1)
new = open(log).read()[len(before):]
m = re.findall(r"prompt (\d+) tokens = .*?, (\d+) generated in (\d+) ms \(([\d.]+) tok/s\), drafts accepted (\d+) of (\d+)", new)
if not m: print("kein Logtreffer", n, name, i); continue
pt, gen, ms, tps, acc, tot = m[-1]
res.append(dict(spec=n, prompt=name, run=i, prompt_tokens=int(pt), generated=int(gen), tok_s=float(tps), accepted=int(acc), drafted=int(tot)))
print(n, name, i, tps, f"{acc}/{tot}", flush=True)
json.dump(res, open(OUT/"sweep.json","w"), indent=1)
print("\nspec prompt median min-max accept")
for n in NS:
for name in PROMPTS:
r = [x for x in res if x["spec"]==n and x["prompt"]==name]
if r:
t = [x["tok_s"] for x in r]
print(f"{n:>4} {name:<10} {statistics.median(t):7.1f} {min(t):.0f}-{max(t):.0f} {sum(x['accepted'] for x in r)/max(1,sum(x['drafted'] for x in r)):.0%}")
stop()
@@ -0,0 +1,6 @@
| Length | Prompt tokens | Prompt tok/s (median, range) | Output tok/s (median, range) | Draft accept |
| --- | ---: | ---: | ---: | ---: |
| 1k | 1023 | 903 (898-908) | 124.0 (122.1-124.5) | 69% |
| 4k | 4233 | 1428 (1428-1429) | 115.4 (113.8-119.4) | 70% |
| 32k | 30793 | 2454 (2449-2463) | 120.0 (110.6-120.7) | 68% |
| 128k | 120605 | 2785 (2784-2794) | 93.0 (90.2-98.4) | 55% |
@@ -0,0 +1,14 @@
{
"source": "local",
"version": "0.1.31",
"archs": [
75
],
"vision": "none",
"cuda_dirs": [
"/usr/local/cuda/bin",
"/usr/local/cuda/lib64"
],
"src": "260e57c13c78d103",
"vision_src": null
}
@@ -0,0 +1,119 @@
# Community benchmark: 2x TITAN RTX (sm_75, NVLink), Xeon E5-2696 v4
Two consumer Turing cards, no AVX-512 on the CPU, and the model's native
262,144-token window. Reported per `docs/COMMUNITY_BENCHMARKS.md`: three runs
per configuration, medians and ranges, prompt and decode throughput kept apart,
and the recall check attached.
> A note from the submitter, since numbers alone do not say why this was worth
> the weekend: I did not expect any of this to be possible. The whole point of
> the exercise was to see whether a 125B MoE could run at all on two consumer
> Turing cards, and it does — 60 tok/s of decode with the model's full
> 262,144-token window, on hardware that was never sold for this. Whatever you
> do with the engine, that part is genuinely surprising. Thank you.
## Hardware and software
| | |
| --- | --- |
| GPU | 2x NVIDIA TITAN RTX, 24 GB each (Turing, sm_75), driver 615.71.09 |
| GPU-GPU | `NV2` — 2 bonded NVLinks (`nvidia-smi topo -m`). **Not used**, see below |
| PCIe | x16, **gen 3** under load on both cards (gen 1 at idle), 220 W power limit each |
| CPU | Intel Xeon E5-2696 v4 @ 2.20 GHz, 22 cores / 44 threads, 1 socket, **no AVX-512** |
| RAM | 125 GiB |
| Storage | Samsung MZVLW1T0HMLH NVMe 1 TB (model), SATA SSD also present |
| OS | AlmaLinux 9.8, kernel 5.14.0-687.51.1.el9_8.x86_64 |
| CUDA | 12.9 (nvcc), source build |
| Strata | commit `9259cad`, engine 0.1.31, built from source for `CMAKE_CUDA_ARCHITECTURES=75` |
Build used no non-default options beyond `-DSTRATA_ENABLE_CUDA=ON
-DSTRATA_BUILD_TESTS=OFF -DCMAKE_CUDA_ARCHITECTURES=75`. No prebuilt engine:
the release ships `strata-windows-x64.zip` only.
## Model and configuration
Qwen3.8-Flash-Next **IQ3_S** (3.5 bpw, the recommended quality tier),
`ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF`, two GGUF shards
(54,817,524,224 + 28,800,138,432 bytes), plus the MTP draft layer packed by
`setup.sh` into `Strata-data/mtp/rt` (787 MB).
```
--pack Strata-data/packs/iq3_s --native ...IQ3_S-00001-of-00002.gguf
--ple-gguf ...IQ3_S-00002-of-00002.gguf --expert-profile data/expert-profile.bin
--expert-cache auto --prefill auto --spec 4 --spec-min-p 0.5
--mtp Strata-data/mtp/rt --max-context 262144 --kv int8 --kv-resident 65536
--ple-io ram --layer-split auto
```
`host 0.0.0.0`, `api_key` set (removed here). Calibration was not enabled and
the experimental speed projection was off.
Observed after warm-up: 23,652 / 23,778 MiB VRAM in use, 61 GB RAM in use
plus ~68 GB page cache, of which 27.1 GiB is the n-gram table under
`--ple-io ram` (file-backed, so `free` reports it as cache, not `used`).
## Method and reproduction
`benchmark.py` in this directory. Three runs at each of 4,096 / 32,768 /
128,000 target prompt tokens, 256-token output cap, `temperature=0`, non-
streaming. **Each run sends a distinct prompt** (a `Document revision marker`
differs per run) so no run reuses another one's prefix; a warm expert cache is
shared across runs by design, and the engine log's `expert cache NN% hit` is
recorded per run.
Prompts are a repeated paragraph of MoE-routing prose, so the filler itself
selects a narrow expert set; the recall check below is the correctness
evidence for long context, not these prompts.
Startup (~1 min, reading ~55 GB of experts into RAM) is **not** included in any
timing. Decode throughput comes from the engine's own `timings.predicted_per_second`,
which counts reasoning tokens; TTFT is wall-clock minus generation time.
## Results
Prompt throughput rises with prompt length — the opposite of a degradation
curve, presumably from amortising expert-cache warm-up and larger prefill
chunks.
| Prompt tokens | Prompt t/s (3 runs) | Median | Decode t/s (3 runs) | Median | TTFT (3 runs) |
| --- | --- | --- | --- | --- | --- |
| 4,096 | 840.9 / 865.4 / 864.7 | 864.7 | 67.3 / 79.5 / 68.1 | 68.1 | 3.64 / 3.54 / 3.54 s |
| 32,768 | 1619.9 / 1611.8 / 1603.2 | 1611.8 | 59.5 / 62.6 / 57.9 | 59.5 | 15.32 / 15.39 / 15.48 s |
| 128,000 | 1786.2 / 1782.9 / 1784.6 | 1784.6 | 61.6 / 59.1 / 62.6 | 61.6 | 54.28 / 54.40 / 54.36 s |
Longer replies decode at the same rate as short ones: a separate 2,000-token
run at a 95-token prompt measured 59.7-65.4 t/s with an expert cache hit rate
of 99.0-99.7%, so decode is context-independent here.
Both GPUs work throughout: sampled `utilization.gpu` during decode was
**57% / 42%** with 195 W / 155 W and ~1900 MHz on both. The imbalance is the
auto layer split (one card carries slightly more layers plus the MTP draft).
Idle: both drop to 0% and ~1350 MHz.
## Recall and limitations
`needle_bench.py --lengths 32k,128k --depths 10,50,90`: **6 of 6 found**, no
misses or errors. Actual prompt lengths 30,742 (32k) and 122,707 (128k);
wall-clock 19 s and 67-74 s respectively.
**NVLink is present but unused.** `nvidia-smi topo -m` reports `NV2`
between the two cards, but per `docs/MULTI_GPU.md` the engine deliberately
does not use NVLink or peer-to-peer access: activations cross cards through
pinned RAM once per verify window rather than twice per layer, so the same
numbers should be expected on cards with no bridge. Do not expect a gain from
adding a bridge on consumer boards.
Settings I compared, in case it helps someone choosing:
- `--spec 2` measured 58.3 / 58.5 t/s against `--spec 4`'s 60.1 / 65.4, so
the default depth 4 was kept. Acceptance stayed at 97.5-99.7% hit rate either way.
- `--kv-resident 65536` cost 101 expert slots (10,080 vs 10,181) and gained
~2% prompt throughput (1595.9 vs 1565.2 t/s on a 176,460-token prompt);
decode was unchanged. Kept at 65536.
- `--ple-io ram` measured **the same** as the default `direct` (1596.0 vs
1595.9 t/s on the same prompt) while holding 27.1 GiB more resident. Kept
here only because this machine has RAM to spare; on a smaller machine the
default is the better choice.
Not measured: vision (the ready-made encoder has no sm_75 code, so it needs a
local build and ~1.4 GiB of VRAM), and the low-RAM variant.
@@ -0,0 +1,10 @@
# Trimmed files
Ported from PR #389 for 0.1.40.2. Raw dumps, logs and binaries were left out to keep the repository small;
the README, summaries, configs and per-run results are unchanged. The files below were in the original PR.
1 files, 0.0 MB:
| file | bytes | why |
|---|---:|---|
| `engine.log` | 20,273 | log/raw dump |
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
import json, os, subprocess, time, urllib.request, statistics
# Set STRATA_API_KEY in the environment; nothing is committed.
URL = "http://127.0.0.1:8080/v1/chat/completions"
KEY = os.environ.get("STRATA_API_KEY", "")
OUT = "/mnt/1TB/src/Strata/bench-titan"
os.makedirs(OUT, exist_ok=True)
def pcie():
o = subprocess.run(["nvidia-smi","--query-gpu=index,pcie.link.gen.current,pcie.link.width.current",
"--format=csv,noheader,nounits"],capture_output=True,text=True).stdout.strip()
return o.replace("\n","; ")
def req(prompt, max_tokens=256):
body = json.dumps({"model":"strata","messages":[{"role":"user","content":prompt}],
"max_tokens":max_tokens,"temperature":0,"stream":False}).encode()
r = urllib.request.Request(URL, data=body, headers={"Content-Type":"application/json",
"Authorization":"Bearer "+KEY})
t0=time.time()
with urllib.request.urlopen(r, timeout=900) as f:
d = json.loads(f.read())
wall=time.time()-t0
tm=d.get("timings",{}); u=d.get("usage",{})
return {"prompt_n":tm.get("prompt_n"),"prompt_ms":tm.get("prompt_ms"),
"prompt_tps":tm.get("prompt_per_second"),"gen_n":tm.get("predicted_n"),
"gen_ms":tm.get("predicted_ms"),"gen_tps":tm.get("predicted_per_second"),
"ttft_s":round(wall-(tm.get("predicted_ms",0) or 0)/1000,2),
"total_s":round(wall,2),"usage_prompt":u.get("prompt_tokens"),
"usage_completion":u.get("completion_tokens"),
"finish":d["choices"][0].get("finish_reason")}
BASE = ("Sparse mixture-of-experts routing sends each token through a small subset of "
"feed-forward experts. The gate projects hidden states into per-expert scores, applies a "
"top-k mask and renormalizes the surviving weights. Recurrent delta layers maintain linear "
"state across the sequence while sparse attention indexes only a subset of keys. "
"Quantized weights trade numerical precision for memory bandwidth at every matmul. ")
def make(n_tokens, variant):
s = BASE + (" Document revision marker %d. " % variant)
per = len(s)//4
reps = max(1, n_tokens//per)
return s*reps
results={}
for length in (4096, 32768, 128000):
runs=[]
for v in range(3):
p = make(length, v)
r = req(p)
r["variant"]=v; r["pcie_during"]=pcie()
runs.append(r)
print(length, "run", v+1, "prompt_tps", round(r["prompt_tps"] or 0,1),
"gen_tps", round(r["gen_tps"] or 0,2), "ttft", r["ttft_s"], flush=True)
results[str(length)]=runs
summary={}
for k,v in results.items():
pt=[x["prompt_tps"] for x in v if x["prompt_tps"]]
gt=[x["gen_tps"] for x in v if x["gen_tps"]]
tt=[x["ttft_s"] for x in v]
summary[k]={"prompt_tps":{"runs":[round(x,1) for x in pt],
"median":round(statistics.median(pt),1) if pt else None,
"min":round(min(pt),1) if pt else None,"max":round(max(pt),1) if pt else None},
"decode_tps":{"runs":[round(x,2) for x in gt],
"median":round(statistics.median(gt),2) if gt else None,
"min":round(min(gt),2) if gt else None,"max":round(max(gt),2) if gt else None},
"ttft_s":{"runs":tt,"median":round(statistics.median(tt),2) if tt else None}}
json.dump({"summary":summary,"runs":results}, open(OUT+"/titan-results.json","w"), indent=1)
print("=== SUMMARY ===")
print(json.dumps(summary, indent=1))
@@ -0,0 +1,18 @@
{
"note": "startup snapshot taken after warm-up, with a long context and a warm expert cache",
"process_pid": "2762916",
"process_smaps_rollup": {
"Rss_GiB": 81.32,
"Private_Clean_GiB": 27.08,
"Private_Dirty_GiB": 54.23,
"Locked_GiB": 0.0
},
"gpu_memory_used_MiB": "0, 23652 MiB\n1, 23778 MiB",
"host_ram_GiB": {
"total": "125",
"used": "61",
"buff_cache": "68",
"available": "64"
},
"model_ram_note": "46.84 GiB of experts are anonymous (Private_Dirty); the n-gram table under --ple-io ram is file-backed (Private_Clean)"
}
@@ -0,0 +1,18 @@
{
"repo": "ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF",
"quant": "IQ3_S",
"shards": [
{
"file": "Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"bytes": 54817524224,
"sha256_first_64MiB": "b73591643d79bb3eeea1f8daea8e98ff57400d8aa786e80899a87eee9dc7dfd5"
},
{
"file": "Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"bytes": 28800138432,
"sha256_first_64MiB": "55e48098de7a4b66be53aff004c6e067ca1f6a50751eb5ec049d2302d6914bc8"
}
],
"mtp": "packed from the base checkpoint by setup (tools/mtp_fetch.py, mtp_pack.py) -> Strata-data/mtp/rt",
"pack": "iq_pack.py, every tensor as the GGUF stores it; experts read from the GGUF at start"
}
@@ -0,0 +1,56 @@
[
{
"length": "32k",
"depth": 10,
"prompt_tokens": 30742,
"seconds": 18.7,
"found": true,
"word": "meadow-copper-504",
"answer": "meadow-copper-504"
},
{
"length": "32k",
"depth": 50,
"prompt_tokens": 30742,
"seconds": 18.8,
"found": true,
"word": "falcon-quartz-940",
"answer": "falcon-quartz-940"
},
{
"length": "32k",
"depth": 90,
"prompt_tokens": 30743,
"seconds": 18.8,
"found": true,
"word": "willow-cobalt-696",
"answer": "willow-cobalt-696"
},
{
"length": "128k",
"depth": 10,
"prompt_tokens": 122709,
"seconds": 73.2,
"found": true,
"word": "falcon-saffron-138",
"answer": "falcon-saffron-138"
},
{
"length": "128k",
"depth": 50,
"prompt_tokens": 122707,
"seconds": 73.7,
"found": true,
"word": "quartz-tundra-528",
"answer": "quartz-tundra-528"
},
{
"length": "128k",
"depth": 90,
"prompt_tokens": 122707,
"seconds": 66.8,
"found": true,
"word": "quartz-glacier-192",
"answer": "quartz-glacier-192"
}
]
@@ -0,0 +1,237 @@
{
"summary": {
"4096": {
"prompt_tps": {
"runs": [
840.9,
865.4,
864.7
],
"median": 864.7,
"min": 840.9,
"max": 865.4
},
"decode_tps": {
"runs": [
67.3,
79.5,
68.1
],
"median": 68.1,
"min": 67.3,
"max": 79.5
},
"ttft_s": {
"runs": [
3.64,
3.54,
3.54
],
"median": 3.54
}
},
"32768": {
"prompt_tps": {
"runs": [
1619.9,
1611.8,
1603.2
],
"median": 1611.8,
"min": 1603.2,
"max": 1619.9
},
"decode_tps": {
"runs": [
59.5,
62.6,
57.9
],
"median": 59.5,
"min": 57.9,
"max": 62.6
},
"ttft_s": {
"runs": [
15.32,
15.39,
15.48
],
"median": 15.39
}
},
"128000": {
"prompt_tps": {
"runs": [
1786.2,
1782.9,
1784.6
],
"median": 1784.6,
"min": 1782.9,
"max": 1786.2
},
"decode_tps": {
"runs": [
61.6,
59.1,
62.6
],
"median": 61.6,
"min": 59.1,
"max": 62.6
},
"ttft_s": {
"runs": [
54.28,
54.4,
54.36
],
"median": 54.36
}
}
},
"runs": {
"4096": [
{
"prompt_n": 3040,
"prompt_ms": 3615.2,
"prompt_tps": 840.9,
"gen_n": 256,
"gen_ms": 3801.4,
"gen_tps": 67.3,
"ttft_s": 3.64,
"total_s": 7.45,
"usage_prompt": 3040,
"usage_completion": 256,
"finish": "length",
"variant": 0,
"pcie_during": "0, 3, 16; 1, 3, 16"
},
{
"prompt_n": 3040,
"prompt_ms": 3512.8,
"prompt_tps": 865.4,
"gen_n": 256,
"gen_ms": 3218.7,
"gen_tps": 79.5,
"ttft_s": 3.54,
"total_s": 6.76,
"usage_prompt": 3040,
"usage_completion": 256,
"finish": "length",
"variant": 1,
"pcie_during": "0, 3, 16; 1, 3, 16"
},
{
"prompt_n": 3040,
"prompt_ms": 3515.5,
"prompt_tps": 864.7,
"gen_n": 256,
"gen_ms": 3761.4,
"gen_tps": 68.1,
"ttft_s": 3.54,
"total_s": 7.31,
"usage_prompt": 3040,
"usage_completion": 256,
"finish": "length",
"variant": 2,
"pcie_during": "0, 3, 16; 1, 3, 16"
}
],
"32768": [
{
"prompt_n": 24537,
"prompt_ms": 15147.5,
"prompt_tps": 1619.9,
"gen_n": 256,
"gen_ms": 4306.0,
"gen_tps": 59.5,
"ttft_s": 15.32,
"total_s": 19.63,
"usage_prompt": 24537,
"usage_completion": 256,
"finish": "length",
"variant": 0,
"pcie_during": "0, 3, 16; 1, 3, 16"
},
{
"prompt_n": 24537,
"prompt_ms": 15223.8,
"prompt_tps": 1611.8,
"gen_n": 256,
"gen_ms": 4086.6,
"gen_tps": 62.6,
"ttft_s": 15.39,
"total_s": 19.48,
"usage_prompt": 24537,
"usage_completion": 256,
"finish": "length",
"variant": 1,
"pcie_during": "0, 3, 16; 1, 3, 16"
},
{
"prompt_n": 24537,
"prompt_ms": 15305.4,
"prompt_tps": 1603.2,
"gen_n": 256,
"gen_ms": 4418.2,
"gen_tps": 57.9,
"ttft_s": 15.48,
"total_s": 19.9,
"usage_prompt": 24537,
"usage_completion": 256,
"finish": "length",
"variant": 2,
"pcie_during": "0, 3, 16; 1, 3, 16"
}
],
"128000": [
{
"prompt_n": 95751,
"prompt_ms": 53606.7,
"prompt_tps": 1786.2,
"gen_n": 256,
"gen_ms": 4155.7,
"gen_tps": 61.6,
"ttft_s": 54.28,
"total_s": 58.44,
"usage_prompt": 95751,
"usage_completion": 256,
"finish": "length",
"variant": 0,
"pcie_during": "0, 3, 16; 1, 3, 16"
},
{
"prompt_n": 95751,
"prompt_ms": 53706.4,
"prompt_tps": 1782.9,
"gen_n": 256,
"gen_ms": 4332.9,
"gen_tps": 59.1,
"ttft_s": 54.4,
"total_s": 58.74,
"usage_prompt": 95751,
"usage_completion": 256,
"finish": "length",
"variant": 1,
"pcie_during": "0, 3, 16; 1, 3, 16"
},
{
"prompt_n": 95751,
"prompt_ms": 53654.6,
"prompt_tps": 1784.6,
"gen_n": 256,
"gen_ms": 4087.8,
"gen_tps": 62.6,
"ttft_s": 54.36,
"total_s": 58.45,
"usage_prompt": 95751,
"usage_completion": 256,
"finish": "length",
"variant": 2,
"pcie_during": "0, 3, 16; 1, 3, 16"
}
]
}
}
@@ -0,0 +1,48 @@
{
"exe": "/mnt/1TB/src/Strata/engine/strata",
"args": [
"--pack",
"/mnt/1TB/src/Strata-data/packs/iq3_s",
"--native",
"/mnt/1TB/models/strata-iq3s/IQ3_S/Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"/mnt/1TB/models/strata-iq3s/IQ3_S/Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"/mnt/1TB/src/Strata/data/expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"/mnt/1TB/src/Strata-data/mtp/rt",
"--max-context",
"262144",
"--kv",
"int8",
"--kv-resident",
"65536",
"--ple-io",
"ram"
],
"cwd": "/mnt/1TB/src/Strata",
"tokenizer": "/mnt/1TB/src/Strata-data/packs/iq3_s/tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "/mnt/1TB/src/Strata/strata-iq3_s.log",
"lib_dirs": [
"/usr/local/cuda/bin",
"/usr/local/cuda/lib64"
],
"port": 8080,
"gpu": [
0,
1
],
"gpus_asked": true,
"layer_split": "auto",
"host": "0.0.0.0",
"api_key": "<redacted-for-publication>"
}
@@ -0,0 +1,20 @@
{
"version": "0.1.32",
"source": "release",
"archs": [
75,
86,
89,
120
],
"vision_archs": [
75,
86,
89,
120
],
"ptx": true,
"cuda": "13.0",
"vision": "gpu",
"portable": true
}
@@ -0,0 +1,20 @@
{
"version": "0.1.33",
"source": "release",
"archs": [
75,
86,
89,
120
],
"vision_archs": [
75,
86,
89,
120
],
"ptx": true,
"cuda": "13.0",
"vision": "gpu",
"portable": true
}
@@ -0,0 +1,20 @@
{
"version": "0.1.34",
"source": "release",
"archs": [
75,
86,
89,
120
],
"vision_archs": [
75,
86,
89,
120
],
"ptx": true,
"cuda": "13.0",
"vision": "gpu",
"portable": true
}
@@ -0,0 +1,20 @@
{
"version": "0.1.36",
"source": "release",
"archs": [
75,
86,
89,
120
],
"vision_archs": [
75,
86,
89,
120
],
"ptx": true,
"cuda": "13.0",
"vision": "gpu",
"portable": true
}
@@ -0,0 +1,20 @@
{
"version": "0.1.37",
"source": "release",
"archs": [
75,
86,
89,
120
],
"vision_archs": [
75,
86,
89,
120
],
"ptx": true,
"cuda": "13.0",
"vision": "gpu",
"portable": true
}
@@ -0,0 +1,20 @@
{
"version": "0.1.38",
"source": "release",
"archs": [
75,
86,
89,
120
],
"vision_archs": [
75,
86,
89,
120
],
"ptx": true,
"cuda": "13.0",
"vision": "gpu",
"portable": true
}
@@ -0,0 +1,20 @@
{
"version": "0.1.39",
"source": "release",
"archs": [
75,
86,
89,
120
],
"vision_archs": [
75,
86,
89,
120
],
"ptx": true,
"cuda": "13.0",
"vision": "gpu",
"portable": true
}
@@ -0,0 +1,249 @@
# Community benchmark on RTX 5090 + Ryzen 9 5950X (AVX2): UD-Q4_K_XL, IQ3_S and Swift IQ3_XXS, engines 0.1.31 to 0.1.39
Measured on 2026-10-01 to 2026-10-04 by [brenoperucchi](https://github.com/brenoperucchi). The same machine and prompts
across engine versions. Findings:
- UD-Q4_K_XL, every expert in VRAM + RAM: 0.1.32 to 0.1.34 read prompts 4-10% slower than 0.1.31, and the 0.1.31 stager
values (`STRATA_STAGER_THREADS=4 STRATA_STAGER_RING=16`) recover most of it.
- UD-Q4_K_XL on 0.1.38: prompts 15-40% slower than on 0.1.34, because the file tier now reads unbuffered on this PC
(#357/#362). `STRATA_UNBUFFERED_LOAD=0` brings them back, slightly above 0.1.34 (reported in #577).
- `--prefill auto:32768` reads the 14.7K prompt 35% faster than the default on UD-Q4_K_XL (0.1.34) and 14-31% faster on
IQ3_S at 14.7-28.9K (0.1.34 and 0.1.38). Prompts of 2.7K do not change, and neither does decode.
- 0.1.38 reads prompts 11-20% faster than earlier versions on the packs (IQ3_S at 14.7-28.9K, Swift IQ3_XXS at every
size), with the same decode.
- UD-Q4_K_XL on 0.1.39: the file tier fix (#577) brings both budgets back without the variable (14.7K prompt: 2,043
tok/s at 72 GiB and 1,896 at 40 GiB, within 2% of 0.1.38 with `STRATA_UNBUFFERED_LOAD=0`).
- On 0.1.39 `--prefill auto:32768` still reads IQ3_S's 14.7K and 28.9K prompts 14% and 25% faster than `auto`, the same
gain as on 0.1.38, so the new ring sizing (#583) does not change it on this PC.
Main limitation: one machine, 3 runs per cell, and the 40 GiB budget does not reproduce a 64 GB PC (see below).
## Hardware and software
- NVIDIA RTX 5090, 32 GB, 600 W limit, PCIe Gen 4 x16 (the CPU's maximum); the engine's PCIe probe reads 28.3 GB/s
host-to-device when started without `--pcie-frac`. Single GPU, which also drives the display (the desktop takes about
320-360 MiB of VRAM).
- AMD Ryzen 9 5950X, 16 cores / 32 threads, AVX2 only (no AVX-512); the engine chose AVX2 and 15 expert-pool workers.
- 96 GB DDR4-3200 (2x32 + 2x16, dual channel); models on an NVMe SSD.
- Windows 11 (build 26200), NVIDIA driver 616.64.
- Engines 0.1.32 to 0.1.39: release binaries (`BUILD-0.1.3x.json` here), each with `serve/server.py` from its own tag's
checkout. Engine 0.1.31: the release binary that setup installed, run with the 0.1.32 `server.py`.
- Background: nothing else on the GPU; normal desktop use. No power limit changes.
## Model and configuration
UD-Q4_K_XL:
- The four `Qwen3.8-Flash-Next-UD-Q4_K_XL-0000N-of-00004.gguf` shards from Unsloth (revision not recorded), packed by
hand on 0.1.31 following `docs/UNSLOTH_Q4.md`; the same pack for every engine. No vision encoder.
- Context 32,768; KV int8, no KV streaming; expert cache auto (7,808 slots, 22.79 GiB of VRAM; 351-385 MiB of VRAM free
with everything loaded); prefill auto unless noted; MTP on (`--spec 4 --spec-min-p 0.5`); `--pcie-frac` left to the
engine (0.55); no calibration, no speed projection. Reasoning left at the model default.
- Budget 72 GiB (setup's default for 96 GB): every expert the GPU cache does not hold (48.94 GiB) is resident in RAM.
Up to 0.1.34 the engine lowered the budget to the free RAM it read (58-60 GiB here), which still held them all.
- Budget 40 GiB (setup's default for 64 GB): 40 GiB in RAM, the rest read from the GGUF. With 96 GB installed the OS
file cache holds those reads, so this does not reproduce a 64 GB PC's SSD traffic.
```text
strata.exe --serve --pack <packs>\ud-q4_k_xl --native <UD-Q4_K_XL shard 1> --resident-budget-gib 72|40
--expert-profile data\expert-profile.bin --expert-cache auto --prefill auto|auto:32768 --spec 4
--spec-min-p 0.5 --mtp <mtp>\rt --max-context 32768 --kv int8
```
IQ3_S (0.1.34, 0.1.38 and 0.1.39): ISTA-DASLab Flash-Next GSQ-RCO IQ3_S (2 shards + PLE): `--expert-cache auto
--prefill auto|auto:32768 --spec 4 --spec-min-p 0.70 --mtp <mtp>\rt --max-context 65536 --kv int8 --pcie-frac 0.55` and
`STRATA_IQ_MT_MIN=1` in `env`.
Swift IQ3_XXS (0.1.34 to 0.1.39): Swift 1.5 IQ3_XXS pack, with the config this PC serves in production:
`--expert-cache auto --prefill auto:32768 --spec 4 --spec-min-p 0.70 --mtp <mtp>\rt --max-context 32768 --kv int8
--pcie-frac 0.20` and `STRATA_IQ_MT_MIN=1` (expert cache 14,864 of the 24,576 experts). The `STRATA_PF_FUSED=1` row is
for speed only: with that flag the answers change, see #519.
The "setup's defaults" rows (0.1.39, IQ3_S and Swift IQ3_XXS) use the arguments setup 0.1.39 writes for this PC, read
from its `setup.py` (setup itself was not run): `--expert-cache auto --prefill auto --spec 4 --spec-min-p 0.5 --mtp
<mtp>\rt --max-context 32768 --kv int8`, no `--pcie-frac` and no `env`. UD-Q4_K_XL's 72 GiB rows already match
setup's defaults for 96 GB.
Every server config is in [configs/](configs/) (paths as on this PC). The `stager4` configs add
`STRATA_STAGER_THREADS=4` and `STRATA_STAGER_RING=16` to `env`, the `buffered` ones `STRATA_UNBUFFERED_LOAD=0`.
## Method
- [run_bench.py](run_bench.py) sends `/v1/chat/completions` requests (temperature 0, seed 42, `cache_prompt: false`,
256-token output cap), one at a time: 1 warm-up and 3 measured runs per prompt, one server process per
configuration. Throughput is the engine's own `timings` (`prompt_per_second`, `predicted_per_second`).
- The server reuses a matching prefix even with `cache_prompt: false`, so every request starts with a unique first
line and each run reads its whole prompt fresh (`cache_n` is 0 in every measured run). Model loading is not timed.
The expert cache warms up across the warm-up and measured runs of each configuration.
- [prompts/](prompts/) are built from this repo at tag `v0.1.32`, with hashes in `SHA256SUMS`: short (one sentence),
medium (a review instruction plus the first 9,000 bytes of `src/core/expert_source.cpp`), long (a summary
instruction plus the first 50,000 bytes of the same file) and xlong (a summary instruction plus the first
100,000 bytes of `src/program/generate.cpp`). Token counts below include the chat template and the unique first line.
- Memory: peak VRAM 31.4 GB in every UD-Q4_K_XL configuration (`nvidia-smi`, sampled every second on 0.1.32 and
0.1.33). Host RAM was not measured usefully (the sampler counted the OS file cache).
- [runs/](runs/) has one JSON per configuration: `sizes.<prompt>.runs[]` holds each measured request (`timings` as
returned by the server, `wall_s` in seconds at the client, `content_sha256` of the answer), `warmup[]` the warm-up
requests, and `prompt_tps` / `decode_tps` the median, min and max in tokens/s. [logs/](logs/) has the engine log of
each configuration. `logs/0132-b72.log` also holds an earlier, discarded pass where the prompts were reused, and
`logs/swift-0137.log` other requests made on the same server.
## Results
UD-Q4_K_XL:
| Configuration | Prompt | Actual prompt tokens | Reused tokens | Generated tokens | Runs | Prompt tok/s median [range] | Decode tok/s median [range] | TTFT seconds |
| --- | --- | ---: | ---: | --- | ---: | --- | --- | --- |
| 0.1.31, 72 GiB | short | 103 | 0 | 227, 109, 237 | 3 | 88.0 [88.0-94.9] | 71.0 [61.8-71.8] | not measured |
| 0.1.31, 72 GiB | medium | 2,684 | 0 | 256, 256, 256 | 3 | 961 [960-982] | 75.2 [62.7-76.2] | not measured |
| 0.1.31, 72 GiB | long | 14,689 | 0 | 256, 256, 256 | 3 | 2,161 [2,156-2,167] | 88.3 [81.8-93.8] | not measured |
| 0.1.32, 72 GiB | short | 103 | 0 | 168, 156, 237 | 3 | 103.3 [99.5-109.7] | 68.9 [67.8-76.4] | not measured |
| 0.1.32, 72 GiB | medium | 2,684 | 0 | 256, 256, 256 | 3 | 902 [890-912] | 64.8 [61.4-66.0] | not measured |
| 0.1.32, 72 GiB | long | 14,689 | 0 | 256, 256, 256 | 3 | 1,992 [1,980-2,036] | 91.6 [87.5-92.0] | not measured |
| 0.1.32, 72 GiB, second server run | short | 105 | 0 | 227, 154, 159 | 3 | 86.6 [81.1-91.9] | 61.3 [59.5-72.6] | not measured |
| 0.1.32, 72 GiB, second server run | medium | 2,686 | 0 | 256, 256, 256 | 3 | 884 [853-887] | 72.9 [64.0-76.6] | not measured |
| 0.1.32, 72 GiB, second server run | long | 14,691 | 0 | 256, 256, 256 | 3 | 1,997 [1,972-1,998] | 87.3 [72.4-95.3] | not measured |
| 0.1.33, 72 GiB | short | 103 | 0 | 237, 148, 157 | 3 | 92.3 [85.5-94.0] | 66.0 [55.9-69.9] | not measured |
| 0.1.33, 72 GiB | medium | 2,684 | 0 | 256, 256, 256 | 3 | 923 [914-932] | 74.8 [60.3-80.4] | not measured |
| 0.1.33, 72 GiB | long | 14,689 | 0 | 256, 256, 256 | 3 | 2,021 [1,962-2,029] | 89.2 [83.5-90.1] | not measured |
| 0.1.34, 72 GiB | short | 99 | 0 | 155, 235, 235 | 3 | 86.2 [70.6-86.7] | 68.2 [55.1-72.7] | not measured |
| 0.1.34, 72 GiB | medium | 2,680 | 0 | 256, 256, 256 | 3 | 890 [862-894] | 73.7 [54.4-75.9] | not measured |
| 0.1.34, 72 GiB | long | 14,685 | 0 | 256, 256, 256 | 3 | 1,949 [1,942-2,005] | 74.6 [72.7-80.5] | not measured |
| 0.1.38, 72 GiB | short | 99 | 0 | 230, 222, 241 | 3 | 99.1 [95.3-100.7] | 72.2 [69.3-75.4] | not measured |
| 0.1.38, 72 GiB | medium | 2,680 | 0 | 256, 256, 256 | 3 | 757 [752-764] | 71.3 [63.2-73.7] | not measured |
| 0.1.38, 72 GiB | long | 14,685 | 0 | 256, 256, 256 | 3 | 1,658 [1,645-1,681] | 87.9 [84.4-93.8] | not measured |
| 0.1.38, 72 GiB, `STRATA_UNBUFFERED_LOAD=0` | medium | 2,682 | 0 | 256, 256, 256 | 3 | 899 [891-916] | 67.9 [53.7-69.3] | not measured |
| 0.1.38, 72 GiB, `STRATA_UNBUFFERED_LOAD=0` | long | 14,687 | 0 | 256, 256, 256 | 3 | 2,038 [2,010-2,065] | 78.3 [77.0-82.3] | not measured |
| 0.1.39, 72 GiB | short | 99 | 0 | 222, 227, 210 | 3 | 87.3 [81.1-87.4] | 65.9 [59.4-68.5] | not measured |
| 0.1.39, 72 GiB | medium | 2,680 | 0 | 256, 256, 256 | 3 | 916 [914-961] | 74.2 [67.9-87.6] | not measured |
| 0.1.39, 72 GiB | long | 14,685 | 0 | 256, 256, 256 | 3 | 2,043 [2,040-2,059] | 81.3 [77.8-92.0] | not measured |
| 0.1.39, 72 GiB, `STRATA_UNBUFFERED_LOAD=0` | medium | 2,682 | 0 | 256, 256, 256 | 3 | 941 [911-943] | 67.2 [66.2-75.3] | not measured |
| 0.1.39, 72 GiB, `STRATA_UNBUFFERED_LOAD=0` | long | 14,687 | 0 | 256, 256, 256 | 3 | 2,062 [2,002-2,106] | 86.0 [84.8-87.2] | not measured |
| 0.1.32, 72 GiB, old stager values | medium | 2,687 | 0 | 256, 256, 256 | 3 | 942 [931-945] | 70.7 [60.1-71.3] | not measured |
| 0.1.32, 72 GiB, old stager values | long | 14,692 | 0 | 256, 256, 256 | 3 | 2,116 [2,059-2,137] | 79.6 [72.0-81.0] | not measured |
| 0.1.33, 72 GiB, old stager values | medium | 2,687 | 0 | 256, 256, 256 | 3 | 947 [943-947] | 69.5 [57.5-72.7] | not measured |
| 0.1.33, 72 GiB, old stager values | long | 14,692 | 0 | 256, 256, 256 | 3 | 2,139 [2,122-2,141] | 87.5 [83.0-96.1] | not measured |
| 0.1.34, 72 GiB, old stager values | medium | 2,683 | 0 | 256, 256, 256 | 3 | 944 [941-952] | 70.5 [68.7-77.3] | not measured |
| 0.1.34, 72 GiB, old stager values | long | 14,688 | 0 | 256, 256, 256 | 3 | 2,133 [2,133-2,148] | 79.9 [78.5-81.8] | not measured |
| 0.1.38, 72 GiB, old stager values | medium | 2,683 | 0 | 256, 256, 256 | 3 | 772 [766-772] | 78.1 [64.5-82.8] | not measured |
| 0.1.38, 72 GiB, old stager values | long | 14,688 | 0 | 256, 256, 256 | 3 | 1,704 [1,699-1,709] | 80.6 [78.2-82.8] | not measured |
| 0.1.39, 72 GiB, old stager values | medium | 2,683 | 0 | 256, 256, 256 | 3 | 988 [987-998] | 71.4 [66.7-75.8] | not measured |
| 0.1.39, 72 GiB, old stager values | long | 14,688 | 0 | 256, 256, 256 | 3 | 2,169 [2,166-2,187] | 81.9 [76.4-83.1] | not measured |
| 0.1.34, 72 GiB, `--prefill auto:32768` | short | 103 | 0 | 210, 225, 229 | 3 | 85.5 [85.0-88.0] | 66.2 [62.0-71.8] | not measured |
| 0.1.34, 72 GiB, `--prefill auto:32768` | medium | 2,684 | 0 | 256, 256, 256 | 3 | 900 [881-912] | 67.0 [64.6-79.3] | not measured |
| 0.1.34, 72 GiB, `--prefill auto:32768` | long | 14,689 | 0 | 256, 256, 256 | 3 | 2,623 [2,527-2,680] | 79.9 [71.2-80.9] | not measured |
| 0.1.38, 72 GiB, `--prefill auto:32768` | short | 103 | 0 | 228, 233, 231 | 3 | 101.9 [99.3-102.7] | 72.8 [71.9-73.0] | not measured |
| 0.1.38, 72 GiB, `--prefill auto:32768` | medium | 2,684 | 0 | 256, 256, 256 | 3 | 738 [642-743] | 74.6 [71.8-83.0] | not measured |
| 0.1.38, 72 GiB, `--prefill auto:32768` | long | 14,689 | 0 | 256, 256, 256 | 3 | 1,802 [1,796-1,819] | 77.2 [70.6-77.9] | not measured |
| 0.1.39, 72 GiB, `--prefill auto:32768` | short | 103 | 0 | 235, 165, 105 | 3 | 88.4 [82.6-94.1] | 65.9 [62.0-91.9] | not measured |
| 0.1.39, 72 GiB, `--prefill auto:32768` | medium | 2,684 | 0 | 256, 256, 256 | 3 | 948 [939-949] | 62.4 [56.7-76.5] | not measured |
| 0.1.39, 72 GiB, `--prefill auto:32768` | long | 14,689 | 0 | 256, 256, 256 | 3 | 2,736 [2,645-2,777] | 83.3 [74.8-84.7] | not measured |
| 0.1.32, 40 GiB | short | 103 | 0 | 149, 160, 231 | 3 | 95.6 [83.1-100.3] | 54.8 [50.2-65.1] | not measured |
| 0.1.32, 40 GiB | medium | 2,684 | 0 | 256, 256, 256 | 3 | 797 [785-799] | 54.4 [54.1-59.8] | not measured |
| 0.1.32, 40 GiB | long | 14,689 | 0 | 256, 256, 256 | 3 | 1,839 [1,810-1,856] | 73.5 [72.3-79.5] | not measured |
| 0.1.33, 40 GiB | short | 103 | 0 | 148, 228, 105 | 3 | 94.8 [82.4-95.1] | 48.7 [43.8-70.9] | not measured |
| 0.1.33, 40 GiB | medium | 2,684 | 0 | 256, 256, 256 | 3 | 796 [794-808] | 62.8 [62.0-68.5] | not measured |
| 0.1.33, 40 GiB | long | 14,689 | 0 | 256, 256, 256 | 3 | 1,803 [1,797-1,804] | 75.6 [67.2-79.3] | not measured |
| 0.1.34, 40 GiB | short | 99 | 0 | 204, 228, 212 | 3 | 90.2 [83.7-91.0] | 55.2 [51.4-58.7] | not measured |
| 0.1.34, 40 GiB | medium | 2,680 | 0 | 256, 256, 256 | 3 | 803 [787-816] | 67.3 [63.4-67.5] | not measured |
| 0.1.34, 40 GiB | long | 14,685 | 0 | 256, 256, 256 | 3 | 1,844 [1,823-1,848] | 69.8 [64.3-74.3] | not measured |
| 0.1.38, 40 GiB | short | 99 | 0 | 235, 231, 144 | 3 | 66.0 [64.1-70.8] | 47.8 [46.9-48.6] | not measured |
| 0.1.38, 40 GiB | medium | 2,680 | 0 | 256, 256, 256 | 3 | 472 [472-474] | 45.3 [40.4-55.8] | not measured |
| 0.1.38, 40 GiB | long | 14,685 | 0 | 256, 256, 256 | 3 | 1,105 [1,103-1,105] | 68.0 [56.0-68.6] | not measured |
| 0.1.38, 40 GiB, `STRATA_UNBUFFERED_LOAD=0` | medium | 2,682 | 0 | 256, 256, 256 | 3 | 844 [818-846] | 62.0 [61.8-66.4] | not measured |
| 0.1.38, 40 GiB, `STRATA_UNBUFFERED_LOAD=0` | long | 14,687 | 0 | 256, 256, 256 | 3 | 1,913 [1,887-1,930] | 72.9 [66.9-73.9] | not measured |
| 0.1.39, 40 GiB | short | 99 | 0 | 160, 233, 208 | 3 | 89.9 [79.4-92.2] | 60.4 [58.9-60.8] | not measured |
| 0.1.39, 40 GiB | medium | 2,680 | 0 | 256, 256, 256 | 3 | 857 [822-866] | 70.8 [69.1-79.1] | not measured |
| 0.1.39, 40 GiB | long | 14,685 | 0 | 256, 256, 256 | 3 | 1,896 [1,888-1,953] | 78.3 [74.5-80.4] | not measured |
| 0.1.39, 40 GiB, `STRATA_UNBUFFERED_LOAD=0` | medium | 2,682 | 0 | 256, 256, 256 | 3 | 842 [814-860] | 68.5 [61.9-69.7] | not measured |
| 0.1.39, 40 GiB, `STRATA_UNBUFFERED_LOAD=0` | long | 14,687 | 0 | 256, 256, 256 | 3 | 1,954 [1,927-1,968] | 80.3 [73.0-83.1] | not measured |
IQ3_S:
| Configuration | Prompt | Actual prompt tokens | Reused tokens | Generated tokens | Runs | Prompt tok/s median [range] | Decode tok/s median [range] | TTFT seconds |
| --- | --- | ---: | ---: | --- | ---: | --- | --- | --- |
| 0.1.34, `--prefill auto` | medium | 2,680 | 0 | 256, 256, 256 | 3 | 2,331 [2,288-2,333] | 141.6 [140.1-157.1] | not measured |
| 0.1.34, `--prefill auto` | long | 14,685 | 0 | 256, 256, 256 | 3 | 4,610 [4,604-4,612] | 156.0 [153.0-168.7] | not measured |
| 0.1.34, `--prefill auto` | xlong | 28,883 | 0 | 256, 256, 256 | 3 | 4,682 [4,665-4,694] | 148.6 [144.0-148.8] | not measured |
| 0.1.34, `--prefill auto:32768` | medium | 2,683 | 0 | 256, 256, 256 | 3 | 2,309 [2,285-2,338] | 140.4 [132.0-168.2] | not measured |
| 0.1.34, `--prefill auto:32768` | long | 14,688 | 0 | 256, 256, 256 | 3 | 5,406 [5,381-5,421] | 148.5 [148.1-165.5] | not measured |
| 0.1.34, `--prefill auto:32768` | xlong | 28,886 | 0 | 256, 256, 256 | 3 | 6,150 [6,149-6,151] | 142.0 [139.7-144.2] | not measured |
| 0.1.38, `--prefill auto` | medium | 2,680 | 0 | 256, 256, 256 | 3 | 2,349 [2,347-2,350] | 152.2 [128.5-174.9] | not measured |
| 0.1.38, `--prefill auto` | long | 14,685 | 0 | 256, 256, 256 | 3 | 5,520 [5,508-5,531] | 154.0 [152.1-163.2] | not measured |
| 0.1.38, `--prefill auto` | xlong | 28,883 | 0 | 256, 256, 256 | 3 | 5,495 [5,490-5,518] | 148.1 [146.0-148.4] | not measured |
| 0.1.38, `--prefill auto:32768` | medium | 2,683 | 0 | 256, 256, 256 | 3 | 2,353 [2,351-2,357] | 148.6 [122.2-150.6] | not measured |
| 0.1.38, `--prefill auto:32768` | long | 14,688 | 0 | 256, 256, 256 | 3 | 6,287 [6,283-6,290] | 168.8 [159.8-177.1] | not measured |
| 0.1.38, `--prefill auto:32768` | xlong | 28,886 | 0 | 256, 256, 256 | 3 | 6,870 [6,869-6,870] | 147.9 [144.1-154.4] | not measured |
| 0.1.39, `--prefill auto` | medium | 2,680 | 0 | 256, 256, 256 | 3 | 2,373 [2,366-2,374] | 148.8 [147.5-174.5] | not measured |
| 0.1.39, `--prefill auto` | long | 14,685 | 0 | 256, 256, 256 | 3 | 5,573 [5,567-5,583] | 175.2 [158.3-188.1] | not measured |
| 0.1.39, `--prefill auto` | xlong | 28,883 | 0 | 256, 256, 256 | 3 | 5,533 [5,517-5,537] | 156.5 [154.8-158.4] | not measured |
| 0.1.39, `--prefill auto:32768` | medium | 2,683 | 0 | 256, 256, 256 | 3 | 2,372 [2,369-2,374] | 178.5 [165.3-182.9] | not measured |
| 0.1.39, `--prefill auto:32768` | long | 14,688 | 0 | 256, 256, 256 | 3 | 6,362 [6,332-6,380] | 169.2 [167.0-175.9] | not measured |
| 0.1.39, `--prefill auto:32768` | xlong | 28,886 | 0 | 256, 256, 256 | 3 | 6,941 [6,929-6,950] | 159.8 [159.6-163.0] | not measured |
| 0.1.39, setup's defaults | medium | 2,680 | 0 | 256, 256, 256 | 3 | 2,407 [2,402-2,407] | 158.2 [147.4-165.0] | not measured |
| 0.1.39, setup's defaults | long | 14,685 | 0 | 256, 256, 256 | 3 | 5,605 [5,528-5,605] | 177.9 [177.6-181.2] | not measured |
| 0.1.39, setup's defaults | xlong | 28,883 | 0 | 256, 256, 256 | 3 | 5,565 [5,541-5,565] | 170.2 [160.9-172.2] | not measured |
Swift IQ3_XXS:
| Configuration | Prompt | Actual prompt tokens | Reused tokens | Generated tokens | Runs | Prompt tok/s median [range] | Decode tok/s median [range] | TTFT seconds |
| --- | --- | ---: | ---: | --- | ---: | --- | --- | --- |
| 0.1.34 | medium | 2,677 | 0 | 256, 256, 256 | 3 | 2,650 [2,638-2,676] | 148.0 [140.4-156.7] | not measured |
| 0.1.34 | long | 14,682 | 0 | 256, 256, 256 | 3 | 5,513 [5,509-5,516] | 174.4 [168.1-176.1] | not measured |
| 0.1.34 | xlong | 28,880 | 0 | 256, 256, 256 | 3 | 6,222 [6,220-6,226] | 163.4 [152.8-171.9] | not measured |
| 0.1.36 | medium | 2,677 | 0 | 256, 256, 256 | 3 | 2,690 [2,668-2,700] | 169.3 [151.0-169.5] | not measured |
| 0.1.36 | long | 14,682 | 0 | 256, 256, 256 | 3 | 5,534 [5,516-5,549] | 159.9 [158.9-178.3] | not measured |
| 0.1.36 | xlong | 28,880 | 0 | 256, 256, 256 | 3 | 6,231 [6,224-6,240] | 158.2 [146.3-159.5] | not measured |
| 0.1.36, `STRATA_PF_FUSED=1` | medium | 2,679 | 0 | 256, 256, 256 | 3 | 3,192 [3,181-3,199] | 191.8 [177.6-196.1] | not measured |
| 0.1.36, `STRATA_PF_FUSED=1` | long | 14,684 | 0 | 256, 256, 256 | 3 | 6,784 [6,777-6,786] | 159.1 [156.5-173.4] | not measured |
| 0.1.36, `STRATA_PF_FUSED=1` | xlong | 28,882 | 0 | 256, 256, 256 | 3 | 7,043 [7,030-7,045] | 158.0 [155.6-159.3] | not measured |
| 0.1.37 | medium | 2,676 | 0 | 256, 256, 256 | 3 | 2,675 [2,655-2,680] | 169.8 [167.8-193.7] | not measured |
| 0.1.37 | long | 14,681 | 0 | 256, 256, 256 | 3 | 5,534 [5,514-5,541] | 171.0 [162.4-181.7] | not measured |
| 0.1.38 | medium | 2,677 | 0 | 256, 256, 256 | 3 | 3,148 [3,144-3,152] | 165.1 [154.8-191.9] | not measured |
| 0.1.38 | long | 14,682 | 0 | 256, 256, 256 | 3 | 6,374 [6,279-6,397] | 174.5 [165.4-176.7] | not measured |
| 0.1.38 | xlong | 28,880 | 0 | 256, 256, 256 | 3 | 6,908 [6,900-6,912] | 160.8 [159.3-165.5] | not measured |
| 0.1.39 | medium | 2,677 | 0 | 256, 256, 256 | 3 | 3,187 [3,179-3,195] | 177.8 [176.2-190.5] | not measured |
| 0.1.39 | long | 14,682 | 0 | 256, 256, 256 | 3 | 6,454 [6,430-6,462] | 186.9 [171.8-201.2] | not measured |
| 0.1.39 | xlong | 28,880 | 0 | 256, 256, 256 | 3 | 6,999 [6,977-7,000] | 164.3 [163.5-176.7] | not measured |
| 0.1.39, setup's defaults | medium | 2,678 | 0 | 256, 256, 256 | 3 | 3,195 [3,195-3,203] | 215.4 [215.0-215.8] | not measured |
| 0.1.39, setup's defaults | long | 14,683 | 0 | 256, 256, 256 | 3 | 5,853 [5,846-5,856] | 188.1 [174.4-209.5] | not measured |
| 0.1.39, setup's defaults | xlong | 28,881 | 0 | 256, 256, 256 | 3 | 5,941 [5,930-5,943] | 183.2 [181.6-187.2] | not measured |
The short prompt's output length varied between runs, so its decode numbers are less comparable. Decode expert-cache
hit rate on the first UD-Q4_K_XL requests: 81-96%.
UD-Q4_K_XL prompt reading with all experts in memory (72 GiB), relative to 0.1.31: 0.1.32 is 6-8% slower on both
prompts (the second 0.1.32 server run reproduced it), 0.1.33 4-7% and 0.1.34 7-10%. With the stager at 0.1.31's values
(4 threads, 16 in flight), 0.1.32 to 0.1.34 stay within 2% of 0.1.31. The 0.1.32 notes describe the larger stager (32
threads, 128 in flight) for the GGUF-in-place mode, measured on a 64 GB PC where a third of the experts come from the
SSD. When nothing comes from the SSD it seems to cost a little.
On 0.1.38 the engine logs `the file tier reads unbuffered (... 83.0 GiB available, 103.7 GiB of files)` for both
budgets, and every UD-Q4_K_XL configuration reads prompts slower than on 0.1.34 (the
14.7K prompt: 1,658 instead of 1,949 tok/s at 72 GiB, 1,105 instead of 1,844 at 40 GiB). With
`STRATA_UNBUFFERED_LOAD=0` it reads through the file cache again: 2,038 and 1,913 tok/s.
`--prefill auto:32768`: on 0.1.34, UD-Q4_K_XL reads the 14.7K prompt at 2,623 tok/s against 1,949 with `auto` (+35%);
IQ3_S goes from 4,610 to 5,406 at 14.7K (+17%) and from 4,682 to 6,150 at 28.9K (+31%), in line with #440 on a
9950X3D. On 0.1.38, IQ3_S gains 14% and 25% at those sizes. The 2.7K prompt is the same with either setting.
0.1.38 on the packs: Swift IQ3_XXS reads the 2.7K / 14.7K / 28.9K prompts at 3,148 / 6,374 / 6,908 tok/s against
2,690 / 5,534 / 6,231 on 0.1.36 (+17% / +15% / +11%); 0.1.36 and 0.1.37 are the same as 0.1.34 within 2%. IQ3_S with
`auto` reads 14.7K and 28.9K 20% and 17% faster than on 0.1.34. Decode is unchanged.
0.1.39: UD-Q4_K_XL reads the 2.7K / 14.7K prompts at 916 / 2,043 tok/s (72 GiB) and 857 / 1,896 (40 GiB) with no
variable, where 0.1.38 needed `STRATA_UNBUFFERED_LOAD=0` for 899 / 2,038 and 844 / 1,913; the variable now changes
nothing beyond the run-to-run range. Swift IQ3_XXS reads within 1-2% of 0.1.38 (3,187 / 6,454 / 6,999 tok/s). IQ3_S
with `--prefill auto:32768` reads 14.7K and 28.9K at 6,362 and 6,941 tok/s against 5,573 and 5,533 with `auto`.
Against setup's defaults, this PC's Swift config (which also sets `--pcie-frac 0.20`, `--spec-min-p 0.70` and
`STRATA_IQ_MT_MIN=1`) reads 14.7K and 28.9K 10% and 18% faster and 2.7K the same; setup's defaults decode as fast or
faster in these runs (215 against 178 tok/s at 2.7K), within wide ranges.
Decode does not show a consistent difference between the engines or prefill settings; its run-to-run range is wide
(for example 72.4-95.3 tok/s on the long prompt in the second 0.1.32 run). No failed or cancelled requests.
## Correctness and limitations
No quality checks here: answers were only hashed (for Swift with `STRATA_PF_FUSED=1`, see #519). One machine, one
request at a time, greedy decoding, 3 runs per cell, one 72 GiB configuration for 0.1.31, IQ3_S on 0.1.34, 0.1.38 and
0.1.39 only, Windows with the GPU also driving the display. Time to first token was not measured separately; the engine's
prompt time (`prompt_ms`) is in the run JSON.
@@ -0,0 +1,11 @@
# Trimmed files
Ported from PR #433 for 0.1.40.2. Raw dumps, logs and binaries were left out to keep the repository small;
the README, summaries, configs and per-run results are unchanged. The files below were in the original PR.
2 files, 0.2 MB:
| file | bytes | why |
|---|---:|---|
| `prompts/long.txt` | 50,234 | large text |
| `prompts/xlong.txt` | 100,218 | large (>100 KB) |
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-src\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0132",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0132\\bench\\engine-ud-b72-0131.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0132\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"40",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0132",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0132\\bench\\engine-ud-b40.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0132\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0132",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0132\\bench\\engine-ud-b72-rep.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,41 @@
{
"exe": "E:\\strata-0132\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0132",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0132\\bench\\engine-ud-b72-stager4.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_STAGER_THREADS": "4",
"STRATA_STAGER_RING": "16"
}
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0132\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0132",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0132\\bench\\engine-ud-b72.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0133\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"40",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0133",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0133\\bench\\engine-ud-b40.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,41 @@
{
"exe": "E:\\strata-0133\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0133",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0133\\bench\\engine-ud-b72-stager4.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_STAGER_THREADS": "4",
"STRATA_STAGER_RING": "16"
}
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0133\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0133",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0133\\bench\\engine-ud-b72.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0134\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"40",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0134",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0134\\bench\\engine-ud-b40.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0134\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0134",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0134\\bench\\engine-ud-b72-auto32k.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,41 @@
{
"exe": "E:\\strata-0134\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0134",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0134\\bench\\engine-ud-b72-stager4.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_STAGER_THREADS": "4",
"STRATA_STAGER_RING": "16"
}
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0134\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0134",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0134\\bench\\engine-ud-b72.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0134\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\iq3_s",
"--native",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.70",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"65536",
"--kv",
"int8",
"--pcie-frac",
"0.55"
],
"cwd": "E:\\strata-0134",
"tokenizer": "E:\\strata-data\\packs\\iq3_s\\tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "E:\\strata-0134\\bench\\engine-iq3s-auto.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0134\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\iq3_s",
"--native",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--spec-min-p",
"0.70",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"65536",
"--kv",
"int8",
"--pcie-frac",
"0.55"
],
"cwd": "E:\\strata-0134",
"tokenizer": "E:\\strata-data\\packs\\iq3_s\\tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "E:\\strata-0134\\bench\\engine-iq3s-auto32k.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,40 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"40",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0138\\bench\\engine-ud-b40-buffered.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_UNBUFFERED_LOAD": "0"
}
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"40",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0138\\bench\\engine-ud-b40.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0138\\bench\\engine-ud-b72-auto32k.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,40 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0138\\bench\\engine-ud-b72-buffered.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_UNBUFFERED_LOAD": "0"
}
}
@@ -0,0 +1,41 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0138\\bench\\engine-ud-b72-stager4.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_STAGER_THREADS": "4",
"STRATA_STAGER_RING": "16"
}
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0138\\bench\\engine-ud-b72.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\iq3_s",
"--native",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.70",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"65536",
"--kv",
"int8",
"--pcie-frac",
"0.55"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\iq3_s\\tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "E:\\strata-0138\\bench\\engine-iq3s-auto.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\iq3_s",
"--native",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--spec-min-p",
"0.70",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"65536",
"--kv",
"int8",
"--pcie-frac",
"0.55"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\iq3_s\\tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "E:\\strata-0138\\bench\\engine-iq3s-auto32k.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,40 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"40",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0139\\bench\\engine-ud-b40-buffered.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_UNBUFFERED_LOAD": "0"
}
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"40",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0139\\bench\\engine-ud-b40.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0139\\bench\\engine-ud-b72-auto32k.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,40 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0139\\bench\\engine-ud-b72-buffered.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_UNBUFFERED_LOAD": "0"
}
}
@@ -0,0 +1,41 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0139\\bench\\engine-ud-b72-stager4.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1",
"env": {
"STRATA_STAGER_THREADS": "4",
"STRATA_STAGER_RING": "16"
}
}
@@ -0,0 +1,37 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\ud-q4_k_xl",
"--native",
"E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"--resident-budget-gib",
"72",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\ud-q4_k_xl\\tokenizer",
"model_name": "qwen3.8-flash-next-ud-q4_k_xl",
"log": "E:\\strata-0139\\bench\\engine-ud-b72.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"host": "127.0.0.1"
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\iq3_s",
"--native",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.70",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"65536",
"--kv",
"int8",
"--pcie-frac",
"0.55"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\iq3_s\\tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "E:\\strata-0139\\bench\\engine-iq3s-auto.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\iq3_s",
"--native",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--spec-min-p",
"0.70",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"65536",
"--kv",
"int8",
"--pcie-frac",
"0.55"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\iq3_s\\tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "E:\\strata-0139\\bench\\engine-iq3s-auto32k.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,39 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\iq3_s",
"--native",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models\\IQ3_S\\Qwen3.8-Flash-Next-GSQ-RCO-IQ3_S-00002-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\iq3_s\\tokenizer",
"model_name": "qwen3.8-flash-next-iq3_s",
"log": "E:\\strata-0139\\bench\\engine-iq3s-default.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0134\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\swift-iq3_xxs",
"--native",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8",
"--pcie-frac",
"0.20",
"--spec-min-p",
"0.70"
],
"cwd": "E:\\strata-0134",
"tokenizer": "E:\\strata-data\\packs\\swift-iq3_xxs\\tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "E:\\strata-0136\\bench\\engine-swift-0134.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,44 @@
{
"exe": "E:\\strata-0136\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\swift-iq3_xxs",
"--native",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8",
"--pcie-frac",
"0.20",
"--spec-min-p",
"0.70"
],
"cwd": "E:\\strata-0136",
"tokenizer": "E:\\strata-data\\packs\\swift-iq3_xxs\\tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "E:\\strata-0136\\bench\\engine-swift-0136-fused.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1",
"STRATA_PF_FUSED": "1"
}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0136\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\swift-iq3_xxs",
"--native",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8",
"--pcie-frac",
"0.20",
"--spec-min-p",
"0.70"
],
"cwd": "E:\\strata-0136",
"tokenizer": "E:\\strata-data\\packs\\swift-iq3_xxs\\tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "E:\\strata-0136\\bench\\engine-swift-0136.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0137\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\swift-iq3_xxs",
"--native",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8",
"--pcie-frac",
"0.20",
"--spec-min-p",
"0.70"
],
"cwd": "E:\\strata-0137",
"tokenizer": "E:\\strata-data\\packs\\swift-iq3_xxs\\tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "E:\\strata-0137\\bench\\engine-base.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0138\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\swift-iq3_xxs",
"--native",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8",
"--pcie-frac",
"0.20",
"--spec-min-p",
"0.70"
],
"cwd": "E:\\strata-0138",
"tokenizer": "E:\\strata-data\\packs\\swift-iq3_xxs\\tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "E:\\strata-0138\\bench\\engine-swift.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,39 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\swift-iq3_xxs",
"--native",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--expert-cache",
"auto",
"--prefill",
"auto",
"--spec",
"4",
"--spec-min-p",
"0.5",
"--max-context",
"32768",
"--kv",
"int8"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\swift-iq3_xxs\\tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "E:\\strata-0139\\bench\\engine-swift-default.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {}
}
@@ -0,0 +1,43 @@
{
"exe": "E:\\strata-0139\\engine\\strata.exe",
"args": [
"--pack",
"E:\\strata-data\\packs\\swift-iq3_xxs",
"--native",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--ple-gguf",
"E:\\strata-data\\models-swift\\IQ3_XXS\\Swift-Qwen3.8-Flash-Next-GSQ-RCO-IQ3_XXS-00001-of-00002.gguf",
"--expert-profile",
"E:\\strata-src\\data\\expert-profile.bin",
"--expert-cache",
"auto",
"--prefill",
"auto:32768",
"--spec",
"4",
"--mtp",
"E:\\strata-data\\mtp\\rt",
"--max-context",
"32768",
"--kv",
"int8",
"--pcie-frac",
"0.20",
"--spec-min-p",
"0.70"
],
"cwd": "E:\\strata-0139",
"tokenizer": "E:\\strata-data\\packs\\swift-iq3_xxs\\tokenizer",
"model_name": "swift-1.5-iq3_xxs",
"log": "E:\\strata-0139\\bench\\engine-swift.log",
"lib_dirs": [
"E:\\strata-src\\.venv\\Lib\\site-packages\\nvidia\\cu13\\bin\\x86_64"
],
"port": 18199,
"gpu": 0,
"gpus_asked": true,
"host": "127.0.0.1",
"env": {
"STRATA_IQ_MT_MIN": "1"
}
}
@@ -0,0 +1,4 @@
2cd69728112a942401591b3d19141361105a8b2e9195ae19c007b593d70e9402 short.txt
e4c2b6ad18b235d3f0466f0aeb1256612c533188c75b2c5c6741ea6e5463b1c6 medium.txt
2b54045914a7bd362c93587902dc16af4d589cea3493cd0301a116df8495844a long.txt
acf504f5230c14fdb6e6ca5d8125a9dabd4176d8f5d09882b5a3aed4eee98b83 xlong.txt
@@ -0,0 +1,242 @@
Review the following C++ source file from the Strata engine (v0.1.32, src/core/expert_source.cpp, first 9,000 bytes). List the three most important correctness risks you see, with line references.
// src/core/expert_source.cpp - the adapter. See the header for the three clauses of the contract.
#include "strata/core/expert_source.hpp"
#include "strata/core/remote_experts.hpp"
#include "strata/kernels/cpu/expert_layout.hpp"
#include "strata/artifact/gguf_reader.hpp"
#include "strata/core/pinned.hpp"
#include "strata/platform/memory.hpp"
#include "strata/kernels/elementwise.hpp"
#include "strata/kernels/quantize_act.hpp"
#include "strata/kernels/s2_expert_grouped.hpp"
#include "strata/kernels/cpu/kq_avx2.hpp"
#include <cuda_runtime.h>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <filesystem>
#include <sstream>
#include <limits>
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX // std::numeric_limits<T>::max() below
#endif
#include <windows.h>
#else
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
// a 64-bit seek (as in pinned.cu): the 32-bit `fseek` wraps past 4 GiB, and the spelling differs per platform
#ifndef STRATA_FSEEK64
#ifdef _WIN32
#define STRATA_FSEEK64(f, o) _fseeki64((f), (long long) (o), SEEK_SET)
#else
#define STRATA_FSEEK64(f, o) fseeko((f), (off_t) (o), SEEK_SET)
#endif
#endif
namespace strata::core {
namespace detail {
bool cgroup_available_bytes(uint64_t limit, const CgroupMemoryStat& stat, uint64_t& bytes) {
bytes = 0;
if (!stat.valid) return false;
// memory.stat's inactive_file can race memory.current, so bound it to charged usage first.
uint64_t reclaimable = std::min(stat.inactive_file, stat.current);
reclaimable = stat.file_dirty >= reclaimable ? 0 : reclaimable - stat.file_dirty;
reclaimable = stat.file_writeback >= reclaimable ? 0 : reclaimable - stat.file_writeback;
// Reclaiming clean file pages reduces usage; saturating subtraction also handles a transient over-limit read.
const uint64_t usage_after_reclaim = stat.current - reclaimable;
bytes = usage_after_reclaim < limit ? limit - usage_after_reclaim : 0;
return true;
}
bool make_cache_complement_plan(
int64_t n_layers, int64_t n_expert, const std::vector<uint64_t>& layer_blob_bytes,
const std::vector<std::pair<int32_t, int32_t>>& primary_gpu_pairs,
const std::vector<std::pair<int32_t, int32_t>>& additional_gpu_pairs,
std::vector<uint64_t>& offsets, uint64_t& bytes, std::string& err) {
offsets.clear();
bytes = 0;
err.clear();
if (n_layers <= 0 || n_expert <= 0 || layer_blob_bytes.size() != (size_t) n_layers) {
err = "FileExpertSource: invalid geometry for the cache complement plan";
return false;
}
if ((uint64_t) n_layers > (uint64_t) std::numeric_limits<size_t>::max() / (uint64_t) n_expert) {
err = "FileExpertSource: cache complement index table is too large";
return false;
}
const size_t count = (size_t) n_layers * (size_t) n_expert;
std::vector<uint8_t> omitted(count, 0);
auto mark_pairs = [&](const std::vector<std::pair<int32_t, int32_t>>& pairs, uint8_t bit,
const char* label) -> bool {
for (const auto& pair : pairs) {
if (pair.first < 0 || pair.second < 0 || pair.first >= n_layers || pair.second >= n_expert) {
err = std::string("FileExpertSource: ") + label + " pair is outside the expert geometry";
return false;
}
const size_t index = (size_t) pair.first * (size_t) n_expert + (size_t) pair.second;
if ((omitted[index] & bit) != 0) {
err = std::string("FileExpertSource: duplicate ") + label + " pair in the cache complement plan";
return false;
}
if (bit == 2 && (omitted[index] & 1) != 0) {
err = "FileExpertSource: the primary and additional GPU expert tiers overlap";
return false;
}
omitted[index] |= bit;
}
return true;
};
if (!mark_pairs(primary_gpu_pairs, 1, "primary GPU") ||
!mark_pairs(additional_gpu_pairs, 2, "additional GPU")) return false;
for (uint64_t blob_bytes : layer_blob_bytes) {
if (blob_bytes == 0) {
err = "FileExpertSource: cache complement layer has zero-sized expert blobs";
return false;
}
}
offsets.assign(count, kNoCacheComplement);
for (int64_t layer = 0; layer < n_layers; ++layer) {
const uint64_t blob_bytes = layer_blob_bytes[(size_t) layer];
for (int64_t expert = 0; expert < n_expert; ++expert) {
const size_t index = (size_t) layer * (size_t) n_expert + (size_t) expert;
if (omitted[index] != 0) continue;
if (bytes > std::numeric_limits<uint64_t>::max() - blob_bytes) {
offsets.clear();
bytes = 0;
err = "FileExpertSource: cache complement size overflows";
return false;
}
offsets[index] = bytes;
bytes += blob_bytes;
}
}
if (bytes > (uint64_t) std::numeric_limits<size_t>::max()) {
offsets.clear();
bytes = 0;
err = "FileExpertSource: cache complement exceeds the host address space";
return false;
}
return true;
}
const uint8_t* cache_complement_blob_or_fallback(
size_t index, const std::vector<uint64_t>& offsets, const uint8_t* complement_host,
const uint8_t* mapped_fallback) {
if (complement_host != nullptr && index < offsets.size() && offsets[index] != kNoCacheComplement)
return complement_host + (size_t) offsets[index];
return mapped_fallback;
}
int64_t choose_resident_keep_from(const std::vector<uint64_t>& slot_bytes, uint64_t base_bytes, uint64_t budget,
int64_t lend_from) {
if (base_bytes > budget) return -1;
const int64_t slots = (int64_t) slot_bytes.size();
if (lend_from < 0 || lend_from > slots) lend_from = slots; // no lend region: only the experts no slot holds
int64_t keep = slots;
uint64_t bytes = base_bytes;
while (keep > lend_from) {
const uint64_t b = slot_bytes[(size_t) keep - 1];
if (b > budget - bytes) break;
bytes += b;
--keep;
}
return keep;
}
bool exchange_cache_complement(std::vector<uint64_t>& offsets, size_t in, size_t out) {
if (in == out || in >= offsets.size() || out >= offsets.size() || offsets[in] == kNoCacheComplement ||
offsets[out] != kNoCacheComplement) return false;
offsets[out] = offsets[in];
offsets[in] = kNoCacheComplement;
return true;
}
} // namespace detail
namespace {
#if defined(__linux__)
bool read_cgroup_memory_stat(const std::filesystem::path& path, uint64_t current,
detail::CgroupMemoryStat& stat) {
std::ifstream input(path / "memory.stat");
if (!input) return false;
bool inactive_file = false, file_dirty = false, file_writeback = false;
std::string line;
while (std::getline(input, line)) {
std::istringstream fields(line);
std::string key;
uint64_t value = 0;
if (!(fields >> key >> value)) return false;
fields >> std::ws;
if (!fields.eof()) return false;
if (key == "inactive_file") {
if (inactive_file) return false;
inactive_file = true;
stat.inactive_file = value;
} else if (key == "file_dirty") {
if (file_dirty) return false;
file_dirty = true;
stat.file_dirty = value;
} else if (key == "file_writeback") {
if (file_writeback) return false;
file_writeback = true;
stat.file_writeback = value;
}
}
if (!input.eof() || !inactive_file || !file_dirty || !file_writeback) return false;
stat.current = current;
stat.valid = true;
return true;
}
#endif
bool available_memory_bytes(uint64_t& bytes) {
#if defined(_WIN32)
MEMORYSTATUSEX status{};
status.dwLength = sizeof(status);
if (!GlobalMemoryStatusEx(&status)) return false;
bytes = (uint64_t) status.ullAvailPhys;
return bytes > 0;
#elif defined(__linux__)
// MemAvailable includes reclaimable page cache, unlike _SC_AVPHYS_PAGES.
std::ifstream info("/proc/meminfo");
std::string line;
bytes = 0;
while (std::getline(info, line)) {
std::istringstream fields(line);
std::string key, unit;
uint64_t value = 0;
if (fields >> key >> value >> unit && key == "MemAvailable:" && unit == "kB" &&
value <= std::numeric_limits<uint64_t>::max() / 1024) bytes = value * 1024;
}
if (bytes == 0) return false;
// Account for the tightest cgroup-v2 ancestor limit when its normal mount is visible.
// This is a point-in-time guard, not
@@ -0,0 +1 @@
Explain in three sentences what a software container is.
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Strata community benchmark runner: warm-up + N measured runs per prompt, server timings, one JSON per config.
Follows docs/COMMUNITY_BENCHMARKS.md: prompt and decode throughput from the engine's own timings, actual token
counts, reused (cache_n) vs fresh prompt tokens, every run kept (median and range computed from them).
"""
import argparse, hashlib, json, statistics, time, urllib.request
from pathlib import Path
def call(base, prompt, max_tokens):
body = {"messages": [{"role": "user", "content": prompt}], "temperature": 0, "seed": 42,
"max_tokens": max_tokens, "cache_prompt": False}
req = urllib.request.Request(base + "/v1/chat/completions", data=json.dumps(body).encode(),
headers={"content-type": "application/json"})
t0 = time.perf_counter()
with urllib.request.urlopen(req, timeout=1800) as r:
d = json.load(r)
wall = time.perf_counter() - t0
t = d.get("timings", {})
text = d["choices"][0]["message"].get("content") or ""
return {"wall_s": round(wall, 3), "timings": t, "usage": d.get("usage"),
"finish_reason": d["choices"][0].get("finish_reason"),
"content_sha256": hashlib.sha256(text.encode()).hexdigest()}
def summary(runs, key):
v = [r["timings"].get(key) for r in runs if r["timings"].get(key) is not None]
return {"median": statistics.median(v), "min": min(v), "max": max(v)} if v else None
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--base", required=True)
p.add_argument("--label", required=True)
p.add_argument("--prompts", default=str(Path(__file__).parent / "prompts"))
p.add_argument("--sizes", default="short,medium,long")
p.add_argument("--runs", type=int, default=3)
p.add_argument("--warmup", type=int, default=1)
p.add_argument("--max-tokens", type=int, default=256)
p.add_argument("--out", required=True)
a = p.parse_args()
props = json.load(urllib.request.urlopen(a.base + "/props", timeout=30))
res = {"label": a.label, "build_info": props.get("build_info"), "model_path": props.get("model_path"),
"n_ctx": props.get("default_generation_settings", {}).get("n_ctx"), "max_tokens": a.max_tokens,
"sampling": "temperature 0, seed 42, cache_prompt false", "sizes": {}}
for size in a.sizes.split(","):
prompt = (Path(a.prompts) / f"{size}.txt").read_text(encoding="utf-8")
# The server reuses a matching prefix even with cache_prompt false, so every request starts with a unique
# line: each run then reads the whole prompt fresh (cache_n stays at the chat template's few tokens).
tag = lambda kind, i: f"[{a.label} {size} {kind} {i} {time.time_ns()}]\n"
warm = [call(a.base, tag("warmup", i) + prompt, a.max_tokens) for i in range(a.warmup)]
runs = []
for i in range(a.runs):
r = call(a.base, tag("run", i + 1) + prompt, a.max_tokens)
r["run"] = i + 1
runs.append(r)
t = r["timings"]
print(f"{a.label} {size} run {i+1}: prompt {t.get('prompt_n')} (+{t.get('cache_n')} reused) "
f"{t.get('prompt_per_second')} tok/s, decode {t.get('predicted_n')} tok "
f"{t.get('predicted_per_second')} tok/s, wall {r['wall_s']} s", flush=True)
res["sizes"][size] = {"warmup": warm, "runs": runs,
"prompt_tps": summary(runs, "prompt_per_second"),
"decode_tps": summary(runs, "predicted_per_second"),
"prompt_ms": summary(runs, "prompt_ms")}
Path(a.out).write_text(json.dumps(res, indent=2), encoding="utf-8")
print("written", a.out)
if __name__ == "__main__":
main()
@@ -0,0 +1,394 @@
{
"label": "ud-q4kxl-0131-b72",
"build_info": "Strata 0.1.31",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 7.937,
"timings": {
"cache_n": 0,
"prompt_n": 104,
"prompt_ms": 2318.1,
"prompt_per_token_ms": 22.289,
"prompt_per_second": 44.9,
"predicted_n": 236,
"predicted_ms": 5485.3,
"predicted_per_token_ms": 23.243,
"predicted_per_second": 43.0,
"draft_n": 196,
"draft_n_accepted": 157
},
"usage": {
"prompt_tokens": 104,
"completion_tokens": 236,
"total_tokens": 340,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "d2dd5b0466e67bd8b31194ee1e22dda8f62d7af35a624b46ca2a7f7b80a57c5d"
}
],
"runs": [
{
"wall_s": 4.916,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1170.1,
"prompt_per_token_ms": 11.36,
"prompt_per_second": 88.0,
"predicted_n": 227,
"predicted_ms": 3675.2,
"predicted_per_token_ms": 16.19,
"predicted_per_second": 61.8,
"draft_n": 189,
"draft_n_accepted": 151
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 227,
"total_tokens": 330,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "29b2a0e2872320c8d8d3d38053ac6f18bf3e392790ea12ebbb0e0c09a159b2f3",
"run": 1
},
{
"wall_s": 2.757,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1170.2,
"prompt_per_token_ms": 11.361,
"prompt_per_second": 88.0,
"predicted_n": 109,
"predicted_ms": 1534.2,
"predicted_per_token_ms": 14.075,
"predicted_per_second": 71.0,
"draft_n": 97,
"draft_n_accepted": 64
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 109,
"total_tokens": 212,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "0a693fcb7df2a55222e67fb8b1026cc44464fa3178328ab70c7ba0b74b42af22",
"run": 2
},
{
"wall_s": 4.508,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1085.0,
"prompt_per_token_ms": 10.534,
"prompt_per_second": 94.9,
"predicted_n": 237,
"predicted_ms": 3299.2,
"predicted_per_token_ms": 13.921,
"predicted_per_second": 71.8,
"draft_n": 196,
"draft_n_accepted": 158
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 237,
"total_tokens": 340,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "29b2a0e2872320c8d8d3d38053ac6f18bf3e392790ea12ebbb0e0c09a159b2f3",
"run": 3
}
],
"prompt_tps": {
"median": 88.0,
"min": 88.0,
"max": 94.9
},
"decode_tps": {
"median": 71.0,
"min": 61.8,
"max": 71.8
},
"prompt_ms": {
"median": 1170.1,
"min": 1085.0,
"max": 1170.2
}
},
"medium": {
"warmup": [
{
"wall_s": 10.957,
"timings": {
"cache_n": 0,
"prompt_n": 2685,
"prompt_ms": 6047.2,
"prompt_per_token_ms": 2.252,
"prompt_per_second": 444.0,
"predicted_n": 256,
"predicted_ms": 4795.9,
"predicted_per_token_ms": 18.734,
"predicted_per_second": 53.4,
"draft_n": 170,
"draft_n_accepted": 139
},
"usage": {
"prompt_tokens": 2685,
"completion_tokens": 256,
"total_tokens": 2941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 6.971,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2797.3,
"prompt_per_token_ms": 1.042,
"prompt_per_second": 959.5,
"predicted_n": 256,
"predicted_ms": 4084.8,
"predicted_per_token_ms": 15.956,
"predicted_per_second": 62.7,
"draft_n": 191,
"draft_n_accepted": 154
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 6.342,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2791.7,
"prompt_per_token_ms": 1.04,
"prompt_per_second": 961.4,
"predicted_n": 256,
"predicted_ms": 3406.3,
"predicted_per_token_ms": 13.306,
"predicted_per_second": 75.2,
"draft_n": 174,
"draft_n_accepted": 153
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 6.143,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2734.1,
"prompt_per_token_ms": 1.019,
"prompt_per_second": 981.7,
"predicted_n": 256,
"predicted_ms": 3360.0,
"predicted_per_token_ms": 13.125,
"predicted_per_second": 76.2,
"draft_n": 172,
"draft_n_accepted": 150
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 961.4,
"min": 959.5,
"max": 981.7
},
"decode_tps": {
"median": 75.2,
"min": 62.7,
"max": 76.2
},
"prompt_ms": {
"median": 2791.7,
"min": 2734.1,
"max": 2797.3
}
},
"long": {
"warmup": [
{
"wall_s": 17.204,
"timings": {
"cache_n": 0,
"prompt_n": 14690,
"prompt_ms": 12690.5,
"prompt_per_token_ms": 0.864,
"prompt_per_second": 1157.6,
"predicted_n": 256,
"predicted_ms": 4432.6,
"predicted_per_token_ms": 17.315,
"predicted_per_second": 57.8,
"draft_n": 191,
"draft_n_accepted": 152
},
"usage": {
"prompt_tokens": 14690,
"completion_tokens": 256,
"total_tokens": 14946,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 10.137,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 6797.4,
"prompt_per_token_ms": 0.463,
"prompt_per_second": 2161.0,
"predicted_n": 256,
"predicted_ms": 3129.1,
"predicted_per_token_ms": 12.223,
"predicted_per_second": 81.8,
"draft_n": 192,
"draft_n_accepted": 143
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 9.83,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 6779.1,
"prompt_per_token_ms": 0.462,
"prompt_per_second": 2166.8,
"predicted_n": 256,
"predicted_ms": 2899.7,
"predicted_per_token_ms": 11.327,
"predicted_per_second": 88.3,
"draft_n": 204,
"draft_n_accepted": 145
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 9.6,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 6811.8,
"prompt_per_token_ms": 0.464,
"prompt_per_second": 2156.4,
"predicted_n": 256,
"predicted_ms": 2730.2,
"predicted_per_token_ms": 10.665,
"predicted_per_second": 93.8,
"draft_n": 197,
"draft_n_accepted": 151
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 2161.0,
"min": 2156.4,
"max": 2166.8
},
"decode_tps": {
"median": 88.3,
"min": 81.8,
"max": 93.8
},
"prompt_ms": {
"median": 6797.4,
"min": 6779.1,
"max": 6811.8
}
}
}
}
@@ -0,0 +1,394 @@
{
"label": "ud-q4kxl-0132-b40",
"build_info": "Strata 0.1.32",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 10.133,
"timings": {
"cache_n": 0,
"prompt_n": 104,
"prompt_ms": 2466.5,
"prompt_per_token_ms": 23.716,
"prompt_per_second": 42.2,
"predicted_n": 230,
"predicted_ms": 7613.8,
"predicted_per_token_ms": 33.103,
"predicted_per_second": 30.2,
"draft_n": 187,
"draft_n_accepted": 152
},
"usage": {
"prompt_tokens": 104,
"completion_tokens": 230,
"total_tokens": 334,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "d64aeac5e51858d44f3ba89dbd283f7174893ee1f0c4b41cb133a690f1e229d0"
}
],
"runs": [
{
"wall_s": 4.302,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1239.0,
"prompt_per_token_ms": 12.029,
"prompt_per_second": 83.1,
"predicted_n": 149,
"predicted_ms": 2970.8,
"predicted_per_token_ms": 19.938,
"predicted_per_second": 50.2,
"draft_n": 130,
"draft_n_accepted": 92
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 149,
"total_tokens": 252,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "432dfb689601cec022cdaec891e4d838f10aa35969955f391e108b8041328fc6",
"run": 1
},
{
"wall_s": 3.624,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1077.0,
"prompt_per_token_ms": 10.456,
"prompt_per_second": 95.6,
"predicted_n": 160,
"predicted_ms": 2457.1,
"predicted_per_token_ms": 15.357,
"predicted_per_second": 65.1,
"draft_n": 136,
"draft_n_accepted": 101
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 160,
"total_tokens": 263,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "8e98bc094205c2999a1d6e230734dea754c0f671570eaabb0179dc667ac72f70",
"run": 2
},
{
"wall_s": 5.325,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1026.6,
"prompt_per_token_ms": 9.967,
"prompt_per_second": 100.3,
"predicted_n": 231,
"predicted_ms": 4215.5,
"predicted_per_token_ms": 18.249,
"predicted_per_second": 54.8,
"draft_n": 202,
"draft_n_accepted": 149
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 231,
"total_tokens": 334,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "f44c42f22c15b68b92715535447f932f8b604dc22dc1d42203210c9a4073515f",
"run": 3
}
],
"prompt_tps": {
"median": 95.6,
"min": 83.1,
"max": 100.3
},
"decode_tps": {
"median": 54.8,
"min": 50.2,
"max": 65.1
},
"prompt_ms": {
"median": 1077.0,
"min": 1026.6,
"max": 1239.0
}
},
"medium": {
"warmup": [
{
"wall_s": 11.881,
"timings": {
"cache_n": 0,
"prompt_n": 2685,
"prompt_ms": 6622.9,
"prompt_per_token_ms": 2.467,
"prompt_per_second": 405.4,
"predicted_n": 256,
"predicted_ms": 5170.9,
"predicted_per_token_ms": 20.199,
"predicted_per_second": 49.5,
"draft_n": 187,
"draft_n_accepted": 153
},
"usage": {
"prompt_tokens": 2685,
"completion_tokens": 256,
"total_tokens": 2941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 7.779,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3418.8,
"prompt_per_token_ms": 1.274,
"prompt_per_second": 785.1,
"predicted_n": 256,
"predicted_ms": 4283.6,
"predicted_per_token_ms": 16.733,
"predicted_per_second": 59.8,
"draft_n": 176,
"draft_n_accepted": 151
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 8.201,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3359.4,
"prompt_per_token_ms": 1.252,
"prompt_per_second": 799.0,
"predicted_n": 256,
"predicted_ms": 4708.7,
"predicted_per_token_ms": 18.393,
"predicted_per_second": 54.4,
"draft_n": 193,
"draft_n_accepted": 158
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 8.183,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3368.0,
"prompt_per_token_ms": 1.255,
"prompt_per_second": 796.9,
"predicted_n": 256,
"predicted_ms": 4733.7,
"predicted_per_token_ms": 18.491,
"predicted_per_second": 54.1,
"draft_n": 191,
"draft_n_accepted": 146
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 796.9,
"min": 785.1,
"max": 799.0
},
"decode_tps": {
"median": 54.4,
"min": 54.1,
"max": 59.8
},
"prompt_ms": {
"median": 3368.0,
"min": 3359.4,
"max": 3418.8
}
},
"long": {
"warmup": [
{
"wall_s": 18.33,
"timings": {
"cache_n": 0,
"prompt_n": 14690,
"prompt_ms": 13238.7,
"prompt_per_token_ms": 0.901,
"prompt_per_second": 1109.6,
"predicted_n": 256,
"predicted_ms": 4966.3,
"predicted_per_token_ms": 19.4,
"predicted_per_second": 51.5,
"draft_n": 188,
"draft_n_accepted": 149
},
"usage": {
"prompt_tokens": 14690,
"completion_tokens": 256,
"total_tokens": 14946,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 11.775,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 8113.8,
"prompt_per_token_ms": 0.552,
"prompt_per_second": 1810.4,
"predicted_n": 256,
"predicted_ms": 3542.2,
"predicted_per_token_ms": 13.837,
"predicted_per_second": 72.3,
"draft_n": 210,
"draft_n_accepted": 149
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 11.264,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7912.1,
"prompt_per_token_ms": 0.539,
"prompt_per_second": 1856.5,
"predicted_n": 256,
"predicted_ms": 3221.2,
"predicted_per_token_ms": 12.583,
"predicted_per_second": 79.5,
"draft_n": 206,
"draft_n_accepted": 148
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 11.569,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7986.3,
"prompt_per_token_ms": 0.544,
"prompt_per_second": 1839.3,
"predicted_n": 256,
"predicted_ms": 3485.2,
"predicted_per_token_ms": 13.614,
"predicted_per_second": 73.5,
"draft_n": 206,
"draft_n_accepted": 146
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 1839.3,
"min": 1810.4,
"max": 1856.5
},
"decode_tps": {
"median": 73.5,
"min": 72.3,
"max": 79.5
},
"prompt_ms": {
"median": 7986.3,
"min": 7912.1,
"max": 8113.8
}
}
}
}
@@ -0,0 +1,394 @@
{
"label": "ud-q4kxl-0132-b72-rep",
"build_info": "Strata 0.1.32",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 7.629,
"timings": {
"cache_n": 0,
"prompt_n": 106,
"prompt_ms": 2286.1,
"prompt_per_token_ms": 21.567,
"prompt_per_second": 46.4,
"predicted_n": 231,
"predicted_ms": 5262.2,
"predicted_per_token_ms": 22.78,
"predicted_per_second": 43.9,
"draft_n": 189,
"draft_n_accepted": 153
},
"usage": {
"prompt_tokens": 106,
"completion_tokens": 231,
"total_tokens": 337,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "d2dd5b0466e67bd8b31194ee1e22dda8f62d7af35a624b46ca2a7f7b80a57c5d"
}
],
"runs": [
{
"wall_s": 5.075,
"timings": {
"cache_n": 0,
"prompt_n": 105,
"prompt_ms": 1211.9,
"prompt_per_token_ms": 11.542,
"prompt_per_second": 86.6,
"predicted_n": 227,
"predicted_ms": 3815.1,
"predicted_per_token_ms": 16.807,
"predicted_per_second": 59.5,
"draft_n": 188,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 105,
"completion_tokens": 227,
"total_tokens": 332,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "e9ab8a2995421c90b981489dd5315fef286c1a8b8efd9df6c74ed4b9d78083d3",
"run": 1
},
{
"wall_s": 3.875,
"timings": {
"cache_n": 0,
"prompt_n": 105,
"prompt_ms": 1294.0,
"prompt_per_token_ms": 12.324,
"prompt_per_second": 81.1,
"predicted_n": 154,
"predicted_ms": 2512.2,
"predicted_per_token_ms": 16.313,
"predicted_per_second": 61.3,
"draft_n": 137,
"draft_n_accepted": 89
},
"usage": {
"prompt_tokens": 105,
"completion_tokens": 154,
"total_tokens": 259,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "b2f62d36acdc0512b9c0500671a7e39f8cf10581ec2ec502ba48a87b9bcfce80",
"run": 2
},
{
"wall_s": 3.439,
"timings": {
"cache_n": 0,
"prompt_n": 105,
"prompt_ms": 1142.1,
"prompt_per_token_ms": 10.877,
"prompt_per_second": 91.9,
"predicted_n": 159,
"predicted_ms": 2189.0,
"predicted_per_token_ms": 13.767,
"predicted_per_second": 72.6,
"draft_n": 135,
"draft_n_accepted": 97
},
"usage": {
"prompt_tokens": 105,
"completion_tokens": 159,
"total_tokens": 264,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "68b1598e5c9277f2e79547b89e478d1a14082b20ec2a29ad75c18c286917de2f",
"run": 3
}
],
"prompt_tps": {
"median": 86.6,
"min": 81.1,
"max": 91.9
},
"decode_tps": {
"median": 61.3,
"min": 59.5,
"max": 72.6
},
"prompt_ms": {
"median": 1211.9,
"min": 1142.1,
"max": 1294.0
}
},
"medium": {
"warmup": [
{
"wall_s": 11.161,
"timings": {
"cache_n": 0,
"prompt_n": 2687,
"prompt_ms": 6366.5,
"prompt_per_token_ms": 2.369,
"prompt_per_second": 422.1,
"predicted_n": 256,
"predicted_ms": 4627.1,
"predicted_per_token_ms": 18.075,
"predicted_per_second": 55.3,
"draft_n": 171,
"draft_n_accepted": 144
},
"usage": {
"prompt_tokens": 2687,
"completion_tokens": 256,
"total_tokens": 2943,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 7.27,
"timings": {
"cache_n": 0,
"prompt_n": 2686,
"prompt_ms": 3150.5,
"prompt_per_token_ms": 1.173,
"prompt_per_second": 852.6,
"predicted_n": 256,
"predicted_ms": 4000.5,
"predicted_per_token_ms": 15.627,
"predicted_per_second": 64.0,
"draft_n": 170,
"draft_n_accepted": 142
},
"usage": {
"prompt_tokens": 2686,
"completion_tokens": 256,
"total_tokens": 2942,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 6.453,
"timings": {
"cache_n": 0,
"prompt_n": 2686,
"prompt_ms": 3037.6,
"prompt_per_token_ms": 1.131,
"prompt_per_second": 884.3,
"predicted_n": 256,
"predicted_ms": 3340.4,
"predicted_per_token_ms": 13.048,
"predicted_per_second": 76.6,
"draft_n": 191,
"draft_n_accepted": 156
},
"usage": {
"prompt_tokens": 2686,
"completion_tokens": 256,
"total_tokens": 2942,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 6.656,
"timings": {
"cache_n": 0,
"prompt_n": 2686,
"prompt_ms": 3029.2,
"prompt_per_token_ms": 1.128,
"prompt_per_second": 886.7,
"predicted_n": 256,
"predicted_ms": 3509.8,
"predicted_per_token_ms": 13.71,
"predicted_per_second": 72.9,
"draft_n": 172,
"draft_n_accepted": 150
},
"usage": {
"prompt_tokens": 2686,
"completion_tokens": 256,
"total_tokens": 2942,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 884.3,
"min": 852.6,
"max": 886.7
},
"decode_tps": {
"median": 72.9,
"min": 64.0,
"max": 76.6
},
"prompt_ms": {
"median": 3037.6,
"min": 3029.2,
"max": 3150.5
}
},
"long": {
"warmup": [
{
"wall_s": 17.106,
"timings": {
"cache_n": 0,
"prompt_n": 14692,
"prompt_ms": 12689.9,
"prompt_per_token_ms": 0.864,
"prompt_per_second": 1157.8,
"predicted_n": 256,
"predicted_ms": 4266.8,
"predicted_per_token_ms": 16.667,
"predicted_per_second": 60.0,
"draft_n": 199,
"draft_n_accepted": 153
},
"usage": {
"prompt_tokens": 14692,
"completion_tokens": 256,
"total_tokens": 14948,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 11.054,
"timings": {
"cache_n": 0,
"prompt_n": 14691,
"prompt_ms": 7356.2,
"prompt_per_token_ms": 0.501,
"prompt_per_second": 1997.1,
"predicted_n": 256,
"predicted_ms": 3537.2,
"predicted_per_token_ms": 13.817,
"predicted_per_second": 72.4,
"draft_n": 195,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 14691,
"completion_tokens": 256,
"total_tokens": 14947,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 10.341,
"timings": {
"cache_n": 0,
"prompt_n": 14691,
"prompt_ms": 7354.2,
"prompt_per_token_ms": 0.501,
"prompt_per_second": 1997.6,
"predicted_n": 256,
"predicted_ms": 2931.4,
"predicted_per_token_ms": 11.451,
"predicted_per_second": 87.3,
"draft_n": 197,
"draft_n_accepted": 142
},
"usage": {
"prompt_tokens": 14691,
"completion_tokens": 256,
"total_tokens": 14947,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 10.241,
"timings": {
"cache_n": 0,
"prompt_n": 14691,
"prompt_ms": 7451.0,
"prompt_per_token_ms": 0.507,
"prompt_per_second": 1971.7,
"predicted_n": 256,
"predicted_ms": 2686.2,
"predicted_per_token_ms": 10.493,
"predicted_per_second": 95.3,
"draft_n": 197,
"draft_n_accepted": 139
},
"usage": {
"prompt_tokens": 14691,
"completion_tokens": 256,
"total_tokens": 14947,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 1997.1,
"min": 1971.7,
"max": 1997.6
},
"decode_tps": {
"median": 87.3,
"min": 72.4,
"max": 95.3
},
"prompt_ms": {
"median": 7356.2,
"min": 7354.2,
"max": 7451.0
}
}
}
}
@@ -0,0 +1,266 @@
{
"label": "ud-q4kxl-0132-b72-stager4",
"build_info": "Strata 0.1.32",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"medium": {
"warmup": [
{
"wall_s": 12.841,
"timings": {
"cache_n": 0,
"prompt_n": 2688,
"prompt_ms": 7838.3,
"prompt_per_token_ms": 2.916,
"prompt_per_second": 342.9,
"predicted_n": 256,
"predicted_ms": 4905.8,
"predicted_per_token_ms": 19.163,
"predicted_per_second": 52.2,
"draft_n": 183,
"draft_n_accepted": 158
},
"usage": {
"prompt_tokens": 2688,
"completion_tokens": 256,
"total_tokens": 2944,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 6.593,
"timings": {
"cache_n": 0,
"prompt_n": 2687,
"prompt_ms": 2844.0,
"prompt_per_token_ms": 1.058,
"prompt_per_second": 944.8,
"predicted_n": 256,
"predicted_ms": 3621.4,
"predicted_per_token_ms": 14.146,
"predicted_per_second": 70.7,
"draft_n": 196,
"draft_n_accepted": 167
},
"usage": {
"prompt_tokens": 2687,
"completion_tokens": 256,
"total_tokens": 2943,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 7.231,
"timings": {
"cache_n": 0,
"prompt_n": 2687,
"prompt_ms": 2853.9,
"prompt_per_token_ms": 1.062,
"prompt_per_second": 941.5,
"predicted_n": 256,
"predicted_ms": 4260.3,
"predicted_per_token_ms": 16.642,
"predicted_per_second": 60.1,
"draft_n": 197,
"draft_n_accepted": 163
},
"usage": {
"prompt_tokens": 2687,
"completion_tokens": 256,
"total_tokens": 2943,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 6.554,
"timings": {
"cache_n": 0,
"prompt_n": 2687,
"prompt_ms": 2885.8,
"prompt_per_token_ms": 1.074,
"prompt_per_second": 931.1,
"predicted_n": 256,
"predicted_ms": 3592.9,
"predicted_per_token_ms": 14.035,
"predicted_per_second": 71.3,
"draft_n": 173,
"draft_n_accepted": 158
},
"usage": {
"prompt_tokens": 2687,
"completion_tokens": 256,
"total_tokens": 2943,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 941.5,
"min": 931.1,
"max": 944.8
},
"decode_tps": {
"median": 70.7,
"min": 60.1,
"max": 71.3
},
"prompt_ms": {
"median": 2853.9,
"min": 2844.0,
"max": 2885.8
}
},
"long": {
"warmup": [
{
"wall_s": 17.511,
"timings": {
"cache_n": 0,
"prompt_n": 14693,
"prompt_ms": 12849.4,
"prompt_per_token_ms": 0.875,
"prompt_per_second": 1143.5,
"predicted_n": 256,
"predicted_ms": 4455.4,
"predicted_per_token_ms": 17.404,
"predicted_per_second": 57.5,
"draft_n": 205,
"draft_n_accepted": 156
},
"usage": {
"prompt_tokens": 14693,
"completion_tokens": 256,
"total_tokens": 14949,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 10.65,
"timings": {
"cache_n": 0,
"prompt_n": 14692,
"prompt_ms": 6941.8,
"prompt_per_token_ms": 0.472,
"prompt_per_second": 2116.5,
"predicted_n": 256,
"predicted_ms": 3553.8,
"predicted_per_token_ms": 13.882,
"predicted_per_second": 72.0,
"draft_n": 195,
"draft_n_accepted": 153
},
"usage": {
"prompt_tokens": 14692,
"completion_tokens": 256,
"total_tokens": 14948,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 10.137,
"timings": {
"cache_n": 0,
"prompt_n": 14692,
"prompt_ms": 6875.7,
"prompt_per_token_ms": 0.468,
"prompt_per_second": 2136.8,
"predicted_n": 256,
"predicted_ms": 3160.6,
"predicted_per_token_ms": 12.346,
"predicted_per_second": 81.0,
"draft_n": 196,
"draft_n_accepted": 145
},
"usage": {
"prompt_tokens": 14692,
"completion_tokens": 256,
"total_tokens": 14948,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 10.445,
"timings": {
"cache_n": 0,
"prompt_n": 14692,
"prompt_ms": 7136.7,
"prompt_per_token_ms": 0.486,
"prompt_per_second": 2058.7,
"predicted_n": 256,
"predicted_ms": 3217.5,
"predicted_per_token_ms": 12.568,
"predicted_per_second": 79.6,
"draft_n": 207,
"draft_n_accepted": 141
},
"usage": {
"prompt_tokens": 14692,
"completion_tokens": 256,
"total_tokens": 14948,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 2116.5,
"min": 2058.7,
"max": 2136.8
},
"decode_tps": {
"median": 79.6,
"min": 72.0,
"max": 81.0
},
"prompt_ms": {
"median": 6941.8,
"min": 6875.7,
"max": 7136.7
}
}
}
}
@@ -0,0 +1,394 @@
{
"label": "ud-q4kxl-0132-b72",
"build_info": "Strata 0.1.32",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 4.441,
"timings": {
"cache_n": 0,
"prompt_n": 104,
"prompt_ms": 1428.1,
"prompt_per_token_ms": 13.732,
"prompt_per_second": 72.8,
"predicted_n": 139,
"predicted_ms": 2837.2,
"predicted_per_token_ms": 20.412,
"predicted_per_second": 49.0,
"draft_n": 125,
"draft_n_accepted": 82
},
"usage": {
"prompt_tokens": 104,
"completion_tokens": 139,
"total_tokens": 243,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "b2f62d36acdc0512b9c0500671a7e39f8cf10581ec2ec502ba48a87b9bcfce80"
}
],
"runs": [
{
"wall_s": 3.561,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1035.4,
"prompt_per_token_ms": 10.052,
"prompt_per_second": 99.5,
"predicted_n": 168,
"predicted_ms": 2476.5,
"predicted_per_token_ms": 14.741,
"predicted_per_second": 67.8,
"draft_n": 142,
"draft_n_accepted": 102
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 168,
"total_tokens": 271,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "c5f9f3aef74a8fefc29268af91fece547caae1c8a0e5a00a3df097eca44cd3c0",
"run": 1
},
{
"wall_s": 3.154,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 996.8,
"prompt_per_token_ms": 9.678,
"prompt_per_second": 103.3,
"predicted_n": 156,
"predicted_ms": 2043.2,
"predicted_per_token_ms": 13.097,
"predicted_per_second": 76.4,
"draft_n": 136,
"draft_n_accepted": 96
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 156,
"total_tokens": 259,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "310342b90761d1025df304699f3bbf49901418f121fdfbb40f86f97c57531996",
"run": 2
},
{
"wall_s": 4.433,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 939.0,
"prompt_per_token_ms": 9.117,
"prompt_per_second": 109.7,
"predicted_n": 237,
"predicted_ms": 3441.3,
"predicted_per_token_ms": 14.52,
"predicted_per_second": 68.9,
"draft_n": 184,
"draft_n_accepted": 148
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 237,
"total_tokens": 340,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "29b2a0e2872320c8d8d3d38053ac6f18bf3e392790ea12ebbb0e0c09a159b2f3",
"run": 3
}
],
"prompt_tps": {
"median": 103.3,
"min": 99.5,
"max": 109.7
},
"decode_tps": {
"median": 68.9,
"min": 67.8,
"max": 76.4
},
"prompt_ms": {
"median": 996.8,
"min": 939.0,
"max": 1035.4
}
},
"medium": {
"warmup": [
{
"wall_s": 7.753,
"timings": {
"cache_n": 0,
"prompt_n": 2685,
"prompt_ms": 2941.4,
"prompt_per_token_ms": 1.095,
"prompt_per_second": 912.8,
"predicted_n": 256,
"predicted_ms": 4634.7,
"predicted_per_token_ms": 18.104,
"predicted_per_second": 55.2,
"draft_n": 173,
"draft_n_accepted": 154
},
"usage": {
"prompt_tokens": 2685,
"completion_tokens": 256,
"total_tokens": 2941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 7.269,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2974.1,
"prompt_per_token_ms": 1.108,
"prompt_per_second": 902.5,
"predicted_n": 256,
"predicted_ms": 4166.3,
"predicted_per_token_ms": 16.275,
"predicted_per_second": 61.4,
"draft_n": 188,
"draft_n_accepted": 161
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 6.907,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2942.7,
"prompt_per_token_ms": 1.096,
"prompt_per_second": 912.1,
"predicted_n": 256,
"predicted_ms": 3948.8,
"predicted_per_token_ms": 15.425,
"predicted_per_second": 64.8,
"draft_n": 191,
"draft_n_accepted": 155
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 7.019,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3015.0,
"prompt_per_token_ms": 1.123,
"prompt_per_second": 890.2,
"predicted_n": 256,
"predicted_ms": 3881.2,
"predicted_per_token_ms": 15.161,
"predicted_per_second": 66.0,
"draft_n": 177,
"draft_n_accepted": 131
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 902.5,
"min": 890.2,
"max": 912.1
},
"decode_tps": {
"median": 64.8,
"min": 61.4,
"max": 66.0
},
"prompt_ms": {
"median": 2974.1,
"min": 2942.7,
"max": 3015.0
}
},
"long": {
"warmup": [
{
"wall_s": 11.675,
"timings": {
"cache_n": 0,
"prompt_n": 14690,
"prompt_ms": 7543.9,
"prompt_per_token_ms": 0.514,
"prompt_per_second": 1947.3,
"predicted_n": 256,
"predicted_ms": 3974.2,
"predicted_per_token_ms": 15.524,
"predicted_per_second": 64.4,
"draft_n": 200,
"draft_n_accepted": 148
},
"usage": {
"prompt_tokens": 14690,
"completion_tokens": 256,
"total_tokens": 14946,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 10.272,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7419.4,
"prompt_per_token_ms": 0.505,
"prompt_per_second": 1979.8,
"predicted_n": 256,
"predicted_ms": 2782.9,
"predicted_per_token_ms": 10.871,
"predicted_per_second": 92.0,
"draft_n": 194,
"draft_n_accepted": 145
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 10.311,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7214.5,
"prompt_per_token_ms": 0.491,
"prompt_per_second": 2036.0,
"predicted_n": 256,
"predicted_ms": 2926.1,
"predicted_per_token_ms": 11.43,
"predicted_per_second": 87.5,
"draft_n": 194,
"draft_n_accepted": 152
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 10.341,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7375.2,
"prompt_per_token_ms": 0.502,
"prompt_per_second": 1991.7,
"predicted_n": 256,
"predicted_ms": 2795.9,
"predicted_per_token_ms": 10.921,
"predicted_per_second": 91.6,
"draft_n": 192,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 1991.7,
"min": 1979.8,
"max": 2036.0
},
"decode_tps": {
"median": 91.6,
"min": 87.5,
"max": 92.0
},
"prompt_ms": {
"median": 7375.2,
"min": 7214.5,
"max": 7419.4
}
}
}
}
@@ -0,0 +1,394 @@
{
"label": "ud-q4kxl-0133-b40",
"build_info": "Strata 0.1.33",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 7.771,
"timings": {
"cache_n": 0,
"prompt_n": 104,
"prompt_ms": 2577.0,
"prompt_per_token_ms": 24.779,
"prompt_per_second": 40.4,
"predicted_n": 139,
"predicted_ms": 5073.8,
"predicted_per_token_ms": 36.502,
"predicted_per_second": 27.4,
"draft_n": 132,
"draft_n_accepted": 87
},
"usage": {
"prompt_tokens": 104,
"completion_tokens": 139,
"total_tokens": 243,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "b2f62d36acdc0512b9c0500671a7e39f8cf10581ec2ec502ba48a87b9bcfce80"
}
],
"runs": [
{
"wall_s": 4.815,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1250.0,
"prompt_per_token_ms": 12.136,
"prompt_per_second": 82.4,
"predicted_n": 148,
"predicted_ms": 3379.2,
"predicted_per_token_ms": 22.832,
"predicted_per_second": 43.8,
"draft_n": 144,
"draft_n_accepted": 92
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 148,
"total_tokens": 251,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "f201481e45b5bafd8604b7e846af01a3f6d552d36daa9476ef33c1ed2432d356",
"run": 1
},
{
"wall_s": 5.907,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1086.5,
"prompt_per_token_ms": 10.549,
"prompt_per_second": 94.8,
"predicted_n": 228,
"predicted_ms": 4679.5,
"predicted_per_token_ms": 20.524,
"predicted_per_second": 48.7,
"draft_n": 195,
"draft_n_accepted": 152
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 228,
"total_tokens": 331,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "29b2a0e2872320c8d8d3d38053ac6f18bf3e392790ea12ebbb0e0c09a159b2f3",
"run": 2
},
{
"wall_s": 2.694,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1083.2,
"prompt_per_token_ms": 10.517,
"prompt_per_second": 95.1,
"predicted_n": 105,
"predicted_ms": 1480.6,
"predicted_per_token_ms": 14.101,
"predicted_per_second": 70.9,
"draft_n": 89,
"draft_n_accepted": 61
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 105,
"total_tokens": 208,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "0a693fcb7df2a55222e67fb8b1026cc44464fa3178328ab70c7ba0b74b42af22",
"run": 3
}
],
"prompt_tps": {
"median": 94.8,
"min": 82.4,
"max": 95.1
},
"decode_tps": {
"median": 48.7,
"min": 43.8,
"max": 70.9
},
"prompt_ms": {
"median": 1086.5,
"min": 1083.2,
"max": 1250.0
}
},
"medium": {
"warmup": [
{
"wall_s": 11.55,
"timings": {
"cache_n": 0,
"prompt_n": 2685,
"prompt_ms": 6613.5,
"prompt_per_token_ms": 2.463,
"prompt_per_second": 406.0,
"predicted_n": 256,
"predicted_ms": 4877.2,
"predicted_per_token_ms": 19.052,
"predicted_per_second": 52.5,
"draft_n": 175,
"draft_n_accepted": 150
},
"usage": {
"prompt_tokens": 2685,
"completion_tokens": 256,
"total_tokens": 2941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 7.263,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3382.6,
"prompt_per_token_ms": 1.26,
"prompt_per_second": 793.5,
"predicted_n": 256,
"predicted_ms": 3739.9,
"predicted_per_token_ms": 14.609,
"predicted_per_second": 68.5,
"draft_n": 174,
"draft_n_accepted": 157
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 7.564,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3322.2,
"prompt_per_token_ms": 1.238,
"prompt_per_second": 807.9,
"predicted_n": 256,
"predicted_ms": 4073.9,
"predicted_per_token_ms": 15.914,
"predicted_per_second": 62.8,
"draft_n": 178,
"draft_n_accepted": 155
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 7.578,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3370.3,
"prompt_per_token_ms": 1.256,
"prompt_per_second": 796.4,
"predicted_n": 256,
"predicted_ms": 4129.9,
"predicted_per_token_ms": 16.132,
"predicted_per_second": 62.0,
"draft_n": 178,
"draft_n_accepted": 157
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 796.4,
"min": 793.5,
"max": 807.9
},
"decode_tps": {
"median": 62.8,
"min": 62.0,
"max": 68.5
},
"prompt_ms": {
"median": 3370.3,
"min": 3322.2,
"max": 3382.6
}
},
"long": {
"warmup": [
{
"wall_s": 18.33,
"timings": {
"cache_n": 0,
"prompt_n": 14690,
"prompt_ms": 13146.1,
"prompt_per_token_ms": 0.895,
"prompt_per_second": 1117.4,
"predicted_n": 256,
"predicted_ms": 5101.5,
"predicted_per_token_ms": 19.928,
"predicted_per_second": 50.2,
"draft_n": 202,
"draft_n_accepted": 145
},
"usage": {
"prompt_tokens": 14690,
"completion_tokens": 256,
"total_tokens": 14946,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 12.083,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 8145.1,
"prompt_per_token_ms": 0.555,
"prompt_per_second": 1803.4,
"predicted_n": 256,
"predicted_ms": 3811.7,
"predicted_per_token_ms": 14.889,
"predicted_per_second": 67.2,
"draft_n": 195,
"draft_n_accepted": 128
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 11.675,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 8174.2,
"prompt_per_token_ms": 0.556,
"prompt_per_second": 1797.0,
"predicted_n": 256,
"predicted_ms": 3384.4,
"predicted_per_token_ms": 13.22,
"predicted_per_second": 75.6,
"draft_n": 208,
"draft_n_accepted": 145
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 11.43,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 8142.4,
"prompt_per_token_ms": 0.554,
"prompt_per_second": 1804.0,
"predicted_n": 256,
"predicted_ms": 3229.0,
"predicted_per_token_ms": 12.613,
"predicted_per_second": 79.3,
"draft_n": 198,
"draft_n_accepted": 143
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 1803.4,
"min": 1797.0,
"max": 1804.0
},
"decode_tps": {
"median": 75.6,
"min": 67.2,
"max": 79.3
},
"prompt_ms": {
"median": 8145.1,
"min": 8142.4,
"max": 8174.2
}
}
}
}
@@ -0,0 +1,266 @@
{
"label": "ud-q4kxl-0133-b72-stager4",
"build_info": "Strata 0.1.33",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"medium": {
"warmup": [
{
"wall_s": 12.842,
"timings": {
"cache_n": 0,
"prompt_n": 2688,
"prompt_ms": 7769.7,
"prompt_per_token_ms": 2.891,
"prompt_per_second": 346.0,
"predicted_n": 256,
"predicted_ms": 4883.8,
"predicted_per_token_ms": 19.077,
"predicted_per_second": 52.4,
"draft_n": 171,
"draft_n_accepted": 155
},
"usage": {
"prompt_tokens": 2688,
"completion_tokens": 256,
"total_tokens": 2944,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 7.37,
"timings": {
"cache_n": 0,
"prompt_n": 2687,
"prompt_ms": 2836.6,
"prompt_per_token_ms": 1.056,
"prompt_per_second": 947.3,
"predicted_n": 256,
"predicted_ms": 4450.0,
"predicted_per_token_ms": 17.383,
"predicted_per_second": 57.5,
"draft_n": 189,
"draft_n_accepted": 159
},
"usage": {
"prompt_tokens": 2687,
"completion_tokens": 256,
"total_tokens": 2943,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 6.451,
"timings": {
"cache_n": 0,
"prompt_n": 2687,
"prompt_ms": 2848.5,
"prompt_per_token_ms": 1.06,
"prompt_per_second": 943.3,
"predicted_n": 256,
"predicted_ms": 3519.2,
"predicted_per_token_ms": 13.747,
"predicted_per_second": 72.7,
"draft_n": 183,
"draft_n_accepted": 154
},
"usage": {
"prompt_tokens": 2687,
"completion_tokens": 256,
"total_tokens": 2943,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 6.656,
"timings": {
"cache_n": 0,
"prompt_n": 2687,
"prompt_ms": 2836.1,
"prompt_per_token_ms": 1.055,
"prompt_per_second": 947.4,
"predicted_n": 256,
"predicted_ms": 3681.3,
"predicted_per_token_ms": 14.38,
"predicted_per_second": 69.5,
"draft_n": 178,
"draft_n_accepted": 155
},
"usage": {
"prompt_tokens": 2687,
"completion_tokens": 256,
"total_tokens": 2943,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 947.3,
"min": 943.3,
"max": 947.4
},
"decode_tps": {
"median": 69.5,
"min": 57.5,
"max": 72.7
},
"prompt_ms": {
"median": 2836.6,
"min": 2836.1,
"max": 2848.5
}
},
"long": {
"warmup": [
{
"wall_s": 17.51,
"timings": {
"cache_n": 0,
"prompt_n": 14693,
"prompt_ms": 12626.1,
"prompt_per_token_ms": 0.859,
"prompt_per_second": 1163.7,
"predicted_n": 256,
"predicted_ms": 4703.8,
"predicted_per_token_ms": 18.374,
"predicted_per_second": 54.4,
"draft_n": 199,
"draft_n_accepted": 139
},
"usage": {
"prompt_tokens": 14693,
"completion_tokens": 256,
"total_tokens": 14949,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 10.138,
"timings": {
"cache_n": 0,
"prompt_n": 14692,
"prompt_ms": 6922.3,
"prompt_per_token_ms": 0.471,
"prompt_per_second": 2122.4,
"predicted_n": 256,
"predicted_ms": 3082.9,
"predicted_per_token_ms": 12.043,
"predicted_per_second": 83.0,
"draft_n": 199,
"draft_n_accepted": 144
},
"usage": {
"prompt_tokens": 14692,
"completion_tokens": 256,
"total_tokens": 14948,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 9.933,
"timings": {
"cache_n": 0,
"prompt_n": 14692,
"prompt_ms": 6869.6,
"prompt_per_token_ms": 0.468,
"prompt_per_second": 2138.7,
"predicted_n": 256,
"predicted_ms": 2926.9,
"predicted_per_token_ms": 11.433,
"predicted_per_second": 87.5,
"draft_n": 208,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 14692,
"completion_tokens": 256,
"total_tokens": 14948,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 9.622,
"timings": {
"cache_n": 0,
"prompt_n": 14692,
"prompt_ms": 6861.3,
"prompt_per_token_ms": 0.467,
"prompt_per_second": 2141.3,
"predicted_n": 256,
"predicted_ms": 2663.7,
"predicted_per_token_ms": 10.405,
"predicted_per_second": 96.1,
"draft_n": 185,
"draft_n_accepted": 149
},
"usage": {
"prompt_tokens": 14692,
"completion_tokens": 256,
"total_tokens": 14948,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 2138.7,
"min": 2122.4,
"max": 2141.3
},
"decode_tps": {
"median": 87.5,
"min": 83.0,
"max": 96.1
},
"prompt_ms": {
"median": 6869.6,
"min": 6861.3,
"max": 6922.3
}
}
}
}
@@ -0,0 +1,394 @@
{
"label": "ud-q4kxl-0133-b72",
"build_info": "Strata 0.1.33",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 6.488,
"timings": {
"cache_n": 0,
"prompt_n": 104,
"prompt_ms": 2415.1,
"prompt_per_token_ms": 23.222,
"prompt_per_second": 43.1,
"predicted_n": 145,
"predicted_ms": 3865.3,
"predicted_per_token_ms": 26.657,
"predicted_per_second": 37.5,
"draft_n": 129,
"draft_n_accepted": 87
},
"usage": {
"prompt_tokens": 104,
"completion_tokens": 145,
"total_tokens": 249,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "b2f62d36acdc0512b9c0500671a7e39f8cf10581ec2ec502ba48a87b9bcfce80"
}
],
"runs": [
{
"wall_s": 5.487,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1116.2,
"prompt_per_token_ms": 10.837,
"prompt_per_second": 92.3,
"predicted_n": 237,
"predicted_ms": 4240.7,
"predicted_per_token_ms": 17.893,
"predicted_per_second": 55.9,
"draft_n": 191,
"draft_n_accepted": 155
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 237,
"total_tokens": 340,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "29b2a0e2872320c8d8d3d38053ac6f18bf3e392790ea12ebbb0e0c09a159b2f3",
"run": 1
},
{
"wall_s": 3.524,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1204.3,
"prompt_per_token_ms": 11.692,
"prompt_per_second": 85.5,
"predicted_n": 148,
"predicted_ms": 2241.9,
"predicted_per_token_ms": 15.148,
"predicted_per_second": 66.0,
"draft_n": 128,
"draft_n_accepted": 91
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 148,
"total_tokens": 251,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "811541968c32df664bb304d9ebec6d36f4f7bbec2793a88b90d3d48bba557d39",
"run": 2
},
{
"wall_s": 3.439,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1095.8,
"prompt_per_token_ms": 10.639,
"prompt_per_second": 94.0,
"predicted_n": 157,
"predicted_ms": 2247.0,
"predicted_per_token_ms": 14.312,
"predicted_per_second": 69.9,
"draft_n": 133,
"draft_n_accepted": 93
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 157,
"total_tokens": 260,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "b88fafbd705162d47ac032048a110b5dfbced7b6f79dbcc8cb776fbc34409b54",
"run": 3
}
],
"prompt_tps": {
"median": 92.3,
"min": 85.5,
"max": 94.0
},
"decode_tps": {
"median": 66.0,
"min": 55.9,
"max": 69.9
},
"prompt_ms": {
"median": 1116.2,
"min": 1095.8,
"max": 1204.3
}
},
"medium": {
"warmup": [
{
"wall_s": 10.939,
"timings": {
"cache_n": 0,
"prompt_n": 2685,
"prompt_ms": 6359.1,
"prompt_per_token_ms": 2.368,
"prompt_per_second": 422.2,
"predicted_n": 256,
"predicted_ms": 4514.2,
"predicted_per_token_ms": 17.634,
"predicted_per_second": 56.7,
"draft_n": 178,
"draft_n_accepted": 154
},
"usage": {
"prompt_tokens": 2685,
"completion_tokens": 256,
"total_tokens": 2941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 6.186,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2935.7,
"prompt_per_token_ms": 1.094,
"prompt_per_second": 914.3,
"predicted_n": 256,
"predicted_ms": 3184.5,
"predicted_per_token_ms": 12.439,
"predicted_per_second": 80.4,
"draft_n": 178,
"draft_n_accepted": 157
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 7.212,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2880.4,
"prompt_per_token_ms": 1.073,
"prompt_per_second": 931.8,
"predicted_n": 256,
"predicted_ms": 4248.1,
"predicted_per_token_ms": 16.594,
"predicted_per_second": 60.3,
"draft_n": 191,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 6.486,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2907.6,
"prompt_per_token_ms": 1.083,
"prompt_per_second": 923.1,
"predicted_n": 256,
"predicted_ms": 3424.1,
"predicted_per_token_ms": 13.375,
"predicted_per_second": 74.8,
"draft_n": 185,
"draft_n_accepted": 156
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 923.1,
"min": 914.3,
"max": 931.8
},
"decode_tps": {
"median": 74.8,
"min": 60.3,
"max": 80.4
},
"prompt_ms": {
"median": 2907.6,
"min": 2880.4,
"max": 2935.7
}
},
"long": {
"warmup": [
{
"wall_s": 17.306,
"timings": {
"cache_n": 0,
"prompt_n": 14690,
"prompt_ms": 12587.5,
"prompt_per_token_ms": 0.857,
"prompt_per_second": 1167.0,
"predicted_n": 256,
"predicted_ms": 4549.4,
"predicted_per_token_ms": 17.771,
"predicted_per_second": 56.3,
"draft_n": 188,
"draft_n_accepted": 146
},
"usage": {
"prompt_tokens": 14690,
"completion_tokens": 256,
"total_tokens": 14946,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 10.445,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7487.9,
"prompt_per_token_ms": 0.51,
"prompt_per_second": 1961.7,
"predicted_n": 256,
"predicted_ms": 2868.6,
"predicted_per_token_ms": 11.205,
"predicted_per_second": 89.2,
"draft_n": 190,
"draft_n_accepted": 149
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 10.24,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7268.1,
"prompt_per_token_ms": 0.495,
"prompt_per_second": 2021.0,
"predicted_n": 256,
"predicted_ms": 2842.4,
"predicted_per_token_ms": 11.103,
"predicted_per_second": 90.1,
"draft_n": 202,
"draft_n_accepted": 145
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 10.385,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 7240.8,
"prompt_per_token_ms": 0.493,
"prompt_per_second": 2028.6,
"predicted_n": 256,
"predicted_ms": 3066.5,
"predicted_per_token_ms": 11.979,
"predicted_per_second": 83.5,
"draft_n": 206,
"draft_n_accepted": 142
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 2021.0,
"min": 1961.7,
"max": 2028.6
},
"decode_tps": {
"median": 89.2,
"min": 83.5,
"max": 90.1
},
"prompt_ms": {
"median": 7268.1,
"min": 7240.8,
"max": 7487.9
}
}
}
}
@@ -0,0 +1,394 @@
{
"label": "0134-ud-b40",
"build_info": "Strata 0.1.34",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 10.174,
"timings": {
"cache_n": 0,
"prompt_n": 100,
"prompt_ms": 2691.3,
"prompt_per_token_ms": 26.913,
"prompt_per_second": 37.2,
"predicted_n": 213,
"predicted_ms": 7339.6,
"predicted_per_token_ms": 34.458,
"predicted_per_second": 29.0,
"draft_n": 177,
"draft_n_accepted": 141
},
"usage": {
"prompt_tokens": 100,
"completion_tokens": 213,
"total_tokens": 313,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "a5ffdf9de765fe5de2f24520fa6f06a5a77466bf23482b9d910241dffc86cf0c"
}
],
"runs": [
{
"wall_s": 5.222,
"timings": {
"cache_n": 0,
"prompt_n": 99,
"prompt_ms": 1182.4,
"prompt_per_token_ms": 11.943,
"prompt_per_second": 83.7,
"predicted_n": 204,
"predicted_ms": 3967.3,
"predicted_per_token_ms": 19.448,
"predicted_per_second": 51.4,
"draft_n": 173,
"draft_n_accepted": 133
},
"usage": {
"prompt_tokens": 99,
"completion_tokens": 204,
"total_tokens": 303,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "010c709882cf97bf2cfcd8f0583e4f5858129890bc0f4e20be87b583bc4b251a",
"run": 1
},
{
"wall_s": 5.243,
"timings": {
"cache_n": 0,
"prompt_n": 99,
"prompt_ms": 1097.6,
"prompt_per_token_ms": 11.087,
"prompt_per_second": 90.2,
"predicted_n": 228,
"predicted_ms": 4132.1,
"predicted_per_token_ms": 18.123,
"predicted_per_second": 55.2,
"draft_n": 182,
"draft_n_accepted": 148
},
"usage": {
"prompt_tokens": 99,
"completion_tokens": 228,
"total_tokens": 327,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "29b2a0e2872320c8d8d3d38053ac6f18bf3e392790ea12ebbb0e0c09a159b2f3",
"run": 2
},
{
"wall_s": 4.8,
"timings": {
"cache_n": 0,
"prompt_n": 99,
"prompt_ms": 1087.4,
"prompt_per_token_ms": 10.984,
"prompt_per_second": 91.0,
"predicted_n": 212,
"predicted_ms": 3613.7,
"predicted_per_token_ms": 17.046,
"predicted_per_second": 58.7,
"draft_n": 168,
"draft_n_accepted": 134
},
"usage": {
"prompt_tokens": 99,
"completion_tokens": 212,
"total_tokens": 311,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "010c709882cf97bf2cfcd8f0583e4f5858129890bc0f4e20be87b583bc4b251a",
"run": 3
}
],
"prompt_tps": {
"median": 90.2,
"min": 83.7,
"max": 91.0
},
"decode_tps": {
"median": 55.2,
"min": 51.4,
"max": 58.7
},
"prompt_ms": {
"median": 1097.6,
"min": 1087.4,
"max": 1182.4
}
},
"medium": {
"warmup": [
{
"wall_s": 11.871,
"timings": {
"cache_n": 0,
"prompt_n": 2681,
"prompt_ms": 6639.7,
"prompt_per_token_ms": 2.477,
"prompt_per_second": 403.8,
"predicted_n": 256,
"predicted_ms": 5118.8,
"predicted_per_token_ms": 19.995,
"predicted_per_second": 50.0,
"draft_n": 184,
"draft_n_accepted": 154
},
"usage": {
"prompt_tokens": 2681,
"completion_tokens": 256,
"total_tokens": 2937,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 7.211,
"timings": {
"cache_n": 0,
"prompt_n": 2680,
"prompt_ms": 3337.8,
"prompt_per_token_ms": 1.245,
"prompt_per_second": 802.9,
"predicted_n": 256,
"predicted_ms": 3803.0,
"predicted_per_token_ms": 14.855,
"predicted_per_second": 67.3,
"draft_n": 181,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 2680,
"completion_tokens": 256,
"total_tokens": 2936,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 7.432,
"timings": {
"cache_n": 0,
"prompt_n": 2680,
"prompt_ms": 3283.3,
"prompt_per_token_ms": 1.225,
"prompt_per_second": 816.3,
"predicted_n": 256,
"predicted_ms": 4037.4,
"predicted_per_token_ms": 15.771,
"predicted_per_second": 63.4,
"draft_n": 184,
"draft_n_accepted": 158
},
"usage": {
"prompt_tokens": 2680,
"completion_tokens": 256,
"total_tokens": 2936,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 7.225,
"timings": {
"cache_n": 0,
"prompt_n": 2680,
"prompt_ms": 3405.8,
"prompt_per_token_ms": 1.271,
"prompt_per_second": 786.9,
"predicted_n": 256,
"predicted_ms": 3790.6,
"predicted_per_token_ms": 14.807,
"predicted_per_second": 67.5,
"draft_n": 188,
"draft_n_accepted": 165
},
"usage": {
"prompt_tokens": 2680,
"completion_tokens": 256,
"total_tokens": 2936,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 802.9,
"min": 786.9,
"max": 816.3
},
"decode_tps": {
"median": 67.3,
"min": 63.4,
"max": 67.5
},
"prompt_ms": {
"median": 3337.8,
"min": 3283.3,
"max": 3405.8
}
},
"long": {
"warmup": [
{
"wall_s": 18.273,
"timings": {
"cache_n": 0,
"prompt_n": 14686,
"prompt_ms": 13050.4,
"prompt_per_token_ms": 0.889,
"prompt_per_second": 1125.3,
"predicted_n": 256,
"predicted_ms": 5127.2,
"predicted_per_token_ms": 20.028,
"predicted_per_second": 49.9,
"draft_n": 196,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 14686,
"completion_tokens": 256,
"total_tokens": 14942,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 11.783,
"timings": {
"cache_n": 0,
"prompt_n": 14685,
"prompt_ms": 7963.4,
"prompt_per_token_ms": 0.542,
"prompt_per_second": 1844.1,
"predicted_n": 256,
"predicted_ms": 3667.0,
"predicted_per_token_ms": 14.324,
"predicted_per_second": 69.8,
"draft_n": 196,
"draft_n_accepted": 154
},
"usage": {
"prompt_tokens": 14685,
"completion_tokens": 256,
"total_tokens": 14941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 12.186,
"timings": {
"cache_n": 0,
"prompt_n": 14685,
"prompt_ms": 8055.5,
"prompt_per_token_ms": 0.549,
"prompt_per_second": 1823.0,
"predicted_n": 256,
"predicted_ms": 3979.4,
"predicted_per_token_ms": 15.545,
"predicted_per_second": 64.3,
"draft_n": 197,
"draft_n_accepted": 152
},
"usage": {
"prompt_tokens": 14685,
"completion_tokens": 256,
"total_tokens": 14941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 11.571,
"timings": {
"cache_n": 0,
"prompt_n": 14685,
"prompt_ms": 7946.0,
"prompt_per_token_ms": 0.541,
"prompt_per_second": 1848.1,
"predicted_n": 256,
"predicted_ms": 3447.2,
"predicted_per_token_ms": 13.466,
"predicted_per_second": 74.3,
"draft_n": 198,
"draft_n_accepted": 151
},
"usage": {
"prompt_tokens": 14685,
"completion_tokens": 256,
"total_tokens": 14941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 1844.1,
"min": 1823.0,
"max": 1848.1
},
"decode_tps": {
"median": 69.8,
"min": 64.3,
"max": 74.3
},
"prompt_ms": {
"median": 7963.4,
"min": 7946.0,
"max": 8055.5
}
}
}
}
@@ -0,0 +1,394 @@
{
"label": "0134-ud-b72-auto32k",
"build_info": "Strata 0.1.34",
"model_path": "E:\\strata-data\\models-unsloth\\UD-Q4_K_XL\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf",
"n_ctx": 32768,
"max_tokens": 256,
"sampling": "temperature 0, seed 42, cache_prompt false",
"sizes": {
"short": {
"warmup": [
{
"wall_s": 7.101,
"timings": {
"cache_n": 0,
"prompt_n": 104,
"prompt_ms": 2343.7,
"prompt_per_token_ms": 22.536,
"prompt_per_second": 44.4,
"predicted_n": 208,
"predicted_ms": 4629.6,
"predicted_per_token_ms": 22.258,
"predicted_per_second": 44.9,
"draft_n": 172,
"draft_n_accepted": 139
},
"usage": {
"prompt_tokens": 104,
"completion_tokens": 208,
"total_tokens": 312,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "a5ffdf9de765fe5de2f24520fa6f06a5a77466bf23482b9d910241dffc86cf0c"
}
],
"runs": [
{
"wall_s": 4.713,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1212.3,
"prompt_per_token_ms": 11.77,
"prompt_per_second": 85.0,
"predicted_n": 210,
"predicted_ms": 3387.6,
"predicted_per_token_ms": 16.131,
"predicted_per_second": 62.0,
"draft_n": 170,
"draft_n_accepted": 135
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 210,
"total_tokens": 313,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "010c709882cf97bf2cfcd8f0583e4f5858129890bc0f4e20be87b583bc4b251a",
"run": 1
},
{
"wall_s": 4.708,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1204.8,
"prompt_per_token_ms": 11.697,
"prompt_per_second": 85.5,
"predicted_n": 225,
"predicted_ms": 3398.4,
"predicted_per_token_ms": 15.104,
"predicted_per_second": 66.2,
"draft_n": 189,
"draft_n_accepted": 149
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 225,
"total_tokens": 328,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "0f566f4d92516c5bfe818e8b19856b5f8742b3540e6915ff7908797d818a35ed",
"run": 2
},
{
"wall_s": 4.404,
"timings": {
"cache_n": 0,
"prompt_n": 103,
"prompt_ms": 1170.6,
"prompt_per_token_ms": 11.365,
"prompt_per_second": 88.0,
"predicted_n": 229,
"predicted_ms": 3190.3,
"predicted_per_token_ms": 13.931,
"predicted_per_second": 71.8,
"draft_n": 188,
"draft_n_accepted": 151
},
"usage": {
"prompt_tokens": 103,
"completion_tokens": 229,
"total_tokens": 332,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "stop",
"content_sha256": "29b2a0e2872320c8d8d3d38053ac6f18bf3e392790ea12ebbb0e0c09a159b2f3",
"run": 3
}
],
"prompt_tps": {
"median": 85.5,
"min": 85.0,
"max": 88.0
},
"decode_tps": {
"median": 66.2,
"min": 62.0,
"max": 71.8
},
"prompt_ms": {
"median": 1204.8,
"min": 1170.6,
"max": 1212.3
}
},
"medium": {
"warmup": [
{
"wall_s": 10.65,
"timings": {
"cache_n": 0,
"prompt_n": 2685,
"prompt_ms": 6252.0,
"prompt_per_token_ms": 2.328,
"prompt_per_second": 429.5,
"predicted_n": 256,
"predicted_ms": 4365.1,
"predicted_per_token_ms": 17.051,
"predicted_per_second": 58.6,
"draft_n": 182,
"draft_n_accepted": 167
},
"usage": {
"prompt_tokens": 2685,
"completion_tokens": 256,
"total_tokens": 2941,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 6.967,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 3046.6,
"prompt_per_token_ms": 1.135,
"prompt_per_second": 881.0,
"predicted_n": 256,
"predicted_ms": 3821.8,
"predicted_per_token_ms": 14.929,
"predicted_per_second": 67.0,
"draft_n": 181,
"draft_n_accepted": 147
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 6.241,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2981.4,
"prompt_per_token_ms": 1.111,
"prompt_per_second": 900.2,
"predicted_n": 256,
"predicted_ms": 3227.1,
"predicted_per_token_ms": 12.606,
"predicted_per_second": 79.3,
"draft_n": 181,
"draft_n_accepted": 153
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 6.97,
"timings": {
"cache_n": 0,
"prompt_n": 2684,
"prompt_ms": 2941.5,
"prompt_per_token_ms": 1.096,
"prompt_per_second": 912.5,
"predicted_n": 256,
"predicted_ms": 3960.3,
"predicted_per_token_ms": 15.47,
"predicted_per_second": 64.6,
"draft_n": 193,
"draft_n_accepted": 161
},
"usage": {
"prompt_tokens": 2684,
"completion_tokens": 256,
"total_tokens": 2940,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 900.2,
"min": 881.0,
"max": 912.5
},
"decode_tps": {
"median": 67.0,
"min": 64.6,
"max": 79.3
},
"prompt_ms": {
"median": 2981.4,
"min": 2941.5,
"max": 3046.6
}
},
"long": {
"warmup": [
{
"wall_s": 18.223,
"timings": {
"cache_n": 0,
"prompt_n": 14690,
"prompt_ms": 13680.7,
"prompt_per_token_ms": 0.931,
"prompt_per_second": 1073.8,
"predicted_n": 256,
"predicted_ms": 4344.0,
"predicted_per_token_ms": 16.969,
"predicted_per_second": 58.9,
"draft_n": 209,
"draft_n_accepted": 157
},
"usage": {
"prompt_tokens": 14690,
"completion_tokens": 256,
"total_tokens": 14946,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"runs": [
{
"wall_s": 9.523,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 5812.3,
"prompt_per_token_ms": 0.396,
"prompt_per_second": 2527.2,
"predicted_n": 256,
"predicted_ms": 3595.4,
"predicted_per_token_ms": 14.045,
"predicted_per_second": 71.2,
"draft_n": 200,
"draft_n_accepted": 142
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 1
},
{
"wall_s": 8.91,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 5599.7,
"prompt_per_token_ms": 0.381,
"prompt_per_second": 2623.2,
"predicted_n": 256,
"predicted_ms": 3165.6,
"predicted_per_token_ms": 12.366,
"predicted_per_second": 80.9,
"draft_n": 204,
"draft_n_accepted": 153
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 2
},
{
"wall_s": 8.805,
"timings": {
"cache_n": 0,
"prompt_n": 14689,
"prompt_ms": 5481.7,
"prompt_per_token_ms": 0.373,
"prompt_per_second": 2679.6,
"predicted_n": 256,
"predicted_ms": 3203.6,
"predicted_per_token_ms": 12.514,
"predicted_per_second": 79.9,
"draft_n": 210,
"draft_n_accepted": 158
},
"usage": {
"prompt_tokens": 14689,
"completion_tokens": 256,
"total_tokens": 14945,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"finish_reason": "length",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"run": 3
}
],
"prompt_tps": {
"median": 2623.2,
"min": 2527.2,
"max": 2679.6
},
"decode_tps": {
"median": 79.9,
"min": 71.2,
"max": 80.9
},
"prompt_ms": {
"median": 5599.7,
"min": 5481.7,
"max": 5812.3
}
}
}
}
Loaded 100 of 2309 files, more files were not shown because too many files have changed in this diff. Show more