DeepseekV4 MTP + DSpark - #25784
Conversation
|
Is there a way to download Deepseek MTP as a module, not as a part of the whole GGUF? Some quants, like Unsloth, have them absent from the ggufs |
|
@kabachuha @ddh0 uploaded some MTP only GGUFs https://huggingface.co/ddh0/DeepSeek-V4-Flash-GGUF |
Lol, I was just about to convert shard 46 and try your PR. Any idea if it will work for rocm or will I be the one to tell you. On gfx1201 - 4 x radeon 9700, the rest offloaded to ddr4 system ram. |
|
@am17an Thank you ❤️ For noobs, what commandline flag can I use to enable it in llama.cpp when using a separate file in addition to the main quant? Edit.: Okay, I think I found out. It's |
|
Thanks @am17an your PR works for me. Ubuntu 24.06 Hardware: Gigabye MC62-g40, 5955wx, 512gb ddr4 ECC ram 3200mhz, 4 x radeon 9700 Used mtp head from https://huggingface.co/ddh0/DeepSeek-V4-Flash-GGUF (Q8_0) could not fit MTP gguf with fit, could only do so with n-cpu-moe regex: ran with CTX 131072 Prompts - each run got 2 prompts:
Ok the rest is drafted by AI because my markdown just sucks: DeepSeek V4 Flash PR25784 Benchmark (4× Radeon AI PRO R9700)Hardware
Main Model PlacementThis spills 15 MoE expert blocks (~45 GiB) to CPU while keeping all fused DeepSeek V4 kernels enabled. Results
Relative Decode Speed
Observations
Just really wanted to share because there are not many with 4x 9700 out there. |
Also had strange issue with fit - model loaded with fit on (used separate draft @ Q8) but it only fitted to 2 GPUs of 3, 2 fist was loaded according to fit-target, but third one was loaded with 5Gbs (out of 24), despite |
|
With one rtx pro 6000 and one rtx 5060ti, this branch appears to not work unless I put at least some weights on the host RAM (with -ncmoe 1, or -ot "blk.42.ffn_up_exps.weight=CPU"), even though I should have enough VRAM left (6gb on the 6000, and 4gb on the 5060ti with ). Also, it crashes immediately unless I set --fit off. With -ncmoe 1, it appears to work correctly (acceptance rate is high, mean length >2 for mtp=3). Command, compiled from 753d015: Output near crash: |
|
I assume you have rebased that merged PR into this branch and recompiled. The error is still happening. (Both the immediate crash with --fit on, and the later crash with --fit off). |
ggerganov
left a comment
There was a problem hiding this comment.
Should the llama_kv_cache_dsv4::state_write and llama_kv_cache_dsv4::state_read handle the n_rs stuff in some way? My guess is the test-recurrent-state-rollback would fail atm?
These MTP ggufs are working great with DeepSeek-V4-Flash-0731! |
|
Seems a regression, Single RTX 6000 Blackwell here, (512k context) : About 94Gb VRAM usage both. |
|
Single RTX 6000 pro 96gb here with 768gb of system ram. Built commit 563dec. Here it is with dspark disabled: Details# NUMA NPS=4
echo 0 | sudo tee /proc/sys/kernel/numa_balancing
echo 3 | sudo tee /proc/sys/vm/drop_caches
./build/bin/llama-server \
--model /data2/bullerwins/DeepSeek-V4-Flash-0731-GGUF/DeepSeek-V4-Flash-0731-MXFP4_MOE-Q8_0.gguf \
--alias DeepSeek-V4-Flash-0731:MXFP4_MOE-Q8_0 \
--numa numactl \
--threads 32 \
--ctx-size 131072 \
--n-gpu-layers 99 \
-ot "blk\.(3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26)\.ffn_.*=CUDA0,exps=CPU" \
-ub 4096 -b 4096 \
--seed 3407 \
--temp 1.0 \
--top-p 1.0 \
--log-colors on \
--flash-attn on \
--host 0.0.0.0 \
--jinja \
--prio 2 \
--port 114340.19.035.971 I slot print_timing: id 3 | task 0 | prompt eval time = 154.13 ms / 5 tokens ( 30.83 ms per token, 32.44 tokens per second)
0.19.035.975 I slot print_timing: id 3 | task 0 | eval time = 3277.54 ms / 139 tokens ( 23.58 ms per token, 42.41 tokens per second)
0.19.035.975 I slot print_timing: id 3 | task 0 | total time = 3431.68 ms / 144 tokensAnd here it is with dspark enabled: Details# NUMA NPS=4
echo 0 | sudo tee /proc/sys/kernel/numa_balancing
echo 3 | sudo tee /proc/sys/vm/drop_caches
./build/bin/llama-server \
--model /data2/bullerwins/DeepSeek-V4-Flash-0731-GGUF/DeepSeek-V4-Flash-0731-MXFP4_MOE-Q8_0.gguf \
--alias DeepSeek-V4-Flash-0731:MXFP4_MOE-Q8_0 \
--numa numactl \
--threads 32 \
--ctx-size 131072 \
--n-gpu-layers 99 \
-ot "blk\.(3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26)\.ffn_.*=CUDA0,exps=CPU" \
-ub 4096 -b 4096 \
--spec-type draft-dspark \
--spec-draft-n-max 5 \
--fit off \
--seed 3407 \
--temp 1.0 \
--top-p 1.0 \
--log-colors on \
--flash-attn on \
--host 0.0.0.0 \
--jinja \
--prio 2 \
--port 114340.20.068.375 I slot print_timing: id 3 | task 0 | prompt eval time = 159.72 ms / 5 tokens ( 31.94 ms per token, 31.30 tokens per second)
0.20.068.379 I slot print_timing: id 3 | task 0 | eval time = 3657.26 ms / 139 tokens ( 26.31 ms per token, 38.01 tokens per second)
0.20.068.380 I slot print_timing: id 3 | task 0 | total time = 3816.98 ms / 144 tokensSame simple prompt, just Slower with dspark. Is this because I'm running a hybrid ram + vram system? |
|
@createthis Probably yes, but you can try to fully load the drafter on the GPU and see if it improves things |
|
@am17an I wasn't specifying With dspark: Details./build/bin/llama-server \
--model /data2/unsloth/DeepSeek-V4-Flash-0731-GGUF/UD-Q8_K_XL/DeepSeek-V4-Flash-0731-UD-Q8_K_XL-00001-of-00005.gguf \
--model-draft /data2/unsloth/DeepSeek-V4-Flash-0731-GGUF/dspark/dspark-DeepSeek-V4-Flash-0731-BF16.gguf \
--alias DeepSeek-V4-Flash-0731:UD-Q8_K_XL \
--numa numactl \
--threads 32 \
--ctx-size 131072 \
--n-gpu-layers 99 \
--n-gpu-layers-draft 99 \
-ot "blk\.(3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21)\.ffn_.*=CUDA0,exps=CPU" \
-ub 4096 -b 4096 \
--spec-type draft-dspark \
--spec-draft-n-max 5 \
--fit off \
--seed 3407 \
--temp 1.0 \
--top-p 0.95 \
--log-colors on \
--flash-attn on \
--host 0.0.0.0 \
--jinja \
--prio 2 \
--port 11434Without dspark: Details./build/bin/llama-server \
--model /data2/unsloth/DeepSeek-V4-Flash-0731-GGUF/UD-Q8_K_XL/DeepSeek-V4-Flash-0731-UD-Q8_K_XL-00001-of-00005.gguf \
--alias DeepSeek-V4-Flash-0731:UD-Q8_K_XL \
--numa numactl \
--threads 32 \
--ctx-size 131072 \
--n-gpu-layers 99 \
-ot "blk\.(3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24)\.ffn_.*=CUDA0,exps=CPU" \
-ub 4096 -b 4096 \
--seed 3407 \
--temp 1.0 \
--top-p 0.95 \
--log-colors on \
--flash-attn on \
--host 0.0.0.0 \
--jinja \
--prio 2 \
--port 11434However, with With dspark and a longer 5k prompt: Details./build/bin/llama-server \
--model /data2/unsloth/DeepSeek-V4-Flash-0731-GGUF/UD-IQ1_S/DeepSeek-V4-Flash-0731-UD-IQ1_S-00001-of-00003.gguf \
--model-draft /data2/unsloth/DeepSeek-V4-Flash-0731-GGUF/dspark/dspark-DeepSeek-V4-Flash-0731-BF16.gguf \
--alias DeepSeek-V4-Flash-0731:UD-IQ1_S \
--numa numactl \
--threads 32 \
--ctx-size 131072 \
--n-gpu-layers 99 \
--n-gpu-layers-draft 99 \
-ot "blk\.(3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42)\.ffn_.*=CUDA0,exps=CPU" \
-ub 4096 -b 4096 \
--spec-type draft-dspark \
--spec-draft-n-max 5 \
--fit off \
--seed 3407 \
--temp 1.0 \
--top-p 0.95 \
--log-colors on \
--flash-attn on \
--host 0.0.0.0 \
--jinja \
--prio 2 \
--port 11434Without dspark: Details./build/bin/llama-server \
--model /data2/unsloth/DeepSeek-V4-Flash-0731-GGUF/UD-IQ1_S/DeepSeek-V4-Flash-0731-UD-IQ1_S-00001-of-00003.gguf \
--alias DeepSeek-V4-Flash-0731:UD-IQ1_S \
--numa numactl \
--threads 32 \
--ctx-size 131072 \
--n-gpu-layers 99 \
-ot "blk\.(3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42)\.ffn_.*=CUDA0,exps=CPU" \
-ub 4096 -b 4096 \
--seed 3407 \
--temp 1.0 \
--top-p 0.95 \
--log-colors on \
--flash-attn on \
--host 0.0.0.0 \
--jinja \
--prio 2 \
--port 11434This is the expected result? |
|
@createthis I've posted my results in the PR description which is when the model and the drafter are both on the GPU. I have not measured various tensor offloads |
|
I'm puzzled why this model runs ~ 16% slower with dspark. It doesn't seem to be pure memory bandwidth. In contrast, running Qwen 3.5 397 with MTP and offloading MOE to CPU, I got a 25-50% boost (from 8 t/s to 12 t/s). And that's a larger model with more layers in RAM across 2 machines. Also as of b10229 offloading layers with RPC is still brittle and prone to crashing. I've tested with single server (Vulkan) and I see crashes likewise. ROCm always crashes unlike other models. Perhaps this arch is more solid with Cuda? |
|
Sorry for disturbing but I just want to share my results on this. My setup is RTX 4090 + EPYC 7B13 with 4 channel DDR4, all MoE weights offloaded to CPU. TL;DR: on my setup, Here is the command I use to run with or without (just remove the last few lines) DSpark: Details./llama-server `
-m "C:\Users\transwarp829\.lmstudio\models\unsloth\DeepSeek-V4-Flash-0731-GGUF\DeepSeek-V4-Flash-0731-UD-IQ3_S-00001-of-00004.gguf" `
-a "DeepSeek-V4-Flash-0731-UD-IQ3_S" `
-t 48 `
-c 393216 `
-b 4096 `
-ub 4096 `
-fa 1 `
-ctk bf16 `
-ctv bf16 `
-lm mlock `
-cmoe `
-ngl 999 `
--temp 1.0 `
--top_p 1.0 `
-np 1 `
-kvu `
--fit off `
-lv 4 `
-cmoed `
--spec-draft-n-max 2 `
-ngld all `
-md "C:\Users\transwarp829\.lmstudio\models\am17an\DeepseekV4-Flash-20260731-DSpark.gguf" `
--spec-type draft-dspark `Baseline speed without DSpark:7.10.663.972 I slot print_timing: id 0 | task 0 | prompt eval time = 20573.13 ms / 5636 tokens ( 3.65 ms per token, 273.95 tokens per second)
7.10.663.983 I slot print_timing: id 0 | task 0 | eval time = 305686.97 ms / 4010 tokens ( 76.23 ms per token, 13.12 tokens per second)Tests on
|
| Task | --spec-draft-n-max setting |
Token generation speed | Draft acceptance rate | Speedup compared to baseline |
|---|---|---|---|---|
| translation | --spec-draft-n-max 1 |
14.53 t/s | 0.80699 | +10.7% |
| translation | --spec-draft-n-max 2 |
15.03 t/s | 0.69690 | +14.6% |
| translation | --spec-draft-n-max 3 |
12.49 t/s | 0.51393 | -4.8% |
| translation | --spec-draft-n-max 4 |
11.36 t/s | 0.49870 | -13.4% |
| translation | --spec-draft-n-max 5 |
8.79 t/s | 0.34143 | -33.0% |
| code review | --spec-draft-n-max 1 |
13.57t/s | 0.70157 | +3.4% |
| code review | --spec-draft-n-max 2 |
12.75t/s | 0.56278 | -2.8% |
| code review | --spec-draft-n-max 2 (another round) |
13.37t/s | 0.61761 | +1.9% |
| code review | --spec-draft-n-max 5 |
9.07t/s | 0.35388 | -30.9% |
| creative writing | --spec-draft-n-max 1 |
13.20t/s | 0.62031 | +0.6% |
| creative writing | --spec-draft-n-max 1 (another round) |
12.86t/s | 0.60234 | -2.0% |
| creative writing | --spec-draft-n-max 2 |
11.79t/s | 0.45682 | -10.1% |
acceptance per position (translation) = (0.767, 0.488, 0.264, 0.137, 0.051)
acceptance per position (code review) = (0.692, 0.458, 0.295, 0.197, 0.128)
acceptance per position (creative writing) = (0.594, 0.319), --spec-draft-n-max >2 not tested
There could be large variations between different round of tests. Generally, --spec-draft-n-max 1 works best for me. For tasks that is likely to produce higher acceptance rate, can try --spec-draft-n-max 2. --spec-draft-n-max > 2 will result in performance regression.
|
Can you specify your RAM? I tried to reproduce your config but 128Gb definitely not enough for that. |
My RAM is 256GB. With other daily programs in background, typical total usage is ~145GB, with ~109GB (seems increasing with longer context) usage from |
This comment has been minimized.
This comment has been minimized.
Can't get this working. What speed-up is achieved on full weight (162GB) model with 2x3090 + 128GB SDRAM? |
Merges 251 upstream commits on top of the fork's 392. Base was 22b208b (2026-07-15). What this brings in for DeepSeek V4: - CUDA kernels for the hyper-connection ops and the lightning indexer (dsv4-hc.cu, lightning-indexer.cu, upstream ggml-org#25585 and ggml-org#25545). These landed upstream after our base, so the graph no longer needs a CPU fallback for those ops. - MTP and DSpark support (ggml-org#25784), the wo_a reshape fix on load, and the same-K/V-cache-type enforcement (ggml-org#25871). - Exclusion of the i32 ffn_gate_tid2eid routing table from quantization, which the fork did not carry. Conflict resolution kept both architectures everywhere the two sides touched the same code: - llama-kv-cache: kept the fork's default-off attention-rotation policy and its env overrides, took upstream's GLM_DSA addition to the DSA indexer arch list. - llama-context: moved the TurboQuant flash-attention auto-enable above upstream's generic quantized-V check, which would otherwise reject turbo cache types under -fa off, and dropped the fork's older V-cache check in favour of upstream's. - mmq.cuh: kept the fork's int64 offsets in all three of upstream's new NVFP4 branches. - fattn.cu: dropped the WMMA block, since upstream removed that kernel and its helpers entirely; kept the RDNA4 turbo path. - ggml-cuda.cu: kept the host-staged cross-device copy and routed its peer copy through upstream's new virtual-to-physical device mapping. - chat.cpp: rebuilt on upstream's file with the fork's Inkling and Laguna parsers and the leading-whitespace tolerance reapplied; thinking_end_tag became thinking_end_tags upstream. - laguna.cpp/laguna.py and mtmd-image.cpp: took upstream, which already carries the fork's own upstreamed review fixes plus later refinements. - Removed the inherited upstream workflows again, per 0c9a069. GGML_OP_COUNT is 103: upstream's 101 plus the fork's TURBO_WHT and FLASH_ATTN_EXT_BANDED. Also drops a duplicate LLM_ARCH_LAGUNA case in test-llama-archs that the merge would otherwise have left in moe_mandatory.
…ge-0 router
MY MERGE ERROR, not a pre-existing bug. Two self-consistent designs got stitched
into a hybrid that was neither:
OURS (pre-merge) target exports UNCOLLAPSED taps, hc_mult*n_embd wide; the
DSpark encoder graph collapses them with ggml_mean before fc.
UPSTREAM (ggml-org#25784) target collapses AT THE TAP via dsv4_hc_mean, so taps are
n_embd wide and fc consumes them directly.
The merge took upstream's dsv4_hc_mean in deepseek4.cpp but left our hc_mult-wide
consumers in common/speculative.cpp, because that file auto-merged with no
conflict and I never diffed it against the design change I had accepted next
door. Same failure mode that silently dropped 171 lines of router-fleet config
earlier today: a clean auto-merge means the TEXT did not overlap, not that the
SEMANTICS compose.
Symptom: the gather read 16384 floats per layer from a tap that fills only 4096,
so 12288 floats of uninitialised memory per layer per token went into DSpark
stage 0 -- and the worker's validation guard caught it:
E process_ubatch: expert dispatch failed: worker :8803 rejected expert(s) 1,3,5
on layer 43 with code 2: pipe: expert dispatch has a non-finite weight
E llama_decode: failed to decode, ret = -3
Adopting upstream's collapse-at-the-tap end to end. Width chain now:
tap 4096 -> buffer stride 4096 -> gather stride 4096 -> n_embd_enc 3*4096=12288
-> fc input 12288 (fc.weight is (12288, 4096) in the GGUF)
speculative.cpp:964 n_embd_enc drops hc_mult
speculative.cpp:1091 gather stride drops hc_mult
llama-context.cpp layer_inp_size for DEEPSEEK4 is n_embd, matching the tap;
output_reserve, extract_layer_inputs and output_reorder
verified to agree on that width
deepseek4.cpp encoder graph feeds fc directly; the reshape/permute/mean
was a SECOND collapse over already-collapsed data
Worth noting the EAGLE3 gather at speculative.cpp:620 already used the collapsed
n_embd_tgt and was correct throughout. The DSpark impl beside it carried the
hc-wide assumption. The two are now structurally identical on this point, which
is the smell test that the merge is finally coherent rather than merely quiet.
Also moves 4x less data across the extraction path.
Builds clean: build-hip llama + llama-server.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lz74kRRC7s6a3hSMLbf1Gt
74 upstream commits, 14 in our paths. Two textual conflicts, both expected, plus ONE silent-drift resolution that did NOT conflict -- which is the whole reason upstream-check.sh warns about auto-merges. ☠☠ fattn.cpp -- THE ONE THAT AUTO-MERGED CLEAN (upstream 66fa168 / ggml-org#25874) ggml-org#25874 extends oneDNN SDPA to non-F16 KV (Q4_0..Q8_0) and inserts its gate ABOVE the MKL gate -- which our 0801 sync had already deferred to MMA for exactly this reason. Its envelope (Q->ne[1] >= 32, K->ne[1] >= 1024, quantized KV now accepted) matches our deploy prefill exactly: q8_0 KV, ubatch 2048, any real depth. Verbatim it would have silently swapped the measured MMA kernel (2.87x served prefill at 43k) for an unmeasured one and staged the whole q8_0 KV cache to dense F16 first. ⇒ added the SAME conjunct, same wording, as the MKL gate below it: !ggml_sycl_fattn_mma_supported(dst). oneDNN keeps its full envelope everywhere MMA declines. A/B reachable both ways without a rebuild (GGML_SYCL_FATTN_MMA=0 / GGML_SYCL_FA_ONEDNN=0 -- the latter is checked inside _supported(), so it still governs the new early return). This is a precedence choice, not a revert, and it is UNMEASURED on this box. B59 is the finding that prices it. The three conflicts git DID flag in this file were cosmetic (debug kname lines + upstream re-calling get_best_fattn_kernel where we hoist it). Kept ours, added upstream's ONEDNN kname lines. concat.cpp -- upstream 6c8dcaa (ggml-org#25852) is a duplicate of our own fix Kept OURS: strict superset (same launch geometry + the GGML_SYCL_CONCAT_WG door + the o[dim] hoist + the i64 loop var). Ours deliberately omits their WARP_SIZE floor, which would make WG=1 unreachable and destroy the one-binary positive control for the 8.36x launch fix. Noted in the source so the next sync does not resolve toward upstream. Assessed, no action needed: 272700b (ggml-org#26105) iGPU classification -- NULL here, B70 is discrete and still reports TYPE_GPU. Relevant later for PVC/Max-1100 and B51. dbadb68 (ggml-org#22789) dynamic split-graph inputs -- mechanical, auto-merged beside our GGML_SCHED_HANDOFF_CENSUS. 596a579 (ggml-org#25784) DeepseekV4 MTP + DSpark -- ⚠ changes SHARED MTP plumbing (n_embd_out vs n_embd_inp in llama_context::decode, set_embeddings_layer_inp bounds, embeddings_layer_inp sized n_layer+1). We run MTP every step. NOT yet gated -- see the build/gate that follows. ☠ NOT YET BUILT, NOT YET GATED. Every absolute in f306 predates this merge.
Overview
Note
Deepseek did not ship MTP with the latest deepseek models (0731). Only use DSpark! Here is one https://huggingface.co/am17an/DeepseekV4-Flash-20260731-DSpark/
Roughly ~50% speedup with
--spec-draft-n-max2 with MTP. Currently WIP, will cleanup over the next few days. Note that we the new checkpoint (20260731) only the DSpark head is included. This PR also adds the DSpark for the model.Additional information
Performance on MTP bench on a DGX spark (with MTP):
Before
After
With DSpark (
--spec-type draft-dspark --spec-draft-n-max 5)Requirements