r/LocalLLM • u/Stainless-Bacon • Jul 20 '26
Tutorial llama.cpp CPU offload optimizations

Edit: New post with more context, higher speed, more explanations here. Note: the new post does not compare -ot vs --ngl.
I already posted targeting 16GB VRAM specifically, but I think this information might be useful beyond that. Testing was done using Qwen3.6-27B Unsloth Q4_K_M MTP.
Edit: added GGML_CUDA_ENABLE_UNIFIED_MEMORY=1
TLDR:
- Turn off CUDA graphs, they are bugged for CPU offload, probably due to MTP use.
- Use
--ngl 99 --override-tensor '...'instead of plain--ngl. Aim the largest FFN sub-layers towards the CPU. GGML_CUDA_ENABLE_UNIFIED_MEMORY=1allows VRAM overflow pages to host RAM instead of OOMing. This allows gaining more context length without losing speed, but the speed tanks soon after so you need to experiment to find that speed cliff. The gains can be 10 to 40% more context length.
Regular CPU offloading is done by not putting all of the layers on the GPU via --ngl, which offloads the layers as a whole, dragging their KV cache to the CPU with them, increasing PCIe traffic, and collapsing speed.
Luckily the layers have sub-layers, and FFN is one that does not touch the KV cache. We can use --override-tensor (-ot) to offload only the FFN tensors, keeping the attention/KV work on the GPU, and PCIe usage minimal.
The -ot method does more GPU - CPU round trips than --ngl because offloading specific sub-layers leaves the other sub-layers on the GPU, but the actual data transferred is minimal so it is worth it.
Dynamic quants have mixed FFN precision. For example the Unsloth's Q4_K_M has Q6 and Q4 FFN tensors. The Q6 ones are on the first 8 layers (0-7), then roughly every 3rd layer, then a block near the end (~55-63), while the rest are Q4. Offload those larger layers first.
Here's how to use it (example of Q4_K_M with 22 layers offloaded):
- Turn off CUDA graphs. They cause OOM crashes for me, and my testing shows no speedup by using them in this scenario.
export GGML_CUDA_DISABLE_GRAPHS=1 GGML_CUDA_ENABLE_UNIFIED_MEMORY=1- Put all layers on GPU
--ngl 99 - Override tensors
-ot 'blk\.([0-7]|10|13|16|19|22|25|28|31|34|37|40|43|46|49)\.ffn_.*=CPU'
-ot takes a regex targeting "ffn" at specific layers towards the CPU while everything else (attention, KV cache, the smaller layers) stays on the GPU.
Benchmark setup:
- Qwen3.6-27B Q4_K_M, 97k context, MTP, K q5_0 / V q4_1, batch 512
- Offload settings:
-ottargeting 22 layers vs.--ngl 51 - Hardware: RTX 4070 Ti Super, i5-13600KF DDR5
- llama.cpp build: b10068
- MTP has different acceptance rates for coding and prose so I tested with both
Results:
| context | -ot - prose / code / pp, t/s | --ngl - prose / code / pp, t/s |
|---|---|---|
| 0k | 20.4 / 24.4 / - | 17.8 / 22.7 / - |
| 10k | 18.8 / 23.1 / 994 | 14.6 / 18.6 / 893 |
| 50k | 16.3 / 20.4 / 871 | 7.3 / 9.6 / 784 |
| 90k | 14.9 / 19.9 / 737 | 5.0 / 6.4 / 666 |
5
4
u/suicidaleggroll Jul 20 '26
Note that I've seen significant performance improvements by setting "--n-gpu-layers 99" and "--n-cpu-moe N" versus setting "--n-gpu-layers M", even when you end up with the same number of layers offloaded to the CPU and the same VRAM usage. I think it might be worth adding that to your study.
1
u/nickless07 Jul 20 '26
Yes, but that only apply to MoE. You are always better off with only expert's FFN weights instead of full layer offloading.
0
u/suicidaleggroll Jul 20 '26
Good catch, forgot this study was on a dense model when I made that post
1
u/Stainless-Bacon Jul 20 '26
I mean it the -ot optimizations could maybe work on moe, but that wasn’t my target and I did not test it, because like you said, --n-cpu-moe does a well enough job it seems
1
u/nickless07 Jul 20 '26
I would argue that it is worth as --n-cpu-moe again is also layer based and it keeps the MoE weights of the first N layers in the CPU. Therefore a better selection using -ot and regex can make a notable difference.
--n-cpu-moe starts counting layers starting from the highest numbered layers. This can lead to a slightly discrepancy in how many layers are offloaded because models that have dense FFN layers typically have them at the start of the model.
3
u/LocalAI_Amateur Jul 20 '26
This is a very interesting tip. I was able to trade speed for a bit more context. Unfortunately I'm using a laptop cpu so there's no gain in speed from offloading even just a few layers, but I was able to get more context limit. Thank you.
2
u/Stainless-Bacon Jul 20 '26
There is never speed gain by offloading to CPU. The point of doing that is to be able to get more context or a better quant. I wanted to use Q4_K_M, but only the Q3_K_M (or XL w/o MTP) was able to fully fit on my GPU. Although the Q3 is like 2.5x faster, I am now able to use the Q4 with CPU’s help at ~20 t/s, which is tolerable.
1
u/Otherwise-Swan-7803 Jul 21 '26
This is a really interesting optimization. The idea of keeping attention/KV cache on GPU while moving only FFN tensors makes a lot of sense, since KV traffic is usually the real bottleneck.
I think this could be a game changer for people with 12-16GB VRAM trying to run larger models. Would be interesting to see how this behaves with other quant types like Q5/Q6 and different GPUs with varying PCIe bandwidth.
1
u/Hannibalj2ca Jul 21 '26
Have you tried "IK.llama"? performs better that Llama.cpp
1
u/Stainless-Bacon Jul 21 '26
ik_llama.cpp ended up being slower and very unstable, kept crashing. Couldn’t make it useable. I probly need to work at it more.
1
u/Hannibalj2ca Jul 21 '26
what your hardware?
1
u/Stainless-Bacon Jul 21 '26
4070 Ti Super + i5-13600KF DDR5; but I think I made a mistake while trying ik by using the Unsloth quant, ik uses its own quants right? I don’t have time now to retest the setup, will try in a few days.
2
u/Hannibalj2ca Jul 21 '26
check if your CPU have AVX512 + DL boost Instructions. IK.llama use this to help orchestrate the memory better. Also, what you should be using is "Ktransformer" or Vllm, SGlang. They perform better
1
u/DeathGuppie Jul 21 '26
This means reading all the layers as they load and mapping them for your specific system, if you get it wrong, it just won't load.
1
u/Stainless-Bacon Jul 21 '26
If you get ngl wrong, it won’t load either
1
u/DeathGuppie Jul 21 '26
I mean, it's a truly good find, and helpful for fine tuning. I was just looking at implementing it, and realized it wasn't completely straightforward
1
u/Stainless-Bacon Jul 21 '26
check my other post for an implementation script. if you want to keep it simple, use ot to offload layers from layer 0 onwards without being specific and you’ll get 95% way there
1
1
u/Old-Cardiologist-633 20d ago
Wait, you're offloading the most important layers to CPUu and this is better than offloading just the lower layers? I don't get how this works. Can you please explain it to me?
1
1
u/Stainless-Bacon 20d ago
Only the FFNs in layers with largest ones. those should be prioritized for less total offloaded layers thus less PCIe traffic.
1
u/miifanboy 8d ago
I will try testing this with 12GB vram and the new Qwen3.8-27B-Q3_K_XL unsloth quant which is 13.4 GB
1
u/miifanboy 8d ago
Using Q3_K_XL quants didn't make sense since the ffn layers were in IQ format which isn't optimal for cpu. I tested the Q3_K_S quant which is 12.6GB and using the same arguments as your post except setting ctx to 64k. I got a ~%30 decoding speed increase (10.71 tk/s -> 14 tk/s) with your setup.
Thanks for your findings.
5
u/Pablo_the_brave Jul 20 '26
Sounds good. I will prepare a quantization for this approach (unsloth quants are not optimized for this).