Skip to content

feat(prefetch): async expert prefetcher v4 -- 94.3% cache hit rate, 2.8x speedup, zero RAM overhead#362

Merged
JustVugg merged 11 commits into
JustVugg:devfrom
EgonRuiter:feature/async-expert-prefetcher-v4
Jul 19, 2026
Merged

feat(prefetch): async expert prefetcher v4 -- 94.3% cache hit rate, 2.8x speedup, zero RAM overhead#362
JustVugg merged 11 commits into
JustVugg:devfrom
EgonRuiter:feature/async-expert-prefetcher-v4

Conversation

@EgonRuiter

Copy link
Copy Markdown

Summary

This PR implements an asynchronous expert prefetcher (PILOT modes 0–3) for olmoe.c, significantly accelerating streaming inference directly from SSD storage.

By combining multi-layer lookahead prediction, stale request pruning, consolidated expert I/O, routing EMA (momentum logits), and persistent pre-warmed expert pinning, this achieves a 94.3% overall cache hit rate (up from 62.4% baseline) and a 2.8Γ— speedup (1.26 β†’ 3.54 tok/s), with zero additional RAM overhead (Peak RSS stays at 4.80 GB).

All code has been reviewed and hardened for thread safety, numerical stability, and memory safety before submission.


Benchmark Results (OLMoE-1B-7B merged model, cache=32 experts/layer, bits=8)

Version Description Cache Hit Rate Speed SSD reads/token RAM (Peak RSS)
Baseline No Prefetch 62.4% 1.26 tok/s 64 4.80 GB
PILOT=1 1-layer lookahead 79.3% 1.83 tok/s 35 4.80 GB
Consolidated I/O 1 disk read/expert instead of 3 86.9% 2.12 tok/s 22 4.80 GB
Prefetcher v3.0 Deduplication, stale pruning, topk limit 90.1% 2.95 tok/s 16 4.80 GB
Prefetcher v3.1 Routing EMA + dynamic pinning 90.5% 3.25 tok/s 16 4.80 GB
Prefetcher v3.2 (this PR) Persistent pinning + pre-warmup (PILOT=3) 94.3% πŸš€ 3.54 tok/s πŸš€ 16 4.80 GB

The hit rate during the decode phase alone reaches 95.2% under the final configuration.


Core Enhancements

  1. Consolidated Expert I/O

    • Merges gate_proj, up_proj, and down_proj weights into a single contiguous file (merged_weight) plus a .qs scale file per expert.
    • Reduces disk read syscalls from 3 per expert to 1, maximising sequential SSD throughput.
  2. Stale Request Pruning & Queue De-duplication

    • Lock-free ring buffer (pilot_q) decouples prediction from loading.
    • On every new token step the queue is flushed so the background thread focuses on the current token's early layers immediately.
  3. Routing Momentum (EMA Logits)

    • Tracks an exponential moving average (EMA) of routing logits across tokens (SMOOTH env var, default 0.3).
    • Stabilises prediction for recurring routing patterns and closes the early-token prediction gap.
  4. Persistent Hot Pinning & Pre-warmup

    • Saves dynamic pinning masks to hot_pinned.bin in the snapshot folder after the warmup phase.
    • On subsequent runs the engine pre-loads these experts into the cache at startup, eliminating cold-start and prefill misses without extra RAM.
  5. Multi-layer Lookahead (PILOT=2/3)

    • PILOT=2 dispatches prefetch at layer i+2 (after MoE residual is available).
    • PILOT=3 adds an additional i+3 speculative pass to hide even deeper I/O latency.
  6. Cross-platform Test Suite & Tooling

    • Python helpers to download the model, convert to merged format, run determinism checks, and compare against the HF PyTorch reference.

