Skip to content

[Engine] prefill: opt-in sparse copied events and deduplicated slot waits (Windows HIP prompt reads +36% at 4K, +15% at 32K) - #4005

Closed
Vop3p wants to merge 1 commit into
Niko1221:mainfrom
Vop3p:prefill-copy-events
Closed

Vop3p wants to merge 1 commit into
Niko1221:mainfrom
Vop3p:prefill-copy-events

Conversation

@Vop3p

@Vop3p Vop3p commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Branch: Vop3p/Strata:prefill-copy-events (one commit on main 7dd5cf1). Not run on NVIDIA, SYCL or Metal hardware (see Not run). No default changes.

Problem and result

On Windows (WDDM) every cudaEventRecord and every cross-stream cudaStreamWaitEvent is a submission of its own. A small HIP probe on an RX 6900 XT (Windows 11, driver 32.0.21045.16010, ROCm 10.2.0a20260930 runtime) against the same card model on Linux (ROCm 10.0), 2.79 MB host-to-device copies from registered memory, µs per copy:

per copy Windows Linux
copy only 222 205
+ cudaEventRecord on the copy stream 301 (+79) 208 (+3)
+ cudaStreamWaitEvent on another stream 343 (+42) 208 (+0)
record + wait + small kernel, one event per copy 352 209
the same, one event per 8 copies 239 205

Bandwidth is not the gap: back-to-back copies reach 12.5 GB/s (5.5 µs fixed cost per copy) against 14.1 GB/s (7.5 µs) on Linux.

The streamed walk of the prompt path pays one record and one wait per expert on the copy stream: the issuer records copied[slot] after every copy and waits on the slot's release event before every copy. The consumer, though, gathers an MMQ group of 16 experts after one wait on its last copy and releases the whole group with one event, so 15 of every 16 records are never waited on, and the copy stream waits on the same release event up to 16 times in a row. With STRATA_PREFILL_TIMING=1 on Windows this shows as wait copy 2.1 s of a 4K prompt (Linux: 0.08 s).

This PR adds two opt-in switches. Both off (the default), the walk issues exactly the calls it issued before.

  • STRATA_PREFILL_COPY_EVENT_EVERY=N (2..64): the issuer records the copied event every N-th entry, and also before it waits (ring full, or the stager behind) and on the last entry; the consumer waits on the first recorded entry at or after the one it needs. The copy stream is in order, so a later entry's event covers the earlier copies.
  • STRATA_PREFILL_COPY_WAIT_DEDUPE=1: the copy stream skips the wait on a slot's release event when the previous slot was released by the same event (one flush releases a whole group), from the second ring round of the walk on.

