Skip to content

qwen4exp: keep the QSA indexer cache in lockstep with the attention cache - #135

Open
vladimir-voinea wants to merge 1 commit into
unslothai:qwen4exp/qwen3.8-flash-nextfrom
vladimir-voinea:fix/qwen4exp-indexer-cell-sync
Open

qwen4exp: keep the QSA indexer cache in lockstep with the attention cache#135
vladimir-voinea wants to merge 1 commit into
unslothai:qwen4exp/qwen3.8-flash-nextfrom
vladimir-voinea:fix/qwen4exp-indexer-cell-sync

Conversation

@vladimir-voinea

Copy link
Copy Markdown

Problem

llama-server built from the qwen4exp branch (PR ggml-org#27742) SIGABRTs when a request's context exceeds the QSA sparse-attention budget and decodes long, or when two concurrent unified-KV slots have different KV lengths:

src/models/qwen4exp.cpp:284:
GGML_ASSERT(mctx_idx->get_n_kv() == inp->mctx->get_attn()->get_n_kv() &&
            "the indexer cache must track the attention cache cell for cell") failed

Reproduced on DGX Spark / GB10 (SM121), Q4_K_XL, -c 262144 -ngl 48 -kvu --cache-ram -1 --parallel 2.

Root cause

The QSA indexer cache (llama_memory_hybrid_idx::mem_idx) was a separate llama_kv_cache with its own cell metadata (v_cells_impl: positions, sequence ownership, used-set). Although init_batch hands it the attention cache's slot infos, the two caches' cell state drifts apart under server slot management (seq_rm on slot release): the indexer drops cells the attention cache keeps. Because get_n_kv() is driven by used_max_p1 (highest used cell index), the two caches' n_kv diverge and the graph builder aborts.

Fix

Construct the indexer cache with the attention cache as mem_other, so it shares the attention cache's cell metadata (v_cells_impl) via the existing other mechanism. The two then agree cell-for-cell by construction and can never drift. The indexer keeps its own K/V tensors (the share callback is null), and the attn_rot/n_embd_head values it inherits from the attention cache are harmless because the indexer path never uses them (cpy_k stores raw keys).

Verification (DGX Spark, Q4_K_XL, 2 slots)

  • No n_kv divergence across all decode steps; the assert no longer fires.
  • Deep-context 25k–175k tps sweep rows all complete; 32k-token prompt generation stays coherent (answers correctly).
  • Two concurrent requests with different KV lengths complete without abort.
  • Short and long-context throughput unchanged (coding ~33–34 t/s per stream, TTFT ~0.6s).

This fix targets the qwen4exp branch in PR ggml-org#27742 (the code is not on master yet). Refs ggml-org#27780.

…ache

The indexer cache (llama_memory_hybrid_idx::mem_idx) was a separate
llama_kv_cache with its own cell metadata (positions, sequence ownership,
used-set). Although init_batch hands it the attention cache's slot infos,
the two caches' cell state can drift apart under server slot management
(seq_rm on slot release): the indexer drops cells the attention cache keeps,
so used_max_p1 -- and therefore get_n_kv() -- diverge, and the graph builder
asserts in llama_model_qwen4exp::graph::graph:

  GGML_ASSERT(mctx_idx->get_n_kv() == inp->mctx->get_attn()->get_n_kv() &&
              "the indexer cache must track the attention cache cell for cell")

This aborts deep-context (QSA budget exceeded) and multi-slot unified-KV
generation.

Fix: construct the indexer cache with the attention cache as `mem_other`, so
it shares the attention cache's cell metadata (v_cells_impl) via the existing
`other` mechanism. The two then agree cell-for-cell by construction and can
never drift. The indexer keeps its own K/V tensors (the share callback is
null), and the attn_rot/n_embd_head values inherited from the attention cache
are harmless because the indexer path never uses them (cpy_k stores raw keys).

Fixes ggml-org#27780

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70013d030c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

model, hparams_idx, type_k, type_v, v_trans, offload, unified,
kv_size, n_seq_max, n_pad, n_swa, swa_type,
nullptr, filter_idx, nullptr, nullptr, "idx_");
get_mem_attn(), filter_idx, nullptr, nullptr, "idx_");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve indexer tensors when sharing cells

Passing get_mem_attn() here sets llama_kv_cache::other on mem_idx. In that mode llama_kv_cache::state_write() and state_read_sinfo() immediately return, but the idx_ cache still allocates its own K/V tensors because the share callback is null; saving/loading a Qwen4Exp session therefore restores attention cell metadata without restoring the indexer keys that QSA top-k reads, producing stale or zero scores for past tokens. Please keep cell sharing from disabling indexer tensor serialization, or add a metadata-only sharing path.

Useful? React with 👍 / 👎.

Comment on lines +55 to +59
// the indexer cache is a side buffer addressed cell-for-cell by the attention
// cache: it must track the attention cache's cells exactly or QSA top-k reads
// the wrong cells. sharing the attention cache's cell metadata (v_cells_impl)
// via `other` makes the two agree by construction -- the indexer keeps its own
// K/V tensors but sees the same positions/sequences/used-set as attention, so

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Shorten the added cache comment

This new block is a seven-line explanation with sentences split across comment lines; the repo guidance asks comments to stay concise, usually 1-2 lines, and not to split a sentence just to wrap. In this hot memory code, condensing it to the invariant, metadata is shared with the attention cache while tensors are not, would reduce maintenance noise.

AGENTS.md reference: AGENTS.md:L73-L81

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant