Skip to content

llama : add a GPU cache for MoE experts kept in host memory - #29887

Merged
ggerganov merged 2 commits into
ggml-org:masterfrom
am17an:aman/moe-cache
Oct 7, 2026
Merged

ggerganov merged 2 commits into
ggml-org:masterfrom
am17an:aman/moe-cache

Conversation

@am17an

@am17an am17an commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Port of the qvac-fabric MoE cache. MUL_MAT_ID ops on host experts run on the GPU with an LRU cache of experts, only the misses are uploaded. Small batches only (<= 32 tokens), large batches use the regular offload path. Layers with different expert layouts get separate banks.

Enable with --moe-cache-mib N. In experiments it was found 10% of the total expert size is good.

Performance

Qwen3.8-Flash-Next Q4_0 (93.7 GiB, 65.4 GiB of experts), SPEED-Bench qualitative (11 categories x 3 samples, --osl 512, temp 0), no MTP.
llama-server -fa on -c 32768 -b 2048 -ub 2048 -t 16, CPU on the GPU's NUMA node (EPYC 7742, 16 cores), PCIe 4.0 x16.
Baseline is master with the default --fit. With --moe-cache-mib, --fit counts the cache and keeps fewer whole expert layers on the GPU.

GPU config experts in VRAM (layers + cache) tg t/s speed-up hit rate
RTX 4090 master --fit 16.5 + 0.0 GB 25.0 1.00x -
RTX 4090 --fit --moe-cache-mib 6544 10.4 + 6.4 GB 39.4 1.57x 72%
RTX 4090 -cmoe --moe-cache-mib 11000 0.0 + 10.7 GB 40.7 1.62x 77%
RTX 5090 master --fit 24.5 + 0.0 GB 30.8 1.00x -
RTX 5090 --fit --moe-cache-mib 6544 17.9 + 6.4 GB 54.5 1.77x 77%
RTX 5090 -cmoe --moe-cache-mib 19000 0.0 + 18.6 GB 67.8 2.20x 89%

Overview

Additional information

Requirements

@am17an
am17an requested review from a team and ggerganov as code owners October 3, 2026 04:27
@github-actions github-actions Bot added the ggml changes relating to the ggml tensor library for machine learning label Oct 3, 2026
@Green-Sky

Copy link
Copy Markdown
Collaborator

Related:

#17044
#21609
#21614
#23170
#24524
#24528
#25294
#25469
#25932
#26414
#26563
#26824
#27861
#28248

I'm definitely missing issues and discussion, maybe even more prs.

@Volunteer-1

Volunteer-1 commented Oct 3, 2026 •

Copy link
Copy Markdown

0.00.345.120 I common_params_fit_impl: getting device memory data for initial parameters:
0.00.687.833 E llama_init_from_model: failed to initialize the context: MoE cache does not support multiple devices
0.00.743.976 E common_fit_params: encountered an error while trying to fit params to free device memory: failed to create llama_context from model

From the original https://github.com/tetherto/qvac-fabric-llm.cpp

The cache supports single-GPU inference; OpenCL, tensor parallelism, multi-GPU execution, and training are not supported

@am17an Why did you chose to invest your time and work into something that declares to not support OpenCL, tensor parallelism, multi-GPU execution, and training? There are dozens of expert cache PRs and forks on Github that do run on multi-GPU systems and have been waiting for many months for a code review.

@am17an

am17an commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Because I want something that can be reviewed and merged?

@sourenaraya

Copy link
Copy Markdown

Sounds promising. Is there any plans to add multi-gpu support in the future?

@LeoBorcherding

LeoBorcherding commented Oct 3, 2026 •

Copy link
Copy Markdown

Tested on AMD (ROCm/HIP, Windows) at 6b7b03aab, built with -DGGML_HIP=ON -DGPU_TARGETS=gfx1201 (HIP SDK 7.1).

