New GGML_OP_LIGHTNING_INDEXER that implements DeepSeek V3.2/V4 lightning indexer - #24231
Conversation
… lightning indexer
|
Pinging @am17an for any DeepSeek V4 related suggestions. |
|
I think not materialising the intermediate tensor would be crucial to make dsv4 work because this can't be solved by op fusion. I'm thinking there should be a better way to add new ops which don't fall back to CPU in case the implementation is not present in a backend. We do this for FA and GDN, I'm wondering if we can refactor the code to make it easier to add any op like this (i.e. have a decomposed ggml fallback instead of relying on CPU impl), in that case the maintainability burden would be greatly reduced. cc @ggerganov |
…eepSeek V3.2/V4 lightning indexer
86cb044 to
218d396
Compare
…eepSeek V3.2/V4 lightning indexer
|
Wired the fused CPU implementation and implemented the CUDA kernel for GPU support for personal use. With solid interest from users on reddit, decided to PR it into this branch to bring it in, but let me know if you would prefer I make a separate PR directly into master https://www.reddit.com/r/LocalLLaMA/comments/1ulymml/llamacpp_patch_deepseek_v4_flash_running_with/ |
|
@spencer-zaid Check out #21149, I have my CUDA lightning indexer implementation there. |
Should be OK as it is - already have an op with the same name length.
Should be OK to have the scales as arguments.
We can perform the compute in F32 and later extend with
For consistency with the FA op it's probably worth adding the mask. It can be optional.
@am17an Yes, we can simplify and streamline the logic for such ops. A solution at the llama.cpp level similar to #24646 should be good. |
When prescaling indexer weights: llama.cpp/src/models/deepseek4.cpp Lines 563 to 565 in 20a04b2 we only do If we pass these as indexer arguments then with naive implementation (like the current one) it will be That's why I left a note about removing these from arguments, it may be simply more efficient solution - unless there's some specific reason to leave them here. Of course we could prescale indexer weights internally inside the lightning indexer implementation, but that would unnecessarily complicate the implementation.
It's not about accumulator type, but Q and K types when doing calculations. In the current CPU implementation I do for each Q K vector pair:
If I understand correctly in the CPU flash attention implementation we have:
In the original DeepSeek V3.2 |
I see. So it seems like we want to do the indexer weights scaling before the op and keep the score scale as argument?
Sounds like it's safe to use quantized multiplications here. I don't have sense about how computationally expensive this op is. My feeling is keep it simple for now (always convert to F32) and later we can explore quantizing the Q and how much this help the performace. |
@ggerganov I think there's some misunderstanding. The current implementation calculates each score like this: but if you do: you get the same result, so by precalculating Edit: Sometimes I wonder what's the point of scaling at all considering the fact that the next operation is |
|
IMO anyway would be fine for now and we can refine later. Ideally, we don't want to break the ggml API by adding/removing arguments, but it seems it is unavoidable for new ops (unless we adopt the process in #24803) because it's hard to foresee all the implications. |
|
@fairydreaming is that PR ok to merge? |
@am17an By "that" you mean #25370? Yeah, I think I'm done with it. |
- ggml-org#24231: LID GGML OP by fairydreaming: ggml-org#24231 @ 428831b - ggml-org#25521: clear cache only for seq rather than full by am17an: ggml-org#25521 @ 8026938
including: - llama.cpp master at 4f37f51 - PR ggml-org#24231 at 428831b - PR fairydreaming#2 by spencer-zaid at fbb92d2 (not incl. deepseek.cpp)
|
giving this PR a go and it seems I can load full context on my A6000 using -ngl 99 -cmoe now. kinda crazy it will take me forever to pp 1M tokens lol. |
including: - ggml-org/master at `4f37f51` - ggml-org#24231 at `428831b` - ggml-org#25545 at `50c49c83`
Squash-rebase of the ds4-flash-experiments branch (post-cleanup: 8 dead flags + MOE_TILE/FP4_RT ops removed, LID_CACHE_MXFP4 default-on) onto current upstream, which had independently evolved DeepSeek-V4 (fused HC ops ggml-org#25585, kv_stream cache refactor ggml-org#25702, seq_rm fix ggml-org#25588, lightning-indexer ggml-org#24231). Reconciliation (per Teej's calls — keep our tuned versions, adopt upstream only where cleanly additive): - KV cache: adopted upstream's kv_stream per-stream views; kept our kv_stash MTP frontier-rewind AND our frontier-aware seq_rm (upstream's ggml-org#25588 seq_rm rejects the 1-token-tail eviction our server/MTP post_decode relies on -> aborts; caught in re-validation, reverted to ours). - HC fusion: kept OUR validated bandwidth-minimal HC op; upstream's HC_PRE/COMB/ POST sit dormant. Renamed our colliding ggml_dsv4_hc_post -> _hc_fused_post (+ test struct); fixed models.h class decl to match our deepseek4 impl. - Server: adapted our --cache-disk L2 tier to upstream's split prompt-cache (server_prompt vs server_prompt_cache_state / server_prompt_data). - Kept all unique work: LID indexer CUDA kernels, CSA_TILE, FA_SPLIT/FA_MERGE, fp4-mma + packed MXFP4 container, radix/int8/dec, power governor, MTP, CVEC. - experiments/ untracked (repo-local ignore) — branch delta is code only. Verified on the rebased tree: - build clean (cli + server + tests) on upstream 86d86ed - DSV4 backend-ops 2/2 all kept ops (LID_TOPK/UNION/MEMB, HC_FUSED, QAT_SET_ROWS, FA_MERGE) - llama-cli shallow smoke c8192: coherent, byte-identical greedy to pre-rebase - llama-server c32768 + MTP + --cache-disk (LAN): MTP 21.5 t/s (draft 47/95 accepted), 3 requests no crash, --cache-disk spill + 660-tok disk restore Not yet run: deep-context (>=131k) serving — defer to an attended run (wedge risk).
… lightning indexer (ggml-org#24231) * ggml : add GGML_OP_LIGHTNING_INDEXER that implements DeepSeek V3.2/V4 lightning indexer * ggml : remove scale parameters from lightning indexer OP, add f16 mask parameter * tests : add GGML_OP_LIGHTNING_INDEXER tests * ggml : bump RPC version * chore : check if lightning indexer input tensors are not transposed * tests : count flops instead of bandwidth in lightning indexer test * chore : add missing const * chore : whitespace * ggml : renamed variables in CPU lightning indexer implementation * ggml : fix lightning indexer mask broadcasting * tests : tests for lightning indexer mask broadcasting * chore : whitespace * llama : use GGML_OP_LIGHTNING_INDEXER in DeepSeek V3.2 and DeepSeek V4 models --------- Co-authored-by: Stanisław Szymczyk <sszymczy@gmail.com>
… lightning indexer (ggml-org#24231) * ggml : add GGML_OP_LIGHTNING_INDEXER that implements DeepSeek V3.2/V4 lightning indexer * ggml : remove scale parameters from lightning indexer OP, add f16 mask parameter * tests : add GGML_OP_LIGHTNING_INDEXER tests * ggml : bump RPC version * chore : check if lightning indexer input tensors are not transposed * tests : count flops instead of bandwidth in lightning indexer test * chore : add missing const * chore : whitespace * ggml : renamed variables in CPU lightning indexer implementation * ggml : fix lightning indexer mask broadcasting * tests : tests for lightning indexer mask broadcasting * chore : whitespace * llama : use GGML_OP_LIGHTNING_INDEXER in DeepSeek V3.2 and DeepSeek V4 models --------- Co-authored-by: Stanisław Szymczyk <sszymczy@gmail.com>
Overview
This PR adds new GGML_OP_LIGHTNING_INDEXER that implements DeepSeek V3.2/V4 lightning indexer. The purpose of this OP is to reduce compute buffer sizes. Savings are enormous, for example:
No GGML_OP_LIGHTNING_INDEXER
With GGML_OP_LIGHTNING_INDEXER
Performance on CPU is unchanged (actually it's slightly faster with GGML_OP_LIGHTNING_INDEXER on my machine):
No GGML_OP_LIGHTNING_INDEXER
With GGML_OP_LIGHTNING_INDEXER
Additional information
DeepSeek lightning indexer torch implementation is as follows (taken from DeepSeek V4 model.py):
The problem with the naive GGML implementation of this is that the einsum matrix multiplication produces a temporary result with size proportional to the ubatch size, kv cache length and the number of indexer heads (64). When using the full context of DeepSeek V3.2 (163840) or DeepSeek V4 (1048576/4) this will take a lot of memory. By adding a specialized OP that fuses all operations we can reduce the compute buffer memory size 64 times (the number of indexer heads).
Next Steps
The code was initially taken straight from #21149, but I think there are still some decisions to be made:
Requirements