Skip to content

Experimental: routed experts on disk (--experts-on-disk SHARE) - #56

Merged
rromenskyi merged 3 commits into
mainfrom
experts-on-disk
Oct 19, 2026
Merged

rromenskyi merged 3 commits into
mainfrom
experts-on-disk

Conversation

@rromenskyi

Copy link
Copy Markdown

A MoE model that doesn't fit memory runs with its routed experts left in the checkpoint.

How it works

  • Each SwitchGLU keeps SHARE of its experts in an LRU cache. The rest are read with pread when the router picks them (on macOS the reads bypass the page cache).
  • A decoding step (up to 4 tokens) computes each needed expert with quantized_matmul on its own arrays.
  • A prompt or a batch runs gather_qmm on a stack of just the experts it uses.
  • Stacked checkpoints keep each expert in one slice per tensor. A module maps to a tensor only by an exact match of its layers.N tail and the names before it; anything ambiguous stays in memory.
  • Also left in memory: experts with a linear bias and nvfp4 global scales. Adapters and distributed serving refuse the option.

Measured on Gemma 4 26B-A4B (15.6 GB) with 25% kept: 7.8 tok/s at a 6.1 GB peak.

  • The server answers single and batched requests.
  • Research and the prototype: rromenskyi/quant-ternary moe-offload (Ornith 35B-A3B: 7.4 tok/s in 5.9 GB, against 20.6 tok/s in 15.2 GB).

Tests

  • test_expert_offload.py: the same logits as in memory at 100% and 25% over a prompt and 6 decode steps; the cache stays within its share; tensor-name mapping, including an MTP head's layer 0.
  • Server and utils tests pass (134).

Reviews (codex, muse), fixed in the second commit

  • An MTP head mapped to the model's layer 0.
  • Linear expert biases dropped.
  • Adapters failing to load.
  • A share of 0 ignored.
  • File descriptor and thread leaks.
  • The cache going over its share.
  • No lock around the prompt path.
  • Linux fcntl.
  • Distributed mode ignoring the flag.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XhEwWJCoJPXFyyCzp988Bx

Roman Romenskyi and others added 3 commits October 19, 2026 13:32
A MoE model that doesn't fit memory runs with its routed experts left in
the checkpoint: each SwitchGLU keeps SHARE of its experts in an LRU cache
and reads the others with pread (page cache bypassed) when the router
picks them. A decoding step computes each expert with quantized_matmul on
its own arrays; a prompt or a batch runs gather_qmm on a stack of just
the experts it uses. Stacked checkpoints keep each expert in one slice
per tensor; file names are matched to modules by their layers.N tail.

Gemma 4 26B-A4B (15.6 GB) with 25%: 7.8 tok/s, 6.1 GB peak; the server
answers single and batched requests. Research and measurements:
rromenskyi/quant-ternary moe-offload (Ornith 35B-A3B 7.4 tok/s in 5.9 GB).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhEwWJCoJPXFyyCzp988Bx
- A module maps to a checkpoint tensor only when its layers.N tail and
  the names before it match exactly (in any order); otherwise it stays in
  memory. An MTP head's layer 0 no longer reads the model's.
- Experts with a linear bias (GPT-OSS) stay in memory.
- The cache never holds more than its share, also when one step needs
  more experts.
- A lock per layer around the cache and the prompt path's weight swap.
- The reader's pool and file descriptors close with the model.
- Page cache bypass only on macOS (F_NOCACHE).
- A share of 0 still leaves experts on disk; adapters and distributed
  serving refuse the option instead of ignoring it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhEwWJCoJPXFyyCzp988Bx
The option is read from the load's model_config only, never from a
checkpoint's config.json, so the adapter and distributed refusals can't
be bypassed. Names before layers.N compared with their counts
(model.model is not model). The reader's pool waits for its threads
before its files close.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhEwWJCoJPXFyyCzp988Bx
@rromenskyi
rromenskyi merged commit 45541ca into main Oct 19, 2026
1 of 2 checks passed
@rromenskyi
rromenskyi deleted the experts-on-disk branch October 19, 2026 19:39
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.

1 participant