Box: Radeon AI PRO R9700 32 GB (gfx1201), Ryzen 7 8700F (8 cores), 32 GB RAM, Windows 11.
Model: Qwen3.8-Flash-Next GSQ-RCO Coder IQ1_M (24 GB of experts).
Flags: llama-server -ngl 99 -cmoe -fa on -c 8192 -t 8. The card holds the whole model, so -cmoe stands in for a smaller card.

--moe-cache-mib experts cached tg, warm (200 tok) pp, 4,543-token prompt tg after that prompt
0 - 11.0 51.5 to 59.3 7.9
8000 33% 14.5 (1.3x) 65.2 9.4
16000 67% 27.6 (2.5x) 66.5 14.2

The cache builds and runs on HIP (ROCm0 MoE cache size = 7995.36 MiB for 23981.25 MiB of host experts), and prompt processing didn't drop on this box. The 0-cache row ran with the default 4 slots and the others with -np 1. Host experts are mmapped from a 55 GB buffer on a 32 GB box, so misses also pay for page faults here.

Two things from reading the diff:

  1. Hit rates (edited: I first said the stats line never printed; it's info-level and hidden at the default verbosity, and it shows with -lv 4 and a clean exit). llama-cli -st -lv 4, same flags, 300 tokens: decode (ubatch <= 8) 85.0% at 8000 MiB and 94.6% at 16000 MiB; ubatch > 8 about 18% at both.
  2. A question for HIP: misses are uploaded with ggml_backend_tensor_set_async straight from b.src->data, which is mmapped, pageable memory. On HIP a pageable source gets staged or registered per copy. Strata hit this and copies through one pinned staging buffer instead. I haven't measured whether it matters here, and with the page-fault caveat above my numbers can't separate the two. Something to check if the small-cache gain looks low on AMD.

Am happy to run tests again in the future as this PR moves :)

AI disclosure: I used an AI assistant to run the benchmarks and draft this comment. I checked the numbers against the logs.

@transwarp829

Copy link
Copy Markdown

Would it be good to add an argument to control the number of layers that participate in the cache, like --moe-cache-layers N?

For a very limited VRAM budget (e.g. ~5% of total expert size, boundary depends on hardware), hit rate could be low. The process of uploading a layer's routed experts through PCIe + computing them on GPU could be slower than just computing all the experts of that layer on CPU. In this case, specifying only a portion of host experts to participate in cache could increase the hit rate for higher performance gain.

@bjude

bjude commented Oct 4, 2026

Copy link
Copy Markdown

I've been turning the idea of an moe cache over in my head for a while too. Haven't had a chance to look at this implementation in detail yet, will try later today.

One idea is to reuse the compute buffer for caching during generation. It's sized for prompt processing which is way more than is needed during generation so is potentially free cache space for tg. The cache (or the portion in the reused buffer) would need to be wiped each time we swap between pp & tg but it should warm up pretty quickly once generation starts again. I suppose you could store the cached expert indices when pp starts then reload them when swapping to tg.

The other idea was to update the cache asynchronously in a lagged fashion. On a cache miss we still process that expert on the CPU but queue up a transfer into the GPU cache. If hit rates are sufficiently high it should still be beneficial for the next token (on average) and let's overlap the transfers with compute for the current token rather than holding up computation. Need to take a closer look to see how you're handling it here.

Would it be good to add an argument to control the number of layers that participate in the cache, like --moe-cache-layers N?

I think that's a good idea, how beneficial the cache is will depend heavily on hit/miss rate so limiting the cache to specific layers is one way to push up hit rates. A raw number of layers is a good simple knob to adjust, might be worth adding a way to select which layers are cached (a regex like -ot maybe?) but that could always be a future PR.

@pashko-k

pashko-k commented Oct 4, 2026

Copy link
Copy Markdown