Implementation Quality

  • Thread safety: all mutations of is_queued and the prefetch queue are guarded by g_pilot_mx. The worker thread is detached (pthread_detach) immediately after creation; thread creation failures exit cleanly.
  • Numerical stability: candidate logits are shifted by max_logit before expf() to prevent overflow to INF in the softmax-style scoring loop.
  • Memory safety: malloc return value is checked in slot_ensure_allocated; hot_eids array bounds are clamped to 256 to prevent stack overflow.
  • Wide prefetch: max_cand is now correctly derived from topk * g_wide so the WIDE env var has the intended effect.
  • Dead code removed: unused variables, the buggy rebalance_cache() function, and its associated REBAL env var have been removed entirely.

Verification

  • Compiled and tested on Windows/x86-64 with GCC (-O2 -march=native -fopenmp).
  • Determinism confirmed: two consecutive runs produce identical token outputs (Matching tokens: 12/12 vs HF oracle).
  • Peak RSS confirmed: stays within the original 4.80 GB budget (no memory regression).
  • Hit rate confirmed: 94.3% measured via c/tools/test_olmoe_real.py.

…ng, adaptive cache, RMSNorm scaling, and routing EMA
…0 EMA update to reach 90.9% hit rate and 3.25 tok/s
- Fix ENV VARS header: document PILOT=0-3, SMOOTH, CONF_LIMIT; remove stale REBAL entry
- Fix per-layer EMA: apply routing momentum to all layers (not just layer 0) with correct offset
- Fix in-flight slot race in expert_get: LRU eviction now skips slots with eid==-1 (being loaded)
- Fix in-flight slot race in pilot_realload: same fix, prevents concurrent writes into active slot
- Fix idx[] buffer overflow: clamp max_cand to 128 before E in pilot_prefetch
- Fix LRU fallback: when all evictable slots are in-flight, find oldest
  non-in-flight slot (pinned ok) before falling back to slot 0
- Fix pin_hot_experts: guard enqueue behind g_pilot>0, call
  ensure_pilot_worker_started(), and set is_queued flag to prevent
  duplicate in-flight loads from pilot_prefetch()
- Fix token counting: increment token_count/freq_token_count by S (batch
  size) instead of 1 so prefill tokens are counted accurately and warmup
  threshold triggers at the right time
- Fix queue flush race: clear is_queued under mutex only; never move
  pilot_w backwards (would break r<=w ring-buffer invariant). Worker
  skips stale entries via new is_queued guard at start of pilot_realload
- Add early-exit in pilot_realload when is_queued==0 (entry flushed
  between enqueue and worker pickup), preventing unnecessary loads
- Fix misleading EMA struct comment: momentum_logits is used only by
  the PILOT prefetcher, not blended into actual MoE routing decisions
- Fix Slot pinned comment: 'never evicted' was too strong; clarify that
  pinned slots may be displaced under extreme all-pinned cache pressure
- Use st_read_f32() for scale tensor (.qs) instead of st_read_raw() to
  handle potential future BF16/F16 dtype changes robustly
Copilot AI review requested due to automatic review settings July 17, 2026 15:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@JustVugg
JustVugg changed the base branch from main to dev July 17, 2026 17:19
@JustVugg

Copy link
Copy Markdown
Owner

First: thank you β€” and not as a formality. This PR gets right, on its own, several things this repo learned the hard way over the past week:

Housekeeping: I've retargeted the PR to dev β€” everything lands there first now; main is release-only.

The ask: would you run the same protocol on GLM-5.2?

Here's the honest reason, explained calmly rather than as an objection. Your OLMoE numbers are credible in OLMoE's regime: with cache=32 of 64 experts per layer, the cache covers 50% of the expert pool, so there's real headroom for prefetch to land in, and 62→94% hit is physically plausible. GLM-5.2 — the model this project is named for — lives in a different regime on the low-RAM machines colibrì targets:

None of that says your design won't help GLM β€” multi-layer lookahead and the EMA predictor are exactly the kind of thing that pays on a latency-bound queue, and glm.c already has a PILOT ring your ideas could slot into. It says the numbers won't transfer: on GLM the win, if it's there, will look like +15-30% from queue depth, not 94% hit β€” and a measured +15-30% on the flagship would honestly be a bigger deal for this project than 2.8Γ— on OLMoE.

