char-fork/llama.cpp

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

Dominik Pantaleoniggml-metal: add chunked SSD MMA for Mamba-2 prefill optimization (#26647)11cd98842

char/aliceai-t5-moe
19.6 KiB473 linesraw
1#include "common.h"
2
3// ref: ggml.c:ggml_compute_forward_ssm_conv_f32
4kernel void kernel_ssm_conv_f32_f32(
5        constant ggml_metal_kargs_ssm_conv & args,
6        device const  void * src0,
7        device const  void * src1,
8        device       float * dst,
9        uint3 tgpig[[threadgroup_position_in_grid]],
10        uint3 tpitg[[thread_position_in_threadgroup]],
11        uint3   ntg[[threads_per_threadgroup]]) {
12    const int64_t ir = tgpig.x;
13    const int64_t i2 = tgpig.y;
14    const int64_t i3 = tgpig.z;
15
16    const int64_t nc  = args.ne10;
17  //const int64_t ncs = args.ne00;
18  //const int64_t nr  = args.ne01;
19  //const int64_t n_t = args.ne1;
20  //const int64_t n_s = args.ne2;
21
22    device const float * s = (device const float *) ((device const char *) src0 + ir*args.nb01 + i2*args.nb00 + i3*args.nb02);
23    device const float * c = (device const float *) ((device const char *) src1 + ir*args.nb11);
24    device       float * x = (device       float *) ((device       char *) dst  + ir*args.nb0  + i2*args.nb1  + i3*args.nb2);
25
26    float sumf = 0.0f;
27
28    for (int64_t i0 = 0; i0 < nc; ++i0) {
29        sumf += s[i0] * c[i0];
30    }
31
32    x[0] = sumf;
33}
34
35kernel void kernel_ssm_conv_f32_f32_4(
36        constant ggml_metal_kargs_ssm_conv & args,
37        device const  void * src0,
38        device const  void * src1,
39        device       float * dst,
40        uint3 tgpig[[threadgroup_position_in_grid]],
41        uint3 tpitg[[thread_position_in_threadgroup]],
42        uint3   ntg[[threads_per_threadgroup]]) {
43    const int64_t ir = tgpig.x;
44    const int64_t i2 = tgpig.y;
45    const int64_t i3 = tgpig.z;
46
47    const int64_t nc  = args.ne10;
48  //const int64_t ncs = args.ne00;
49  //const int64_t nr  = args.ne01;
50  //const int64_t n_t = args.ne1;
51  //const int64_t n_s = args.ne2;
52
53    device const float4 * s = (device const float4 *) ((device const char *) src0 + ir*args.nb01 + i2*args.nb00 + i3*args.nb02);
54    device const float4 * c = (device const float4 *) ((device const char *) src1 + ir*args.nb11);
55    device       float  * x = (device       float  *) ((device       char *) dst  + ir*args.nb0  + i2*args.nb1  + i3*args.nb2);
56
57    float sumf = 0.0f;
58
59    for (int64_t i0 = 0; i0 < nc/4; ++i0) {
60        sumf += dot(s[i0], c[i0]);
61    }
62
63    x[0] = sumf;
64}
65
66constant short FC_ssm_conv_bs   [[function_constant(FC_SSM_CONV + 0)]];
67
68// Batched version: each threadgroup processes multiple tokens for better efficiency
69// Thread layout: each thread handles one token, threadgroup covers BATCH_SIZE tokens
70kernel void kernel_ssm_conv_f32_f32_batched(
71        constant ggml_metal_kargs_ssm_conv & args,
72        device const  void * src0,
73        device const  void * src1,
74        device       float * dst,
75        uint3 tgpig[[threadgroup_position_in_grid]],
76        uint3 tpitg[[thread_position_in_threadgroup]],
77        uint3   ntg[[threads_per_threadgroup]]) {
78    // tgpig.x = row index (ir)
79    // tgpig.y = batch of tokens (i2_base / BATCH_SIZE)
80    // tgpig.z = sequence index (i3)
81    // tpitg.x = thread within batch (0..BATCH_SIZE-1)
82    const short BATCH_SIZE = FC_ssm_conv_bs;
83
84    const int64_t ir      = tgpig.x;
85    const int64_t i2_base = tgpig.y * BATCH_SIZE;
86    const int64_t i3      = tgpig.z;
87    const int64_t i2_off  = tpitg.x;
88    const int64_t i2      = i2_base + i2_off;
89
90    const int64_t nc  = args.ne10;  // conv kernel size (typically 4)
91    const int64_t n_t = args.ne1;   // number of tokens
92
93    // Bounds check for partial batches at the end
94    if (i2 >= n_t) {
95        return;
96    }
97
98    // Load conv weights (shared across all tokens for this row)
99    device const float * c = (device const float *) ((device const char *) src1 + ir*args.nb11);
100
101    // Load source for this specific token
102    device const float * s = (device const float *) ((device const char *) src0 + ir*args.nb01 + i2*args.nb00 + i3*args.nb02);
103
104    // Output location for this token
105    device float * x = (device float *) ((device char *) dst + ir*args.nb0 + i2*args.nb1 + i3*args.nb2);
106
107    float sumf = 0.0f;
108    for (int64_t i0 = 0; i0 < nc; ++i0) {
109        sumf += s[i0] * c[i0];
110    }
111
112    x[0] = sumf;
113}
114
115kernel void kernel_ssm_conv_f32_f32_batched_4(
116        constant ggml_metal_kargs_ssm_conv & args,
117        device const  void * src0,
118        device const  void * src1,
119        device       float * dst,
120        uint3 tgpig[[threadgroup_position_in_grid]],
121        uint3 tpitg[[thread_position_in_threadgroup]],
122        uint3   ntg[[threads_per_threadgroup]]) {
123    // tgpig.x = row index (ir)
124    // tgpig.y = batch of tokens (i2_base / BATCH_SIZE)
125    // tgpig.z = sequence index (i3)
126    // tpitg.x = thread within batch (0..BATCH_SIZE-1)
127    const short BATCH_SIZE = FC_ssm_conv_bs;
128
129    const int64_t ir      = tgpig.x;
130    const int64_t i2_base = tgpig.y * BATCH_SIZE;
131    const int64_t i3      = tgpig.z;
132    const int64_t i2_off  = tpitg.x;
133    const int64_t i2      = i2_base + i2_off;
134
135    const int64_t nc  = args.ne10;  // conv kernel size (typically 4)
136    const int64_t n_t = args.ne1;   // number of tokens
137
138    // Bounds check for partial batches at the end
139    if (i2 >= n_t) {
140        return;
141    }
142
143    // Load conv weights (shared across all tokens for this row)
144    device const float4 * c = (device const float4 *) ((device const char *) src1 + ir*args.nb11);
145
146    // Load source for this specific token
147    device const float4 * s = (device const float4 *) ((device const char *) src0 + ir*args.nb01 + i2*args.nb00 + i3*args.nb02);
148
149    // Output location for this token
150    device float * x = (device float *) ((device char *) dst + ir*args.nb0 + i2*args.nb1 + i3*args.nb2);
151
152    float sumf = 0.0f;
153    for (int64_t i0 = 0; i0 < nc/4; ++i0) {
154        sumf += dot(s[i0], c[i0]);
155    }
156
157    x[0] = sumf;
158}
159
160// ref: ggml.c:ggml_compute_forward_ssm_scan_f32, Mamba-2 part
161// Optimized version: reduces redundant memory loads by having one thread load shared values
162// TAIL == false is the whole-sequence / decode path: token_offset folds away at compile time.
163template<bool TAIL>
164kernel void kernel_ssm_scan_impl(
165        constant ggml_metal_kargs_ssm_scan & args,
166        device const void * src0,
167        device const void * src1,
168        device const void * src2,
169        device const void * src3,
170        device const void * src4,
171        device const void * src5,
172        device const void * src6,
173        device      float * dst,
174        threadgroup float * shared [[threadgroup(0)]],
175        uint3   tgpig[[threadgroup_position_in_grid]],
176        ushort3 tpitg[[thread_position_in_threadgroup]],
177        ushort  sgitg[[simdgroup_index_in_threadgroup]],
178        ushort  tiisg[[thread_index_in_simdgroup]],
179        ushort  sgptg[[simdgroups_per_threadgroup]],
180        uint3    tgpg[[threadgroups_per_grid]]) {
181    constexpr short NW = N_SIMDWIDTH;
182
183    // Shared memory layout:
184    // [0..sgptg*NW-1]: partial sums for reduction (existing)
185    // [sgptg*NW..sgptg*NW+sgptg-1]: pre-computed x_dt values for each token in batch
186    // [sgptg*NW+sgptg..sgptg*NW+2*sgptg-1]: pre-computed dA values for each token in batch
187    threadgroup float * shared_sums = shared;
188    threadgroup float * shared_x_dt = shared + sgptg * NW;
189    threadgroup float * shared_dA   = shared + sgptg * NW + sgptg;
190
191    shared_sums[tpitg.x] = 0.0f;
192
193    const int32_t i0 = tpitg.x;
194    const int32_t i1 = tgpig.x;
195    const int32_t ir = tgpig.y; // current head
196    const int32_t i3 = tgpig.z; // current seq
197
198    const int32_t nc  = args.d_state;
199    const int32_t nr  = args.d_inner;
200    const int32_t nh  = args.n_head;
201    const int32_t ng  = args.n_group;
202    const int32_t n_t = args.n_seq_tokens;
203    const int32_t n_s = args.n_seqs;
204    const int32_t K   = args.K;
205    const int32_t n_t_total = TAIL ? args.n_seq_tokens_total : n_t;
206    const int32_t t_off     = TAIL ? args.token_offset       : 0;
207
208    const int32_t s_off = args.s_off;
209
210    device const int32_t * ids = (device const int32_t *) src6;
211
212    device       float * s_buff  = (device       float *) ((device       char *) dst  + ir*args.nb02 +      i3*args.nb03 + s_off);
213    device const float * s0_buff = t_off != 0 ?
214        s_buff :
215        (device const float *) ((device const char *) src0 + ir*args.nb02 + ids[i3]*args.nb03);
216
217    const int32_t i = i0 + i1*nc;
218    const int32_t g = ir / (nh / ng); // repeat_interleave
219
220    float s0 = s0_buff[i];
221    float s  = 0.0f;
222
223    device const float * A = (device const float *) ((device const char *) src3 + ir*args.nb31); // {ne30, nh}
224
225    const float A0 = A[i0%args.ne30];
226
227    device const float * x  = (device const float *)((device const char *) src1 + i1*args.nb10 + ir*args.nb11 + t_off*args.nb12 + i3*args.nb13); // {dim, nh, nt, ns}
228    device const float * dt = (device const float *)((device const char *) src2 + ir*args.nb20 + t_off*args.nb21 + i3*args.nb22);                 // {nh, nt, ns}
229    device const float * B  = (device const float *)((device const char *) src4 + g*args.nb41 + t_off*args.nb42 + i3*args.nb43);                  // {d_state, ng, nt, ns}
230    device const float * C  = (device const float *)((device const char *) src5 + g*args.nb51 + t_off*args.nb52 + i3*args.nb53);                  // {d_state, ng, nt, ns}
231
232    device float * y = dst + (i1 + ir*nr + t_off*nh*nr + i3*(n_t_total*nh*nr)); // {dim, nh, nt, ns}
233
234    for (int i2 = 0; i2 < n_t; i2 += sgptg) {
235        threadgroup_barrier(mem_flags::mem_threadgroup);
236
237        // Pre-compute x_dt and dA for this batch of tokens
238        // Only first sgptg threads do the loads and expensive math
239        if (i0 < sgptg && i2 + i0 < n_t) {
240            // ns12 and ns21 are element strides (nb12/nb10, nb21/nb20)
241            device const float * x_t  = x  + i0 * args.ns12;
242            device const float * dt_t = dt + i0 * args.ns21;
243
244            const float dt0  = dt_t[0];
245            const float dtsp = dt0 <= 20.0f ? log(1.0f + exp(dt0)) : dt0;
246            shared_x_dt[i0] = x_t[0] * dtsp;
247            shared_dA[i0]   = dtsp;  // Store dtsp, compute exp(dtsp * A0) per-thread since A0 varies
248        }
249
250        threadgroup_barrier(mem_flags::mem_threadgroup);
251
252        for (int t = 0; t < sgptg && i2 + t < n_t; t++) {
253            const float x_dt = shared_x_dt[t];
254            const float dA   = exp(shared_dA[t] * A0);
255
256            s = (s0 * dA) + (B[i0] * x_dt);
257
258            const float sumf = simd_sum(s * C[i0]);
259
260            if (tiisg == 0) {
261                shared_sums[t*NW + sgitg] = sumf;
262            }
263
264            // recurse
265            s0 = s;
266
267            const int32_t slot = n_t - 1 - (i2 + t);
268            if (slot > 0 && slot < K) {
269                device float * s_snapshot = (device float *) ((device char *) s_buff + (int64_t) slot*n_s*args.nb03);
270                s_snapshot[i] = s;
271            }
272
273            B  += args.ns42;
274            C  += args.ns52;
275        }
276
277        // Advance pointers for next batch
278        x  += sgptg * args.ns12;
279        dt += sgptg * args.ns21;
280
281        threadgroup_barrier(mem_flags::mem_threadgroup);
282
283        const float sumf = simd_sum(shared_sums[sgitg*NW + tiisg]);
284
285        if (tiisg == 0 && i2 + sgitg < n_t) {
286            y[sgitg*nh*nr] = sumf;
287        }
288
289        y += sgptg*nh*nr;
290    }
291
292    s_buff[i] = s;
293}
294
295typedef decltype(kernel_ssm_scan_impl<false>) kernel_ssm_scan_t;
296
297template [[host_name("kernel_ssm_scan_f32")]]      kernel kernel_ssm_scan_t kernel_ssm_scan_impl<false>;
298template [[host_name("kernel_ssm_scan_f32_tail")]] kernel kernel_ssm_scan_t kernel_ssm_scan_impl<true>;
299
300// Chunked SSD SSM scan via Metal simdgroup MMatrix Multiply-Accumulate (simdgroup_float8x8) fast path.
301// One threadgroup per (head, sequence) and tokens are processed in chunks.
302// C*B^T computed in each chunk one time and reused across the head_dim channel tiles.
303kernel void kernel_ssm_scan_ssd_mma_f32(
304        constant ggml_metal_kargs_ssm_scan & args,
305        device const void * src0,
306        device const void * src1,
307        device const void * src2,
308        device const void * src3,
309        device const void * src4,
310        device const void * src5,
311        device const void * src6,
312        device      float * dst,
313        threadgroup float * shared [[threadgroup(0)]],
314        uint3   tgpig[[threadgroup_position_in_grid]],
315        ushort  tiitg[[thread_index_in_threadgroup]],
316        ushort  sgitg[[simdgroup_index_in_threadgroup]],
317        ushort  tiisg[[thread_index_in_simdgroup]]) {
318    constexpr short CS  = OP_SSM_SCAN_SSD_CS;
319    constexpr short TC  = 8; // Tile Count of each edge in a simdgroup 8x8 tile
320    constexpr short HD  = OP_SSM_SCAN_SSD_HD;
321    constexpr short NSG = OP_SSM_SCAN_SSD_NSG;
322
323    // acs/exp(acs)/state-decay vectors, dtX[CS][HD], four private SAM row tiles [8][CS],
324    // and two 8x8 scratch tiles per simdgroup. Total: 26.75 KiB.
325    threadgroup float * shared_acs         = shared;
326    threadgroup float * shared_exp_acs     = shared + CS;
327    threadgroup float * shared_state_decay = shared + 2*CS;
328    threadgroup float * shared_dtx         = shared + 3*CS;
329    threadgroup float * shared_sam         = shared + 3*CS + CS*HD;
330    threadgroup float * sam_rows    = shared_sam + sgitg*TC*CS;
331    threadgroup float * shared_tile = shared_sam + NSG*TC*CS;
332    threadgroup float * tile0       = shared_tile + sgitg*2*TC*TC;
333    threadgroup float * tile1       = tile0 + TC*TC;
334
335    const int32_t ir = tgpig.y; // current head
336    const int32_t i3 = tgpig.z; // current seq
337
338    const int32_t nc  = args.d_state;
339    const int32_t nr  = args.d_inner;
340    const int32_t nh  = args.n_head;
341    const int32_t ng  = args.n_group;
342    const int32_t n_t = args.n_seq_tokens;
343    const int32_t n_t_total = args.n_seq_tokens_total;
344    const int32_t g   = ir / (nh / ng);
345
346    device const int32_t * ids = (device const int32_t *) src6;
347
348    device const float * s0_buff = (device const float *) ((device const char *) src0 + ir*args.nb02 + ids[i3]*args.nb03);
349    device       float * s_buff  = (device       float *) ((device       char *) dst  + ir*args.nb02 +      i3*args.nb03 + args.s_off);
350
351    device const float * A  = (device const float *) ((device const char *) src3 + ir*args.nb31);
352    device const float * x  = (device const float *) ((device const char *) src1 + ir*args.nb11 + i3*args.nb13);
353    device const float * dt = (device const float *) ((device const char *) src2 + ir*args.nb20 + i3*args.nb22);
354    device const float * B  = (device const float *) ((device const char *) src4 +  g*args.nb41 + i3*args.nb43);
355    device const float * C  = (device const float *) ((device const char *) src5 +  g*args.nb51 + i3*args.nb53);
356
357    device float * y = dst + (ir*nr + i3*(n_t_total*nh*nr));
358
359    for (int32_t t0 = 0; t0 < n_t; t0 += CS) {
360        for (int32_t idx = tiitg; idx < CS*HD; idx += NSG*N_SIMDWIDTH) {
361            const int32_t t = idx / HD;
362            const int32_t c = idx % HD;
363            const float dt0  = dt[(t0 + t) * (int32_t) args.ns21];
364            const float dtsp = dt0 <= 20.0f ? log(1.0f + exp(dt0)) : dt0;
365            shared_dtx[idx] = x[(t0 + t) * (int32_t) args.ns12 + c] * dtsp;
366        }
367        if (tiitg < CS) {
368            const float dt0  = dt[(t0 + tiitg) * (int32_t) args.ns21];
369            const float dtsp = dt0 <= 20.0f ? log(1.0f + exp(dt0)) : dt0;
370            shared_acs[tiitg] = dtsp * A[0];
371        }
372        threadgroup_barrier(mem_flags::mem_threadgroup);
373
374        if (tiitg == 0) {
375            float acc = 0.0f;
376            for (short t = 0; t < CS; ++t) {
377                acc += shared_acs[t];
378                shared_acs[t] = acc;
379            }
380        }
381        threadgroup_barrier(mem_flags::mem_threadgroup);
382        if (tiitg < CS) {
383            shared_exp_acs[tiitg] = exp(shared_acs[tiitg]);
384            shared_state_decay[tiitg] = exp(shared_acs[CS - 1] - shared_acs[tiitg]);
385        }
386        threadgroup_barrier(mem_flags::mem_threadgroup);
387
388        device const float * state = t0 == 0 ? s0_buff : s_buff;
389
390        // Build one 8x64 row tile of SAM per simdgroup, then reuse it across every channel tile.
391        for (short ib = sgitg; ib < CS/TC; ib += NSG) {
392            for (short jb = 0; jb <= ib; ++jb) {
393                simdgroup_float8x8 cb = make_filled_simdgroup_matrix<float, 8>(0.0f);
394
395                for (int32_t k0 = 0; k0 < nc; k0 += TC) {
396                    simdgroup_float8x8 mc;
397                    simdgroup_float8x8 mb;
398                    simdgroup_load(mc, C + (t0 + ib*TC)*(int32_t) args.ns52 + k0, args.ns52);
399                    simdgroup_load(mb, B + (t0 + jb*TC)*(int32_t) args.ns42 + k0, args.ns42, 0, true);
400                    simdgroup_multiply_accumulate(cb, mc, mb, cb);
401                }
402
403                threadgroup float * sam = sam_rows + jb*TC;
404                simdgroup_store(cb, sam, CS);
405                simdgroup_barrier(mem_flags::mem_threadgroup);
406                for (short e = tiisg; e < TC*TC; e += N_SIMDWIDTH) {
407                    const short ri = e / TC;
408                    const short rj = e % TC;
409                    const short i  = ib*TC + ri;
410                    const short j  = jb*TC + rj;
411                    sam[ri*CS + rj] = j <= i ?
412                        sam[ri*CS + rj] * exp(shared_acs[i] - shared_acs[j]) : 0.0f;
413                }
414                simdgroup_barrier(mem_flags::mem_threadgroup);
415            }
416
417            for (short ch = 0; ch < HD/TC; ++ch) {
418                simdgroup_float8x8 y_diag  = make_filled_simdgroup_matrix<float, 8>(0.0f);
419                simdgroup_float8x8 y_inter = make_filled_simdgroup_matrix<float, 8>(0.0f);
420
421                for (short jb = 0; jb <= ib; ++jb) {
422                    simdgroup_float8x8 sam;
423                    simdgroup_float8x8 mdtx;
424                    simdgroup_load(sam,  sam_rows + jb*TC,                   CS);
425                    simdgroup_load(mdtx, shared_dtx + jb*TC*HD + ch*TC,     HD);
426                    simdgroup_multiply_accumulate(y_diag, sam, mdtx, y_diag);
427                }
428
429                for (int32_t k0 = 0; k0 < nc; k0 += TC) {
430                    simdgroup_float8x8 mc;
431                    simdgroup_float8x8 ms;
432                    simdgroup_load(mc, C + (t0 + ib*TC)*(int32_t) args.ns52 + k0, args.ns52);
433                    simdgroup_load(ms, state + ch*TC*nc + k0, nc, 0, true);
434                    simdgroup_multiply_accumulate(y_inter, mc, ms, y_inter);
435                }
436
437                simdgroup_store(y_diag,  tile0, TC);
438                simdgroup_store(y_inter, tile1, TC);
439                simdgroup_barrier(mem_flags::mem_threadgroup);
440                for (short e = tiisg; e < TC*TC; e += N_SIMDWIDTH) {
441                    const short ri = e / TC;
442                    const short ci = e % TC;
443                    const int32_t token = t0 + ib*TC + ri;
444                    y[token*nh*nr + ch*TC + ci] =
445                        tile0[e] + shared_exp_acs[ib*TC + ri] * tile1[e];
446                }
447                simdgroup_barrier(mem_flags::mem_threadgroup);
448            }
449        }
450
451        // All simdgroups must finish reading s_buff before any thread overwrites it.
452        threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup);
453
454        // Keep the carried-state reduction in token order. Reassociating this particular product
455        // with MMA compounds rounding differences at every chunk boundary; CB, y_diag, and C*S
456        // remain on the matrix unit.
457        const float chunk_decay = exp(shared_acs[CS - 1]);
458        for (int32_t idx = tiitg; idx < nc*HD; idx += NSG*N_SIMDWIDTH) {
459            const int32_t ci = idx / nc;
460            const int32_t si = idx % nc;
461            float state_c = 0.0f;
462            for (short t = 0; t < CS; ++t) {
463                state_c += shared_state_decay[t] *
464                    B[(t0 + t)*(int32_t) args.ns42 + si] *
465                    shared_dtx[t*HD + ci];
466            }
467            s_buff[idx] = chunk_decay * state[idx] + state_c;
468        }
469
470        // All state tiles must be visible before the next chunk consumes s_buff as S_prev.
471        threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup);
472    }
473}