Expert caching that greatly increases performance. Self contained, off by default, use -ehs N to activate - #26563
Expert caching that greatly increases performance. Self contained, off by default, use -ehs N to activate#26563miltos22 wants to merge 39 commits into
Conversation
hooks into build_moe_ffn and process_ubatch, new file llama-expert-heatmap does the actual tracking, minimal diff on upstream files. --expert-heat-decay and --expert-heat-log-period for config.
heatmap only allocated when log period or hot S is set, hot store only when hot S is set, defaults off
decay_all once per batch, not once per layer; log on crossed boundaries instead of exact multiples Assisted-by: Gemini 3.1 Pro
- add MUL_MAT_ID_COLD op that skips hot experts on the CPU cold path
- port cold kernel from original llama-wackMall
- add llama-expert-tier register/build hook (hot LUT remap + cold op + scale + add)
- slim layer_lut to {hot_lut, cold_mask}, drop cold_lut/hot_mask
- add static-plant and hit-rate diagnostics
- add ggml-cpu-mul-mat-id-cold.c with the cold-expert kernel - add shared header for mmid helpers (mmid_row_mapping, incr_ptr_aligned, one_chunk) - drop static on the two helpers now shared with stock mul_mat_id
Expert hot store is only correct on CUDA; on Vulkan it produces corrupt output. Restrict the tiered hot store to devices named CUDA*, and add LLAMA_EXPERT_HOT_FORCE=1 to override for testing.
--expert-hot-s N>0 auto-pushes the MoE-to-CPU tensor override (with a warning) so hot store slot copies always read host pointers.
fit.cpp now reports how many MoE expert bytes the fitted placement leaves on GPU; common.cpp maps that to S slots and forces all experts to CPU so the hot store copy reads host pointers. S=0 disables the tier.
Swap a resident only when a cold expert scores >= hyst x the incumbent (default 1.3). Add --expert-hyst/--expert-dwell, raise decay default to 0.999. Dwell counts real tokens and initial fill is eligible so the first sync can correct drift.
sync fork with ggml-org upstream (15 commits): vulkan shaders, new model templates, gguf reader hardening, sycl/vulkan fixes. no conflicts with the expert hot store feature.
--expert-heat-decay help said 0.99 but the default is 0.999; --expert-hot-s help now documents -1 = autofit from free VRAM.
|
Guys im stuck. basically this tradeoff: small self contained diff similar in size to current, -10% best case performance compared to current push, + 60% In my testing with artificial bottlenecks worst case performance compared to current push, corruption edge cases mathematically impossible on all backends, batching disabled so slower reads (but still workable) and only -np1. 100 lines of extra code outside my files and around 500 within mine iirc that doesn't affect behavior without my flags and an extra op, +30% top end performance compared to current commit, +60% low end performance, batching works, sidecar, can use any -np N. Touches cuda kernel (when my flags off no changes) Both: Multi gpu support (although still non optimized), dont affect process path if flags disabled, corruption edge cases mathematically impossible on all back ends. genuinely uses vram as additional memory rather than just a copy, freeing ram. but the first ones way is more hacky when the second ones is proper integration I made both, but i am not sure which one is right. I have made both because I was unsure if the tradeoffs were or not worth it. Which one do I push to this PR? |
I did try the wackMall version and the behavior was similar in initial memory consumption, but i was getting a bit worse performance. I run it twice so that there was a sidecar file, but i believe it failed, following is the debug output load_sidecar: loaded sidecar unsloth/DeepSeek-V4-Flash-0731-GGUF/UD-IQ3_XXS/DeepSeek-V4-Flash-0731-UD-IQ3_XXS-00001-of-00004.gguf.tier (fingerprint 6dc5e3b91c09a9fe) |
Funny story. It needs special arguments to work properly. Iirc for the current release that would be: Don't use no mmap and instead set tam pool in GB. I think it's in GB? May be in slots don't recall. I'm finally going to sleep I've been 40 hours awake. I should have better documentation because without the rampool argument it's basically had no advantage. But my new push here when I actually push it is not going to need special treatment, no mmap will work fine without eating ram that can fit in gpu |
Do the proper integration, but... Good luck and keep up the good work. :) |
|
Please also make it work with multiple cards, it's kind of essential given the current hardware situation. Also, a lot of people will be running wildly mismatched GPUs - different VRAM sizes, mem bandwidths, TFLOPS, so, maybe a way to set priorities for the device placement would be a win? E.g. -ehs-prio CUDA1,CUDA0,CUDA3 ? Architecture looks solid. Good luck, let us know if you need to test something. |
|
E.g. -ehs-prio CUDA1,CUDA0,CUDA3 ? that can be achieved though existing variables. you can -ot the dense layers to the best gpu, and this integration will respect -ts, so if you will just be able to set less experts to- wait you dont want less experts you which will have the most used. OK! that should be done. |
|
As i was doing that i stripped all the tiering and swapping logic to try a rewrite for efficiency as i figured out it had some inefficiencies I get why people did not attempt this. my test results alone, which just include mostly the run commands, 256 token runs along with the output, the prompt, the speed and a few lines of my own diagnostics alone are enough to where i can no longer give the data to an ai with 1 million context to summarize, as they dont fit. Now someone more knowledgeable OR more reckless could probably do this with less tests but i have to test different hypothesis to find the issues as I cannot understand the exact operation of the entire token generation loop just though code |
|
thanks for your work on this. been trying out a lot of different approaches from different PRs to optimize performance on my machine. here are some copy paste test results from [AGENT] Datapoint that may matter for the rewrite: I tested this on a load-balanced many-expert MoE (DeepSeek-V4-Flash-0731, 256 experts / 6+1 active, experts on CPU, 2× RTX 4090) and the routing distribution turns out to be the limiting factor, not the implementation.
So the 1.7–2.1× from the Qwen A3B results likely depends on routing skew and a high slot:expert ratio; on balanced-routing models with hundreds of experts, heat caching can't beat static layer pinning. Might be worth a note in the docs about which model classes benefit. Two ops footguns from testing (current branch, may be moot post-rewrite): |
Thanks for testing. I have a few points i want to clarify, I will take into account the extra data This is supposed to help the most when at least 40% of the models total fits in your vram. on other instances, especially ram limited, the current implimentation has a slowdown as the experts are only copied to the GPU not stored there, meaning you need N gpu experts size more ram to fit it in your ram. even if using mmap, it means higher cache misses on the mmap itself which is what causes the biggest stall. I hope this helps clarify what this implimentation does, however, soon it will actually store experts on the gpu, it already does so on my pc it just hasnt been pushed yet as im still working on it and its in a semi broken state with stuff stripped out for testing. Furthermore, there is no proper multi GPU support in this version. It happening to not error out is a coincidence but the speed regression is major The other parts i have also fixed. ALTHOUGH autofit not working without proper autofit was intentional too many people disliked it Also I think you may be wrong about the hit rate hypothesis. its not at all uniform. as even if the majority of experts gets activated often, they do so at different frequencies. Someone else here already tested deepseek flash with what seems to be a slightly better hardware/quantization combination as they had higher speeds from the start and they got x1.7 or so speed Also sorry for the caps i feel like i sound rude im just tired |
|
Im reffering to this
|
|
Sorry for delaying the push. Here is what ive been doing: Rebuilding everything I had wrote from scratch, trying to match on every level the equivalent scenario to stock as much as possible. For instance what my goal is: If all experts are calculated on the cpu using my own flag dependent path, they match stock with -cmoe. It was 25% behind. If the equivalent vrams worth of slots are used as the equevilent -ncmoe, it should match stock. (it was 10%ish too even with all experts on the GPU) Unavoidable overheads and why they are fine: Why its fine: GPU work: Currently all ways I tried to get the gpu to not calculate at all extra experts have failed. My workaround is having the GPU make calculations even for experts that are not present. Why its fine 2 problems i faced and I want to give details on: Moving experts back and forth, allows full use of ram + vram rather than just ram which was the old copy cache system. BUT it basically doubles bandwidth requirement. Synchronize stalls: Due to the very nature of moving experts around, you need to synchronize to make sure you wont read an expert at a wrong state for instance. But doing that can actually have a pretty big cost Solution: 2 different ones. not decided fully yet Move to CPU, sync, start calculating there and free gpu, that slot becomes eligible for copying GPU experts to it. move to GPU, synchronise, delete from cpu. And basically that loop happens over N tokens based on the auto adjust. Lastly the proper implementation path which was what people seem to prefer faces a lot of barriers in regards to gpu back end compatibility. While my more "hacky" fixes worked for the simple implementation, they had tradeoffs, and the proper way is to forego them and fix the issues that caused them, and thats what ive spent most my time trying to do These are obviously not all the challenges I faced working on these, simply some of the ones that are the easiest to talk about Current numbers of custom cold expert CPU path vs -cmoe (both calculate all experts fully on the cpu): So basically I am about as efficient as stock. meaning any benefit from caching built on top of this will now be pure gain. my previous baseline was 20-30% bellow stock so the gain was there just because it outperformed the loss Currently working on: Trying a new idea for a more efficient way to calculate on the gpu I have finished all the core systems redesign. My final static gpu results are these, and i think they are as good as they can be given the complexity of splitting experts in llama without a core rework: stock ncmoe 17average across 15 runs: 39.9 Note this is not with expert caching, just static experts like stock just split per expert instead of per layer. I will now re introduce caching and i will likely be done in a day or two as of this edit. I finally found why in my old build, that instance of multiplying 0s didn't return 0. I had forgot to have it clear a buffer, making it work on first launch and fail in subsequent which i had interpreted as a race condition incorrectly Adding edge case logic failures such good autofit, warnings etc as well as efficient device to device transfers killing the pcie bottleneck on multi gpu that has been the biggest issue for people here. this is getting close to done |
|
[AGENT] Tested on 4x RTX 3090 (96 GiB total) with unsloth's DeepSeek-V4-Flash-0731
The store only ever allocates on card 0. Handing the cache a whole card with No perf number from me. |
|
I think I will soon close this pull request and open a new one. the scope has expanded into something thats both bigger and more refined, while truly adding multiple tiers of cache to the point it outperforms stock on large models by a considerable margin |
Mark this pr as draft then. |
Hi, would you be able to test with me before I push anything or make a new pull request encompassing everything? Im not pushing it here so lets coordinate |
Thanks for the heads up |
I've sent you an email 😃 |
|
I've got a few boxes I can test it (including ROCm ones, not sure if applicable), would love to see deepseek-v4-flash running on them fast. |
It is not the latest. In fact I have not worked on that for about 2 weeks. I only updated it 3 days ago in case people wanted to test the rampool to the latest not stripped for testing build i had at hand (which was another week old) So It is pretty slow compared to the actual latest wackMall research repo at my own pc as it lacks semantic heatmap database. it also needs you to understand the flags well to run it, they are well documented but doing something wrong like using -no-mmap insttead of the rampool flag would be detrimental as the rampool is the tiered version of no mmap already. Finally if im not mistaken that one has even worse gpu support than the one here. But its comparitive feature wise to what the merge request will have just a lot more hacky and less polished llama-wackmall is a research repo where the original idea came from. Not a release grade repo. It may be obvious by reading the chat but I am a bit of a perfectionist so i wont release the latest version of llama-wackMall, with the semantic heatmap and prediction untill it is at a satisfactory state. And currently im not trying to get it to be in such a state as im putting all my programming time into refining the pull request version. I will probably have it up within the day in the same repo of this pull request |
|
Ok, then I'm waiting until you let us know it's ready for testing. No rushing though, just waiting for signal. |
|
@rlewczuk @Tha14 @mgabor3141('s agent?) @blakemartz @vlascik @clort81 @siganos I have added the features and improvements you requested. Would you like to test? I am closing this and opening a new request. I hope you guys see as much of a gain as I do. |
|
Im reopening with a broader scope |
|
@miltos22 Try to ping me in the new PR and if you can't just send me an email please. |
Its already up, just not merge request yet cause im writing it |
|
Looks like it's the same concept as https://github.com/borisk1/llama.cpp-fusion fork with its moe-cache feature |
And Atomic-Germ/Guanaco... which was the first of these. It's still not decided how the community will integrate that feature but it's pretty clear it will. I've been the Guanaco extension for months, but it's never up to date with the mainline llama.cpp because it changes so fast. Hopefully they figure out that gguf's already structured in a predictable way you can just address and then grab the active experts for vram, and the less frequent but recently used ones in system ram to a configurable number, and the rest on disk. Take the moe concept and lean in hard, that's where you logically arrive. |
This has been closed with plans to organize and re-open
I have redesigned about half of the entire system in ways that fix all major issues exposed by this pr. I have not decided yet if ill push here or make a new clean pr with a new commit history after applying the final changes one by one. I still have some work but this is just a heads up that I am aware of the issues and im fixing them
Overview
I want to make this as short as possible. I made a CUDA only for now feature where it tracks a heat map of all expert usage and the hottest experts are cached on GPU and computed there, while cold experts are computed on the CPU. off by default can be set by "--expert-hot-s N" (or -ehs for short), autofit from free VRAM using the native auto fit functions sizing based on how many experts would be offset to the cpu "--expert-hot-s -1", or off (0, default). But if you want to test you an use --ecf to force it on
Also adds: hysteresis gate + dwell on slot swaps, per-expert scale handling, a CUDA-only backend guard (Vulkan/CPU are rejected with a warning as I have not managed to get non cuda gpus to work fully and honestly can't figure it out and it does nothing on a cpu install, but still left a force activate argument), and auto-activation of --cmoe when manual slots are selected as the GPU will act as caching not slots.
Measured on Qwen3.6-35B-A3B Q2_M / Q5_K_P with 8 GB VRAM: ~1.7-2.1x
steady-state decode speedup over stock (56 vs 33 tok/s, 36 vs 17 tok/s) using autofit.
Additional information
Known limitations:
gate_up_expsmodels breaking my implementation. but aparently the model i tried to run had stopped working on the latest upstream too, even tho it works fine on the one from a few days ago. So does not seem like my issue. I am almost certain it works properly, but i would appreciate a correctionRequirements
I am not to my knowledge, but thanks for the warning?
Some results in more detail
Some numbers have been updated to reflect the minor optimization I commited. The full optimization numbers are better, but I wont put them here till its ready as thats misleading
Original llama.cpp vs my pull request
*It caues some CPU overhead, recommended to not run with -t at your max logical threads or if cpu usage is max during normal generation. Results on different hardware may vary
Important Arguments:
-ehs N | -1 - auto requires auto fit. | 0 - Disabled (default) | N - set manually, auto sets cmoe too |
--ecf | Forces slot caching to activate on backends i have not verified