One idea is to reuse the compute buffer for caching during generation. It's sized for prompt processing which is way more than is needed during generation so is potentially free cache space for tg. The cache (or the portion in the reused buffer) would need to be wiped each time we swap between pp & tg but it should warm up pretty quickly once generation starts again. I suppose you could store the cached expert indices when pp starts then reload them when swapping to tg.

I've tried pretty much that in my fork, combined with the frequency-based admission and eviction (simple LRU performs very poorly). This got me around 7-8Gb (with MTP) for caching on the 20Gb of VRAM budget (4070+3070) with 2048 to 3072 buffer size.

On the 4K_M quant of qwen3.8-flash-next it translated to around 60% cache hit ratio and 200-400 t/s prefill and 35-20 t/s decode depending on the context size.

@voidpush

voidpush commented Oct 4, 2026

Copy link
Copy Markdown

This implementation being backend-agnostic is a plus. Seems like a good starting point and can be further optimized in later commits/PRs.
@miltos22 Your insights would be appreciated in this project if you have time.

@miltos22

miltos22 commented Oct 4, 2026

Copy link
Copy Markdown

This implementation being backend-agnostic is a plus. Seems like a good starting point and can be further optimized in later commits/PRs. @miltos22 Your insights would be appreciated in this project if you have time.

I have been getting bombarded with ideas and recommendations. This seems like a feature that everyone wants and I can't claim that my implementations do things better as I've seen at least 1 that does at least 1 part better, but can be behind in others, ideally we need to organize all PRs into 1 taking the best (compatible/adapted) ideas from each. I already have a local basic document I strictly manually maintained comparing the ones I tested so far vs mine in code and final result and I could make a version that is the best of all world's, but I'd rather directly collaborate with people. I don't know what the right place is to organize that at as I only have their github usernames

@am17an

am17an commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Just to be clear: I haven't seeked out any help regarding this and probably won't be looking at any other PRs for this feature.

@miltos22

miltos22 commented Oct 4, 2026

Copy link
Copy Markdown

Because I want something that can be reviewed and merged?

Damn you are much better than me at sticking to scope. I started with the same logic but then ended up making it unmergable, my first iteration and your current one are almost identical feature wise but with different priorities

This comment was marked as spam.

@sasa7812

sasa7812 commented Oct 5, 2026

Copy link
Copy Markdown

@am17an I'm running an unusual configuration - multi-GPU, but the second GPU runs only experts (-ts 1,0 + -ot "blk.(32-47).ffn_*_exps=Vulkan2"), everything else, including the KV / indexer / RS buffer, is on the first GPU. On top of that, -ncmoe 30 keeps the experts of layers 0–29 host-resident, so the layers this cache would actually manage all have their MUL_MAT_ID nodes computing on the first GPU, which is where the cache would attach.

At first glance I don't see why --moe-cache-mib shouldn't work here (not saying I'm 100% confident and happy to be told I'm missing something).

@sasa7812

sasa7812 commented Oct 5, 2026

Copy link
Copy Markdown

@am17an it does work with my dual GPU weirdo setup! Got up to +40% decode speed - great job. May I ask not to rule out gpu > 1 entirely with a throw but maybe leaving an option (with ENV?) to bypass it to a warning? This will make life so much easier when it lands in main.

@am17an

am17an commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

@sasa7812 glad it works for you! I just want to keep it as simple as possible for now. Everything will be added eventually with a good test coverage.

@voidpush

voidpush commented Oct 5, 2026

Copy link
Copy Markdown

@am17an If possible, can you add a benchmark for a smaller MoE model (say Qwen 35A3B) that fits entirely on GPU, so we can compare the difference between full vs partial vs no offloading?
Would be cool to see how far off the theoretical max speed (GPU only) we are.

@miltos22

miltos22 commented Oct 5, 2026

Copy link
Copy Markdown

@am17an If possible, can you add a benchmark for a smaller MoE model (say Qwen 35A3B) that fits entirely on GPU, so we can compare the difference between full vs partial vs no offloading? Would be cool to see how far off the theoretical max speed (GPU only) we are.

