New GGML_OP_LIGHTNING_INDEXER that implements DeepSeek V3.2/V4 lightning indexer#24231
New GGML_OP_LIGHTNING_INDEXER that implements DeepSeek V3.2/V4 lightning indexer#24231fairydreaming wants to merge 2 commits into
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. |
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