Skip to content

Commit e95dae1

Browse files
authored
Remove padding and multiple D2D copies for MTP (ggml-org#24086)
* Make ggml_gated_delta_net take only the initial recurrent state (D, 1, n_seqs) and passes the snapshot count K as an op parameter instead of inferring it from state->ne[1]. Remove the padding hack and copy all emitted snapshots into the recurrent cache with a single strided ggml_cpy * Make GDN changes in all backends. Address review comments. * Fix CI build errors
1 parent d2462f8 commit e95dae1

20 files changed

Lines changed: 118 additions & 106 deletions

File tree

ggml/include/ggml.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,18 +2553,25 @@ extern "C" {
25532553
// TODO: add ggml_gated_delta_net_set_bcast() to be able to configure Q, K broadcast type: tiled vs interleaved [TAG_GGML_GDN_BCAST]
25542554
// ref: https://github.com/ggml-org/llama.cpp/pull/19468#discussion_r2786394306
25552555
//
2556-
// state is a 3D tensor of shape (S_v*S_v*H, K, n_seqs):
2557-
// K == 1: output carries the final state only.
2558-
// K > 1: output carries K snapshot slots; the kernel writes the last min(n_tokens, K)
2559-
// per-token snapshots into the trailing slots
2556+
// tensor shapes (S_k == S_v, H_v % H_k == 0):
2557+
// q, k : [S_k, H_k, n_tokens, n_seqs]
2558+
// v : [S_v, H_v, n_tokens, n_seqs]
2559+
// g : [1, H_v, n_tokens, n_seqs] (scalar gate) or [S_v, H_v, n_tokens, n_seqs] (KDA)
2560+
// beta : [1, H_v, n_tokens, n_seqs]
2561+
// state : [S_v, S_v, H_v, n_seqs] -- initial recurrent state s0
2562+
//
2563+
// the output packs the attention scores [S_v, H_v, n_tokens, n_seqs] followed by K state
2564+
// snapshots, most-recent first (slot 0 = final state, slot s = state s tokens back). K == 1
2565+
// keeps only the final state; when n_tokens < K only slots 0..n_tokens-1 are written.
25602566
GGML_API struct ggml_tensor * ggml_gated_delta_net(
25612567
struct ggml_context * ctx,
25622568
struct ggml_tensor * q,
25632569
struct ggml_tensor * k,
25642570
struct ggml_tensor * v,
25652571
struct ggml_tensor * g,
25662572
struct ggml_tensor * beta,
2567-
struct ggml_tensor * state);
2573+
struct ggml_tensor * state,
2574+
int64_t K);
25682575

25692576
// custom operators
25702577

ggml/src/ggml-backend-meta.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -776,8 +776,8 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
776776
GGML_ASSERT(src_ss[2].axis == GGML_BACKEND_SPLIT_AXIS_1);
777777
GGML_ASSERT(src_ss[3].axis == GGML_BACKEND_SPLIT_AXIS_1);
778778
GGML_ASSERT(src_ss[4].axis == GGML_BACKEND_SPLIT_AXIS_1);
779-
// state shape is (S_v*S_v*H, K, n_seqs); the heads dim is nested inside axis 0,
780-
// so a head-aligned split on the input cache reshapes to axis 0 here (not axis 2).
779+
// state shape is [S_v, S_v, H_v, n_seqs] (s0 only); the heads dim is its own axis 2,
780+
// so a head-aligned split on the input cache lands on axis 2 here.
781781
GGML_ASSERT(src_ss[5].axis == GGML_BACKEND_SPLIT_AXIS_2 || src_ss[5].axis == GGML_BACKEND_SPLIT_AXIS_1 || src_ss[5].axis == GGML_BACKEND_SPLIT_AXIS_0);
782782
return {GGML_BACKEND_SPLIT_AXIS_0, {0}, {1}, 1};
783783
};