I may be misunderstanding your idea, but it should if i understand the question right not give any extra speed to full fit. Tho not heavily looked into the details but dont see why it would and know my implementation wouldnt

@voidpush

voidpush commented Oct 5, 2026

Copy link
Copy Markdown

I may be misunderstanding your idea, but it should if i understand the question right not give any extra speed to full fit. Tho not heavily looked into the details but dont see why it would and know my implementation wouldnt

Far off as in the fraction, not the multiple. No offload (GPU only) is 1.0 i.e. the theoretical max. With offloading or caching, we enter fractional territory. Then in later PRs, this fraction can be increased to get closer to max throughput (1.0).
Sorry if it sounded confusing

@miltos22

miltos22 commented Oct 5, 2026

Copy link
Copy Markdown

I may be misunderstanding your idea, but it should if i understand the question right not give any extra speed to full fit. Tho not heavily looked into the details but dont see why it would and know my implementation wouldnt

Far off as in the fraction, not the multiple. No offload (GPU only) is 1.0 i.e. the theoretical max. With offloading or caching, we enter fractional territory. Then in later PRs, this fraction can be increased to get closer to max throughput (1.0). Sorry if it sounded confusing

OH its a bench marking number question. you want to see fully on gpu vs partially offloaded even if it fits. Got it. I am doing a big consolidation of all implementations, reading all the code and testing them to find each ones strengths. If someone else hasnt ill share my results to this test when i get to it for both his and mine (and others if you want) obviously if someone responds prior to me i wont need to

@Atomic-Germ

Copy link
Copy Markdown
Contributor

I may be misunderstanding your idea, but it should if i understand the question right not give any extra speed to full fit. Tho not heavily looked into the details but dont see why it would and know my implementation wouldnt

Far off as in the fraction, not the multiple. No offload (GPU only) is 1.0 i.e. the theoretical max. With offloading or caching, we enter fractional territory. Then in later PRs, this fraction can be increased to get closer to max throughput (1.0). Sorry if it sounded confusing

OH its a bench marking number question. you want to see fully on gpu vs partially offloaded even if it fits. Got it. I am doing a big consolidation of all implementations, reading all the code and testing them to find each ones strengths. If someone else hasnt ill share my results to this test when i get to it for both his and mine (and others if you want) obviously if someone responds prior to me i wont need to

Qwen3.8-Flash-Next is a great test for these kinds of things

@miltos22

miltos22 commented Oct 5, 2026

Copy link
Copy Markdown

Far off as in the fraction, not the multiple. No offload (GPU only) is 1.0 i.e. the theoretical max. With offloading or caching, we enter fractional territory. Then in later PRs, this fraction can be increased to get closer to max throughput (1.0). Sorry if it sounded confusing

I tried comparing lfm 8b with pure fit vs 50% cached. it reached 90% of full fit of stock. however looking into it deeper, LFM repeatedly reactivated the same couple experts so its expected, I do not have the hardware to test (locally, could use cloud but wont for now) on a real model people use as my setup cant fully fit it for a baseline

