This repository was archived by the owner on Oct 16, 2026. It is now read-only.
Repository navigation
Conversation
With -fa off, llama.cpp stores the V cache transposed, with value elements as rows and tokens along each row. The KV slicing code assumes one token per row and binds the wrong data when n_kv < kv_size. Detect transposed caches with GgmlOvDecoder::is_kvcache_token_rows() and skip slicing for them. For grouped-query attention, avoid repeating K and V for each query head. Stack the query heads for each KV head along the row dimension and let SOFT_MAX apply the scale, mask, and softmax so the GPU plugin can fuse the operation with the MatMuls. Read only the current layer's V-cache rows when layers use different cache sizes. Reject -fa off in stateful mode with a clear error and document that it requires stateless mode.
With `-fa off`, the graph writes the V cache in transposed form, which is not supported by the stateful KV cache. Detect this pattern in `compute_llm_params()` as a `SET_ROWS` into a KV cache through a `[1, n]` reshape. When stateful execution is enabled, log a warning and fall back to stateless execution instead of failing graph conversion. Update the `-fa off` note in `OPENVINO.md` to document this behavior. Co-authored-by: Suhas Somashekar <suhas.somashekar@intel.com>
With -fa off, the 4-D KV cache layout introduced in ggml-org#344 breaks MUL_MAT attention for Phi-3.5 mini and Phi-4 mini, causing oneDNN to fail with "could not construct a memory descriptor using strides". Preserve the [1, 1, ctx, n_heads_kv * head_size] layout when the graph writes a transposed V cache.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Fix incorrect results with
-fa offin the OpenVINO backend, improve decode performance at long context for models with grouped-query attention (GQA), and handle the incompatibility between transposed V caches and stateful execution.This change:
-fa off.[1, 1, ctx, n_heads_kv * head_size]KV-cache layout with-fa off;-fa oncontinues to use the 4-D layout from the KV-cache change dd04231.Fix
-fa offV-cache slicingWith
-fa off, llama.cpp stores the V cache in transposed form: each row contains one value element, with tokens laid out along the row.try_make_kv_sliced_tensor()previously assumed that each row represented one token and therefore sliced the cache to its firstn_kvrows.That is incorrect for the transposed V cache: when
n_kv < kv_size, the slice selects the wrong data and causes a large accuracy loss at depth.GgmlOvDecoder::is_kvcache_token_rows()now detects caches whoseSET_ROWSwrites rows narrower than the cache row. KV slicing is skipped for these caches, so the full transposed V cache is used.This fixes the accuracy loss with
-fa offwhen the KV cache is not full.Speed up GQA decode
For models with grouped KV heads, the previous implementation replicated K and V for every query head before the KQ and KQV
MUL_MAToperations. This unnecessarily copies the entire K/V cache for every layer and token.The new implementation groups the query heads belonging to each KV head along the rows using a reshape, then performs one MatMul per KV head. This avoids K/V replication while producing the same grouped-attention computation.
The resulting 4-D attention scores are passed through
SOFT_MAX, which applies the scale, tiled causal mask, and softmax together. This allows the GPU plugin to fuse these operations with the MatMuls. Keeping the mask as a separateAddoperation made prefill slower.On GPU, the attention mask is clamped to
-30000, matching the existing behavior inflash_attn_ext. This avoids NaNs in the fused kernel when an entire block contains-inf.Handle different V-cache sizes across layers
Some models use different V head sizes in different layers. For example, Gemma-4 E2B uses a V head size of 512 in full-attention layers and 256 in sliding-window layers.
The transposed V cache is therefore allocated for the widest layer. When reading the cache, the implementation now uses only the
n_heads * head_sizerows required by the current layer.Fall back to stateless execution
The stateful KV-cache path does not support a transposed V cache.
Following Suhas Somashekar's approach in ggml-org/llama.cpp#30350, the decoder now detects a transposed V cache and, when
GGML_OPENVINO_STATEFUL_EXECUTION=1, falls back to stateless execution and logs a warning.The conversion check in
set_rows.cppremains as a safety net.This behavior is also documented in
docs/backend/OPENVINO.md.Keep the flat KV-cache layout with
-fa offThe 4-D KV-cache layout introduced by dd04231 breaks
MUL_MATattention in Phi-3.5 mini and Phi-4 mini with oneDNN:When the graph writes a transposed V cache, the KV cache therefore continues to use the flat
[1, 1, ctx, n_heads_kv * head_size]layout with-fa off.-fa oncontinues to use the 4-D layout introduced by dd04231.The root cause of the Phi-3.5 mini / Phi-4 mini oneDNN failure is left for a follow-up change.
Validation
Arc B390 (Panther Lake) GPU, Q4_K_M, OpenVINO master.
Accuracy
KL divergence against the CPU backend, decode at context 1024 (
-b 1),-fa off:Perplexity on wikitext-2,
-c 1024, 4 chunks:-fa off-fa onWith
GGML_OPENVINO_STATEFUL_EXECUTION=1and-fa off, Llama-3.2 1B, SmolLM2 1.7B, Qwen3.5 4B, Phi-4 mini, and Phi-3.5 mini all print the fallback warning and produce the same perplexity as stateless execution.test-backend-ops: 8024/8024 passed.Performance
llama-bench -fa 0, decode (-n 64, tokens/s). Console runs, mean of two rounds, on the fork before the GQA attention change 2e4dbf0 was merged (ab759cf41), without the stateful fallback.The base implementation produces incorrect results at depth because of the V-cache slicing issue. For reference, the table also includes the base implementation with KV slicing disabled, which produces correct results.
Decode at depth 0 changes by at most 7% (Llama-3.2 1B, 91.5 → 97.8).
Prefill (
-p 512) changes by -10% to +20% depending on model and depth, with no consistent direction. This is within the run-to-run variation observed on this machine:-fa 1prefill, which does not use the changed code, varies by up to 10% between the same two builds.SmolLM2 1.7B and Phi-3.5 mini do not use grouped KV heads and are unchanged from the base implementation when KV slicing is disabled.
-fa 1decode is unchanged within 2%.On the final branch, rebased onto 8619618 and including the stateful fallback, an SSH check at depth 4096 (median of 3) gives:
-fa off-fa onBefore dd04231,
-fa offand-fa ondecoded at the same speed at this depth. The remaining gap is primarily because-fa onuses the 4-D KV-cache layout introduced by dd04231, while-fa offintentionally retains the flat layout for compatibility with the transposed V cache.The first two commits were also A/B tested against
dev_backend_openvinousing three interleaved rounds at depths 0 and 4096. Performance was within 1% on Llama-3.2 1B, Phi-4 mini, and Qwen3.5 4B. The third commit only applies with-fa off.Requirements