Repository navigation
Regression: 256K + KV streaming prefill 1046 -> 803 tok/s (-23%), decode 44.2 -> 40.5 (-8%) from 0.1.40.1 to 0.1.40.2+ on sm_75 (2x T10 layer split) #2007
Description
What I run
- 2x Tesla T10 16 GB (Turing,
sm_75),--layer-split auto(CUDA0 layers 0-22, CUDA1 23-47), 62 GB DDR4. - IQ3_S pack,
--kv int8,--max-context 262144,--kv-resident 32768(KV streaming),--spec 4 --spec-min-p 0.5,--prefill auto,--expert-cache auto,--remote-expert-opt,--vision. - Expert caches hold 9682 of 24576 profiled pairs (~97.2% of the routed mass);
pcie_frac 0.17 / 0.30. - Both engines are
source: local, built on this PC with CUDA 13.0,-DCMAKE_CUDA_ARCHITECTURES=75.
What I see
With the same frozen prompt (256,698 tokens, prompt_token_ids equivalent via text, cache_n=0 cold read) and the same config:
| engine | cold prefill | warm decode (512 tokens) |
|---|---|---|
v0.1.40.1 (82f46a8, engine 0.1.40) |
1046-1048 tok/s | 44.16-44.18 tok/s |
v0.1.43 (7695859, engine 0.1.43) |
801-805 tok/s | 40.2-41.1 tok/s |
- prefill: -23%, decode: -8%.
- Decode is measured as 512 generated tokens on a prompt whose KV is already built (
cache_n=256744), so it is not a prompt-read artifact. - Alternating A/B (new → old → new → old, rebuilding only the engine between runs, same machine, same hour): 804.9 / 1043.7 / 802.6 / 1042.5 tok/s. The two groups do not overlap; the spread inside a group is < 0.5%.
- Earlier, in a different time window, the same new engine got 1048 tok/s once — so I do not claim it is purely a code difference in every window, but in the alternation above the version is the dominant factor.
- In the alternating run I swapped only the engine binary (
engine/strata, 59,494,008 B for 0.1.43 vs 51,955,120 B for 0.1.40) and rebuilt it from source per step, so the JSON'sengine.build.versionfield is stale (it reports the BUILD.json on disk, not the binary in use). The binary size and the rebuild timestamp are the reliable markers; I aligned each measurement with the binary swap by reconstructing the prompt-read start fromtimings.prompt_ms.
The boundary is 0.1.40.1 → 0.1.40.2. Everything at or after 0.1.40.2 that I tried inherits it, and 0.1.40.4's only speed change is Pascal sm_61 decode (#1469), which does not apply to sm_75.
What I ruled out on this machine (each with one 256K run, same frozen prompt):
| candidate | test | result |
|---|---|---|
| machine drift / clocks / thermals | old engine re-measured later in the same hour | 1046 tok/s again (42-50 C, no throttle) |
STRATA_ROUTE_TAIL_SKIP (new default 7 on CUDA) |
forced =7, then =0 |
=7 is 1.4% slower than the default; the counter shows it is active (route tail skip: 34856 missed experts skipped). Matches your own note that a 2-GPU split with nearly every expert cached gains nothing |
STRATA_PREFILL_CPU_SHARE (new default) |
=0 |
802.8 tok/s — unchanged |
STRATA_MMVQ_IL (fork F4 interleaved 2-4 column dense projections, default on) |
=0 |
804.1 tok/s — unchanged |
| prompt chunking | startup lines | both engines: prompt chunk auto: 8192 tokens, a 384-slot ring |
| PCIe share of missed experts | startup lines | both engines: pcie_frac 0.17 / CUDA1 0.30 |
So it is not tail-skip, not the CPU share, not F4/MMVQ-IL, not the chunk size, not the PCIe share.
Reproduction
- Build
82f46a8(v0.1.40.1) and7695859(v0.1.43) engines from source on ansm_75box (or any Turing/Ampere with a layer split). - Start with
--serve,--max-context 262144,--kv int8,--kv-resident 32768,--prefill auto,--expert-cache auto,--remote-expert-opt,--layer-split auto, 2 GPUs. - Send one chat completion with a ~256K-token prompt and
max_tokens: 1; readtimings.prompt_per_second(must havecache_n: 0). - Then send the same prompt 5 times with
max_tokens: 512; readtimings.predicted_per_second(each must havecache_n≈ prompt length).
My harness and raw JSON: scripts/bench_strata_256k.py + the four alt2-*.json files of the alternating run.
What I'd like
- A look at what in the
0.1.40.1..0.1.40.2range could cost ~23% of prompt speed and ~8% of decode on a 2-GPU split with ~39% of the expert pairs cached (the cache holds 9682/24576 pairs / 97.2% of the routed mass). 242 commits there, 37 with a speed/tok/s/decode/prefillkeyword. - If you can point at a switch (env var or config key) that restores the old path, that is enough for me — I do not need a fix for an older release.
- Happy to run a
git bisecton that range if you tell me which subsystem to watch (each step costs me ~8 minutes: engine rebuild + one 256K cold read). Tell me which metric or log line to compare and I will report per-commit numbers.
Environment: Linux (kernel 7.0.0), driver 580.178.04, CUDA 13.0, 2x Tesla T10 16 GB (sm_75), 62 GB RAM, NVMe. Engine source: local, built with -DCMAKE_CUDA_ARCHITECTURES=75.
Activity
xiake-1 commentedon Oct 12, 2026
Follow-up measurements on the same frozen 256K prompt and the same box, prompted by #1964 / #1975 / #1922. Neither of the two switches helps here, and 0.1.42 is already slow — so whatever I hit is older than the 0.1.42 -> 0.1.43 window those reports point at.
All runs:
--max-context 262144 --kv int8 --kv-resident 32768 --prefill auto --expert-cache auto --remote-expert-opt --layer-split auto, 2x T10 (sm_75), one cold prompt read (cache_n=0) + 5x512 decode.So on this machine:
STRATA_ADAPT_OVERLAP=0andSTRATA_LOOKAHEAD=0cost 0.5-0.9% each here (noise level) and do not recover anything.CPU prefill shareandSTRATA_MMVQ_IL(fork F4) were also ruled out earlier in the issue; prompt chunk (8192 / 384-slot ring) andpcie_frac(0.17 / 0.30) are identical on both the slow and the fast engine.mmbeanz commentedon Oct 12, 2026
Another NVIDIA split data point for the tail-skip default, on IQ3_S: RTX 5060 Ti 16 GB (gen5 x8) + RTX 3060 12 GB (gen3 x4), engine
7695859a(v0.1.43),--layer-split 40, 512K ctx, Linux/nspawn.Skip ON (default
=7) vsSTRATA_ROUTE_TAIL_SKIP=0, same config, same evening, 40 requests each:Your −1.4% on the T10 split vs our +5.2% here: both far from the advertised +10-20%, and plausibly the sign turns on how much of the decode window the expert stage actually costs (16 GB-eq cards missing experts vs a pair where the 3060's x4 link already dominates).
A measurement trap for anyone A/B-ing this flag:
hit_rateis not comparable across it. From our two engine logs: with skip ON, lookups per output token are 619.9 including the 98,217 skipped misses, but the reported denominator is 564.3 — the skipped misses are excluded. On a common denominator the sign reverses: skip ON measures 0.752 vs 0.775. So the Monitor's "expert cache hit" and/metrics hit_ratesilently change meaning with the flag, and "hit rate improves with skip ON" is an artifact. Two doc asks: a line for/metrics/Monitor consumers, and a log line when the flag is OFF — our OFF run's log carries no tail-skip lines at all, so the off state is only inferable from absence.Full artifacts (per-request
/metrics?requests=all, both engine logs with the skip counters, configs): #2083.Drafted with AI assistance; I reviewed every line and ran the measurements myself.
Niko1221 commentedon Oct 12, 2026
I tried to reproduce this on 2x RTX A4000 (sm_86, not sm_75), IQ3_XXS, --kv-resident 8192, 131072 context, 32K and 100K prompts, 3 interleaved pairs: v0.1.40.1 vs v0.1.40.2 prefill is 1981 vs 1977 tok/s at 32K (-0.2%) and 2317 vs 2304 at 100K (-0.5%). STRATA_PREFILL_STREAM_AHEAD=0 and STRATA_STAGER_SLEEP=0 (the two prefill defaults that changed in that range on Linux) make no difference either. So the cause is specific to Turing or to the 256K length. Reading the diff I found no Turing default change: the Volta attention fix is sm_70 only, the PR #540 decode attention kernel runs below sm_75 (and on HIP) only, and the FP32 block-score kernel is opt-in. Two cheap tests on your T10s would narrow it: STRATA_PREFILL_STREAM_AHEAD=0 and STRATA_STAGER_SLEEP=0, each on a 32K prompt (a 256K run is not needed to see a 23% change). If both are neutral, a bisect on 32K prompts over the commits touching src/prefill and src/core/verify.cpp in 0.1.40.1..0.1.40.2 is the next step, and I can suggest the order.
xiake-1 commentedon Oct 13, 2026
Root cause found: it is the layer split
--layer-split autopicks, not the prefill defaultsBoth switches you suggested are neutral (measured at 32K on
0.1.40.2: 1122.2 / 1124.7 / 1126.8 tok/s for default /STRATA_PREFILL_STREAM_AHEAD=0/STRATA_STAGER_SLEEP=0, i.e. all inside ±2%; re-checked at 256K on the slow engine: 795.2 / 795.2 / 794.4). So they are not the cause — but neither is the0.1.40.1 → 0.1.40.2range.On this box (2× Tesla T10 16 GB,
sm_75, layer split, driver 580.178.04, CUDA 13.0, IQ3_S,--max-context 262144 --kv int8 --kv-resident 32768 --prefill auto --expert-cache auto --remote-expert-opt --layer-split auto --spec 4 --spec-min-p 0.5, frozen prompts,serve+timings, ABBA, engines rebuilt from source per step):A 2-GPU layer-split prefill is a pipeline, so the slower stage sets the rate: 25 layers → 25/25 = 100%, 34 layers → 25/34 = 73.5%; measured 795.2 / 1034.8 = 76.8% (the difference is hand-off overhead and non-uniform layer cost). That is the whole 23% — and the ~8–15% decode loss comes with it.
Forcing the old split restores everything
0.1.43with--layer-split 23comes up exactly like0.1.40.1and runs exactly as fast:256K: 795.2 → 1033.8 tok/s (0.1.40.1: 1034.8). 32K: 937.9 → 1119.0 (0.1.40.1: 1119.4). Decode: 39.5 → 46.8.
Suggested next steps
0.1.40.1..0.1.40.2bisect on 32K — there is no signal in that range at all."layer_split": "23"in the config, or--layer-split 23. (It is box-specific — 23 is the balanced split for 48 layers over 2 cards — so it is a workaround, not a constant.)0.1.41. It predicts 55.8 ms for its own winner while the old model predicted 49.8 ms for K=23, and on this box K=14 is slower for both prefill (−23%) and decode (−15%). That reads like a misprediction in the chooser rather than an intended trade-off, and fixing it would save every user from pinning a layer count by hand.layer split auto (CUDA): a stage's link scales its miss cost only aga…
Niko1221 commentedon Oct 13, 2026
Thanks @xiake-1, that settles it, and the table shows it clearly: the split, not the prefill defaults.
The change that moves auto is 0391e47 in 0.1.41. Since then the search scales each card's miss cost by
20 GB/s / its probed link(clamped to 1..64). That came from 2x 4090 D with one card on a physical x1 link, where it took the prompt from ~350 to ~1400 tok/s. On your T10s it moved K from 23 to 14, so CUDA1 runs 34 of the 48 layers. A layer split reads a prompt as a pipeline, so the slower stage sets the rate, as you computed.In 0.1.44 (out now) the scale is taken against the fastest card's link instead of 20 GB/s, and only from a 4x gap on, capped at 64. A symmetric pair, or a moderate gap, keeps the fitted formula; the x1 case still gets about x15.
STRATA_SPLIT_LINK_ABS=1gives the 0.1.41 rule back.Could you post the two
layer split auto: CUDAn link X GB/s -> miss cost xYlines from your start? If your links differ by 4x or more, this change does not move your split and we need another look; if less, 0.1.44 should pick 23 again on its own. Until then,--layer-split 23is the right workaround.jds1-ai commentedon Oct 14, 2026
Two symmetric NVIDIA pairs, Linux, CUDA 13.3, driver 595.91.07, IQ3_XXS,
--layer-split auto --max-context 131072 --kv int8 --kv-resident 32768, MTP spec 4. Three starts each: v0.1.43.1, v0.1.44, v0.1.44 withSTRATA_SPLIT_LINK_ABS=1. Freshsetup.sh --buildper version.2x RTX 3060 12 GB, both on Gen3 x16 (probe 12.3 GB/s each):
2x RTX 5060 Ti 16 GB, both at Gen3 x8 (probe 6.9 GB/s each):
All six starts came up, 9/9 short requests plus a 1217-token prompt each,
finish=stop, engine rc=0, no Xid.Observations:
STRATA_SPLIT_LINK_ABS=1reproduces the 0.1.43.1 K and scale lines inside the 0.1.44 build on both boxes.layer_msdepends only on SM count and clock, so with identical cards the compute term is constant in K, and an equal scale on both stages multiplies the whole K-dependent cost without changing its argmin. The 3060 pair read K=26 on all three starts. The 5060 Ti pair moved one layer (24 vs 23) with scales of x2.90 and x2.91, a 0.3% probe difference breaking a near-tie.Logs for all six starts are available on request.
xiake-1 commentedon Oct 14, 2026
Here are the two lines, from the 0.1.44 and 0.1.43 starts on this box (2× Tesla T10 16 GB,
sm_75, one layer split; every engine below built from source here with-DCMAKE_CUDA_ARCHITECTURES=75):0.1.44 default (your new rule — relative to the fastest stage's link, 4× gap or more):
0.1.44 with
STRATA_SPLIT_LINK_ABS=1(the 0.1.41 rule, for contrast):0.1.43 default (0.1.41 rule) gives the same
x3.25/x1.85and the same K=14.Across every start I have logs for, the probes are stable: CUDA0 6.0–6.2 GB/s, CUDA1 10.8–10.9 GB/s — a gap of 1.76–1.79×, i.e. under your 4× threshold, so the fitted formula stays and 0.1.44 picks 23 again on its own. Your change does move my split, so no second look needed on that account.
The
STRATA_SPLIT_LINK_ABS=1arm on the same 0.1.44 binary reproduces the 0.1.41 behaviour exactly (x3.25/x1.85→ K=14), which isolates the difference to that one change and rules out anything else in 0.1.44 moving the placement. I also checked v0.1.44.1's delta — Responses/Codex, setup and nvfp4 material, nothing that touches the split.0.1.44 restores the old path on its own
Frozen prompts, one cold read (
cache_n=0) + 5×512 warm, ABBA order, same box and hour, engines swapped one at a time:So with the new rule auto lands on 23, the −23% prefill and the ~15% decode loss are both gone, and
--layer-splitis no longer needed as a workaround.One thing worth keeping in mind for this box: when I swept the split explicitly, an even 24/24 was a little faster than 23/25 — 256K: 1043.8 vs 1030.2; 32K: 1144.1 vs 1120.3. So auto landing on 23 is the right call and restores the old path, and a T10 user chasing the last 1–2% can still pass
--layer-split 24.層の分割の自動の配置: リンクの遅い段の外れの重みは、最速の段との比が 4 倍からだけ掛ける(upstream 9d41e18、Niko1…
layer split auto (CUDA): a stage's link scales its miss cost only aga…
jds1-ai commentedon Oct 17, 2026
The above-gate data promised in observation 5. One box, two matched pairs in turn, each with one card in the CPU slot and one in the chipset slot (Gen2 x4). Same engine build (
0.1.44, ed4773e, source build), same pack.Box: MSI Z97 (i7-4790K, 4C/8T, 32 GB DDR3), Linux 6.8.0-147, NVIDIA open kernel module 580.178, CUDA 13.0. Qwen3.8-Flash-Next-GSQ-RCO-IQ2_XS native pack,
--layer-split auto --max-context 32768 --kv int8 --kv-resident 32768 --resident-budget-gib 12 --spec 4 --mtp. Differences from our 10-14 runs: IQ2_XS instead of IQ3_XXS and a 32K context, both so the pack fits 32 GB of host RAM (with the default full RAM copy of the experts the engine is OOM-killed at load on this box;setup.pylists 48 GB for this pack, and--resident-budget-gib 12is what makes it start).Pair 1: 2x RTX 5060 Ti 16 GB, Gen3 x8 + Gen2 x4 (sm_120 build; 3 auto starts plus 1 with
STRATA_SPLIT_LINK_ABS=1) [n4-a-auto-s1/s2/s3-1015T23*,n4-a-abs-1015T233323]:With
STRATA_SPLIT_LINK_ABS=1on the same binary:x3.06 / x12.81,K=33 - predicted 44.7 ms, near tie K=36 at 3.3%.Pair 2: 2x RTX 3060 12 GB, Gen3 x16 + Gen2 x4 (sm_86 build of the same commit; auto starts) [
n4d-d-auto-s10-1016T144018and siblings]:Across the 21 starts on this pair CUDA0 probed 10.5-10.7 GB/s and the scale read x6.86-7.02; K=39 and 75.5 ms on every start. No
LINK_ABSstart on this pair.Observations:
Above the 4x gate the relative rule prices the slow stage at the probe ratio and nothing else: x4.17-4.19 = 6.5 / 1.56 on pair 1 and x6.86-7.02 on pair 2, the probe ratio of each start. The slow card received 15 of 48 layers on the 5060 Ti pair and 9 of 48 on the 3060 pair, fewer than the fast card in every start. Pair 1 sits 4-5% above the gate; the ratio was stable to 0.5% over four starts, so the branch did not flip, but a 4% drop in the ratio would read it as symmetric (x1.00 / x1.00) and change K. A margin or hysteresis at the threshold would remove that.
On matched cards the two rules cannot disagree on K, and they did not:
LINK_ABS=1also picked K=33 on pair 1. In the window cost the compute term is constant in K when both cards have the same SM count and clock, and the miss term is weighted (1, r) under 0.1.44 and (20/b0)(1, r) under the old rule, so the argmin is the same. This is the same argument as our symmetric runs (K unchanged on both pairs there), now checked above the gate. So the K=23 to 14 shift on the T10s required the two links to probe differently, which the lines posted above (6.2 and 10.9 GB/s) show.The probe is host-sensitive, and the absolute rule's price moves with it. The x8 5060 Ti probes 6.5 GB/s in this Haswell/DDR3 box and 6.9 GB/s in a Xeon box with the same card model in a Gen3 x8 slot; the x16 3060 probes 10.5-10.7 here and 12.3 there. The probe is an end-to-end host-to-device copy, and a slow host lowers the readings more on the wider link (-6% at x8, -14% at x16), so the old rule's price for the same card model changes with the box (
20/6.5 = x3.06here againstx2.90there) while the relative rule's price moves less, since both links on a box share the host.Hand sweep around the chosen K on pair 1, nine ~105-token requests of 512 tokens per cell, two runs of most cells [
n4pp-app-sweep-K*-1016T00*,n4q-q-*-1016T09*]. ms per verify pass at matched draft acceptance (tok/pass 2.0, fitted per cell), then pooled decode tok/s:* The first auto K=33 start's long prompt was refused (HTTP 400, context), so 269 is the forced
--layer-split 33start and 288 the second auto start.Between-run spread at the same K is 2% or less on the matched-pass column at K=24, 29 and 33 (6% at K=36, where the second fit is unstable) and up to 24% on the prompt column (K=24: 229 against 283), so the prompt column does not separate K=24 from K=33 here. On the pass column K=29 is 7-9% faster than the auto choice in both runs, K=24 ties it, and K=36 and K=41 are 17-42% slower on decode tok/s: at K >= 36 the misses exceed the 12 GiB RAM budget and spill to the file tier (the
RAM budgetload line reads 11.54 GiB of GPU-missed experts at K=24 and 33, all held, against 13.31 GiB at K=36 and 16.80 GiB at K=41, so 1.3 and 4.8 GiB of them are read from the files). So on this pair the rule's K=33 is 4 layers from the measured best and on the side that keeps the misses in RAM; the near tie it printed (K=36) is the worse direction. The predicted window ranks K=33 ahead of K=36 correctly (38.8 against 39.3 ms) but the measured gap is 20%, since the prediction prices decode misses at link speed and does not know when the host runs out of RAM for them.--prefill auto(K=24, 35, 39, 42) measured the SSD, not the split: on this box the prompt path re-reads the lent experts from the drive on every 8192-token chunk (page cache too small for the chunk's set; filed as [Linux, 2x RTX 3060, 32 GB RAM, SATA SSD, 0.1.44]--prefill autopicks 8192 by VRAM alone; lent slots + file tier exceed the page cache, so a 28.7K prompt re-reads 28-45 GB (TTFT 164-287 s);--prefill 4096fits: 0.9-1.4 GB read, TTFT 36-38 s #2824). The paired comparison was therefore rerun at a fitting seat,--prefill 4096 --ple-io mmap --resident-budget-gib 16:--layer-split 24against auto (K=39), with a second auto start at the same seat earlier the same day as the between-run control [n4d-d-k24-b16-pf4096-mmap-reps-1016T223628,n4d-d-auto-b16-pf4096-mmap-reps-1016T224857,n4d-d-auto-s10-b16-pf4096-mmap-reps-1016T211559]. At K=24 the x4 card holds 4617 of its 12288 profiled pairs; at K=39 it holds 4586 of 4608. The drive share was the same on all three cells (0.8-0.9 GB read per short request), so it pairs out. Same 9 short requests and the same 28.7K-token prompt, three reps, as in observation 4.The rule's K=39 halves the long-prompt time (1.9-2x the prefill rate by rep, against both auto starts) and is 3-12% faster on short-prompt decode, winning 7-8 of the 9 prompt-by-rep pairs; one short request on the paired auto start ran at 19.9 tok/s directly after a
308 MiB of VRAM freewarning (#2065), which is what unsettles that cell's fit. At 28K depth the 256-token decode went the other way: K=24 was faster on both reps against both auto starts, per pass as well as per token, so draft acceptance is not the cause. That line has two reps per cell and the two auto starts differ by 21% on it, so it gives a direction, not a size. We have not tested why. The rule prices expert misses and not the attention work of the layers each card runs, and at 28K that work is not small; a sweep at depth would separate the two. Rep 1 of the long prompt (cold fill) is in the logs and not scored: 317 / 453 / 486 prefill tok/s.n4w-w-auto-1016T121634,n4g-g-auto-1016T122703]. Not a rule result; given for scale.Not tested:
LINK_ABS=1on the 3060 pair; gaps between 1.8x and 4.2x; the 4x threshold with unmatched cards (where the compute term is not constant in K and the rules can differ).Logs for every start (engine log, argv, per-request JSON) are available on request.
Posted by jds1-ai. Runs by an agent (Fizz, Claude model); source reading, pre-registered scoring and this write-up by a second agent (Fable, Claude model); reviewed by the account owner before posting.
Layer split auto: STRATA_SPLIT_UNIFORM=1 restores the uniform miss cost
jds1-ai commentedon Oct 17, 2026
Follow-up to our comment of 2026-10-17 (#2007 (comment)), covering the item it listed as not tested: the 4x threshold with unmatched cards, where the compute term is not constant in K and the two rules can pick different K.
Box: the same MSI Z97 (i7-4790K, 32 GB DDR3, Linux 6.8.0-147, NVIDIA open kernel module 580.178, CUDA 13.0), same engine build (
0.1.44, ed4773e, source build withCMAKE_CUDA_ARCHITECTURES=86;120), same pack (Qwen3.8-Flash-Next-GSQ-RCO-IQ2_XS). Cards this time: CUDA0 = RTX 3060 12 GB in the CPU slot, CUDA1 = RTX 5060 Ti 16 GB in the chipset slot.LnkStaread under load during theLINK_ABS=1cell's decode: 01:00.0 at 8 GT/s x16, 02:00.0 at 5 GT/s x4. Flags are the fitting seat from observation 5,--prefill 4096 --ple-io mmap --max-context 32768 --kv int8 --kv-resident 32768 --spec 4 --mtp, with--resident-budget-gib 15(16 left 91 MB in swap after load on this box; 15 andvm.swappiness=10were used for every cell here). One fresh engine start per cell, the same three prompts as before: a 7.7K-token warm-up, nine ~105-token requests of 512 tokens, then the 28.7K-token prompt three times with 256 completion tokens.Split lines,
--layer-split auto[n4c-c-auto-b15-pf4096-mmap-reps-1017T101750], trailing clauses trimmed where marked:With
STRATA_SPLIT_LINK_ABS=1on the same binary [n4c-c-linkabs-b15-pf4096-mmap-reps-1017T102815]:x1.88 / x12.87,K=33 - predicted 68.2 ms, near tie K=30 within 1.7%.Observations:
On unmatched cards the two rules differ, as the argument in observation 2 said they could. The matched 3060 pair probed the same link ratio (x6.86-7.02 there, x6.80 here) and chose K=39; this pair chooses K=29 under the relative rule and K=33 under
LINK_ABS=1. The ratio is the same within 2%, so the 10-layer move from 39 comes from the compute term (1.45 against 0.77 ms per layer), and the absolute scales, which raise both link costs about 1.9x against the fixed per-layer time, move K four layers toward the slow card.Hand sweep around the two picks, same prompts, one start per cell.
CUDA1 slotsis the engine'sexpert cache N slots ... N of its M profiled pairsline for the slow card;CUDA0 slotsis itsexpert cache N slotsline (the engine prints no CUDA0 pair count). Short requests: median over the nine, min and max in brackets. Long prompt: reps 2 and 3 (rep 1 is the cold fill and is not scored).--layer-split 25LINK_ABS=1--layer-split 33--layer-split 37† During this cell's long-prompt reps 2-3 the kernel wrote 663 MB of swap to the SSD that holds the model, against 1,178 MB the prompt read from it. The
--layer-split 33cell is the same split without that, so its prompt column is the one to read; the two cells' short-request columns agree within 1%.‡ This cell paged during the scored phases: 19.8k swap-ins during the short requests against 3.2-3.3k in the K=25 and K=33 cells, and 1.9 GB of the engine's heap was written out during them. Its numbers are reported, not ranked.
Spread inside this sweep:
LINK_ABS=1and--layer-split 33are the same split. Their expert-cache hit and lookup counts are identical on the warm-up and the first two short requests and diverge from the third onward; their short-request medians differ by 1.0%, which is the between-run spread at a fixed split here. Rep 2 against rep 3 on the long prompt differs by 0.3-3.0% in the unflagged cells. On decode at depth the auto cell's two reps differ by 17%, so that column gives a direction, not a size, below that.Expert-cache lines by cell: CUDA0's slot count falls as K rises (5712 at K=25 to 5562 at K=37; the prompt path borrows about 1,530 CUDA0 slots, 2.03 GiB, in every cell). Over the nine short requests the engine's
decode expert cache hit ratereads 94.9-97.3% at K=25, 93.4-96.0% at K=29 (median 95.7), 90.6-95.6% at K=33 (91.1-95.5% in theLINK_ABS=1cell) and 84.9-90.9% at K=37 (median 88.2). Its share of routed experts read over PCIe is 0.2-0.6%, 0.4-1.1% (median 0.5), 0.5-1.6% and 1.1-2.3% (median 1.7) in the same order.On this pair the relative rule's K=29 is not the best cell on any column.
--layer-split 33, theLINK_ABS=1pick, matches it on short-request decode (within 1%) and is 11-13% faster on prompt time, prefill rate and decode at depth, on all four long-prompt reps, outside the 3% rep-to-rep spread. The near-tie line's own caveat applied: the rule's two candidates were a decode tie and the prompt path separated them.--layer-split 25is 13% faster than auto on short-request decode and 22% faster at 28K depth (its two reps are both above both of auto's), and 37% slower on the prompt (610 against 849 tok/s). This is the same shape as pair 2 (observation 5, K=24 against K=39): the lower K wins decode, the higher K wins the prompt, now on a second pair with different cards and an auto pick 10 layers lower.--layer-split 37, the K the link term alone would choose on this ratio (the matched 3060 pair chose 39), is 18% slower than auto on short-request decode and 20% slower at depth, and ties on the prompt. It is also the cell with the fewest CUDA0 slots, a median hit rate 7.5 points under auto's, about 3.4 times auto's over-PCIe share on medians, and the paging in ‡. So its penalty includes CUDA0 misses served from the file tier, and this sweep does not separate that from the compute term the rule prices.The auto cell's short-request spread (38.2-64.8) comes from three requests: two ~105-token prompts that the engine took 5.3 and 7.2 s to read (5.6 and 7.3 s to first token at the client), and one that decoded at 38.2 tok/s. No VRAM warning precedes any of them; the load-time lines (
446 MiB of VRAM free,CUDA1: 330 MiB ... LOW) are the same in every cell except K=37 (2,894 MiB free on CUDA1). The auto cell's short-request phase had 9.8k swap-ins against 3.2-3.3k in the K=25 and K=33 cells; it was the first cell after swap was cleared. Medians are quoted for that reason.Not tested: K between 29 and 33 or below 25; more than one start per hand cell (the K=33 pair is the only replicate); the reverse arrangement, 16 GB card in the CPU slot and 12 GB card on the x4 link. Seat differs from pair 2 by
--resident-budget-gib 15against 16.Logs for every start (engine log, argv, per-request JSON,
/proc/vmstatand/proc/<pid>/statussamples per phase) are available on request [n4c-c-{auto,linkabs,k25,k33,k37}-b15-pf4096-mmap-reps-1017T10*].Posted by jds1-ai. Runs by an agent (Fizz, Claude model); source reading, pre-registered scoring and this write-up by a second agent (Fable, Claude model); reviewed by the account owner before posting.
Niko1221 commentedon Oct 19, 2026
Thanks for the report. This is fixed in 0.2.0-beta (or an earlier release), and we're closing it as part of the tracker reset for 0.2.0-beta.
If it still happens on 0.2.0-beta, add the area prefix to the title ([Engine], [GUI], [TUI], [Benchmark], [Docs] or [Other]), put the Strata version and your hardware in the issue form fields, and comment
/reopen. A check runs and reopens it.Layer split auto: STRATA_SPLIT_UNIFORM=1 restores the uniform miss cost