char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Gaurav GargRemove redundant CUDA copies after gated_delta_net. (#23940)5a460dea9

char/aliceai-t5-moe
13.7 KiB327 linesraw
1#include "gated_delta_net.cuh"
2#include "ggml-cuda/common.cuh"
3
4template <int S_v, bool KDA, bool keep_rs_t>
5__global__ void __launch_bounds__((ggml_cuda_get_physical_warp_size() < S_v ? ggml_cuda_get_physical_warp_size() : S_v) * 4, 2)
6gated_delta_net_cuda(const float * q,
7                                     const float * k,
8                                     const float * v,
9                                     const float * g,
10                                     const float * beta,
11                                     const float * curr_state,
12                                     float *       dst,
13                                     float *       state,
14                                     int64_t       H,
15                                     int64_t       n_tokens,
16                                     int64_t       n_seqs,
17                                     int64_t       sq1,
18                                     int64_t       sq2,
19                                     int64_t       sq3,
20                                     int64_t       sv1,
21                                     int64_t       sv2,
22                                     int64_t       sv3,
23                                     int64_t       sb1,
24                                     int64_t       sb2,
25                                     int64_t       sb3,
26                                     const uint3   neqk1_magic,
27                                     const uint3   rq3_magic,
28                                     float         scale,
29                                     int64_t       state_slot_stride,
30                                     int           K) {
31    const uint32_t h_idx    = blockIdx.x;
32    const uint32_t sequence = blockIdx.y;
33    // each warp owns one column, using warp-level primitives to reduce across rows
34    const int      lane     = threadIdx.x;
35    const int      col      = blockIdx.z * blockDim.y + threadIdx.y;
36
37    const uint32_t iq1 = fastmodulo(h_idx, neqk1_magic);
38    const uint32_t iq3 = fastdiv(sequence, rq3_magic);
39
40    float *       attn_data        = dst;
41
42    // input state holds s0 only: [S_v, S_v, H, n_seqs] — seq stride is D = H * S_v * S_v.
43    // output state layout (per-slot D * n_seqs) — same per-(seq,head) offset as before.
44    const int64_t state_in_offset      = sequence * H * S_v * S_v + h_idx * S_v * S_v;
45    const int64_t state_out_offset     = (sequence * H + h_idx) * S_v * S_v;
46    state += state_out_offset;
47    curr_state += state_in_offset + col * S_v;
48    attn_data += (sequence * n_tokens * H + h_idx) * S_v;
49
50    constexpr int warp_size = ggml_cuda_get_physical_warp_size() < S_v ? ggml_cuda_get_physical_warp_size() : S_v;
51    static_assert(S_v % warp_size == 0, "S_v must be a multiple of warp_size");
52    constexpr int rows_per_lane = (S_v + warp_size - 1) / warp_size;
53    float         s_shard[rows_per_lane];
54    // state is stored transposed: M[col][i] = S[i][col], row col is contiguous
55
56    ggml_cuda_pdl_sync();
57#pragma unroll
58    for (int r = 0; r < rows_per_lane; r++) {
59        const int i = r * warp_size + lane;
60        s_shard[r]  = curr_state[i];
61    }
62
63    for (int t = 0; t < n_tokens; t++) {
64        const float * q_t = q + iq3 * sq3 + t * sq2 + iq1 * sq1;
65        const float * k_t = k + iq3 * sq3 + t * sq2 + iq1 * sq1;
66        const float * v_t = v + sequence * sv3 + t * sv2 + h_idx * sv1;
67
68        const int64_t gb_offset = sequence * sb3 + t * sb2 + h_idx * sb1;
69        const float * beta_t = beta + gb_offset;
70        const float * g_t    = g    + gb_offset * (KDA ? S_v : 1);
71
72        const float beta_val = *beta_t;
73
74        // Cache k and q in registers
75        float k_reg[rows_per_lane];
76        float q_reg[rows_per_lane];
77#pragma unroll
78        for (int r = 0; r < rows_per_lane; r++) {
79            const int i = r * warp_size + lane;
80            k_reg[r] = k_t[i];
81            q_reg[r] = q_t[i];
82        }
83
84        if constexpr (!KDA) {
85            const float g_val = expf(*g_t);
86
87            // kv[col] = (S^T @ k)[col] = sum_i S[i][col] * k[i]
88            float kv_shard = 0.0f;
89#pragma unroll
90            for (int r = 0; r < rows_per_lane; r++) {
91                kv_shard += s_shard[r] * k_reg[r];
92            }
93            float kv_col = warp_reduce_sum<warp_size>(kv_shard);
94
95            // delta[col] = (v[col] - g * kv[col]) * beta
96            float delta_col = (v_t[col] - g_val * kv_col) * beta_val;
97
98            // fused: S[i][col] = g * S[i][col] + k[i] * delta[col]
99            // attn[col] = (S^T @ q)[col] = sum_i S[i][col] * q[i]
100            float attn_partial = 0.0f;
101#pragma unroll
102            for (int r = 0; r < rows_per_lane; r++) {
103                s_shard[r]  = g_val * s_shard[r] + k_reg[r] * delta_col;
104                attn_partial += s_shard[r] * q_reg[r];
105            }
106
107            float attn_col = warp_reduce_sum<warp_size>(attn_partial);
108
109            if (lane == 0) {
110                attn_data[col] = attn_col * scale;
111            }
112        } else {
113            // kv[col] = sum_i g[i] * S[i][col] * k[i]
114            float kv_shard = 0.0f;
115#pragma unroll
116            for (int r = 0; r < rows_per_lane; r++) {
117                const int i = r * warp_size + lane;
118                kv_shard += expf(g_t[i]) * s_shard[r] * k_reg[r];
119            }
120
121            float kv_col = warp_reduce_sum<warp_size>(kv_shard);
122
123            // delta[col] = (v[col] - kv[col]) * beta
124            float delta_col = (v_t[col] - kv_col) * beta_val;
125
126            // fused: S[i][col] = g[i] * S[i][col] + k[i] * delta[col]
127            // attn[col] = (S^T @ q)[col] = sum_i S[i][col] * q[i]
128            float attn_partial = 0.0f;
129#pragma unroll
130            for (int r = 0; r < rows_per_lane; r++) {
131                const int i = r * warp_size + lane;
132                s_shard[r]  = expf(g_t[i]) * s_shard[r] + k_reg[r] * delta_col;
133                attn_partial += s_shard[r] * q_reg[r];
134            }
135
136            float attn_col = warp_reduce_sum<warp_size>(attn_partial);
137
138            if (lane == 0) {
139                attn_data[col] = attn_col * scale;
140            }
141        }
142
143        attn_data += S_v * H;
144
145        if constexpr (keep_rs_t) {
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.
148            const int target_slot = (int) n_tokens - 1 - t;
149            if (target_slot >= 0 && target_slot < K) {
150                float * curr_state = state + target_slot * state_slot_stride;
151#pragma unroll
152                for (int r = 0; r < rows_per_lane; r++) {
153                    const int i = r * warp_size + lane;
154                    curr_state[col * S_v + i] = s_shard[r];
155                }
156            }
157        }
158    }
159
160    if constexpr (!keep_rs_t) {
161#pragma unroll
162        for (int r = 0; r < rows_per_lane; r++) {
163            const int i          = r * warp_size + lane;
164            state[col * S_v + i] = s_shard[r];
165        }
166    }
167}
168
169template <bool KDA, bool keep_rs_t>
170static void launch_gated_delta_net(
171        const float * q_d, const float * k_d, const float * v_d,
172        const float * g_d, const float * b_d, const float * s_d,
173        float * dst_d, float * state_d,
174        int64_t S_v,   int64_t H, int64_t n_tokens, int64_t n_seqs,
175        int64_t sq1,   int64_t sq2, int64_t sq3,
176        int64_t sv1,   int64_t sv2, int64_t sv3,
177        int64_t sb1,   int64_t sb2, int64_t sb3,
178        int64_t neqk1, int64_t rq3,
179        float scale, int64_t state_slot_stride, int K, cudaStream_t stream) {
180    //TODO: Add chunked kernel for even faster pre-fill
181    const int warp_size = ggml_cuda_info().devices[ggml_cuda_get_device()].warp_size;
182    const int num_warps = 4;
183    dim3      grid_dims(H, n_seqs, (S_v + num_warps - 1) / num_warps);
184    dim3      block_dims(warp_size <= S_v ? warp_size : S_v, num_warps, 1);
185
186    const uint3 neqk1_magic = init_fastdiv_values(neqk1);
187    const uint3 rq3_magic   = init_fastdiv_values(rq3);
188
189    const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(grid_dims, block_dims, 0, stream);
190    switch (S_v) {
191        case 16:
192            ggml_cuda_kernel_launch(gated_delta_net_cuda<16, KDA, keep_rs_t>, launch_params,
193                q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d, H,
194                n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
195                sb1, sb2, sb3, neqk1_magic, rq3_magic, scale, state_slot_stride, K);
196            break;
197        case 32:
198            ggml_cuda_kernel_launch(gated_delta_net_cuda<32, KDA, keep_rs_t>, launch_params,
199                q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d, H,
200                n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
201                sb1, sb2, sb3, neqk1_magic, rq3_magic, scale, state_slot_stride, K);
202            break;
203        case 64: {
204            ggml_cuda_kernel_launch(gated_delta_net_cuda<64, KDA, keep_rs_t>, launch_params,
205                q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d, H,
206                n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
207                sb1, sb2, sb3, neqk1_magic, rq3_magic, scale, state_slot_stride, K);
208            break;
209        }
210        case 128: {
211            ggml_cuda_kernel_launch(gated_delta_net_cuda<128, KDA, keep_rs_t>, launch_params,
212                q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d, H,
213                n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
214                sb1, sb2, sb3, neqk1_magic, rq3_magic, scale, state_slot_stride, K);
215            break;
216        }
217        default:
218            GGML_ABORT("fatal error");
219            break;
220    }
221}
222
223static void ggml_cuda_op_gated_delta_net_impl(
224        ggml_backend_cuda_context & ctx, ggml_tensor * dst, const ggml_cuda_gated_delta_net_fused_cache * cache) {
225    ggml_tensor * src_q     = dst->src[0];
226    ggml_tensor * src_k     = dst->src[1];
227    ggml_tensor * src_v     = dst->src[2];
228    ggml_tensor * src_g     = dst->src[3];
229    ggml_tensor * src_beta  = dst->src[4];
230    ggml_tensor * src_state = dst->src[5];
231
232    GGML_TENSOR_LOCALS(int64_t, neq, src_q, ne);
233    GGML_TENSOR_LOCALS(size_t , nbq, src_q, nb);
234    GGML_TENSOR_LOCALS(int64_t, nek, src_k, ne);
235    GGML_TENSOR_LOCALS(size_t , nbk, src_k, nb);
236    GGML_TENSOR_LOCALS(int64_t, nev, src_v, ne);
237    GGML_TENSOR_LOCALS(size_t,  nbv, src_v, nb);
238    GGML_TENSOR_LOCALS(size_t,  nbb, src_beta, nb);
239
240    const int64_t S_v      = nev0;
241    const int64_t H        = nev1;
242    const int64_t n_tokens = nev2;
243    const int64_t n_seqs   = nev3;
244
245    const bool kda = (src_g->ne[0] == S_v);
246
247    GGML_ASSERT(neq1 == nek1);
248    const int64_t neqk1 = neq1;
249
250    const int64_t rq3 = nev3 / neq3;
251
252    const float * q_d = (const float *) src_q->data;
253    const float * k_d = (const float *) src_k->data;
254    const float * v_d = (const float *) src_v->data;
255    const float * g_d = (const float *) src_g->data;
256    const float * b_d = (const float *) src_beta->data;
257
258    const float * s_d   = (const float *) src_state->data;
259    float *       dst_d = (float *) dst->data;
260
261    GGML_ASSERT(ggml_is_contiguous_rows(src_q));
262    GGML_ASSERT(ggml_is_contiguous_rows(src_k));
263    GGML_ASSERT(ggml_is_contiguous_rows(src_v));
264    GGML_ASSERT(ggml_are_same_stride(src_q, src_k));
265    GGML_ASSERT(src_g->ne[0] == 1 || kda);
266    GGML_ASSERT(ggml_is_contiguous(src_g));
267    GGML_ASSERT(ggml_is_contiguous(src_beta));
268    GGML_ASSERT(ggml_is_contiguous(src_state));
269
270    // strides in floats (beta strides used for both g and beta offset computation)
271    const int64_t sq1 = nbq1 / sizeof(float);
272    const int64_t sq2 = nbq2 / sizeof(float);
273    const int64_t sq3 = nbq3 / sizeof(float);
274    const int64_t sv1 = nbv1 / sizeof(float);
275    const int64_t sv2 = nbv2 / sizeof(float);
276    const int64_t sv3 = nbv3 / sizeof(float);
277    const int64_t sb1 = nbb1 / sizeof(float);
278    const int64_t sb2 = nbb2 / sizeof(float);
279    const int64_t sb3 = nbb3 / sizeof(float);
280
281    const float scale = 1.0f / sqrtf((float) S_v);
282
283    cudaStream_t stream = ctx.stream();
284
285    // K (snapshot slot count) is an op param; state holds s0 only [S_v, S_v, H, n_seqs].
286    const int K = ggml_get_op_params_i32(dst, 0);
287    const bool keep_rs = K > 1;
288
289    // recurrent state -> gdn_out tail (after attention scores), or the cache when fusing
290    float * state_d           = dst_d + S_v * H * n_tokens * n_seqs;
291    int64_t state_slot_stride = S_v * S_v * H * n_seqs;
292    if (cache != nullptr) {
293        state_d           = cache->data;
294        state_slot_stride = cache->slot_stride;
295    }
296
297    if (kda) {
298        if (keep_rs) {
299            launch_gated_delta_net<true, true>(q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d,
300                S_v, H, n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
301                sb1, sb2, sb3, neqk1, rq3, scale, state_slot_stride, K, stream);
302        } else {
303            launch_gated_delta_net<true, false>(q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d,
304                S_v, H, n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
305                sb1, sb2, sb3, neqk1, rq3, scale, state_slot_stride, K, stream);
306        }
307    } else {
308        if (keep_rs) {
309            launch_gated_delta_net<false, true>(q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d,
310                S_v, H, n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
311                sb1, sb2, sb3, neqk1, rq3, scale, state_slot_stride, K, stream);
312        } else {
313            launch_gated_delta_net<false, false>(q_d, k_d, v_d, g_d, b_d, s_d, dst_d, state_d,
314                S_v, H, n_tokens, n_seqs, sq1, sq2, sq3, sv1, sv2, sv3,
315                sb1, sb2, sb3, neqk1, rq3, scale, state_slot_stride, K, stream);
316        }
317    }
318}
319
320void ggml_cuda_op_gated_delta_net(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
321    ggml_cuda_op_gated_delta_net_impl(ctx, dst, nullptr);
322}
323
324void ggml_cuda_op_gated_delta_net_fused_cache(
325        ggml_backend_cuda_context & ctx, ggml_tensor * dst, ggml_cuda_gated_delta_net_fused_cache cache) {
326    ggml_cuda_op_gated_delta_net_impl(ctx, dst, &cache);
327}