llama.cpp icon indicating copy to clipboard operation
llama.cpp copied to clipboard

Implement SparseK Attention mechanism — new GGML operator with CPU backend (GPU planned next)

Open yael-works opened this issue 1 month ago • 27 comments

New Attention Mechanism: SparseK Attention (CPU Backend)

This PR introduces a new attention mechanism called SparseK Attention, implemented from scratch as a new operator within the GGML framework, currently with CPU backend support.


Overview

SparseK Attention is a selective and efficient attention mechanism inspired by Flash Attention, but introduces additional sparsity through:

  • Top-K filtering – keeps only the strongest attention weights.
  • Local windowing – limits attention to a configurable local context.
  • Global stride – adds periodic global connections between tokens.

Implementation Details

  • Added new operator: GGML_OP_SPARSEK_ATTN defined in ggml.h and ggml.c.
  • Implemented construction function ggml_sparsek_attn() that creates a computation node with parameters (k_top, win_local, stride_global).
  • Added full CPU backend implementation in:
    • ggml-cpu/ops.h
    • ggml-cpu/ops.cpp
    • ggml-cpu.c

The CPU version includes:

  • Scaled dot-product computation QKᵀ / √d
  • Dynamic Top-K filtering
  • Softmax normalization
  • Multiplication with V

Next Steps

Our next goal is to extend SparseK Attention to the SYCL (GPU) backend in order to:

  • Measure and compare performance between CPU and GPU implementations.
  • Optimize kernel execution for sparse attention patterns.
  • Validate correctness and scaling on Intel GPUs.

We are submitting this initial CPU implementation first to ensure review, integration, and baseline correctness before introducing GPU acceleration.


Co-Authors

Co-authored-by: Yael Shuker ([email protected])
Co-authored-by: Gitty Burstein ([email protected])

yael-works avatar Oct 28 '25 13:10 yael-works

Hi @CISC and @NeoZhangJianyu,

We’d appreciate it if you could review our PR implementing the new SPARSEK Attention operator. We ran internal validation tests we created ourselves, and all passed successfully.

This contribution was developed jointly by both of us (@yael-works and @GittyBurstein ). Please make sure the PR reflects both contributors — if needed, we can adjust the commit authors accordingly.

Thanks in advance for your time and feedback!

GittyBurstein avatar Oct 28 '25 13:10 GittyBurstein

We are talking about this SparseK, right?

CISC avatar Oct 28 '25 13:10 CISC

yes! @CISC

yael-works avatar Oct 28 '25 13:10 yael-works

You need to rebase to fix Server CI failures, also please fix whitespaces: https://github.com/ggml-org/llama.cpp/actions/runs/18935125175/job/54060021809

CISC avatar Oct 30 '25 10:10 CISC

Hi @CISC, Just to clarify — the failing tests are unrelated to my changes. This PR only introduces the new SPARSEK Attention operator within GGML and doesn’t modify any existing server or inference logic.

I’d really appreciate it if you could review the code itself so we can move forward with the merge — all SPARSEK-related tests are passing successfully.

Thanks!

GittyBurstein avatar Oct 31 '25 11:10 GittyBurstein

Hi @CISC, Just to clarify — the failing tests are unrelated to my changes. This PR only introduces the new SPARSEK Attention operator within GGML and doesn’t modify any existing server or inference logic.

Yes, as mentioned, will be resolved if you rebase, it's ok. :)

I’d really appreciate it if you could review the code itself so we can move forward with the merge — all SPARSEK-related tests are passing successfully.

So, my main challenge is where/what/when will SparseK be used? I can't recall seeing any actual implementation being used in the wild. This also means we don't really have any reference to test it against...

CISC avatar Oct 31 '25 11:10 CISC

@CISC The current PR focuses solely on adding the SparseK Attention operator at the GGML level (CPU backend). At this stage, it isn’t directly integrated into the model’s runtime pipeline — it’s designed as a standalone operator for experimentation and future extensions.

Once this PR is merged, the operator can be connected to higher-level use cases such as:

  • selective attention mechanisms for long-context models,

  • experimental low-latency or memory-efficient inference,

  • or research benchmarking against variants like Flash Attention or block-sparse implementations.... Do you have any other idea that could demonstrate or validate this even better?

Thank you!!

GittyBurstein avatar Oct 31 '25 11:10 GittyBurstein

I think @ggerganov will have to weigh in on this.

CISC avatar Oct 31 '25 11:10 CISC

Hi @ggerganov and @CISC, The branch has been successfully rebased on the latest master. All SparseK Attention tests are passing, and the PR is ready for final review and merge. Thanks for the feedback and support! — Yael & Gitty

yael-works avatar Nov 02 '25 09:11 yael-works

Sparse attention implementations such as DSA and SparseK should leverage the existing FA implementations and mask filtering logic. No need to introduce new operators and duplicate all the existing work that already went into optimizing FA.

ggerganov avatar Nov 02 '25 09:11 ggerganov

