Skip to content

ggml-meta : split a host-resident KV cache by head - #48

Closed
Piggidragon wants to merge 49 commits into
GenerelSchwerz:masterfrom
Piggidragon:fix/tp-host-kv-cache
Closed

ggml-meta : split a host-resident KV cache by head#48
Piggidragon wants to merge 49 commits into
GenerelSchwerz:masterfrom
Piggidragon:fix/tp-host-kv-cache

Conversation

@Piggidragon

Copy link
Copy Markdown

Fixes --split-mode tensor together with --no-kv-offload, which on a GQA model
either crashes or silently returns wrong output.

Branched from master (d222767c7); no code from beellama/* or llama/dev is
involved. It applies unchanged on top of current ggml-org master (5d5cb4c3a).

The defect

With -sm tensor, ggml-backend-meta asks the model's split-state callback how each
tensor is divided across devices. That question is only asked for tensors outside a
compute buffer:

if (ggml_backend_buffer_get_usage(tensor->buffer) != GGML_BACKEND_BUFFER_USAGE_COMPUTE && tensor->view_src == nullptr) {

With -nkvo the KV cache lives in host memory, so it is not a tensor of the meta
backend at all. The scheduler copies it in, and that copy is a leaf in the compute
buffer. It therefore never reaches the callback and falls through to MIRRORED, while
the queries stay split by head. Both devices then hold every KV head but only half the
query heads, so handle_flash_attn_ext's kv_mirrored branch is taken, and each
device's renumbered queries attend the other device's keys.

kv_mirrored is correct where it was written for -- MLA, one KV head, every query head
maps to head 0. It is wrong for any GQA cache with more than one KV head.

Reproducing on pristine upstream

Two GPUs (RTX 4070 + RTX 3060), CUDA, NCCL on.

gemma-4-26B-A4B (n_head 16, n_head_kv 8, attention only) aborts:

ggml/src/ggml-cuda/fattn.cu:371: GGML_ASSERT(Q->ne[2] % K->ne[2] == 0) failed
llama-completion -m gemma-4-26B-A4B.gguf -ngl 99 -c 4096 -n 64 \
    --temp 0 --top-k 1 --seed 1 --no-warmup --jinja -p "..." \
    -sm tensor -ts 55,45 -nkvo

Where the query split happens to stay a multiple of the KV head count the assert does
not fire and the output is simply wrong. That is what this looked like before the FA
kernel gained the shape check.

The fix

  1. ggml-backend-meta.cpp -- offer a copied-in compute-buffer leaf to the split-state
    callback. Names the callback does not know still answer MIRRORED, so nothing else
    changes.
  2. llama-model.cpp -- undecorate <backend>#cache_k_l0 (view) (permuted)#0 back to
    cache_k_l0. Only a cache name is undecorated.
  3. llama-model.cpp -- a host-resident cache arrives permuted as
    [head_dim, n_kv, n_head_kv, n_stream], so it splits on axis 2 with one KV head as
    the unit rather than on axis 0 with one head's worth of elements.
  4. ggml-backend-meta.cpp -- writing that split is a strided copy, because the heads
    are interleaved inside each cell rather than laid out end to end.

Testing

gemma-4-26B-A4B-Q?, 2 GPUs, -ts 55,45, greedy, seed 1, sha256 of the generated text:

run before after
-sm layer f3208b98afd39dcf f3208b98afd39dcf
-sm layer -nkvo f3208b98afd39dcf f3208b98afd39dcf
-sm tensor f3208b98afd39dcf f3208b98afd39dcf
-sm tensor -nkvo abort (fattn.cu:371) f3208b98afd39dcf

Qwen3.8-27B-UD-Q5_K_M, same setup, checked for regressions: -sm layer
493f41387bec090f and -sm tensor 79af072c6bb1e154 are unchanged from an
unmodified build of the same commit.

Not fixed here

A hybrid model with linear-attention layers still aborts under -sm tensor -nkvo:

ggml/src/ggml-backend-meta.cpp:826: GGML_ASSERT(src_ss[5].axis == GGML_BACKEND_SPLIT_AXIS_2 || ...) failed

Same root cause, different tensor. --no-kv-offload also puts the recurrent state in
host memory, and it reaches the gated-delta-net op not as cache_s_l<il> but as the
graph tensors state_predelta-<il> and conv_states_reshaped-<il>, which are computed
on the CPU backend and copied in. Those are graph names, not cache names, so the
name-based callback cannot classify them. Keeping the recurrent state on device avoids
it entirely; attention-only models are unaffected.

AI usage disclosure: yes, see the commit trailer.

Anbeeld and others added 22 commits August 25, 2026 13:24
Assisted-by: OpenAI Codex
Assisted-by: Codex
Assisted-by: OpenAI Codex
Assisted-by: Codex
Assisted-by: OpenAI Codex
Assisted-by: Codex
Assisted-by: OpenAI Codex
Assisted-by: OpenAI Codex
Assisted-by: Codex
…v-q8-cpu-cuda-parity

kv-cache: preserve accelerator quantization for host KV stores
Assisted-by: OpenAI Codex
…hase-peak-shared

llama: share target and MTP phase workspaces
Keep the internal metadata bounds check on configurations that can link private llama symbols while preserving the exported-API rollback test binary on Windows shared builds.

Assisted-by: OpenAI Codex
…tp-replay

speculative: cap MTP recurrent rollback planes
Anbeeld and others added 6 commits August 27, 2026 16:46
Assisted-by: OpenAI Codex
…ompact-causal-mask

llama : add compact causal attention masks
Assisted-by: OpenAI Codex
…p-compact-causal-mask

llama : clean up compact causal masks
Anbeeld and others added 20 commits August 27, 2026 22:27
…ive-context-workspace

llama : add live-context workspace sizing
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5
Preserve the pre-ubatch GDN state in the rollback slot when the whole ubatch can be removed. Keep the full-replay logits as the dirty-context oracle and compare exact Qwen3.5 recurrent checkpoints in the multi-sequence regression.

Assisted-by: Codex
@Piggidragon
Piggidragon force-pushed the fix/tp-host-kv-cache branch from 0ecfbb3 to 5d9b5b8 Compare August 28, 2026 20:26
@Piggidragon

Copy link
Copy Markdown
Author

Superseded by #57, which fixes this for every model instead of one shape and is branched from llama/dev rather than master.

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.

6 participants