ggml/src/ggml-cpu/ggml-cpu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2948,7 +2948,7 @@ struct ggml_cplan ggml_graph_plan(
29482948
case GGML_OP_GATED_DELTA_NET:
29492949
{
29502950
const int64_t S_v = node->src[2]->ne[0];
2951-
const int64_t K = node->src[5]->ne[1]; // state is (D, K, n_seqs)
2951+
const int64_t K = ggml_get_op_params_i32(node, 0);
29522952
const int64_t per_thread = S_v + (K > 1 ? S_v * S_v : 0);
29532953
cur = per_thread * sizeof(float) * n_tasks;
29542954
} break;

ggml/src/ggml-cpu/ops.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10624,11 +10624,11 @@ static void ggml_compute_forward_gated_delta_net_one_chunk(
1062410624

1062510625
const bool kda = (neg0 == S_v);
1062610626

10627-
// state is 3D (S_v*S_v*H, K, n_seqs); K is the snapshot slot count.
10628-
const int64_t K = src_state->ne[1];
10627+
// K (snapshot slot count) is an op param; state holds s0 only [S_v, S_v, H, n_seqs].
10628+
const int64_t K = ggml_get_op_params_i32(dst, 0);
1062910629
GGML_ASSERT(K >= 1);
10630-
// per-seq stride in floats (slot 0 of seq s lives at state + s * seq_stride)
10631-
const int64_t state_seq_stride = src_state->nb[2] / sizeof(float);
10630+
// per-seq stride in floats (seq s starts at state + s * seq_stride)
10631+
const int64_t state_seq_stride = src_state->nb[3] / sizeof(float);
1063210632

1063310633
const int64_t per_thread = S_v + (K > 1 ? S_v * S_v : 0);
1063410634
const int ith = params->ith;
@@ -10644,9 +10644,8 @@ static void ggml_compute_forward_gated_delta_net_one_chunk(
1064410644
float * attn_out_base = (float *)dst->data;
1064510645
float * state_out_base = (float *)dst->data + attn_score_elems;
1064610646

10647-
// snapshot slot mapping: target_slot = t - shift. When n_tokens < K only the last
10648-
// n_tokens slots are written; earlier slots are left untouched (caller-owned).
10649-
const int64_t shift = n_tokens - K;
10647+
// snapshot slot mapping: slot 0 = most recent state, slot s = s tokens back.
10648+
// When n_tokens < K only slots 0..n_tokens-1 are written; older slots are caller-owned.
1065010649

1065110650
const float * state_in_base = (const float *)src_state->data;
1065210651

@@ -10674,7 +10673,7 @@ static void ggml_compute_forward_gated_delta_net_one_chunk(
1067410673
: state_out_base + (iv3 * H + iv1) * S_v * S_v;
1067510674

1067610675
// copy input state into the working buffer and operate in-place
10677-
// state layout (D, K, n_seqs): slot 0 of seq iv3 starts at iv3 * state_seq_stride.
10676+
// state layout [S_v, S_v, H, n_seqs]: seq iv3 starts at iv3 * state_seq_stride.
1067810677
const float * s_in = state_in_base + iv3 * state_seq_stride + iv1 * S_v * S_v;
1067910678
memcpy(s_out, s_in, S_v * S_v * sizeof(float));
1068010679

@@ -10727,7 +10726,7 @@ static void ggml_compute_forward_gated_delta_net_one_chunk(
1072710726
attn_data += S_v * H; // advance to next token
1072810727

1072910728
if (K > 1) {
10730-
const int64_t target_slot = t - shift;
10729+
const int64_t target_slot = n_tokens - 1 - t;
1073110730
if (target_slot >= 0 && target_slot < K) {
1073210731
float * curr_state_o = state_out_base + target_slot * state_size_per_snap +
1073310732
(iv3 * H + iv1) * S_v * S_v;

ggml/src/ggml-cuda/gated_delta_net.cu

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ gated_delta_net_cuda(const float * q,
3939
float * attn_data = dst;
4040
float * state = dst + attn_score_elems;
4141

42-
// input state layout (D, K, n_seqs) — seq stride is K * D = K * H * S_v * S_v.
42+
// input state holds s0 only: [S_v, S_v, H, n_seqs] — seq stride is D = H * S_v * S_v.
4343
// output state layout (per-slot D * n_seqs) — same per-(seq,head) offset as before.
44-
const int64_t state_in_offset = sequence * K * H * S_v * S_v + h_idx * S_v * S_v;
44+
const int64_t state_in_offset = sequence * H * S_v * S_v + h_idx * S_v * S_v;
4545
const int64_t state_out_offset = (sequence * H + h_idx) * S_v * S_v;
4646
state += state_out_offset;
4747
curr_state += state_in_offset + col * S_v;
@@ -143,12 +143,10 @@ gated_delta_net_cuda(const float * q,
143143
attn_data += S_v * H;
144144

145145
if constexpr (keep_rs_t) {
146-
// slot mapping: target_slot = t - shift. When n_tokens < K only the last n_tokens slots
147-
// are written; earlier slots are left untouched (caller-owned).
148-
const int shift = (int) n_tokens - K;
149-
146+
// snapshot slot mapping: slot 0 = most recent state, slot s = s tokens back.
147+
// When n_tokens < K only slots 0..n_tokens-1 are written; older slots are caller-owned.
150148
const int64_t state_size_per_token = S_v * S_v * H * n_seqs; // per-slot stride in output
151-
const int target_slot = t - shift;
149+
const int target_slot = (int) n_tokens - 1 - t;
152150
if (target_slot >= 0 && target_slot < K) {
153151
float * curr_state = (dst + attn_score_elems) + target_slot * state_size_per_token + state_out_offset;
154152
#pragma unroll
@@ -286,8 +284,8 @@ void ggml_cuda_op_gated_delta_net(ggml_backend_cuda_context & ctx, ggml_tensor *
286284

287285
cudaStream_t stream = ctx.stream();
288286

289-
// state is 3D (S_v*S_v*H, K, n_seqs); K is the snapshot slot count.
290-
const int K = (int) src_state->ne[1];
287+
// K (snapshot slot count) is an op param; state holds s0 only [S_v, S_v, H, n_seqs].
288+
const int K = ggml_get_op_params_i32(dst, 0);
291289
const bool keep_rs = K > 1;
292290

293291
if (kda) {

ggml/src/ggml-hexagon/ggml-hexagon.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2538,7 +2538,7 @@ static bool ggml_hexagon_supported_gated_delta_net(const struct ggml_hexagon_ses
25382538
const int64_t H = v->ne[1];
25392539
const int64_t n_tokens = v->ne[2];
25402540
const int64_t n_seqs = v->ne[3];
2541-
const int64_t K = state->ne[1];
2541+
const int64_t K = ggml_get_op_params_i32(op, 0);
25422542

25432543
if (S_v <= 0 || S_v > 128 || H <= 0 || n_tokens <= 0 || n_seqs <= 0) {
25442544
return false;
@@ -2551,7 +2551,8 @@ static bool ggml_hexagon_supported_gated_delta_net(const struct ggml_hexagon_ses
25512551
if ((g->ne[0] != 1 && g->ne[0] != S_v) || beta->ne[0] != 1) {
25522552
return false;
25532553
}
2554-
if (ggml_nelements(state) != S_v * S_v * H * n_seqs * K) {
2554+
// state holds s0 only [S_v, S_v, H, n_seqs]; K is op param 0.
2555+
if (ggml_nelements(state) != S_v * S_v * H * n_seqs) {
25552556
return false;
25562557
}
25572558
if (dst->ne[0] != S_v * H || dst->ne[1] != n_tokens * n_seqs + S_v * n_seqs * K) {

ggml/src/ggml-hexagon/htp/gated-delta-net-ops.c

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ static void gated_delta_net_f32_pp_thread(unsigned int nth, unsigned int ith, vo
584584
const uint32_t H = v->ne[1];
585585
const uint32_t n_tokens = v->ne[2];
586586
const uint32_t n_seqs = v->ne[3];
587-
const uint32_t K = state->ne[1];
587+
const uint32_t K = octx->op_params[0];
588588

589589
const uint32_t total_rows = H * n_seqs;
590590
if (ith >= total_rows) {
@@ -618,9 +618,8 @@ static void gated_delta_net_f32_pp_thread(unsigned int nth, unsigned int ith, vo
618618
struct fastdiv_values fd_rq3 = init_fastdiv_values(rq3);
619619
struct fastdiv_values fd_rk3 = init_fastdiv_values(rk3);
620620

621-
const uint64_t state_seq_stride = state->nb[2] / sizeof(float);
621+
const uint64_t state_seq_stride = state->nb[3] / sizeof(float);
622622
const uint64_t state_size_per_snap = (uint64_t) S_v * S_v * H * n_seqs;
623-
const int64_t shift = (int64_t) n_tokens - (int64_t) K;
624623

625624
uint32_t ir_prefetch = ith;
626625
int spad_idx = 0;
@@ -630,7 +629,8 @@ static void gated_delta_net_f32_pp_thread(unsigned int nth, unsigned int ith, vo
630629
const uint32_t piv1 = fastmodulo(ir_prefetch, H, &fd_H);
631630
const uint32_t piv3 = fastdiv(ir_prefetch, &fd_H);
632631
const float * ps_in = state_in_base + (uint64_t) piv3 * state_seq_stride + (uint64_t) piv1 * S_v * S_v;
633-
float * ps_out = state_out_base + (uint64_t) (K - 1) * state_size_per_snap + ((uint64_t) piv3 * H + piv1) * S_v * S_v;
632+
// final state lands in snapshot slot 0 (most-recent-first ordering)
633+
float * ps_out = state_out_base + ((uint64_t) piv3 * H + piv1) * S_v * S_v;
634634

635635
// Push dummy write-back
636636
dma_queue_push(dma, dma_make_ptr(ps_out, s_work[spad_idx]),
@@ -661,7 +661,8 @@ static void gated_delta_net_f32_pp_thread(unsigned int nth, unsigned int ith, vo
661661
const uint32_t iq3 = fastdiv(iv3, &fd_rq3);
662662
const uint32_t ik3 = fastdiv(iv3, &fd_rk3);
663663

664-
float * s_out = state_out_base + (uint64_t) (K - 1) * state_size_per_snap + ((uint64_t) iv3 * H + iv1) * S_v * S_v;
664+
// final state lands in snapshot slot 0 (most-recent-first ordering)
665+
float * s_out = state_out_base + ((uint64_t) iv3 * H + iv1) * S_v * S_v;
665666

666667
float * attn_data = dst_base + ((uint64_t) iv3 * n_tokens * H + iv1) * S_v;
667668

@@ -792,7 +793,8 @@ static void gated_delta_net_f32_pp_thread(unsigned int nth, unsigned int ith, vo
792793
}
793794

794795
if (K > 1) {
795-
const int64_t target_slot = (int64_t) t - shift;
796+
// snapshot slot mapping: slot 0 = most recent state, slot s = s tokens back.
797+
const int64_t target_slot = (int64_t) n_tokens - 1 - (int64_t) t;
796798
if (target_slot >= 0 && target_slot < (int64_t) K) {
797799
float * curr_state_o = state_out_base + (uint64_t) target_slot * state_size_per_snap + ((uint64_t) iv3 * H + iv1) * S_v * S_v;
798800
if (curr_state_o != s_out) {
@@ -844,7 +846,6 @@ static void gated_delta_net_f32_tg_thread(unsigned int nth, unsigned int ith, vo
844846
const uint32_t S_v = v->ne[0];
845847
const uint32_t H = v->ne[1];
846848
const uint32_t n_seqs = v->ne[3];
847-
const uint32_t K = state->ne[1];
848849

849850
const uint32_t total_rows = H * n_seqs;
850851
if (ith >= total_rows) {
@@ -878,8 +879,7 @@ static void gated_delta_net_f32_tg_thread(unsigned int nth, unsigned int ith, vo
878879
struct fastdiv_values fd_rq3 = init_fastdiv_values(rq3);
879880
struct fastdiv_values fd_rk3 = init_fastdiv_values(rk3);
880881

881-
const uint64_t state_seq_stride = state->nb[2] / sizeof(float);
882-
const uint64_t state_size_per_snap = (uint64_t) S_v * S_v * H * n_seqs;
882+
const uint64_t state_seq_stride = state->nb[3] / sizeof(float);
883883

884884
uint32_t ir_prefetch = ith;
885885
int spad_idx = 0;
@@ -889,7 +889,8 @@ static void gated_delta_net_f32_tg_thread(unsigned int nth, unsigned int ith, vo
889889
const uint32_t piv1 = fastmodulo(ir_prefetch, H, &fd_H);
890890
const uint32_t piv3 = fastdiv(ir_prefetch, &fd_H);
891891
const float * ps_in = state_in_base + (uint64_t) piv3 * state_seq_stride + (uint64_t) piv1 * S_v * S_v;
892-
float * ps_out = state_out_base + (uint64_t) (K - 1) * state_size_per_snap + ((uint64_t) piv3 * H + piv1) * S_v * S_v;
892+
// final state lands in snapshot slot 0 (most-recent-first ordering)
893+
float * ps_out = state_out_base + ((uint64_t) piv3 * H + piv1) * S_v * S_v;
893894

894895
// Push dummy write-back
895896
dma_queue_push(dma, dma_make_ptr(ps_out, s_work[spad_idx]),
@@ -920,7 +921,8 @@ static void gated_delta_net_f32_tg_thread(unsigned int nth, unsigned int ith, vo
920921
const uint32_t iq3 = fastdiv(iv3, &fd_rq3);
921922
const uint32_t ik3 = fastdiv(iv3, &fd_rk3);
922923

923-
float * s_out = state_out_base + (uint64_t) (K - 1) * state_size_per_snap + ((uint64_t) iv3 * H + iv1) * S_v * S_v;
924+
// final state lands in snapshot slot 0 (most-recent-first ordering)
925+
float * s_out = state_out_base + ((uint64_t) iv3 * H + iv1) * S_v * S_v;
924926

925927
float * attn_data = dst_base + ((uint64_t) iv3 * H + iv1) * S_v;
926928

@@ -1097,7 +1099,7 @@ int op_gated_delta_net(struct htp_ops_context * octx) {
10971099
const uint32_t H = v->ne[1];
10981100
const uint32_t n_tokens = v->ne[2];
10991101
const uint32_t n_seqs = v->ne[3];
1100-
const uint32_t K = state->ne[1];
1102+
const uint32_t K = octx->op_params[0];
11011103

11021104
if (S_v == 0 || S_v > HTP_GDN_MAX_SV || H == 0 || n_tokens == 0 || n_seqs == 0) {
11031105
return HTP_STATUS_NO_SUPPORT;
@@ -1110,7 +1112,8 @@ int op_gated_delta_net(struct htp_ops_context * octx) {
11101112
(n_seqs % q->ne[3]) != 0 || (n_seqs % k->ne[3]) != 0) {
11111113
return HTP_STATUS_NO_SUPPORT;
11121114
}
1113-
if (state->ne[0] * state->ne[2] * state->ne[3] != S_v * S_v * H * n_seqs) {
1115+
// state holds s0 only: [S_v, S_v, H, n_seqs]
1116+
if (state->ne[0] != S_v || state->ne[1] != S_v || state->ne[2] != H || state->ne[3] != n_seqs) {
11141117
return HTP_STATUS_NO_SUPPORT;
11151118
}
11161119
if (dst->ne[0] != S_v * H || dst->ne[1] != n_tokens * n_seqs + S_v * n_seqs * K) {

ggml/src/ggml-metal/ggml-metal-device.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,8 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_gated_delta_net(
590590
const int ne20 = op->src[2]->ne[0]; // S_v
591591
const int ne21 = op->src[2]->ne[1]; // H
592592
const int ne30 = op->src[3]->ne[0]; // G
593-
// state is src[5], 3D (S_v*S_v*H, K, n_seqs); K is the snapshot slot count.
594-
const int K = op->src[5]->ne[1];
593+
// state is src[5], 4D [S_v, S_v, H_v, n_seqs] (s0 only); K is op param 0.
594+
const int K = ggml_get_op_params_i32(op, 0);
595595

596596
const int nsg = op->src[2]->ne[0]/32;
597597

ggml/src/ggml-metal/ggml-metal.metal

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2599,9 +2599,9 @@ kernel void kernel_gated_delta_net_impl(
25992599

26002600
const float scale = 1.0f / sqrt((float)S_v);
26012601

2602-
// input state layout (D, K, n_seqs): per-seq stride is K*H*D; we read slot 0.
2602+
// input state layout [S_v, S_v, H, n_seqs] (s0 only): per-seq stride is H*D.
26032603
// state is stored transposed: M[i20][is] = S[is][i20], so row i20 is contiguous
2604-
const uint state_in_base = (i23*K*args.ne21 + i21)*S_v*S_v + i20*S_v;
2604+
const uint state_in_base = (i23*args.ne21 + i21)*S_v*S_v + i20*S_v;
26052605
device const float * s_ptr = (device const float *) (s) + state_in_base;
26062606

26072607
float ls[NSG];
@@ -2620,9 +2620,8 @@ kernel void kernel_gated_delta_net_impl(
26202620
device const float * b_ptr = (device const float *) (b) + (i23*args.ne22*args.ne21 + i21);
26212621
device const float * g_ptr = (device const float *) (g) + (i23*args.ne22*args.ne21 + i21)*G;
26222622

2623-
// snapshot slot mapping: target_slot = t - shift. When n_tokens < K, only the last
2624-
// n_tokens slots are written; earlier slots are left untouched (caller-owned).
2625-
const int shift = (int)args.ne22 - (int)K;
2623+
// snapshot slot mapping: slot 0 = most recent state, slot s = s tokens back.
2624+
// When n_tokens < K, only slots 0..n_tokens-1 are written; older slots are caller-owned.
26262625

26272626
// output state base offset: after attention scores
26282627
const uint attn_size = args.ne22 * args.ne21 * S_v * args.ne23;
@@ -2680,7 +2679,7 @@ kernel void kernel_gated_delta_net_impl(
26802679
g_ptr += args.ne21*G;
26812680

26822681
if (K > 1) {
2683-
const int target_slot = (int)t - shift;
2682+
const int target_slot = (int)args.ne22 - 1 - (int)t;
26842683
if (target_slot >= 0 && target_slot < (int)K) {
26852684
device float * dst_state = (device float *) (dst) + attn_size + (uint)target_slot * state_size_per_snap + state_out_base;
26862685
FOR_UNROLL (short j = 0; j < NSG; j++) {

ggml/src/ggml-opencl/ggml-opencl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17750,7 +17750,7 @@ static void ggml_cl_gated_delta_net(ggml_backend_t backend, ggml_tensor * dst) {
1775017750
const cl_uint H_v = (cl_uint) src_v->ne[1];
1775117751
const cl_uint n_tokens = (cl_uint) src_v->ne[2];
1775217752
const cl_uint n_seqs = (cl_uint) src_v->ne[3];
17753-
const cl_uint K = (cl_uint) src_state->ne[1];
17753+
const cl_uint K = (cl_uint) ggml_get_op_params_i32(dst, 0);
1775417754

1775517755
int si;
1775617756
switch (S_v) {

0 commit comments

Comments
 (0)