pereirajair added a commit to pieceofmindcloud/llama.cpp that referenced this pull request Oct 5, 2026
…d direct lazy-row reads (upstream ggml-org#29030)

- llama : add a GPU cache for MoE experts kept in host memory (ggml-org#29887,
  port of the qvac-fabric cache). --moe-cache-mib / llama_context_params::moe_cache_size:
  MUL_MAT_ID on host experts runs on the GPU from an LRU cache, only misses are uploaded,
  for batches of up to 32 tokens. Single device only.
- qwen4exp/gemma4: gather lazy tensor rows with direct reads (ggml-org#29030,
  supersedes ggml-org#28136), -lzm on.

Measured on a whole Qwen3.8-Flash-Next UD-IQ3_XXS (78 GB) with one RTX 5060 Ti 16 GB,
62 GB RAM, --cpu-moe: generation 14.95 -> 19.26 tok/s with --moe-cache-mib 8000,
prompt unchanged (~89 tok/s). -lzm on was slower on this machine (79.5 / 16.38).
The expert prefetch of ggml-org#28414 was left out: with it the prefill aborted in cuBLAS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tmac1973 added a commit to tmac1973/llama-toolchest that referenced this pull request Oct 15, 2026
…ne stage to size it (#280)

llama.cpp can keep the most recently used experts of host-resident MoE
layers in a GPU cache (ggml-org/llama.cpp#29887, split over GPUs since
#30112). Add it as a model setting, written only beside CPU expert
layers and only for builds from b11507. The VRAM estimate counts it,
capped at the experts it caches, and places it on the cards that hold
those layers. The fit planner leaves it off, since it owns GPU memory,
and Autotune gains an expert-cache stage: each batch finalist with the
cache off and at a quarter, a half and all of the room left beside the
profile. The setting is also a benchmark sweep field.
@d-shehu

d-shehu commented Oct 19, 2026

Copy link
Copy Markdown

Does MOE caching support RPC?

@Volunteer-1

This comment was marked as low quality.

@am17an

am17an commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

@Volunteer-1 the results in the PR are with PCIe-4. I think in general you have a poor understanding of benchmarking and should not be commenting or guiding other users. I will mark your responses as "low quality" from now on.

@Atomic-Germ

Atomic-Germ commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Oh so THIS is the commit that completely broke my whole project. @Volunteer-1 is right...

@am17an

am17an commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

@Atomic-Germ I'm quite curious how an optional feature "completely broke" your project?

@d-shehu

d-shehu commented Oct 19, 2026 •

Copy link
Copy Markdown

I should have been more specific. Hopefully someone has a similar setup can confirm. I'm running Qwen 3.8 Flash Next Q4 UD K XL using RPC for one of 2 use cases and to evaluate performance.

  1. Run this model across 2 machines to keep it entirely in VRAM. Currently TG is 37 T/S with MTP. PP is a around 570 t/s.

  2. Run in conjunction with Qwen 3.8 27B.

For #2, I was hoping to use this PR to cache MOE since they obviously can't fit on a 24GB GPU. The way I configured is using OT where RPC.

ngrams and MOE in ram on 2nd machine with draft model and other layers in VRAM.

Release: b11721
RPC0 -> GPU on machine 2 (RTX3090 Ti 24GB)
RPC1 -> CPU on machine 2 (9900x + 128GB DDR5)

-ngl all                 
--jinja                 
-sm layer                 
--fit off                 
-ot per_layer_token_embd=RPC1[<machine ip 2>:26001],
         blk\.([1-4]?[0-9])\.ffn_(up|down|gate)_exps=RPC1[<machine ip 2>:26001] 
--moe-cache-mib 10000                
-fa on                 
-ub 512 -b 2048                 
--model-draft models--unsloth--Qwen3.8-Flash-Next-GGUF/UD-Q4_K_XL/mtp-Qwen3.8-Flash-Next-Q4_K_M.gguf                 
--spec-type draft-mtp 
--spec-draft-n-max 4 
--spec-draft-ngl all 
--device-draft RPC0 
--temp 1.0                 
--top-p 0.95                 
--top-k 20                 
--min-p 0.0                 
--presence-penalty 0.0                 
--repeat-penalty 1.0 
--chat-template-kwargs {"reasoning_effort":"medium","preserve_thinking":true}          
--reasoning-budget 16384 
--cache-type-k q8_0 
--cache-type-v q8_0                     
--spec-draft-type-k q8_0 
--spec-draft-type-v q8_0  

It's possible I missed something so I'll look over it in the meantime. Thanks.

Additional Notes:

Disabled RPC and tested on one machine. 5950x, 128 DDR4 and dual R9700. One-shot tests with short prompt, TG only.

ROCM + layer split

  1. No cache: 25 t/s
  2. Cache: ~23 t/s

ROCM + tensor split
3. crash

Vulkan + layer split
4. No cache: 23 t/s
5. Cache: 18.1 t/s

Single GPU on machine 1 isn't useful though I suppose I could test the RTX 3090 to see if it benefits without RPC.

@Volunteer-1

Volunteer-1 commented Oct 19, 2026 •

Copy link
Copy Markdown

@d-shehu please give information how you connect the two machines.

Run this model across 2 machines : [...]
RPC0 -> GPU on machine 2 (RTX3090 Ti 24GB) RPC1 -> CPU on machine 2 (9900x + 128GB DDR5)

One user reported that this PR consumes on average 20.9 GB/s PCIe bandwidth or 24.7GB/s up to maximal 35 GB/s PCIe bandwidth with GLM-5.3-Flash UD-IQ3_XXS and breaks even with CPU-only inference at 85% cache hit rate.
A different user reported 3.24 GB/s PCIe bandwith consumption with Qwen3.8-Flash-Next.

Comparison with theoretical maximum full duplex PCIe bandwith:
https://en.wikipedia.org/wiki/PCI_Express#Comparison_table
During inference the theoretical maximum values are half of these value because of one-direction transfer, and practical values are additionally 23% lower because of bus overhead:
PCIe 4.0 x8 gives ca 12.3 GB/s in practice with Linux GPU Peer2Peer mod installed. That is ca 35% of the required bandwidth for GLM-5.3-Flash UD-IQ3_XXS. PCIe 5.0 x16 has also too little bandwidth for that model in IQ3_XXS quantization (higher quants need more bandwidth).
On Windows, PCIe bandwidth is much lower than on Linux.

@sanmai

sanmai commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Folks, there are so many things beyond maintainer's control that could go bad and make it slow. For example, you exceptionally large GPU could be sagging and break the contacts just enough to downgrade it from x16 to x8, or worse.

Heck, this very person writing this had to re-seat their card, because apparently I was too gentle during the initial assembly.

@Volunteer-1

Volunteer-1 commented Oct 20, 2026 •

Copy link
Copy Markdown

Here is a Powershell script that calculates the maximum theoretical decode speed for an LRU cache for any open weights MoE model based on the model's meta data, the user's choice for bits per weight for the experts and several PCIe configurations, and compares it to CPU-only decode. Only data transfer bandwidth is used as physical limiter without time spent for calculations, therefore it's a theoretical maximum speed. The shared experts are assumed to be VRAM resident.

tps-calc.zip

Call it like:
powershell -ExecutionPolicy ByPass -File .\tps-calc.ps1 -Repo zai-org -Model GLM-5.3-Flash -Bpw 4.0

SPOILER:
An LRU cache reaches break even with CPU-only decode speed on PCIe 4 x8 at 85% cache hit rate, which occurs at 74% of routed experts being cached in VRAM.

adderek pushed a commit to adderek/llama.cpp that referenced this pull request Oct 21, 2026
276 upstream commits. Conflicts and how they were resolved:

- K2-Horizon: upstream added its own port of the same ifm-ai code (ggml-org#29535),
  with the chat parser, the unicode splitter and tests. Upstream's model,
  converter and names are taken; GGUF names are identical, so existing
  K2-Horizon GGUFs still load. The fork keeps only the K2-Horizon-36B
  tokenizer hash, which upstream dropped. LLAMA_VOCAB_PRE_TYPE_K2_HORIZON
  moves from 60 to 61 to follow upstream (it is not stored in GGUFs).
- MoE: upstream moved the selective expert copy out of the scheduler into a
  llama_context copy callback (ggml-org#29943) and added a GPU cache for host
  experts (ggml-org#29887). The fork's partial mul_mat_id (id < 0 rows, used by
  LLAMA_MOE_HOT) now skips negative ids in that callback. build_lora_mm_id
  takes both the fork's partial/ids_s and upstream's MoE cache slots;
  build_moe_ffn_exps passes the slots on the untiered path only, so the MoE
  cache and LLAMA_MOE_HOT tiering do not stack on one layer.
- mmvq: upstream's shared-expert fusion and the fork's negative-id early
  return combined; the shared-expert block never takes the early return.
- server: upstream's rejection sampling and checkpoint replay paths gained
  the fork's on_sample hook, so speculative logprobs cover them too.
- qwen4exp / build_attn: turbo Q pre-rotation kept on top of upstream's
  reworked QSA mask and the new training path.
- top-k.cu: upstream's CCCL >= 3.4.3 gate; the fork's hipcub arm was dead
  since HIP stopped defining GGML_CUDA_USE_CUB.
- ggml-cuda init_tensor: upstream's async memset on owned buffers, the
  fork's host-side padding clear on externally owned ones.
- tests/test-turbo-kv.cpp, tests/test-moe-tier.cpp: moved to common_batch
  and llama_process, as upstream removed common_batch_add.

Tested on gfx1100: ctest 76/78 (the two failures are environmental: the
ggml-vocabs clone has git-lfs pointers, and system python has no jinja2;
test-jinja-py passes with it). test-llama-archs 506/507; the qwen2moe
"Meta" failure is a seed-dependent flake that the pre-merge build shows
too. Server speculative/logprobs pytests 18/18. Needle at 12k tokens with
turbo4 KV: K2-Horizon-36B, Qwen3.8-Flash-Next, Qwen3.8-27B (plus MTP).
edwardyoon added a commit to edwardyoon/focus-llama that referenced this pull request Oct 25, 2026
Allow configuring the physical batch size of the draft context. The
drafters feed it at most the drafted tokens per sequence and chunk
larger batches by its own n_ubatch, so it does not need the ubatch of
the target. A smaller value shrinks the draft compute buffer and
leaves more VRAM for the target KV cache (the draft is fitted together
with the target).

The default 0 keeps the current behavior of following --n-ubatch.

Ported from upstream commit 3fb4767 (ggml-org#30459): 5 conflicts resolved by
keeping both the fork's --spec-draft-n-min-adaptive and the new option;
the MoE cache line in speculative.cpp belongs to unapplied ggml-org#29887 and
was excluded. Staged stat matches upstream exactly (9 files, +63/-3).
Verified: build of llama + test-arg-parser OK, test-arg-parser all tests OK.
@Hypfer

Hypfer commented Oct 26, 2026

Copy link
Copy Markdown

Enable with --moe-cache-mib N. In experiments it was found 10% of the total expert size is good.

Generally bad practice to comment on already done PRs, but if it was found that a percentage is the way to go, it might be worth considering allowing to pass a percentage for that flag, maybe?

@am17an

am17an commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

We can add helper flags --moe-cache-expert-percentage maybe

@sasa7812

sasa7812 commented Oct 26, 2026 •

Copy link
Copy Markdown

Enable with --moe-cache-mib N. In experiments it was found 10% of the total expert size is good.

Generally bad practice to comment on already done PRs, but if it was found that a percentage is the way to go, it might be worth considering allowing to pass a percentage for that flag, maybe?

Why? It would make it very non user friendly. Let's say you have (and it is easy to see) 10 GiB of spare VRAM, you know how much to give for the cache no problem and the rule is simple - the more, the better, no exceptions almost. Now, imagine you need to find out the total size of your experts and only those that are CPU resident. And people probably trying different quants of the model.

auRose94 pushed a commit to auRose94/llama.cpp that referenced this pull request Oct 28, 2026
…#29887)

* llama : add a GPU cache for MoE experts kept in host memory

Assisted-by: Claude

* use llama_moe_cache_ptr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ggml changes relating to the ggml tensor library for machine learning highlight Changes that will be highlighted in the next release notes testing Everything test related

Projects

None yet

Development

Successfully merging this pull request may close these issues.