mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-11-11 14:22:09 +01:00
* ggml : transpose fused GDN state access for coalesced memory reads (#20436) The fused Gated Delta Net kernel accessed the [S_v, S_v] state matrix column-wise on row-major storage, causing strided reads (stride S_v = 128 floats = 512 bytes) that waste GPU cache bandwidth. This produced a 39% regression on Qwen3.5-9B (Metal, M4 Max) compared to the unfused path. Transpose the state indexing so threads read contiguously: - Metal: s_ptr[is*S_v] -> s_ptr[is] (stride 1 vs S_v) - CUDA: curr_state[i*S_v+col] -> curr_state[col*S_v+i] (coalesced) - CPU: restructured loops for row-wise transposed access Also add --fused-gdn [on|off|auto] CLI flag (mirrors --flash-attn) so users can control fused GDN independently of auto-detection. All GATED_DELTA_NET backend-ops tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ggml : use SIMD dot products in CPU GDN kernel, couple AR/chunked fused flags - Replace scalar inner loops with ggml_vec_dot_f32 for SIMD-optimized dot products in the CPU fused GDN kernel (delta and attention output) - Couple fused_gdn_ar and fused_gdn_ch flags in auto-detection: if one path lacks device support, disable both to prevent state layout mismatch between transposed (fused) and non-transposed (unfused) formats Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * llama : rever fgdn argument changes * graph : remove GDN state transposes * vulkan : adapt * cuda : remove obsolete smem code --------- Co-authored-by: Paul Flynn <paul@arkavo.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Oliver Simons <osimons@nvidia.com>
129 lines
4.5 KiB
Plaintext
129 lines
4.5 KiB
Plaintext
#version 450
|
|
|
|
#extension GL_EXT_control_flow_attributes : require
|
|
|
|
layout(constant_id = 0) const uint S_V = 128;
|
|
layout(constant_id = 1) const uint KDA = 0;
|
|
|
|
layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in;
|
|
|
|
layout(push_constant) uniform Parameters {
|
|
uint H;
|
|
uint n_tokens;
|
|
uint n_seqs;
|
|
uint s_off;
|
|
uint sq1, sq2, sq3;
|
|
uint sv1, sv2, sv3;
|
|
uint sb1, sb2, sb3;
|
|
uint neq1, rq3;
|
|
float scale;
|
|
};
|
|
|
|
layout(binding = 0) readonly buffer QBuf { FLOAT_TYPE data_q[]; };
|
|
layout(binding = 1) readonly buffer KBuf { FLOAT_TYPE data_k[]; };
|
|
layout(binding = 2) readonly buffer VBuf { FLOAT_TYPE data_v[]; };
|
|
layout(binding = 3) readonly buffer GBuf { FLOAT_TYPE data_g[]; };
|
|
layout(binding = 4) readonly buffer BetaBuf { FLOAT_TYPE data_beta[]; };
|
|
layout(binding = 5) readonly buffer StateBuf { FLOAT_TYPE data_state[]; };
|
|
layout(binding = 6) buffer DstBuf { FLOAT_TYPE data_dst[]; };
|
|
|
|
shared FLOAT_TYPE s_k[S_V];
|
|
shared FLOAT_TYPE s_q[S_V];
|
|
shared FLOAT_TYPE s_g[S_V]; // KDA only: cached exp(g[i])
|
|
|
|
void main() {
|
|
const uint head_id = gl_WorkGroupID.x;
|
|
const uint seq_id = gl_WorkGroupID.y;
|
|
const uint col = gl_LocalInvocationID.x;
|
|
|
|
const uint iq1 = head_id % neq1;
|
|
const uint iq3 = seq_id / rq3;
|
|
|
|
const uint state_size = S_V * S_V;
|
|
const uint state_base = (seq_id * H + head_id) * state_size;
|
|
|
|
FLOAT_TYPE state[S_V];
|
|
[[unroll]] for (uint i = 0; i < S_V; i++) {
|
|
state[i] = FLOAT_TYPE(data_state[state_base + col * S_V + i]);
|
|
}
|
|
|
|
uint attn_off = (seq_id * n_tokens * H + head_id) * S_V;
|
|
|
|
for (uint t = 0; t < n_tokens; t++) {
|
|
const uint q_off = iq3 * sq3 + t * sq2 + iq1 * sq1;
|
|
const uint k_off = q_off;
|
|
const uint v_off = seq_id * sv3 + t * sv2 + head_id * sv1;
|
|
|
|
s_q[col] = FLOAT_TYPE(data_q[q_off + col]);
|
|
s_k[col] = FLOAT_TYPE(data_k[k_off + col]);
|
|
|
|
const uint gb_off = seq_id * sb3 + t * sb2 + head_id * sb1;
|
|
|
|
if (KDA != 0) {
|
|
const uint g_base = gb_off * S_V;
|
|
s_g[col] = exp(FLOAT_TYPE(data_g[g_base + col]));
|
|
}
|
|
|
|
barrier();
|
|
|
|
const FLOAT_TYPE v_val = FLOAT_TYPE(data_v[v_off + col]);
|
|
const FLOAT_TYPE beta_val = FLOAT_TYPE(data_beta[gb_off]);
|
|
|
|
if (KDA == 0) {
|
|
const FLOAT_TYPE g_val = exp(FLOAT_TYPE(data_g[gb_off]));
|
|
|
|
FLOAT_TYPE kv_col = 0.0;
|
|
[[unroll]] for (uint i = 0; i < S_V; i += 4) {
|
|
kv_col += dot(
|
|
vec4(state[i], state[i+1], state[i+2], state[i+3]),
|
|
vec4(s_k[i], s_k[i+1], s_k[i+2], s_k[i+3])
|
|
);
|
|
}
|
|
|
|
FLOAT_TYPE delta_col = (v_val - g_val * kv_col) * beta_val;
|
|
|
|
FLOAT_TYPE attn_col = 0.0;
|
|
[[unroll]] for (uint i = 0; i < S_V; i += 4) {
|
|
vec4 sv = vec4(state[i], state[i+1], state[i+2], state[i+3]);
|
|
vec4 kv = vec4(s_k[i], s_k[i+1], s_k[i+2], s_k[i+3]);
|
|
sv = g_val * sv + kv * delta_col;
|
|
state[i] = sv.x; state[i+1] = sv.y; state[i+2] = sv.z; state[i+3] = sv.w;
|
|
|
|
attn_col += dot(sv, vec4(s_q[i], s_q[i+1], s_q[i+2], s_q[i+3]));
|
|
}
|
|
|
|
data_dst[attn_off + col] = attn_col * scale;
|
|
} else {
|
|
FLOAT_TYPE kv_col = 0.0;
|
|
[[unroll]] for (uint i = 0; i < S_V; i += 4) {
|
|
vec4 gv = vec4(s_g[i], s_g[i+1], s_g[i+2], s_g[i+3]);
|
|
vec4 sv = vec4(state[i], state[i+1], state[i+2], state[i+3]);
|
|
vec4 kv = vec4(s_k[i], s_k[i+1], s_k[i+2], s_k[i+3]);
|
|
kv_col += dot(gv * sv, kv);
|
|
}
|
|
|
|
FLOAT_TYPE delta_col = (v_val - kv_col) * beta_val;
|
|
|
|
FLOAT_TYPE attn_col = 0.0;
|
|
[[unroll]] for (uint i = 0; i < S_V; i += 4) {
|
|
vec4 gv = vec4(s_g[i], s_g[i+1], s_g[i+2], s_g[i+3]);
|
|
vec4 sv = vec4(state[i], state[i+1], state[i+2], state[i+3]);
|
|
vec4 kv = vec4(s_k[i], s_k[i+1], s_k[i+2], s_k[i+3]);
|
|
sv = gv * sv + kv * delta_col;
|
|
state[i] = sv.x; state[i+1] = sv.y; state[i+2] = sv.z; state[i+3] = sv.w;
|
|
|
|
attn_col += dot(sv, vec4(s_q[i], s_q[i+1], s_q[i+2], s_q[i+3]));
|
|
}
|
|
|
|
data_dst[attn_off + col] = attn_col * scale;
|
|
}
|
|
|
|
attn_off += S_V * H;
|
|
barrier();
|
|
}
|
|
|
|
[[unroll]] for (uint i = 0; i < S_V; i++) {
|
|
data_dst[s_off + state_base + col * S_V + i] = state[i];
|
|
}
|
|
}
|