Hi @ggerganov and @CISC, Following @ggerganov’s feedback, we refactored SparseK to reuse the existing FlashAttention logic rather than maintaining a separate operator. The new design integrates SparseK’s sparsity mechanism (Top-K + local + stride) within the FlashAttention extension path. This keeps the optimization benefits of FlashAttention while allowing selective sparse attention behavior — all tested and validated on CPU backend.

yael-works avatar Nov 04 '25 12:11 yael-works

@ggerganov Before we start implementing, we want to make sure we understand correctly — We’re not creating a separate operator for SparseK at all, but instead just adding a mask that integrates with ggml_flash_attn_ext, right?

And if that’s the case, where exactly should the mask implementation be added — inside the compute graph logic, or only for testing (e.g., in test-backend-ops)? thanks! Yael & Gitty

GittyBurstein avatar Nov 04 '25 15:11 GittyBurstein

We’re not creating a separate operator for SparseK at all, but instead just adding a mask that integrates with ggml_flash_attn_ext, right?

In llama.cpp, the mask is already being created and passed to ggml_flash_attn_ext. Currently, we populate the mask outside of the compute graph because it is static - i.e. depends only on the token positions in the sequences:

https://github.com/ggml-org/llama.cpp/blob/afd353246d6e91ba3a3c86d4f5b0ae176e54e801/src/llama-kv-cache.cpp#L1223-L1306

I think that the sparse attention implementations should augment this static mask through some extra logic. This extra logic should be implemented for example in the llm_graph_context::build_attn methods. This specific logic could potentially require some new ggml operators, but in general it boils down to setting certain elements of the kq_mask tensor to -INF in some way.

From there, the FA implementations will deal with the provided mask in their own way (i.e. by skipping computations when possible).

And if that’s the case, where exactly should the mask implementation be added — inside the compute graph logic, or only for testing (e.g., in test-backend-ops)?

For testing, you can already take a look how we create KQ masks with blocks of -INF values here:

https://github.com/ggml-org/llama.cpp/blob/afd353246d6e91ba3a3c86d4f5b0ae176e54e801/tests/test-backend-ops.cpp#L134-L176

I imagine that we would need tests that create various sorts of sparse masks and simply run ggml_flash_attn_ext as we do now. And also additional tests as needed, depending on what new operators for constructing these sparse masks are introduced.

ggerganov avatar Nov 04 '25 15:11 ggerganov

Hi @ggerganov, We integrated SparseK as a mask augmentation inside the existing KQ mask (local window + stride), and we pass the result into ggml_flash_attn_ext — without introducing a new operator. Before proceeding further, can you confirm this is the intended direction? Thanks!

GittyBurstein avatar Nov 05 '25 11:11 GittyBurstein

Hi @ggerganov and @CISC, Quick update:

• All SparseK-related tests are passing. • The remaining CI failure is unrelated (also appears on master).

We’ve now integrated SparseK as a mask augmentation inside the existing KQ mask, and pass it into ggml_flash_attn_ext, instead of using a separate operator — as per your guidance.

Before continuing to optimize and extend this further, can you please confirm that this integration approach matches what you had in mind?

Thanks!

yael-works avatar Nov 06 '25 09:11 yael-works

Hi @ggerganov,

Thanks again for the guidance.

We have applied the requested SparseK mask refactor: • Removed the nested placement and moved the logic outside the inner KV loops • Removed the redundant fallback row handling • Simplified the mask construction to a clean window/stride allow-list pass

At this point, the static SparseK masking cleanly augments the existing KQ mask, and all related tests are passing.

We are now ready to proceed with the next step — moving this SparseK masking into llm_graph_context::build_attn(...) so that it becomes part of the compute graph instead of being handled in set_input_kq_mask.

Just let us know if the current static mask integration looks good to you, and once confirmed, we will push the graph integration step.

Thanks!

yael-works avatar Nov 09 '25 10:11 yael-works

Hi @ggerganov, We’d appreciate your review on the current implementation. We’ve already implemented the next step (graph construction) locally, but we’re not sure whether to push it now or wait for your confirmation on the current approach.

Could you please let us know if we should proceed and push the graph-building commit, or first wait for approval on the current masks logic?

Thanks!We’d appreciate your review on the current implementation. We’ve already implemented the next step (graph construction) locally, but we’re not sure whether to push it now or wait for your confirmation on the current approach.

Could you please let us know if we should proceed and push the graph-building commit, or first wait for approval on the current masks logic?

Thanks!

yael-works avatar Nov 10 '25 09:11 yael-works

I'm not sure what is the goal here - this is just a PoC for additional filters to the mask. As a PoC, it is OK, but it's not something we need at this point. What graph construction did you implement - these static filters should not be part of the graph, they are fine to remain in set_input_kq_mask.

ggerganov avatar Nov 10 '25 13:11 ggerganov

Hi @ggerganov! I wanted to clarify something — I’ve been working on a static implementation within the graph that could potentially replace the current PoC we did in set_input_kq_mask. My understanding was that at some point you’d like this logic to become dynamic within the compute graph, but now I’m not entirely sure — Do you want us to already move the masking logic into the graph as a dynamic implementation at this stage? I’ve just committed the static graph integration, so I’d like to make sure the next step aligns with your expectations.

GittyBurstein avatar Nov 10 '25 21:11 GittyBurstein

