Skip to content

Commit fb556b1

Browse files
kmbandyclaude
andcommitted
feat(paged): support sub-128 head_dim for paged turbo cache (LFM2.5 head_dim 64)
The paged turbo kernels quantize 128-element blocks (QK_TURBO=128), so a head_dim < 128 (LFM2.5 hybrid attention: head_dim 64) is smaller than one block and the dispatch rejects it. Zero-pad each head to 128 in build_attn for the paged turbo path (q/k/v padded before the F16 cast since ggml_pad needs F32; output sliced back to head_dim), and size the paged cache to the padded head_dim to match. Mirrors the non-paged turbo path. Correctness: padded Q dims are zero so they contribute nothing to QK (K-pad quant is irrelevant); padded V output dims are sliced off. Validated end-to-end on gfx803 once the warp-width corruption was fixed -- LFM2.5 paged turbo4 now generates coherent, correct text on the full tiered config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 62f4f99 commit fb556b1

2 files changed

Lines changed: 38 additions & 3 deletions

File tree

src/llama-graph.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,9 +2659,27 @@ ggml_tensor * llm_graph_context::build_attn(
26592659
}
26602660
return x;
26612661
};
2662-
ggml_tensor * q_cast = to_f16_cont(q_cur);
2663-
ggml_tensor * k_cast = to_f16_cont(k_cur);
2664-
ggml_tensor * v_cast = to_f16_cont(v_cur);
2662+
// Turbo paged kernel quantizes 128-element blocks (QK_TURBO=128); a
2663+
// head_dim < 128 (e.g. LFM2.5 head_dim 64) is smaller than one block,
2664+
// which the kernel rejects. Zero-pad each head to 128 so the proven
2665+
// HS=128 path runs: the turbo WHT identity makes <WHT(Qp),WHT(Kp)> ==
2666+
// <Q,K>, and the padded V dims contribute zero and are sliced off the
2667+
// output below. Mirrors the non-paged turbo path. ggml_pad needs F32,
2668+
// so pad before the F16 cast.
2669+
const bool paged_turbo_pad =
2670+
(layer.k->type == GGML_TYPE_TURBO2_0 ||
2671+
layer.k->type == GGML_TYPE_TURBO3_0 ||
2672+
layer.k->type == GGML_TYPE_TURBO4_0);
2673+
const int64_t paged_orig_head = q_cur->ne[0];
2674+
const bool paged_pad_head = paged_turbo_pad && (paged_orig_head % 128 != 0);
2675+
auto pad_head_to_128 = [&](ggml_tensor * t) -> ggml_tensor * {
2676+
const int64_t pad = ((paged_orig_head + 127) / 128) * 128 - paged_orig_head;
2677+
ggml_tensor * x = (t->type == GGML_TYPE_F32) ? t : ggml_cast(ctx0, t, GGML_TYPE_F32);
2678+
return ggml_pad(ctx0, x, pad, 0, 0, 0);
2679+
};
2680+
ggml_tensor * q_cast = to_f16_cont(paged_pad_head ? pad_head_to_128(q_cur) : q_cur);
2681+
ggml_tensor * k_cast = to_f16_cont(paged_pad_head ? pad_head_to_128(k_cur) : k_cur);
2682+
ggml_tensor * v_cast = to_f16_cont(paged_pad_head ? pad_head_to_128(v_cur) : v_cur);
26652683

26662684
// 2) Forward-expand the source nodes so they're fully computed
26672685
// before paged_attn reads them.
@@ -2692,6 +2710,14 @@ ggml_tensor * llm_graph_context::build_attn(
26922710
// typically wired only for F32 activations.
26932711
cur = ggml_cast(ctx0, cur, GGML_TYPE_F32);
26942712

2713+
// Slice the turbo head padding back off (see paged_pad_head above):
2714+
// output mirrors q as [padded_head, n_heads, n_tokens]; keep the first
2715+
// paged_orig_head dims per head.
2716+
if (paged_pad_head) {
2717+
cur = ggml_cont(ctx0, ggml_view_3d(ctx0, cur, paged_orig_head, cur->ne[1], cur->ne[2],
2718+
cur->nb[1], cur->nb[2], 0));
2719+
}
2720+
26952721
// 6) Reshape to (head_dim*n_heads, n_tokens) for the wo projection.
26962722
const int64_t n_embd_full = q_cur->ne[0] * q_cur->ne[1];
26972723
const int64_t n_tokens = q_cur->ne[2];

src/llama-kv-cache-paged.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ llama_kv_cache_paged::llama_kv_cache_paged(
120120
}
121121
}
122122

123+
// Turbo cache quantizes 128-element blocks; pad a sub-128 head_dim up to
124+
// 128 so each head is exactly one turbo block (matches the graph-level
125+
// padding in llm_graph_context::build_attn for the paged path).
126+
const bool paged_cache_is_turbo =
127+
(type_k == GGML_TYPE_TURBO2_0 || type_k == GGML_TYPE_TURBO3_0 || type_k == GGML_TYPE_TURBO4_0);
128+
if (paged_cache_is_turbo && head_dim % 128 != 0) {
129+
head_dim = ((head_dim + 127) / 128) * 128;
130+
}
131+
123132
GGML_ASSERT(head_dim > 0 && "head_dim must be > 0");
124133
GGML_ASSERT(n_kv_heads > 0 && "n_kv_heads must be > 0");
125134

0 commit comments

Comments
 (0)