If you take the GLM run, the protocol that will make it land without a second round:

  • TEMP=0 TOKENS=1, one run PILOT=0 and one with your best mode: token streams must be identical (your invariant makes this a formality, but it's the gate);
  • cold-vs-cold (AUTOPIN=0, same or deleted .coli_usage, paste the [RAM_GB=...] cap= line) β€” warm/cold mixes have manufactured fake speedups here before;
  • report experts loaded/token + hit rate as primary, wall clock as color (on VHDX-class disks we've measured the same binary varying 3Γ— on wall time between runs).

And if OLMoE-only is the scope you want for this PR, that's completely fine too β€” say so and we'll review it as an OLMoE improvement on its own merits, with the oracle you already provided. Either way: genuinely good work, and welcome.

@JustVugg

Copy link
Copy Markdown
Owner

Merged into dev β€” thank you, this is careful work and the ablation table is exactly the kind of evidence we like.

One framing note for future readers: this lands in the OLMoE testbed (olmoe.c), and that's where the 62.4%β†’94.3% / 2.8Γ— numbers live. The production GLM engine (glm.c) already ships the equivalent techniques it was missing there β€” coalesced single-pread expert slabs, PILOT router-lookahead, persistent pinning β€” so those gains don't transfer 1:1. The genuinely new ideas here (multi-layer lookahead depth, routing EMA momentum, stale-request pruning) are worth A/B-ing on glm.c next; the testbed now makes that experiment cheap, which is the real value of this PR.

JustVugg pushed a commit that referenced this pull request Jul 19, 2026
…d onto #391 split)

Re-derivation of e7/disk-class-instr @ de6dd6d (base caa49f7) onto
origin/dev @ 61004dc, after PR #391 split c/glm.c into c/colibri.c plus
quant.h/sample.h/kv_persist.h/telemetry.h/grammar.h. Semantic equivalence,
not a copy: same events, same accounting, re-sited onto the new tree.

Placement: colibri.c, unchanged from before the split. telemetry.h (#391)
is the dashboard/stats module (HWINFO/TIERS/EMAP/HITS protocol lines +
usage persistence) β€” prof_report(), expert_load_impl(), moe(), g_prof_io
and g_edisk_ns all stayed in colibri.c, so DISK-CLASS's aggregation and
printing follow them there. No relocation needed, no DEVIATION.

Read path: #362 (prefetcher-v3, 22509fc) turned out to be testbed-only
scope per its own merge message ("glm.c untouched") β€” confirmed zero
c/glm.c changes in that merge's diff. The seven expert_load() call sites
this patch touches (pipe_worker, expert_host_ensure, moe()'s OMP miss
loop, pilot_realload, repin_pass_limit, pin_load x2) are structurally
identical to the pre-split tree; the demand flag re-attaches at the same
sites with the same semantics (1 only at moe()'s own PIPE/OMP miss path,
0 everywhere else), so DISK-CLASS still counts demand loads only.

One real drift, unrelated to #362: #417 (cfcc742) fixed the exact "Metal
pre-routed FASE A never bumps the real elast/eaccess_clock" defect this
feature's comments described as a documented, deliberately-unfixed
upstream issue β€” the real clock now ticks in FASE A too. The private
elast_dc/eaccess_clock_dc clock is kept anyway: its job was never only
to route around that freeze, it also snapshots pre-bump state so a
call's own routing bump can't contaminate its own classification, and
keeping DISK-CLASS's bookkeeping fully separate from stock elast state
is what makes "byte-identical with PROF=0" provable by construction
instead of by argument. Code comments referencing the old defect are
updated to reflect the fix (historical note + #417/cfcc742 pointer)
rather than describing a bug that no longer exists.

Gates: make glm METAL=0 and METAL=1 both clean, zero warnings (matches
stock 61004dc, also built clean with zero warnings for comparison).
make test-c: 0 failures. make test-python: 77 tests, OK.

Authored by Fable 5 in Claude Code, analysis in partnership with
@monotophic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

3 participants