Result on the RX 6900 XT under Windows 11, the same build with the switches off vs EVERY=16 + DEDUPE=1, 10 cold-start pairs at the default --prefill auto (6144 on Windows HIP, #1630):

prompt switches off (median of 10) EVERY=16 DEDUPE=1 (median of 10) per pair faster in
4K 589.2 tok/s 803.5 tok/s +35.5% to +36.8% (median +36.4%) 10/10
32K 828.7 tok/s 951.3 tok/s +14.3% to +15.0% (median +14.9%) 10/10

Order B SD SD B, five times, a fresh engine per run; each run is the median of two requests per size (benchmark.py-style synthetic prompts, a new nonce per request). Decode 44.5 / 44.5 (4K) and 44.2 / 46.2 (32K, different continuations). Every setting both arms share: --prefill auto (6144 here), --expert-cache auto, --vram-reserve-mib 1536, --kv int8 --kv-resident 32768, --max-context 65536, --spec 4 --spec-min-p 0.5, --pcie-frac 0.2, STRATA_IO_THREADS=128, STRATA_HIP_PROMPT_F16=1, STRATA_HIP_ADAPT_KERNEL_COPY=1, STRATA_PREFILL_CPU_SHARE=auto, STRATA_HC_PERSIST=1, STRATA_PIPED_CONCURRENT=1, STRATA_PREFILL_STREAM_MIN=2048, a rocBLAS table tuned on this PC; the display on the iGPU. Every run's numbers are in the table at the end.

Decode is unchanged (it does not use this path). On Linux the switches change nothing measurable (below).

Related discussion

Approach

Only the threaded issuer (STRATA_PREFILL_ISSUER on, the default) and only the streamed walk change; the staged walk (chunks below stream_all_min()) and the inline issuer keep their per-slot events.

Sparse records: the issuer keeps rec_of[slot] (the entry the slot's event marks) and a_rec (one past the last recorded entry). wait_copied(k) spins until a_rec > k, then waits on the first r in [k, a_rec) with rec_of[r % ring] == r. That slot cannot be re-recorded before k is given back, because the issuer stays at most one ring ahead of what was given back and k is not given back yet. The issuer records before every wait of its own, so a consumer never waits on an entry nobody will record. All three consumers of copied[] in the streamed walk (the group flush, the per-expert path when group gather is off, the fused path) go through wait_copied.

Deduplicated waits: within one walk, from the second ring round on, two consecutive slots that point at the same release event were released by the same flush (a flush releases contiguous slots, and its event is the group's last slot, which is not re-recorded before the issuer reaches it), so the wait for the previous slot already covers this one.

Compatibility: no file format, API or default changes; the switches are read once. They are meant for WDDM; on Linux they measure as no-ops.

Validation

Passed:

  • Default path byte-identical and switches output-identical, Linux, RX 6900 XT (gfx1030), ROCm 10.0, built with the setup.py HIP options (-DSTRATA_PREFILL_MMQ=ON -DSTRATA_Q6K_EXPERTS=ON -DSTRATA_MMQ_KQUANTS=ON, plus tests), Qwen3.8-Flash-Next IQ3_S pack, reproducible settings STRATA_IQ_MT_MIN=1 --pcie-frac 0 --adapt-every 0 STRATA_PREFILL_CPU_SHARE=0, temperature 0, 4K and 32K prompts x2: main 7dd5cf1 vs this branch with the switches off vs this branch with EVERY=16 DEDUPE=1: all 4 outputs identical across the three builds. The confirmation line strata: prompt copies: a copied event every 16 streamed experts ..., one wait per release event printed; STRATA_PREFILL_TIMING reports ~14,100 of ~20,700 slot waits skipped per 32K prompt.
  • Linux speed, same runs: prompt 4K 915 / 907 / 913, 32K 1,101 / 1,102 / 1,099 tok/s (main / off / on).
  • ctest -j 1 on gfx1030 (Linux, the branch): 128 passed, 13 skipped (not applicable to gfx1030), 3 failed; the same 3 fail on main 7dd5cf1 with the same output (see Failed).
  • Windows 11, RX 6900 XT: tools\hip\build_windows.bat (gfx1030, ROCm 10.2.0a20260930) builds; the A/B above; strata bench run (standard track) on this branch with the switches on: 21/21 requests, sanity 12/12 (instruction 4/4, retrieval 4/4, tool calls 4/4), prompt 4K 793 / 16K 979 tok/s.
  • CUDA 13.2.86 (GCC 15.2): strata_prefill compiles (-DSTRATA_ENABLE_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=89).

Failed (also on main 7dd5cf1, not caused by this change):

Not run:

  • Not executed on NVIDIA hardware (CUDA compile only). Not built for SYCL or Metal (no toolchain here; sycl/CMakeLists.txt compiles prefill.cpp).
  • No other Windows GPU; no gfx11/gfx12.
  • The ctest suite does not reach the streamed walk with the issuer thread for most tests; the switches' correctness check is the end-to-end comparison above.

Limits

  • Measured on one card (RX 6900 XT, PCIe 3.0 x16, i9-10900K, 64 GB) under Windows 11 with HIP. NVIDIA under WDDM likely has a smaller per-call cost; not measured.
  • The gain shrinks with bigger chunks (fewer streamed passes): at --prefill 8192 (3 pairs) the same switches give +35.8% to +37.4% at 4K but only +3.0% to +3.9% at 32K (1,070-1,072 -> 1,103-1,112 tok/s).
  • EVERY=16 matches MMQ_GROUP; other values were not swept.
Every run (Windows 11, RX 6900 XT, in run order)
# arm --prefill 4K prompt tok/s 32K prompt tok/s 4K decode 32K decode
1 off auto (6144) 587.7 828.6 44.5 43.7
2 on auto (6144) 803.8 951.8 44.4 43.8
3 on auto (6144) 804.6 951.3 44.4 47.9
4 off auto (6144) 589.1 829.1 45.2 43.8
5 off auto (6144) 589.4 827.7 44.4 46.6
6 on auto (6144) 802.9 950.7 44.4 46.6
7 on auto (6144) 802.8 950.8 44.5 43.6
8 off auto (6144) 587.6 829.1 45.1 43.6
9 off auto (6144) 590.2 828.7 44.3 43.7
10 on auto (6144) 805.6 952.6 44.6 44.5
11 on auto (6144) 805.4 950.8 44.4 46.5
12 off auto (6144) 594.4 831.8 44.5 43.9
13 off auto (6144) 588.6 828.6 44.5 45.3
14 on auto (6144) 802.7 952.3 44.5 46.6
15 on auto (6144) 805.1 952.5 44.5 43.2
16 off auto (6144) 590.3 829.3 44.5 44.7
17 off auto (6144) 588.4 827.6 44.8 44.5
18 on auto (6144) 802.1 951.0 44.5 47.2
19 on auto (6144) 803.2 951.3 44.8 45.8
20 off auto (6144) 589.7 827.5 44.4 46.0
21 off 8192 586.9 1071.7 44.3 42.4
22 on 8192 804.9 1103.7 43.9 45.2
23 on 8192 804.2 1103.4 44.5 42.7
24 off 8192 592.2 1070.7 44.6 44.6
25 off 8192 590.1 1070.0 44.4 43.4
26 on 8192 811.0 1112.1 47.2 43.1

Each cell is the median of the run's two requests of that size. No run failed or was dropped.

Developed with an AI coding assistant; every number above was measured on the RX 6900 XT machines named in it (Windows 11 / i9-10900K and Linux / Ryzen 5 5600X).

🤖 Generated with Claude Code

…the streamed walk

Under WDDM every cudaEventRecord and every cross-stream wait is a submission of
its own (~80 us and ~45 us on an RX 6900 XT under Windows 11; ~3 us and ~0 on
Linux). The streamed walk's issuer recorded a copied event and waited on the
slot's release event for every expert, although the consumer gathers an MMQ
group after one wait on its last copy and releases the group with one event.

STRATA_PREFILL_COPY_EVENT_EVERY=N records the copied event every N-th entry
(and before the issuer waits on the ring or the stager, and on the last entry);
the consumer waits on the first recorded entry at or after the one it needs.
STRATA_PREFILL_COPY_WAIT_DEDUPE=1 skips the copy stream's wait on a slot's
release event when the previous slot (in the same walk, from the second ring
round) was released by the same event. Both are off by default; the default
path issues the same calls as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Vop3p added a commit to Vop3p/Strata that referenced this pull request Oct 25, 2026
…at --prefill 8192

The same local build and settings as ...-c97e2108 with --prefill 8192 instead of auto (6,144), so the last step is
comparable with the package run at 8,192 (...-0e04f3ea): complete, 12/12, prompt 4K 808.5 / 16K 1,125.1 tok/s, decode
43.9 / 45.5 / 45.3. README, results.json, config.json, outputs.jsonl and provenance; engine log left out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Vop3p
Vop3p marked this pull request as ready for review October 25, 2026 03:57
@Vop3p

Vop3p commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

The metal-link failure is not from this change. The job stops in metal/src/mtl_rt.mm, which this PR does not touch:

mtl_rt.mm:69:70: error: use of undeclared identifier 'MTLGPUFamilyMetal4'; did you mean 'MTLGPUFamilyMetal3'?
mtl_rt.mm:92:11: error: property 'mathMode' not found on object of type 'MTLCompileOptions *'
mtl_rt.mm:92:34: error: use of undeclared identifier 'MTLMathModeFast'

Those are macOS 26 SDK symbols, and the runner builds with Xcode_15.4.app (its MacOSX.sdk has MTLGPUFamilyMetal3 as the newest family). Other open PRs fail the same step the same way (#4010, #4039, #4040). main does not run this workflow, so it does not show there.

Marking it ready for review. It has still not run on NVIDIA, SYCL or Metal hardware (CUDA compile only; see "Not run" in the description), and the switches it adds are off by default.

Niko1221 pushed a commit that referenced this pull request Oct 27, 2026
…he streamed prompt walk (#4005)

Under WDDM every cudaEventRecord and cross-stream wait is a submission of its own (the author measured ~80 / ~45 us each
on an RX 6900 XT under Windows 11), and the streamed walk paid one record and one wait per expert although a group
gathers after one wait on its last copy. Two tester switches, threaded issuer only:
- STRATA_PREFILL_COPY_EVENT_EVERY=N (2..64): the copied event is recorded every N-th entry, before the issuer waits and on
  the last; the compute stream waits on the first recorded entry at or after the one it needs (the copy stream is in order).
- STRATA_PREFILL_COPY_WAIT_DEDUPE=1: the copy stream skips its wait on a slot's release event when the previous slot was
  released by the same event.
The same copies into the same slots: the same bytes.

Default path (both unset): read line by line. The issuer records copied[slot] and waits on used[used_of[slot]] exactly as
before (the counters are bookkeeping, cover() is a no-op). Added to the PR so this holds by construction: wait_copied takes
the slot the old code used and, without the sparse switch, waits on copied[slot] at all three consumer sites (the PR derived
the slot from the entry number). The record/wait selection is now three pure functions in
include/strata/prefill/copy_events.hpp, with a host test (copy_events_test, registered in CMakeLists.txt) that models the
issuer and consumer with a full ring and checks that every wait names a live recorded entry at or after the one needed.

Tests: copy_events_test passes (z440, g++ -std=c++20). Not yet: a CUDA build of prefill.cpp in this tree (queued), no GPU
run. Not covered: the Windows HIP / WDDM speed claim and NVIDIA (the gate is written down). Needs the author's numbers
repeated before any default talk.
Niko1221 added a commit that referenced this pull request Oct 27, 2026
…released in the current walk and the ring is longer than one group (#4005)

Review fix on the #4005 port. STRATA_PREFILL_COPY_WAIT_DEDUPE=1 skipped the copy stream's wait on a slot's release
event when the previous entry had waited on the same event. Two cases break that:
- At idx == ring the previous entry (ring - 1) is still in the first round, so its wait was on the event of an earlier
  walk. The same slot id can be this walk's first flush, which re-records the event after that wait; skipping the wait for
  slot 0 then lets the next copy overwrite a slot that the flush's gather has not finished reading.
- With a ring of one MMQ group (16, the smallest ring) a flush of the whole ring re-records the event right after the
  previous slot's wait.
The rule is now copy_wait_dedupe_ok() in include/strata/prefill/copy_events.hpp: idx > ring, ring > group, same event.

copy_events_test models two to three walks with random group sizes and random issuer/consumer interleaving and checks that
a refill always waits on a record at or after the flush that released its slot. The same model with the PR's original rule
(idx >= ring) finds the stale-event case, so the test can fail. The default path is unchanged (dedupe off).

Tests: copy_events_test passes (z440, g++ -std=c++20). Not covered: a GPU run.
Niko1221 added a commit that referenced this pull request Oct 27, 2026
…ports for the conversation cache, prefill events, expert source and batch groups)
@Niko1221

Copy link
Copy Markdown
Owner

Thank you. Fixed in 0.2.7: Opt-in sparse copied events and deduplicated slot waits in the streamed prompt walk. Your name is on the commit.

Release: https://github.com/Niko1221/Strata/releases/tag/v0.2.7

@Niko1221 Niko1221 closed this Oct 27, 2026
fragtion added a commit to fragtion/StrataPool that referenced this pull request Oct 27, 2026
… forwarded to the workers

- The coordinator's CONFIG carries "env": the pool-wide opt-ins set on it (kPoolForwardEnv: Niko1221#4005's
  STRATA_PREFILL_COPY_EVENT_EVERY / _COPY_WAIT_DEDUPE, Niko1221#3779/Niko1221#3712's STRATA_PARK_ASYNC / STRATA_CKPT_ASYNC /
  STRATA_CKPT_GATHER, and STRATA_ROUTE_PRIOR).  A worker applies each one it has not set itself, before its first prompt,
  park, checkpoint or verifier reads it, and logs "strata pool: from the coordinator: ...".  STRATA_DEFAULTS=026 is not
  forwarded (a worker reads it before CONFIG) and the VRAM/RAM-sized switches stay per PC.
- <config>.pool.json takes "env": {STRATA_*: value} (checked: up to 32, STRATA_* names, short values without ; or
  newlines), applied to the engine of either split role through pool_env; the Pool tab's save keeps it.
- Niko1221#4010's STRATA_PIPE_ADAPT_EVERY start-up line also on a pool coordinator (the pipelined groups drain the workers'
  stages before a round; the workers already swap between the windows they serve).
- Checked for a worker: the async park's device copy is synchronised before the park returns (only the copy to RAM
  runs on), so a following prompt cannot overwrite what is being copied.
- docs/POOL.md: "Opt-in switches for a split" (what each does, pool-wide or per PC, how to set them).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
riceharvest added a commit to riceharvest/strata-b70-only that referenced this pull request Oct 30, 2026
…ion at 0.1.40.1)

dpct 2025.3 of the 0.1.40.1 tree (23a38a0) and of 870aedd, normalize.sh, a 3-way merge into sycl/ per docs/INTEL.md
(src/dsv41 left out: b70 keeps its own DeepSeek port). Upstream's history restarts at 424b364, whose SYCL copies were still
at 0.1.40.1, so the base is the last re-migration, not the merge base. 38 merged files conflicted (about 520 hunks), plus the
hand-kept copies (verify, mtp, conversation_state, remote/peer experts, direct_file); every conflict resolved with the port's
kernels, launch shapes and B70 defaults kept.

In (opt-in unless noted): layer-major prefill (STRATA_PREFILL_LAYER_MAJOR, FP16 expert path on SYCL), PLE residency
policy (default as upstream: table in RAM only with table + arena + 8 GiB free), PLE_COMPACT / PLE_COPY_STREAM,
DEQUANT_SIDE, KVA, SELECT_LONG (multi-group decode top-k + dx scorer), SELECT_SIMT, QSA_ROWS / slots, SPEC_COST,
SPEC_PROB, PIPED_MTP, logit bias / allowed_token_ids / logprobs in the sampler, conversation cache (PARK_ASYNC,
CKPT_ASYNC, CKPT_POOL_READY, PARK_VRAM_MIB, Niko1221#1503 Niko1221#2409 Niko1221#3350 Niko1221#4420), expert source file-tier prefetch (IO_PF_*, FS_*),
RAM_ADAPT, copy_blobs, graph eviction on out-of-memory (Niko1221#3702), stager waits and copy-wait dedupe (Niko1221#4005), PLE reader
fix (Niko1221#3602), cache reserve (Niko1221#4474), QFUSE commit fix (Niko1221#3948), 9-16 token windows (capability; kVerifyMaxT stays 8),
Gyro-L TQP5/6/7, EXPDEC, IQ_STAGE_TABLES, GU_SWIGLU, HC_PERSIST, GR_UP_WIDE, B6_MMVQ_ROWS, ROUTE_PRIOR (in
strata_kernels now), LOGITS_DUMP, NAN_GUARD, TRACE.
Off on SYCL: PDL (pdl.hpp: no Level Zero counterpart), PF_WMMA (gemm_iq_f16_grouped stub), PATTN_SM60, the sm_60..121 /
gfx / GP100 tables and kernels, NVML, DFlash2 (--draft dflash2 refused), NVFP4, Qwen3.6, argmax_rows (a dpct major version
is not a compute capability), route tail skip default (decode_defaults_arch_ok false). Upstream headers b70 had removed
are back in include/strata (host-only).
Checked: every changed translation unit compiles (JIT), strata links.
bitaccesscomau added a commit to bitaccesscomau/Strata that referenced this pull request Oct 31, 2026
…reamed experts no token of the chunk routes to; test

A prompt chunk of 1,024 tokens or more streams every non-resident expert of every layer through the ring, and the issuer
thread enqueues each copy as soon as the ring has room. The ring is longer than a layer's entries, so a layer's copies are
all on the copy stream before its router has run: an expert no token routes to is copied anyway.

With the switch on, for a streamed chunk on one GPU with the issuer thread:
- the issuer keeps at most STRATA_PREFILL_ROUTED_STREAM_DEPTH copies (default 16) queued ahead of the link: it waits until
  the copy issued that many copies earlier has landed (cudaEventQuery on its slot's copied event). The ring's own rule is
  unchanged, so the stream still runs as far ahead of the compute as before;
- after each layer's host grouping the compute thread publishes which experts at least one token routes to;
- the issuer drops an entry it has not issued yet whose layer is published and whose expert has no token. Entries already
  issued stay. An entry staged through the Stager's pinned buffers is never dropped (its job queue is in order).

Every routed entry is still copied, in the same order, into the same ring slot (k % ring), with the same slot waits and
copied event; the consumer's walk is unchanged and never reads an unrouted entry's slot, and the copy stream stays in order,
so a group's single wait on its last copy still covers the group. The prompt's products are the same bits. After the read
the borrowed slots are refilled from RAM blob by blob into the same slots, independent of what the ring held.

On v0.2.6 (4b9e05a) at the release defaults (RTX 3090, PCIe 3.0 x16, 250 W; the same binary with the switch off and on,
4 pairs):
a 1,790-token follow-up read on an 18.8K-token conversation took 17.9% less time, fresh reads of 2K, 4K and 32K tokens
ran 20%, 8% and 5% faster, and the streamed experts fell by 23%, 16% and 4%. In a deterministic lane the replies are
byte-identical to that base at 1K to 32K with the switch off and on, and the switch dropped 42% of the streamed experts at
a 1,046-token read and 3% at 31K tokens.

Unset, nothing new runs: no gate, no publication, and the default issuer loop is not edited. The fused path, the peer
prompt ring, the inline issuer (STRATA_PREFILL_ISSUER=0) and the staged walk are untouched. With STRATA_TRACE=1 each chunk
prints the ring entries it copied and dropped.

The issuer walk and the route gate are header-only (include/strata/prefill/routed_stream.hpp); routed_stream_test runs
the walk itself against a simulated in-order link and a consumer that walks the layers as prefill.cpp does (every routed
entry issued into its own slot and read with its own bytes, only unrouted entries of published layers dropped, never more
than the depth queued, never past the ring), and on three threads with real atomics.

STRATA_PREFILL_COPY_EVENT_EVERY > 1 and STRATA_PREFILL_COPY_WAIT_DEDUPE=1 (Niko1221#4005) keep the default issuer: the routed
walk records a copied event for every copy, but it does not publish the records a sparse consumer waits on (a_rec,
rec_of) and does not deduplicate release waits, so with either set the consumer would wait forever or the wait policy
would be bypassed. routed_stream::eligible holds the rule and routed_stream_test checks it. Those two settings are now
read before the switch's own, which moves their two declarations up; nothing else in the default issuer loop changes.
utombords pushed a commit to utombords/Strata that referenced this pull request Nov 1, 2026
…reamed experts no token of the chunk routes to; test

A prompt chunk of 1,024 tokens or more streams every non-resident expert of every layer through the ring, and the issuer
thread enqueues each copy as soon as the ring has room. The ring is longer than a layer's entries, so a layer's copies are
all on the copy stream before its router has run: an expert no token routes to is copied anyway.

With the switch on, for a streamed chunk on one GPU with the issuer thread:
- the issuer keeps at most STRATA_PREFILL_ROUTED_STREAM_DEPTH copies (default 16) queued ahead of the link: it waits until
  the copy issued that many copies earlier has landed (cudaEventQuery on its slot's copied event). The ring's own rule is
  unchanged, so the stream still runs as far ahead of the compute as before;
- after each layer's host grouping the compute thread publishes which experts at least one token routes to;
- the issuer drops an entry it has not issued yet whose layer is published and whose expert has no token. Entries already
  issued stay. An entry staged through the Stager's pinned buffers is never dropped (its job queue is in order).

Every routed entry is still copied, in the same order, into the same ring slot (k % ring), with the same slot waits and
copied event; the consumer's walk is unchanged and never reads an unrouted entry's slot, and the copy stream stays in order,
so a group's single wait on its last copy still covers the group. The prompt's products are the same bits. After the read
the borrowed slots are refilled from RAM blob by blob into the same slots, independent of what the ring held.

On v0.2.6 (4b9e05a) at the release defaults (RTX 3090, PCIe 3.0 x16, 250 W; the same binary with the switch off and on,
4 pairs):
a 1,790-token follow-up read on an 18.8K-token conversation took 17.9% less time, fresh reads of 2K, 4K and 32K tokens
ran 20%, 8% and 5% faster, and the streamed experts fell by 23%, 16% and 4%. In a deterministic lane the replies are
byte-identical to that base at 1K to 32K with the switch off and on, and the switch dropped 42% of the streamed experts at
a 1,046-token read and 3% at 31K tokens.

Unset, nothing new runs: no gate, no publication, and the default issuer loop is not edited. The fused path, the peer
prompt ring, the inline issuer (STRATA_PREFILL_ISSUER=0) and the staged walk are untouched. With STRATA_TRACE=1 each chunk
prints the ring entries it copied and dropped.

The issuer walk and the route gate are header-only (include/strata/prefill/routed_stream.hpp); routed_stream_test runs
the walk itself against a simulated in-order link and a consumer that walks the layers as prefill.cpp does (every routed
entry issued into its own slot and read with its own bytes, only unrouted entries of published layers dropped, never more
than the depth queued, never past the ring), and on three threads with real atomics.

STRATA_PREFILL_COPY_EVENT_EVERY > 1 and STRATA_PREFILL_COPY_WAIT_DEDUPE=1 (Niko1221#4005) keep the default issuer: the routed
walk records a copied event for every copy, but it does not publish the records a sparse consumer waits on (a_rec,
rec_of) and does not deduplicate release waits, so with either set the consumer would wait forever or the wait policy
would be bypassed. routed_stream::eligible holds the rule and routed_stream_test checks it. Those two settings are now
read before the switch's own, which moves their two declarations up; nothing else in the default issuer loop changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants