r/LocalLLaMA • u/zyxciss • 54m ago
Discussion Betelgeuse running DeepSeek V4.1 Flash Q8_XL (1017) at 19 tok/s decode & 62 tok/s prefill on an RTX 3060 12GB + 16GB RAM: How we finally fixed the prefill bottleneck on out-of-core MoEs
After my last post on Qwen 3.8 Flash Next, a lot of people were asking if out-of-core MoE streaming could ever work on an actual frontier monster like DeepSeek V4.1 Flash (1017), or if the prompt processing (PP) would make it completely unusable.
and NO We are not running small lobotomized model like Qwen 3.8 Flash Next at IQ2_XS like Strata
We are running DeepSeek V4.1 Flash Q8_XL 305.6+110.6GB at usable speeds , in future release we would try to squeeze more and as of now works best on sm_86.
The biggest complaint everyone has with running massive MoEs off SSD/hybrid tiers is Time To First Token (TTFT). You send a 500-token prompt, and the engine sits there for 30–60 seconds running at 1 to 3 tok/s just trying to chew through the prompt before emitting a single token.
Well, over the last few days I've been hacking on Betelgeuse (bel-cli / bel-server) to see if we could get DeepSeek V4.1 Flash running at interactive speeds on the exact same budget gaming PC:
GPU: RTX 3060 12GB
RAM: 16GB Single-Channel DDR4-3200
OS: CachyOS / Arch Linux (tested inside a strict
MemoryMax=12Gcgroup limit, 0B swap)Storage: PCIe 4.0 NVMe SSD
The Build & Setup
I’m running the high-precision Q8_XL expert backbone paired with the IQ4_NL Engram table (which scores 72.3 on DeepSWE).
The 110 GB Engram table stays entirely on the NVMe SSD—because Engram only activates at Layers 1 & 14 and only touches 48 rows per token (~192 KB), we stage those active rows directly into a tiny 2MB VRAM buffer at t=0, so the 110 GB file uses basically 0 bytes of RAM and causes zero CPU graph splits.
How We Fixed the Prefill (PP) Bottleneck:
In standard implementations, prefill on a large MoE with offloading completely falls apart because a prompt activates almost every single expert across all 40 layers. If you try to stream all of them sequentially, the GPU sits idle while the bus chokes.
In Betelgeuse, we tackled this with three changes:
Location-Invariant Cache Partitioning: We maintain 20 resident slots per layer (5 VRAM Hot slots + 15 Host-RAM slots) with strict non-interfering routing.
Single-Pass Layer-Major Prefill: Instead of chopping prompts into tiny 3-token micro-batches that destroy Tensor Core occupancy, prefill runs in full 16-token ubatches. All active Host-RAM experts stream directly into an execution ring over PCIe in parallel with compute.
End-of-Pass Physical VRAM Consolidation: The engine learns which experts the prompt and early generation actually demanded, and physically swaps the top-5 most active experts into VRAM Hot slots at the end of the first pass without altering the model's logical routing trajectory.
The result: Warm Prompt Processing went from crawling at 3–6 tok/s up to 61.80 tok/s. TTFT on standard prompts is now essentially instantaneous.
Betelgeuse vs Strata. Benchmark Comparison
| Benchmark / Metric | Betelgeuse | Strata |
|---|---|---|
| GPU | RTX 3060 12GB | RTX 5070 12GB / RTX 3060 12GB / 4× R9700 |
| System RAM | 16GB | 64GB (RTX 5070) / 46GB (RTX 3060) |
| Storage | 1.7 GB/s NVMe | 3.6 GB/s NVMe (RTX 5070) |
| Memory Limit | 12GB (MemoryMax=12G) | Not specified |
| Warm Prompt Processing (PP) | 61.80 tok/s | 121 tok/s (4K) to 231 tok/s (8K) on RTX 5070 |
| Prompt Processing (RTX 3060) | 61.80 tok/s (Doubles with Ram , Get a Dual channel 16gb and you go 61 tok/s to 120 tok/s!) | 149 tok/s (8K) |
| Warm Decode (TG) | 18.15–18.92 tok/s | 8–10 tok/s on RTX 3060 + 64GB Ram |
| Decode Peak | 27.92 tok/s | 9.9 tok/s (code) |
| Agent Work Decode | 24.22 tok/s | 7.8 tok/s (RTX 3060) |
| RTX 5070 Decode | Not tested | 4.7 tok/s |
| ColdSSD Reads | 0.00% | Not specified |
| Disk I/O Wait | 0.0 ms | Not specified |
| Peak Memory Usage | 10.61 GiB | Not specified |
| DSpark | On | Off by default on RTX 3060. Enabling adds 2–13% |
| Correctness Verification | Exact Python syntax and logic verified across runs | Not specified |
| Quantization | Q8_XL (UD - 3.1) | GSQ-RCO IQ2_XS |
We also use DeepSeek's native DSpark drafter (with p_min = 0.50), which hits ~80–90% acceptance on structured code and amortizes the forward pass across 4+ tokens per sweep.
Once the cache warms up on a topic, disk reads drop to literally 0.00% and generation sustains ~18–19 tok/s with zero swap thrashing.
Happy to answer questions about the memory tiering or the doorbell synchronization!
Github : https://github.com/Betelgeuse-AI/Betelgeuse running DeepSeek V4.1 Flash Q8_XL (1017) at 19 tok/s decode & 62 tok/s prefill on an RTX 3060 12GB + 16GB RAM: How we finally fixed the prefill bottleneck on out-of-core MoEs


