Repository navigation
llama: support MoE cache over multiple GPUs - #30112
Conversation
|
CI is failing on build: |
pardon my ignorance, how do I calculate the weight of experts only? |
|
@vbooka1 if you run with llama.cpp/src/llama-moe-cache.cpp Line 366 in 2de17c2 |
Would it not be better to have a comma seperated list to support heterogene setups? |
|
Or maybe the best would be to specify the cache size "per layer"? I.e. a single number that has the meaning of the "moe cache size for a layer". This way, each device will allocate moe cache based on how many layers it works on. |
|
Per layer leaves out an optimisation opportunity to share slots between layers which have the same expert types, some layers can use more experts than others. |
Is this something we know for sure? I would expect expert usage to be rather uniform across the different layers. |
|
Yes it increases the hit rate by 5% (it's already implemented in the implementation) |
|
So do we keep the CLI arg like this, or do we do it a list of sizes for each device? The latter would be more complicated in terms of UX. |
|
We should support it per device, one single value can mean it's that value per device. Comma separated values can be per device values. What do you think? |
|
There is also the option to have a single global cache size (i.e. not per-device) and use the existing |
|
Yeah that's also good, more intuitive I guess |
Keep the fork MoE expert cache (src/llama-moecache.*). Drop the upstream one (src/llama-moe-cache.*, PR ggml-org#29887 and ggml-org#30112) and its moe_cache_size plumbing, see scripts/fork-post-merge.sh. Keep the fork server checkpoint store and combine it with the upstream slot save/restore checkpoint appendix (PR ggml-org#26004): load_tgt / load_dft now return bool, an in-memory spec checkpoint aborts when a restore fails and a slot-file checkpoint falls back to full prompt re-processing. Assisted-by: pi (deepseek-flash)
|
Please consider supporting per-device / comma-separated values (like --moe-cache-mib 512,4096)! |
|
Does this patch apply to Qwen-Next family only? GLM5.3 Flash both PP and TG became slower, PP about 10% slower and TG became 200% slower, 33 -> 16 t/s |
…29887 and ggml-org#30112 merged ggml-org#29887 (the MoE expert cache) merged on 10-07 and ggml-org#30112 (the cache over multiple GPUs) on 10-08. On b11496 and later the old Inkling head refused in six files and unslothai#251, which carried its own copy of ggml-org#29887, refused in eight. Both branches now merge upstream master c811cb8. unslothai#251 keeps upstream's cache lines intact, so the merged tree holds both pins in full.
|
@Yozam-87 the way it is made now, from what I see, if you push the cache to the second GPU, the layers cached on that GPU will also get attention. If this is not what you want, you probably should be good with ts 1,0. |
On my setup model doesn't even load with ts 1,0, if I would be able to have second value - I would be able to use cache. I have 16gb and 8gb GPUs - qwen3.8-flash-next |
Nach den Verify-/Budget-Gates: MTP+Gather+Admission sauber (Acceptance 0.54-0.59), Budget-Sweep zeigt 6 GB Sweet Spot (~694 t/s pp), und der separate Upstream-Build (ggml-org#29887/ggml-org#30112, graph-level MoE-Cache) erreicht auf identischer Hardware nur ~80-100 t/s pp (Slot-Indirektion nur <=32 Tokens, Pipeline-Parallel deaktiviert bei aktivem Cache) vs. Fork-Gather ~694 t/s. Jeder Gather-Reject/OOM faellt atomar auf den Baseline-Upload zurueck; worst-case Gather (8 GB Budget, Admission-Churn) bleibt ~5x ueber Baseline.
|
Thanks for #30112. It addresses the per-device sizing request from the #29887 thread. I haven’t enabled the cache yet. Everything below comes from reading the merged code against my layout before spending a restart on it: 3× RTX 3090, Qwen3.8-Flash-Next with about 22 expert layers on the CPU (--n-cpu-moe plus -ot pins), and MTP speculative decoding with verify batches of up to 4 tokens.
The log_stats() counters look like the right way to measure this. If a CPU-on-miss mode lands, I’m happy to run it on this rig and report per-device hit rates and decode speed at depth. |
@mediaogre #30594 (very unlikely to land) is probably what you up for, but as I said, I don't expect drastic speedup. Also, look here #30637 , author tested the approach, it didn't behave well enough. You have different problem to tackle with cache if I got your intent correctly from "With the 1–2 GiB per card I can spare", you probably don't want to spread you cache even across 3 GPUs as expert layers currently get assigned to GPU and you'll get cache fragmentation ditching your hit rate. |
|
As the author of #30637, I will note: a hybrid approach (allowing certain experts to execute directly on CPU instead of ever going to cache) was one of the implementations I tested that performed about 5% better in some systems but ~2% worse on other systems. I didn’t do a deep investigation but I think the ratio between RAM speed and PCI speed needs to be big enough to make that approach worthwhile, which is of course not guaranteed (e.g. if you have a consumer DDR5/PCIe5 system and the memory controller drops your RAM at all below JEDEC speeds because of a sync issue then your RAM and PCI speeds become almost equal). My PR was the best I found that didn’t regress on any of the setups I tested. Edit: there might be room to allow the user to specify a custom approach, or to choose an approach dynamically based on the system specs (akin to |
On my system - whenever I have cache on second GPU (PCIe 3 x4 link) - my generation drops, so it would just make sense to instead execute those experts on CPU, I have ddr4 ram. I'd like to contribute, let me know what angle is useful. |
In my asymmetric multi-GPU system I have found a similar thing, and found it best to force full layers to that GPU instead. For example I have found the following to work best on my 4500+3060 system for Qwen3.8-Flash-Next: This puts three full layers on the 3060 (which fills it ~completely), the non-expert parts of the remaining layers on the 4500, and then
(I am just a contributor, not a maintainer, so take all this with some caution). As a rule of thumb, it seems to be fairly easy to find changes that will speed up one specific hardware configuration, but much harder to find changes that will speed up all hardware configurations (or at least, speed up some configs without causing regressions on other configs). Since you have an asymmetric multi-GPU system this is pretty useful for testing since you can have llama.cpp running against just one GPU, just the other GPU, or both GPUs together, and get three very different performance configurations. There are lots and lots of ideas floating about (#30059 has a big list, for example). Probably the most valuable thing to do here is to pick one or two ideas, and test them in a bunch of configs to see if any of them provide a useful improvement without any regressions. And of course paying attention to how complex it is... I imagine the llama.cpp folks won't accept a PR for 0.5% performance improvement if it takes 10k lines of gnarly code. |
Said pr is actually pretty small. ( <1k ) I dont have a multi-gpu setup, but that pr has given me consistent 50% uplift, so I am kinda stuck on that pr for now. |
Compared to before your 50/50 pr. Also i might be misremembering and it was 30%. Will do some speed benches when I find some time. My setup has a very "weak" cpu and plenty of pcie. Im running a rtx 2070. |
Overview
Cont #29887, adding multi-GPU support. Benchmarks on 2x 4090s
Performance
2x RTX 4090 (layer split), Qwen3.8-Flash-Next Q4_0 (93.7 GiB, 65 GiB of experts), EPYC 7742 (16 threads, one NUMA node)
SPEED-Bench
qualitative, 1 sample per category (11 prompts),--osl 512, temperature 0, concurrency 1--fit--fit --moe-cache-mib 6544-cmoe --moe-cache-mib 15000--moe-cache-mibis per GPU. With--fit, CUDA0 holds 15 full layers, so only CUDA1 has host experts and a cache. With-cmoe, CUDA0 caches 25 layers and CUDA1 caches 23.Additional information
Requirements