llama : stream MoE routed experts from disk - #25294
Conversation
…RECT) Run MoE models larger than RAM: routed expert weights (ffn_*_exps) are not materialized; each streamed layer keeps a small device-side cache of expert slots, filled on demand from the GGUF by a CPU id-remap op after the router top-k. Missing experts load via a pread thread pool while the op waits; eviction is decaying route hotness with an LRU tiebreak. Output is byte-identical to the unstreamed model. Options: --moe-stream, --moe-stream-cache <N|NGiB>, --moe-stream-io-threads N, and --moe-stream-direct (O_DIRECT expert reads, bypassing the page cache; falls back to buffered when the OS/filesystem does not support it, verified by a probe read at open time). Assisted-by: Claude
|
Hi @freedomljc, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
Assisted-by: Claude
|
This saves system ram, but since it overrides cpu-moe and has no cooperation or hybridization with it, using this also seems to mean you need to have a lot of Vram to make up for the heavy loss of ssd streaming. |
It's primarily for the PCs with unified memory (e.g.: mac and dgx spark), where vram and system ram are in the same pool. The hybridization idea of using all three tiers make sense, probably we can tackle it as a follow-up. |
|
Hi @CISC @ggerganov , when you get chance, could you take a look? |
|
What is the performance versus mmap ? |
|
Can the number of experts cached per layer be configured based on the amount of RAM? |
|
VRAM-RAM-NVMe—this three-tier caching setup will be very interesting. |
Pure mmap with full GPU offload is impossible to run this model in GB10 as 240 GB would be required. I did the test for
|
It's supported by setting
Yes, it's an interesting idea, but I'd like to defer it in the separate PR. |
|
Gentle Ping. @CISC @ggerganov @pwilkin Can any one of maintainers take a look? |
|
There are other patches out there, which set up expert RAM<>VRAM swapping according to "expert hotness", or even Disk<>RAM<>VRAM tiered swapping. Would strongly suggest not merging until the others are evaluated, and the best long-term solution or most flexible base swapping architecture for future expansion in this area has been identified. It's going to become more important in future as models quickly ramp up to 1T, 2T, 5T, and beyond. |
|
This is actually really good |
|
Nice work. I've been doing basically the same thing (dense resident, routed experts streamed with O_DIRECT, bit-exact) but on Android phones over UFS, out-of-tree on the public eval-callback so no fork. One thing from the mobile side that might be useful for @lee-b's point about picking a flexible base: the O_DIRECT-vs-page-cache call is even sharper on a phone, because the kernel reclaims hard and you can't pin the dense weights to defend them. Not suggesting my approach over in-tree, just a data point that the design holds down to phone-class hardware. Repo if useful: |
|
PR #24524 has a good overview of previous attempts at VRAM cache solutions and a link to the discussion #24528, where also a 3 tier solution is mentioned. I agree that VRAM cache and MoE Streaming from Disk ("RAM Cache") could be implemented separately, but I also agree with @lee-b that it makes sense to see the bigger picture first to have compatible solutions. I would love to see both implemented as they deliver nice performance gains for MoE models. |
This sounds like it could be applied to the current system that loads host RAM->VRAM during prefill? |
Just for fun, I made a fork of this PR using Vibe (sorry, I used C++ in my student days last time) that loads some of the layers onto an RAM. That is, SSD -> VRAM + RAM -> VRAM. On the vanilla version of llama.cpp I was getting about 1 t/s. My fork is just a rough implementation to test a hypothesis, but if you're interested: |
Dspark MTP would be interesting to see if it can increase the t/s, the max I can get is 3.5 t/s 32GB, RTX 5090, PCIe5 NvME 14 Gb/ps |
Overview
n_slotsexpert slabs per layer.demand-loaded from the GGUF by an async I/O worker pool.
when the model far exceeds RAM: the page cache cannot help and otherwise thrashes competing for the
same memory.
expert GEMMs run in W waves of at most (n_slots - n_expert_used)/2 experts each; the pairs of the
other waves are masked to zero and the wave outputs summed. Each touched expert is loaded once per
ubatch, so the previous n_ubatch clamp is removed and long prompts prefill much faster.
Additional information
Testing / validation
Usage / CLI
--moe-stream-cache <NG|Ns>(GiB budget, orssuffix = slots; implies--moe-stream).--moe-stream-io-threads N,--moe-stream-direct(O_DIRECT).model into RAM and defeat streaming.
Benchmark data
GB10 (Grace-Blackwell, 128 GB unified, PCIe 4.0 SSD), GLM-5.2-UD-Q2_K_XL (~254 GB file, ~754 B params,
256 experts),
-ngl 99 --moe-stream-cache <#cache> --moe-stream-direct -c 4096, greedy, 512-token generation.Known limitations
llama_contextfrom the same streamedmodel shares one cache and can corrupt output.
--parallel Nwithin a SINGLE context is safe(all sequences batched into one graph; remap reserves all needed slots first).
Related discussion
Requirements