Repository navigation
Replies: 5 comments
|
Quick update on the prefill item from the original post: the first round of the expert-staging prefill runtime measured 720.8 tok/s on the 16K prompt at physical ubatch 6144 — +27.3% over its same-condition control, with numerics agreement at cosine 0.9999928. The shape that works is: an eval-callback barrier between graph stages, shared VRAM staging windows, and a pinned pack/DMA arena — full expert bucketing + grouped GEMM is still the road to 1000+, that part continues. Also apologies for the broken title earlier (a scripting artifact on my side) — fixed now. Run data and the design notes are in the repo under evidence/results/prefill-grouped-gate/, driver scripts in evidence/profiling-scripts/. |
|
Formal gate results are in — superseding the quick numbers in my previous comment (the +27.3% there was against an earlier baseline; the gate report re-ran everything).
The mechanism: router-post anchor node → eval-callback barrier → per-layer unique-expert pack into a pinned arena → bulk DMA into shared VRAM windows (6 windows, 512 slots, ~2.8 GiB) → mapping rewrite → grouped MMQ on a large-batch pure-GPU graph. Two negative boundaries from the same gate, since they matter for anyone generalizing this:
Decode regression check: no binary-level regression — 2K decode 18.979 vs 19.531 tok/s on the old binary (-2.8%, environment-level); a 16K plan-mode reading of 15.0 was taken after ~20 h of continuous load and is attributed to environment state, with a cold-restart re-measure queued before citing decode numbers from this build. Full gate report (Chinese, measured-labeled) and all per-run data: evidence/results/prefill-grouped-gate/. |
|
Update — second model, stock llama.cpp, same machine. A data point for the "how does this generalize beyond one model" question at the end of the main post. I put the same 16 GiB host to daily use with a much smaller hybrid-attention MoE: Qwen3.6-35B-A3B (Q4_K_M, 21.2 GB, 40 layers = 10 full attention + 30 linear attention, 256 experts / 8 active per token, 262K native context) on unmodified llama.cpp b10666 —
Per-run JSON and the bench script: To be clear about what this does and doesn't validate: a 21 GB model with static layer split + a big ubatch is already "good enough" on a 16 GB card, so the fixed working-set policy isn't needed at this size. The overlap that matters is the transfer-cost model — whether you use per-layer unique-expert staging windows or a plain large ubatch, per-(layer, ubatch) expert traffic is the quantity to drive down, and it dominates prefill on both a 176.94B model and a 35B one. |
|
Follow-up on the second model: we grafted the MTP/NextN head from the official Qwen3.6-35B-A3B GGUF onto a community fine-tune of the same base (uncensored merge) at the tensor level, since fine-tunes ship without the head and can't use draft-mtp at all. Two non-obvious bits: the head is exactly 20 Measured on the same 16 GiB host, official b10819, One more field note: on current master, Method, graft script, per-run JSON and the negative results (WSL2 decode tax −12…−21%, MSVC self-build regressions on both this project and ik_llama.cpp): https://github.com/zhaoyilun/moe-working-set-16gb (docs/a3b-mtp-deployment.md). |
|
just wanted to say thanks |
Uh oh!
There was an error while loading. Please reload this page.
I've been experimenting with tensor-structured sparse-MoE placement in llama.cpp and put together a result package I'd like feedback on. The starting point was a simple observation: for a sparse MoE that's way bigger than VRAM, the question isn't "which layers go to CPU" but "which tensors". I keep everything that suits CUDA (attention, norms, router, shared/dense FFN) on the GPU and leave routed expert weights in host RAM. At model load, a fixed subset of packed experts gets materialized in a GPU pool; graph construction splits the selected expert IDs into resident hits (GPU) and misses (Native CPU expert), then merges the weighted outputs.
My test box: RTX 4080 Super 16 GiB, i9-14900KF, 96 GiB DDR5-4000, Windows, Qwen3.8-Flash-Next 176.94B IQ4_XS-family GGUF (QWEN4EXP arch), pinned llama.cpp revision
4e97ac8.What I measured at 16K context (three 500-token runs each):
So +23.91% over the control at 8 GiB. Short-context repeats: 19.531 tok/s at 2K, 18.321 at 8K. One thing that surprised me: CUDA Graph is doing a lot of heavy lifting here — the same 8 GiB config with graphs off drops to 13.567 tok/s.
I also ran the context ladder out to 262K with mixed KV quantization (q8_0 K / q4_0 V):
Being honest about this part: the cache gain shrinks from +23.9% at 16K to about +4.6% at 262K. Attention/KV dominates per-token time out there, so the expert working set stops being the bottleneck.
The most painful lesson was a WDDM one. q8-q4 KV with a 6 GiB pool at 262K fits on paper — a short 5+5-token run of it looked fine at 5.508 tok/s with 637 MiB headroom — but a 250-token run collapsed to 0.71 tok/s mean with p95 at 7.5 s, because total demand crossed physical VRAM and Windows started paging through shared memory. And it's nondeterministic: whether you collapse depends on residency. That's why I landed on keeping at least ~1 GiB of allocator headroom as a hard rule, with the pool stepping down 8 GiB → 6 GiB → 4 GiB as context goes 128K → 192K → 262K.
I also tried adjacent-token selective H2D promotion (packed 48×10 route observation tensor). Hit rate went from ~31% to 61.78%, and every threshold setting made throughput worse — up to 114.45 MB/token moved and 22.89 ms/token inside the promotion routine alone. Cache optimization is about end-to-end service time, not hit rate. That path stays in the tree but disabled by default (
threshold=-1).On prefill: physical ubatch 2K → 8K took the same 16K prompt from 287.387 to 524.003 tok/s (three-run mean; 617.342 single-run at 32K). At 262K cold prefill is still 786.9 s (331.8 tok/s), which is the biggest single wait in the system. I'm actively working on expert bucketing + grouped GEMM to push past 1000 tok/s — the first implementation round hasn't landed yet, and measured results will go into the repo as they arrive.
Pieces I think could be upstreamed, and where I'd genuinely like feedback:
Before turning any of this into a PR, I'd rather hear where people think a fixed expert working-set policy belongs in llama.cpp, whether the API shape makes sense, and how to generalize beyond this one model.
To be clear about lineage: this builds on llama.cpp itself as the base; the expert-slot streaming idea came from reading Slotstream (carloslfu — same model on a Mac, streamed from SSD); the CPU-executes-missed-experts trade is straight out of Fiddler (efeslab, arXiv 2402.07033); KTransformers was my heterogeneous-execution comparison. No source copied from any of them.
Everything above ran on the full llama.cpp path, and every number has per-run JSON + stderr logs behind it: https://github.com/zhaoyilun/moe-working-set-16gb (patch, runners, ~1150 evidence files, build/repro docs). Limitations are listed there too — one machine, one model family, and a few matrix cells still unmeasured.
All reactions