merge: TurboQuant KV cache types onto upstream master (MTP built-in)
This commit is contained in:
@@ -1958,6 +1958,10 @@ ggml_tensor * llm_graph_context::build_attn_mha(
|
||||
k = ggml_permute(ctx0, k, 0, 2, 1, 3);
|
||||
v = ggml_permute(ctx0, v, 0, 2, 1, 3);
|
||||
|
||||
// TurboQuant note: graph-side Q rotation (pre-rotate-queries) is implemented below
|
||||
// in the flash-attn path. The VEC kernel bug (wrong Q/K stride in
|
||||
// vec_dot_fattn_vec_KQ_turbo3_0) was fixed in fattn-common.cuh to match f16 pattern.
|
||||
|
||||
ggml_tensor * cur;
|
||||
|
||||
const bool use_flash_attn = cparams.flash_attn && kq_b == nullptr;
|
||||
@@ -1984,6 +1988,20 @@ ggml_tensor * llm_graph_context::build_attn_mha(
|
||||
ggml_flash_attn_ext_add_sinks(cur, sinks);
|
||||
ggml_flash_attn_ext_set_prec (cur, GGML_PREC_F32);
|
||||
|
||||
// TurboQuant: inverse WHT on FA output when V values are WHT-rotated.
|
||||
// For MLA, V is a view of K with different ne[0] (e.g. V=512, K=576).
|
||||
// Group size must come from K (which determines the WHT rotation), not V.
|
||||
if (v->type == GGML_TYPE_TURBO3_0 || v->type == GGML_TYPE_TURBO4_0 || v->type == GGML_TYPE_TURBO2_0) {
|
||||
const bool k_is_turbo = (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0);
|
||||
const ggml_tensor * group_src = k_is_turbo ? k : v;
|
||||
const int turbo_group = (group_src->ne[0] % 128 == 0) ? 128 : 64;
|
||||
if (cur->ne[0] % turbo_group == 0) {
|
||||
if (!ggml_is_contiguous(cur)) { cur = ggml_cont(ctx0, cur); }
|
||||
ggml_tensor * innerq_scale = mctx ? mctx->get_turbo_innerq_scale_inv() : nullptr;
|
||||
cur = ggml_turbo_wht(ctx0, cur, 1, turbo_group, innerq_scale); // 1 = inverse
|
||||
}
|
||||
}
|
||||
|
||||
if (v_mla) {
|
||||
#if 0
|
||||
// v_mla can be applied as a matrix-vector multiplication with broadcasting across dimension 3 == n_tokens.
|
||||
@@ -2050,6 +2068,18 @@ ggml_tensor * llm_graph_context::build_attn_mha(
|
||||
ggml_tensor * kqv = ggml_mul_mat(ctx0, v, kq);
|
||||
cb(kqv, "kqv", il);
|
||||
|
||||
// TurboQuant: inverse WHT on attention output (non-FA path)
|
||||
if (v->type == GGML_TYPE_TURBO3_0 || v->type == GGML_TYPE_TURBO4_0 || v->type == GGML_TYPE_TURBO2_0) {
|
||||
const bool k_is_turbo = (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0);
|
||||
const ggml_tensor * group_src = k_is_turbo ? k : v;
|
||||
const int turbo_group = (group_src->ne[0] % 128 == 0) ? 128 : 64;
|
||||
if (kqv->ne[0] % turbo_group == 0) {
|
||||
if (!ggml_is_contiguous(kqv)) { kqv = ggml_cont(ctx0, kqv); }
|
||||
ggml_tensor * innerq_scale = mctx ? mctx->get_turbo_innerq_scale_inv() : nullptr;
|
||||
kqv = ggml_turbo_wht(ctx0, kqv, 1, turbo_group, innerq_scale);
|
||||
}
|
||||
}
|
||||
|
||||
// for MLA with the absorption optimization, we need to "decompress" from MQA back to MHA
|
||||
if (v_mla) {
|
||||
kqv = ggml_mul_mat(ctx0, v_mla, kqv);
|
||||
@@ -2067,6 +2097,8 @@ ggml_tensor * llm_graph_context::build_attn_mha(
|
||||
}
|
||||
}
|
||||
|
||||
// TurboQuant: graph-side inverse WHT on attention output (undoes V rotation)
|
||||
|
||||
ggml_build_forward_expand(gf, cur);
|
||||
|
||||
return cur;
|
||||
@@ -2227,12 +2259,44 @@ ggml_tensor * llm_graph_context::build_attn(
|
||||
ggml_tensor * k = mctx_cur->get_k(ctx0, il);
|
||||
ggml_tensor * v = mctx_cur->get_v(ctx0, il);
|
||||
|
||||
// TurboQuant pre-rotate-queries: O(d log d) WHT rotation via custom op
|
||||
// Q shape: (n_embd_head, n_head, n_tokens)
|
||||
// For zero-padded models (head_dim not 128-aligned), pad Q to match padded K dim first.
|
||||
if (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0) {
|
||||
// Pad Q per-head to next multiple of 128 if needed
|
||||
if (q->ne[0] % 128 != 0) {
|
||||
const int64_t pad = ((q->ne[0] + 127) / 128) * 128 - q->ne[0];
|
||||
q = ggml_pad(ctx0, q, pad, 0, 0, 0);
|
||||
}
|
||||
if (!ggml_is_contiguous(q)) { q = ggml_cont(ctx0, q); }
|
||||
ggml_tensor * innerq_scale = mctx_cur->get_turbo_innerq_scale_inv();
|
||||
q = ggml_turbo_wht(ctx0, q, 0, 0, innerq_scale); // 0 = forward, 0 = auto group size from q->ne[0]
|
||||
}
|
||||
|
||||
ggml_tensor * cur = build_attn_mha(q, k, v, kq_b, kq_mask, sinks, v_mla, kq_scale, il);
|
||||
cb(cur, "kqv_out", il);
|
||||
|
||||
if (inp->self_v_rot) {
|
||||
cur = ggml_mul_mat_aux(ctx0, cur, inp->self_v_rot);
|
||||
}
|
||||
// TurboQuant: if V was padded, the output has padded dimensions.
|
||||
// Extract original V head_dim after inverse WHT (applied inside build_attn_mha).
|
||||
if (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0) {
|
||||
const int64_t orig_v_head = hparams.n_embd_head_v(il);
|
||||
// cur is 2D: (n_embd_head * n_head, n_tokens) after build_attn_mha
|
||||
const int64_t padded_v_head = v->ne[0];
|
||||
if (padded_v_head != orig_v_head) {
|
||||
// Reshape to 4D, extract original head_dim, reshape back to 2D
|
||||
const int64_t n_head_v = hparams.n_head_kv(il);
|
||||
const int64_t n_tokens_cur = cur->ne[1];
|
||||
cur = ggml_reshape_3d(ctx0, cur, padded_v_head, n_head_v, n_tokens_cur);
|
||||
// ggml_view_3d to extract first orig_v_head elements per head
|
||||
cur = ggml_view_3d(ctx0, cur, orig_v_head, n_head_v, n_tokens_cur,
|
||||
cur->nb[1], cur->nb[2], 0);
|
||||
cur = ggml_cont(ctx0, cur);
|
||||
cur = ggml_reshape_2d(ctx0, cur, orig_v_head * n_head_v, n_tokens_cur);
|
||||
}
|
||||
}
|
||||
|
||||
if (wo) {
|
||||
if (arch == LLM_ARCH_GLM4 || arch == LLM_ARCH_GLM4_MOE || arch == LLM_ARCH_JAIS2) {
|
||||
@@ -2318,9 +2382,39 @@ ggml_tensor * llm_graph_context::build_attn(
|
||||
ggml_tensor * k = mctx_cur->get_k(ctx0, il);
|
||||
ggml_tensor * v = ggml_view_4d(ctx0, k, v_cur->ne[0], k->ne[1], k->ne[2], k->ne[3], k->nb[1], k->nb[2], k->nb[3], 0);
|
||||
|
||||
// TurboQuant: pre-rotate Q for K-only (MLA) attention
|
||||
// For zero-padded models, pad Q to match padded K dim first.
|
||||
if (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0) {
|
||||
// Pad Q per-head to next multiple of 128 if needed
|
||||
if (q->ne[0] % 128 != 0) {
|
||||
const int64_t pad = ((q->ne[0] + 127) / 128) * 128 - q->ne[0];
|
||||
q = ggml_pad(ctx0, q, pad, 0, 0, 0);
|
||||
}
|
||||
if (!ggml_is_contiguous(q)) { q = ggml_cont(ctx0, q); }
|
||||
ggml_tensor * innerq_scale = mctx_cur->get_turbo_innerq_scale_inv();
|
||||
q = ggml_turbo_wht(ctx0, q, 0, 0, innerq_scale); // 0 = forward, 0 = auto group size
|
||||
}
|
||||
|
||||
ggml_tensor * cur = build_attn_mha(q, k, v, kq_b, kq_mask, sinks, v_mla, kq_scale, il);
|
||||
cb(cur, "kqv_out", il);
|
||||
|
||||
// TurboQuant: if V was padded (MLA: V is view of K, may have padded dim),
|
||||
// extract original V head_dim after inverse WHT.
|
||||
if (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0) {
|
||||
const int64_t orig_v_head = v_cur->ne[0]; // original V head_dim from model
|
||||
const int64_t padded_v_head = v->ne[0]; // padded V head_dim in cache
|
||||
if (padded_v_head != orig_v_head) {
|
||||
// cur is 2D: (padded_v_head * n_head, n_tokens) after build_attn_mha
|
||||
const int64_t n_head_v = hparams.n_head_kv(il);
|
||||
const int64_t n_tokens_cur = cur->ne[1];
|
||||
cur = ggml_reshape_3d(ctx0, cur, padded_v_head, n_head_v, n_tokens_cur);
|
||||
cur = ggml_view_3d(ctx0, cur, orig_v_head, n_head_v, n_tokens_cur,
|
||||
cur->nb[1], cur->nb[2], 0);
|
||||
cur = ggml_cont(ctx0, cur);
|
||||
cur = ggml_reshape_2d(ctx0, cur, orig_v_head * n_head_v, n_tokens_cur);
|
||||
}
|
||||
}
|
||||
|
||||
if (wo) {
|
||||
if (arch == LLM_ARCH_GLM4 || arch == LLM_ARCH_GLM4_MOE) {
|
||||
// GLM4 and GLM4_MOE seem to have numerical issues with half-precision accumulators
|
||||
@@ -2406,12 +2500,37 @@ ggml_tensor * llm_graph_context::build_attn(
|
||||
ggml_tensor * k = mctx_cur->get_k(ctx0, il);
|
||||
ggml_tensor * v = mctx_cur->get_v(ctx0, il);
|
||||
|
||||
// TurboQuant: pre-rotate Q for ISWA attention (pad to 128-aligned if needed)
|
||||
if (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0) {
|
||||
if (q->ne[0] % 128 != 0) {
|
||||
const int64_t pad = ((q->ne[0] + 127) / 128) * 128 - q->ne[0];
|
||||
q = ggml_pad(ctx0, q, pad, 0, 0, 0);
|
||||
}
|
||||
if (!ggml_is_contiguous(q)) { q = ggml_cont(ctx0, q); }
|
||||
ggml_tensor * innerq_scale = mctx_cur->get_turbo_innerq_scale_inv();
|
||||
q = ggml_turbo_wht(ctx0, q, 0, 0, innerq_scale);
|
||||
}
|
||||
|
||||
ggml_tensor * cur = build_attn_mha(q, k, v, kq_b, kq_mask, sinks, v_mla, kq_scale, il);
|
||||
cb(cur, "kqv_out", il);
|
||||
|
||||
if (v_rot) {
|
||||
cur = ggml_mul_mat_aux(ctx0, cur, v_rot);
|
||||
}
|
||||
// TurboQuant: if V was padded, extract original V head_dim after inverse WHT
|
||||
if (k->type == GGML_TYPE_TURBO3_0 || k->type == GGML_TYPE_TURBO4_0 || k->type == GGML_TYPE_TURBO2_0) {
|
||||
const int64_t orig_v_head = hparams.n_embd_head_v(il);
|
||||
const int64_t padded_v_head = v->ne[0];
|
||||
if (padded_v_head != orig_v_head) {
|
||||
const int64_t n_head_v = hparams.n_head_kv(il);
|
||||
const int64_t n_tokens_cur = cur->ne[1];
|
||||
cur = ggml_reshape_3d(ctx0, cur, padded_v_head, n_head_v, n_tokens_cur);
|
||||
cur = ggml_view_3d(ctx0, cur, orig_v_head, n_head_v, n_tokens_cur,
|
||||
cur->nb[1], cur->nb[2], 0);
|
||||
cur = ggml_cont(ctx0, cur);
|
||||
cur = ggml_reshape_2d(ctx0, cur, orig_v_head * n_head_v, n_tokens_cur);
|
||||
}
|
||||
}
|
||||
|
||||
if (wo) {
|
||||
cur = build_lora_mm(wo, cur, wo_s);
|
||||
|
||||
Reference in New Issue
Block a user