Hi @ggerganov! We’ve now implemented dynamic mask construction directly within the graph, replacing the previous static approach. This implementation builds the mask nodes at graph time, allowing flexible control through the SparseK parameters (e.g., LLAMA_SPARSEK_ENABLE, LLAMA_SPARSEK_TOPK, etc.).

We’d really appreciate it if you could take a look at the updated code — we’re very eager to move forward to the next step. Gitty & Yael

GittyBurstein avatar Nov 11 '25 20:11 GittyBurstein

Hi @ggerganov! Yesterday @CISC did a code review for us, and we made all the updates according to your guidelines. We’d be happy if you could also take a look so we can move forward with the merge 🙏

yael-works avatar Nov 12 '25 07:11 yael-works

@yael-works See https://github.com/ggml-org/llama.cpp/pull/16817#discussion_r2515667371

CISC avatar Nov 12 '25 21:11 CISC

Hi @CISC :wave: I’ve just pushed the latest update — the fix was actually quite small, mainly aligning the mask reshape and tightening the top-k guard. Everything should now be fully consistent with your feedback I’d really appreciate your guidance on how we can move the PR forward as soon as possible — I’m eager to start working on the GPU implementation, so it’s important to confirm that this version looks good to you. Is there anything else you’d like me to adjust or clarify to help finalize this review? Thanks so much for your time and support :pray:

yael-works avatar Nov 12 '25 21:11 yael-works

Hi @ggerganov @NeoZhangJianyu We’d really appreciate your feedback on our addition — we worked on it with the goal of matching the guidance we received at the beginning. This algorithm implementation is our final project, and we’re really eager to move forward and complete it, especially with our submission deadline coming up in the next few days.

Thank you so much for your time and support! Yael & Gitty

GittyBurstein avatar Nov 13 '25 09:11 GittyBurstein

@CISC has already done a very thorough code review, and we carefully addressed all the comments to ensure the implementation meets all the requirements.

TBC I have merely made sure you have "working" code and pass EditorConfig CI, please do not consider my efforts here as a code review.

CISC avatar Nov 13 '25 09:11 CISC

@CISC You're right, I'm editing the comment again....

GittyBurstein avatar Nov 13 '25 09:11 GittyBurstein

This algorithm implementation is our final project, and we’re really eager to move forward and complete it, especially with our submission deadline coming up in the next few days.

Adjust your expectations - this PR is far from a state where it can be merged. Certainly it's not going to be merged just to meet a submission deadline.

As it is, it has no practical value because no existing open model uses this type of sparse attention. As a PoC it is OK and you can play with these changes if this is interesting to you and your project.

A final version would at the very least have to:

  • have a real model to test with
  • avoid reading env vars and instead get the information from the model metadata
  • reduce the number of graph nodes in some way
  • devise a strategy for efficient -INF filtering in the FA kernels
  • evaluate performance
  • add tests

In short, there is a long way before getting this in master. Please reduce the amount of comments asking to merge if you want to get any further assistance on this.

ggerganov avatar Nov 13 '25 09:11 ggerganov

Hi @ggerganov @NeoZhangJianyu @CISC

Summary of Updates

  • Removed the dedicated operator GGML_OP_SPARSEK_ATTN — Sparse-K is now integrated exclusively through the dynamic mask inside ggml_flash_attn_ext.
  • All Sparse-K parameters are now sourced strictly from GGUF metadata (no environment variables).
  • Cleaned legacy code paths, removed unnecessary reshapes, and reduced the overall graph-node count.
  • Added a dedicated KQ-mask test in test-backend-ops.cpp.

Performance (CODEGEMMA — Sparse-K vs Baseline)

n_ctx Prompt Eval Total Time
1024 2.3× faster ~16% improvement

Additional Notes

  • No memory footprint change.
  • No eval-per-token regression (expected for batch=1).
  • Sparse-K provides the largest gains during prompt ingestion, where attention dominates runtime.
  • Reverting the graph-node reduction to its original size causes runtime crashes — the optimization is required for stability.
  • Benchmarked with and without Sparse-K on CODEGEMMA under identical conditions.

Benchmark Command Used

./build/bin/llama-cli \
  -m /home/gitty/models/codegemma-sparsek-Q4_K_M.gguf \
  -ngl 0 \
  -c 256 \
  -n 400 \
  -t 8

yael-works avatar Nov 17 '25 13:11 yael-works

Added full CPU backend implementation in:

  • ggml-cpu/ops.h
  • ggml-cpu/ops.cpp
  • ggml-cpu.c

It's good to see the improvement of this PR and shared info of the result.

Could you update these info in the description of PR too? replace some wrong info, like:

Added full CPU backend implementation in:
ggml-cpu/ops.h
ggml-cpu/ops.cpp
ggml-cpu.c

NeoZhangJianyu avatar Nov 18 '25 00:11 NeoZhangJianyu

Thank you for the feedback! @NeoZhangJianyu — we will update the comment accordingly. Is there anything else you would like us to adjust, and what would you suggest as the next steps for us?

GittyBurstein avatar Nov 18 '25 05:11 GittyBurstein