char-fork/llama.cpp

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

Aman Guptacuda: fixes races in mmid and mmf (#28475)73a43d1f6

char/aliceai-t5-moe
44.6 KiB927 linesraw
1#pragma once
2
3#include "mma.cuh"
4#include "common.cuh"
5#include "convert.cuh"
6
7using namespace ggml_cuda_mma;
8
9#define MMF_ROWS_PER_BLOCK 32
10#define MMF_ROWS_PER_BLOCK_CDNA 64
11
12static __forceinline__ int64_t mmf_get_max_block_size(int cc) {
13    if (GGML_CUDA_CC_IS_CDNA(cc)) {
14        return 512;
15    } else {
16        return 256;
17    }
18}
19
20static __forceinline__ int mmf_get_padding(int cc) {
21    if (GGML_CUDA_CC_IS_CDNA(cc)) {
22        return 2;
23    } else {
24        return 4;
25    }
26}
27
28static constexpr __device__ int mmf_get_padding() {
29#if defined(AMD_MFMA_AVAILABLE)
30    return 2;
31#else
32    return 4;
33#endif // defined(AMD_MFMA_AVAILABLE)
34}
35
36struct mmf_ids_data {
37    const int32_t * ids_src_compact = nullptr;
38    const int32_t * ids_dst_compact = nullptr;
39    const int32_t * expert_bounds_dev = nullptr;
40    int n_experts = 0;
41    int sis1 = 0;
42};
43
44void ggml_cuda_mul_mat_f(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * ids, ggml_tensor * dst);
45
46bool ggml_cuda_should_use_mmf(enum ggml_type type, int cc, int warp_size, const int64_t * scr0_ne, const size_t * src0_nb, const int src1_ncols, bool mul_mat_id);
47
48template <typename T, int rows_per_block, int cols_per_block, int nwarps, bool has_ids>
49__launch_bounds__(ggml_cuda_get_physical_warp_size()*nwarps, 1)
50static __global__ void mul_mat_f(
51        const T * __restrict__ x, const float * __restrict__ y, const int32_t * __restrict__ ids, float * __restrict__ dst,
52        const int ncols, const int ncols_dst_total, const int nchannels_dst, const int stride_row, const int stride_col_y, const int stride_col_dst,
53        const int stride_col_id, const int stride_row_id,
54        const int channel_ratio, const int stride_channel_x, const int stride_channel_y, const int stride_channel_dst,
55        const int sample_ratio, const int stride_sample_x, const int stride_sample_y, const int stride_sample_dst) {
56// TODO: handle this in a consistent and simpler way after AMD MFMA support has been added
57#if defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
58#if defined(AMD_WMMA_AVAILABLE)
59    if constexpr (!(std::is_same_v<T, half2> || std::is_same_v<T, nv_bfloat162>) || rows_per_block != MMF_ROWS_PER_BLOCK) {NO_DEVICE_CODE;} else {
60    typedef tile<16, 8,  T,     get_input_data_layout()> tile_A;
61    typedef tile<16, 8,  T,     get_input_data_layout()> tile_B;
62    typedef tile<16, 16, float, DATA_LAYOUT_J_MAJOR>     tile_C;
63#elif defined(AMD_MFMA_AVAILABLE)
64    if constexpr (rows_per_block != MMF_ROWS_PER_BLOCK_CDNA) {NO_DEVICE_CODE;} else {
65    typedef tile<16, 8,  T,     DATA_LAYOUT_I_MAJOR> tile_A;
66    typedef tile<16, 8,  T,     DATA_LAYOUT_I_MAJOR> tile_B;
67    typedef tile<16, 16, float, DATA_LAYOUT_J_MAJOR> tile_C;
68#else
69#ifdef VOLTA_MMA_AVAILABLE
70    if constexpr (!std::is_same_v<T, half2> || rows_per_block != MMF_ROWS_PER_BLOCK) {NO_DEVICE_CODE;} else {
71    typedef tile<32, 4, T,     DATA_LAYOUT_I_MAJOR>          tile_A;
72    typedef tile< 8, 4, T,     DATA_LAYOUT_I_MAJOR_MIRRORED> tile_B;
73    typedef tile<32, 8, float, DATA_LAYOUT_I_MAJOR>          tile_C;
74#else
75    if constexpr (rows_per_block != MMF_ROWS_PER_BLOCK) {NO_DEVICE_CODE;} else {
76    typedef tile<16, 8, T>     tile_A;
77    typedef tile<8,  8, T>     tile_B;
78    typedef tile<16, 8, float> tile_C;
79#endif // VOLTA_MMA_AVAILABLE
80#endif // defined(AMD_WMMA_AVAILABLE)
81    if constexpr (!tile_A::supported() || !tile_B::supported() || !tile_C::supported()) {
82        NO_DEVICE_CODE;
83        return;
84    }
85
86    constexpr int warp_size = ggml_cuda_get_physical_warp_size();
87    constexpr int tile_k_padded = warp_size + mmf_get_padding();
88    constexpr int ntA = rows_per_block / tile_A::I;
89    constexpr int ntB = (cols_per_block + tile_B::I - 1) / tile_B::I;
90
91    const int row0        = blockIdx.x * rows_per_block;
92
93    int expert_idx = 0;
94    [[maybe_unused]] int col_base = 0;
95
96    const int channel_dst = has_ids ? 0 : blockIdx.y;
97
98    if constexpr (has_ids) {
99        // experts + tiles of ncols_dst are packed in the y dimension
100        int col_tiles = (ncols_dst_total + cols_per_block - 1) / cols_per_block;
101        const int nchannels_x = gridDim.y / col_tiles;
102        const int tile_idx = blockIdx.y / nchannels_x;
103        expert_idx = blockIdx.y - tile_idx * nchannels_x;
104        col_base = tile_idx * cols_per_block;
105    }
106
107    const int channel_x   = has_ids ? expert_idx : (channel_dst / channel_ratio);
108    const int channel_y   = channel_dst;
109    const int sample_dst  = blockIdx.z;
110    const int sample_x    = sample_dst / sample_ratio;
111    const int sample_y    = sample_dst;
112
113    x   += int64_t(sample_x)  *stride_sample_x   + channel_x  *stride_channel_x  + row0*stride_row ;
114    y   += int64_t(sample_y)  *stride_sample_y   + (has_ids ? 0 : channel_y  *stride_channel_y);
115    dst += int64_t(sample_dst)*stride_sample_dst + (has_ids ? 0 : channel_dst*stride_channel_dst);
116
117    if constexpr (has_ids) {
118        constexpr int y_stride_scale = std::is_same_v<T, float> ? 1 : 2;
119        const int64_t col_offset = col_base;
120        y   += col_offset * stride_col_y * y_stride_scale;
121        dst += col_offset * stride_col_dst;
122        ids += col_offset * stride_row_id;
123    }
124
125    [[maybe_unused]] const float2 * y2 = (const float2 *) y;
126
127    extern __shared__ char data_mmv[];
128
129    char * shmem_base = data_mmv;
130    [[maybe_unused]] int * slot_map = (int *) shmem_base;
131    char * compute_base = has_ids ? (shmem_base + GGML_PAD(cols_per_block, 16) * sizeof(int)) : shmem_base;
132
133    tile_C C[ntA][ntB];
134
135    T * tile_xy = (T *) compute_base + threadIdx.y*(tile_A::I * tile_k_padded);
136
137    if constexpr (has_ids) {
138        int found = 0;
139
140        for (int j0 = 0; j0 < cols_per_block; j0 += nwarps) {
141            const int j = j0 + threadIdx.y;
142
143            if (threadIdx.x == 0) {
144                slot_map[j] = -1;
145            }
146            ggml_cuda_syncwarp();
147
148            if (col_base + j >= ncols_dst_total) {
149                continue;
150            }
151
152            const int32_t * __restrict__ id_row = ids + j*stride_row_id;
153
154            for (int k = threadIdx.x; k < nchannels_dst; k += warp_size) {
155                int match = id_row[k*stride_col_id] == expert_idx;
156
157                if (match) {
158                    slot_map[j] = k;
159                    found = 1;
160                    break;
161                }
162            }
163        }
164
165        if (!__syncthreads_or(found)) {
166            return;
167        }
168    }
169
170
171    for (int col = threadIdx.y*warp_size + threadIdx.x; col < ncols; col += nwarps*warp_size) {
172        tile_A A[ntA][warp_size / tile_A::J];
173#pragma unroll
174        for (int itA = 0; itA < ntA; ++itA) {
175            ggml_cuda_syncwarp();
176#pragma unroll
177            for (int i = 0; i < tile_A::I; ++i) {
178                tile_xy[i*tile_k_padded + threadIdx.x] = x[(itA*tile_A::I + i)*stride_row  + col];
179            }
180            ggml_cuda_syncwarp();
181#pragma unroll
182            for (int k0 = 0; k0 < warp_size; k0 += tile_A::J) {
183                load_ldmatrix(A[itA][k0/tile_A::J], tile_xy + k0, tile_k_padded);
184            }
185        }
186
187#pragma unroll
188        for (int itB = 0; itB < ntB; ++itB) {
189            ggml_cuda_syncwarp();
190            if constexpr (std::is_same_v<T, float>) {
191#pragma unroll
192                for (int j0 = 0; j0 < tile_B::I; ++j0) {
193                    const int j = j0 + itB*tile_B::I;
194
195                    if constexpr (!has_ids) {
196                        tile_xy[j0*tile_k_padded + threadIdx.x] = j < cols_per_block ? y[j*stride_col_y + col] : 0.0f;
197                    } else {
198                        const bool valid = j < cols_per_block && (col_base + j) < ncols_dst_total && slot_map[j] >= 0;
199                        tile_xy[j0*tile_k_padded + threadIdx.x] = valid ? y[slot_map[j]*stride_channel_y + j*stride_col_y + col] : 0.0f;
200                    }
201                }
202            } else if constexpr (std::is_same_v<T, half2> || std::is_same_v<T, nv_bfloat162>) {
203#pragma unroll
204                for (int j0 = 0; j0 < tile_B::I; ++j0) {
205                    const int j = j0 + itB*tile_B::I;
206
207                    if constexpr (!has_ids) {
208                        const float2 tmp = j < cols_per_block ? y2[j*stride_col_y + col] : make_float2(0.0f, 0.0f);
209                        tile_xy[j0*tile_k_padded + threadIdx.x] = ggml_cuda_cast<T>(tmp);
210                    } else {
211                        const bool valid = j < cols_per_block && (col_base + j) < ncols_dst_total && slot_map[j] >= 0;
212                        float2 tmp = valid ? *(const float2*) &y[slot_map[j]*stride_channel_y + 2*(j*stride_col_y + col)] : make_float2(0.0f, 0.0f);
213                        tile_xy[j0*tile_k_padded + threadIdx.x] = ggml_cuda_cast<T>(tmp);
214                    }
215                }
216            } else {
217                static_assert(std::is_same_v<T, void>, "unsupported type");
218            }
219            ggml_cuda_syncwarp();
220#pragma unroll
221            for (int k0 = 0; k0 < warp_size; k0 += tile_B::J) {
222                tile_B B;
223                load_ldmatrix(B, tile_xy + k0, tile_k_padded);
224#pragma unroll
225                for (int itA = 0; itA < ntA; ++itA) {
226                    mma(C[itA][itB], A[itA][k0/tile_B::J], B);
227                }
228            }
229        }
230    }
231
232    float * buf_iw = (float *) compute_base;
233    constexpr int kiw = nwarps*rows_per_block + mmf_get_padding();
234
235    if (nwarps > 1) {
236        __syncthreads();
237    } else {
238        ggml_cuda_syncwarp();
239    }
240#pragma unroll
241    for (int itB = 0; itB < ntB; ++itB) {
242#pragma unroll
243        for (int itA = 0; itA < ntA; ++itA) {
244#pragma unroll
245            for (int l = 0; l < tile_C::ne; ++l) {
246                const int i = threadIdx.y*rows_per_block + itA*tile_C::I + tile_C::get_i(l);
247                const int j = itB*tile_C::J + tile_C::get_j(l);
248                buf_iw[j*kiw + i] = C[itA][itB].x[l];
249            }
250        }
251    }
252
253    if (nwarps > 1) {
254        __syncthreads();
255    } else {
256        ggml_cuda_syncwarp();
257    }
258
259#pragma unroll
260    for (int j0 = 0; j0 < cols_per_block; j0 += nwarps) {
261        const int j = j0 + threadIdx.y;
262
263        if (j0 + nwarps > cols_per_block && j >= cols_per_block) {
264            return;
265        }
266
267        float sum[rows_per_block/warp_size] = {0.0f};
268        static_assert((rows_per_block % warp_size) == 0, "rows_per_block must be a multiple of warp_size.");
269#pragma unroll
270        for (int i0 = 0; i0 < nwarps*rows_per_block; i0 += rows_per_block) {
271#pragma unroll
272            for (int i1 = 0; i1 < sizeof(sum)/sizeof(sum[0]); ++i1) {
273                const int i = i0 + i1*warp_size + threadIdx.x;
274
275                sum[i1] += buf_iw[j*kiw + i];
276            }
277        }
278
279        if constexpr (!has_ids) {
280#pragma unroll
281            for (int i0 = 0; i0 < sizeof(sum)/sizeof(sum[0]); ++i0) {
282                dst[j*stride_col_dst + row0 + i0*warp_size + threadIdx.x] = sum[i0];
283            }
284        } else {
285            const int slot = (j < cols_per_block) ? slot_map[j] : -1;
286            if (slot >= 0 && (col_base + j) < ncols_dst_total) {
287#pragma unroll
288                for (int i0 = 0; i0 < sizeof(sum)/sizeof(sum[0]); ++i0) {
289                    dst[slot*stride_channel_dst + j*stride_col_dst + row0 + i0*warp_size + threadIdx.x] = sum[i0];
290                }
291            }
292        }
293    }
294    }
295#else
296    GGML_UNUSED_VARS(x, y, ids, dst,
297        ncols, ncols_dst_total, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
298        stride_col_id, stride_row_id,
299        channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
300        sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst);
301    NO_DEVICE_CODE;
302#endif // defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
303}
304
305//This kernel is for larger batch sizes of mul_mat_id
306template <typename T, int rows_per_block, int cols_per_block, int nwarps>
307__launch_bounds__(ggml_cuda_get_physical_warp_size()*nwarps, 1)
308static __global__ void mul_mat_f_ids(
309        const T * __restrict__ x, const float * __restrict__ y,
310        const int32_t * __restrict__ ids_src_compact, const int32_t * __restrict__ ids_dst_compact,
311        const int32_t * __restrict__ expert_bounds, float * __restrict__ dst,
312        const int ncols, const int ncols_dst_total, const int nchannels_dst, const int stride_row, const int stride_col_y, const int stride_col_dst,
313        const int channel_ratio, const int stride_channel_x, const int stride_channel_y, const int stride_channel_dst,
314        const int sample_ratio, const int stride_sample_x, const int stride_sample_y, const int stride_sample_dst,
315        const uint3 sis1_fd, const uint3 nch_fd) {
316// TODO: handle this in a consistent and simpler way after AMD MFMA support has been added
317#if defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
318#if defined(AMD_WMMA_AVAILABLE)
319    if constexpr (!(std::is_same_v<T, half2> || std::is_same_v<T, nv_bfloat162>) || rows_per_block != MMF_ROWS_PER_BLOCK) {NO_DEVICE_CODE;} else {
320    typedef tile<16, 8,  T,     get_input_data_layout()> tile_A;
321    typedef tile<16, 8,  T,     get_input_data_layout()> tile_B;
322    typedef tile<16, 16, float, DATA_LAYOUT_J_MAJOR>     tile_C;
323#elif defined(AMD_MFMA_AVAILABLE)
324    if constexpr (rows_per_block != MMF_ROWS_PER_BLOCK_CDNA) {NO_DEVICE_CODE;} else {
325    typedef tile<16, 8,  T,     DATA_LAYOUT_I_MAJOR> tile_A;
326    typedef tile<16, 8,  T,     DATA_LAYOUT_I_MAJOR> tile_B;
327    typedef tile<16, 16, float, DATA_LAYOUT_J_MAJOR> tile_C;
328#else
329#ifdef VOLTA_MMA_AVAILABLE
330    if constexpr (!std::is_same_v<T, half2> || rows_per_block != MMF_ROWS_PER_BLOCK) {NO_DEVICE_CODE;} else {
331    typedef tile<32, 4, T,     DATA_LAYOUT_I_MAJOR>          tile_A;
332    typedef tile< 8, 4, T,     DATA_LAYOUT_I_MAJOR_MIRRORED> tile_B;
333    typedef tile<32, 8, float, DATA_LAYOUT_I_MAJOR>          tile_C;
334#else
335    if constexpr (rows_per_block != MMF_ROWS_PER_BLOCK) {NO_DEVICE_CODE;} else {
336    typedef tile<16, 8, T>     tile_A;
337    typedef tile<8,  8, T>     tile_B;
338    typedef tile<16, 8, float> tile_C;
339#endif // VOLTA_MMA_AVAILABLE
340#endif // defined(AMD_WMMA_AVAILABLE)
341    if constexpr (!tile_A::supported() || !tile_B::supported() || !tile_C::supported()) {
342        NO_DEVICE_CODE;
343        return;
344    }
345
346
347    constexpr int warp_size = ggml_cuda_get_physical_warp_size();
348    constexpr int tile_k_padded = warp_size + mmf_get_padding();
349    constexpr int ntA = rows_per_block / tile_A::I;
350    constexpr int ntB = (cols_per_block + tile_B::I - 1) / tile_B::I;
351
352    const int row0        = blockIdx.x * rows_per_block;
353
354    const int expert_idx = blockIdx.y;
355    const int expert_start = expert_bounds[expert_idx];
356    const int expert_end   = expert_bounds[expert_idx + 1];
357    const int ncols_expert = expert_end - expert_start;
358
359    const int tiles_for_expert = (ncols_expert + cols_per_block - 1) / cols_per_block;
360    const int tile_idx = blockIdx.z;
361    if (tile_idx >= tiles_for_expert) {
362        return;
363    }
364
365    const int col_base = tile_idx * cols_per_block;
366
367    GGML_UNUSED(channel_ratio);
368
369    const int channel_x   = expert_idx;
370    const int sample_dst  = 0;
371    const int sample_x    = sample_dst / sample_ratio;
372    const int sample_y    = sample_dst;
373
374    x   += int64_t(sample_x)  *stride_sample_x   + channel_x  *stride_channel_x  + row0*stride_row;
375    y   += int64_t(sample_y)  *stride_sample_y;
376    dst += int64_t(sample_dst)*stride_sample_dst;
377
378    const int32_t * ids_src_expert = ids_src_compact + expert_start;
379    const int32_t * ids_dst_expert = ids_dst_compact + expert_start;
380
381    extern __shared__ char data_mmv[];
382    char * compute_base = data_mmv;
383
384    //const float2 * y2 = (const float2 *) y;
385
386    tile_C C[ntA][ntB];
387
388    T * tile_xy = (T *) compute_base + threadIdx.y*(tile_A::I * tile_k_padded);
389
390    for (int col = threadIdx.y*warp_size + threadIdx.x; col < ncols; col += nwarps*warp_size) {
391        tile_A A[ntA][warp_size / tile_A::J];
392#pragma unroll
393        for (int itA = 0; itA < ntA; ++itA) {
394            ggml_cuda_syncwarp();
395#pragma unroll
396            for (int i = 0; i < tile_A::I; ++i) {
397                tile_xy[i*tile_k_padded + threadIdx.x] = x[(itA*tile_A::I + i)*stride_row  + col];
398            }
399            ggml_cuda_syncwarp();
400#pragma unroll
401            for (int k0 = 0; k0 < warp_size; k0 += tile_A::J) {
402                load_ldmatrix(A[itA][k0/tile_A::J], tile_xy + k0, tile_k_padded);
403            }
404        }
405
406        if constexpr (std::is_same_v<T, float>) {
407            float vals_buf[2][tile_B::I];
408            auto gather_tile = [&](int tile_idx_local, float *vals) {
409#pragma unroll
410                for (int j0 = 0; j0 < tile_B::I; ++j0) {
411                    const int j = j0 + tile_idx_local*tile_B::I;
412                    const int global_j = col_base + j;
413                    float val = 0.0f;
414                    if (j < cols_per_block && global_j < ncols_expert) {
415                        const int src_entry = ids_src_expert[global_j];
416                        const uint2 qrm = fast_div_modulo((uint32_t) src_entry, sis1_fd);
417                        const int token   = (int) qrm.x;
418                        const int channel = (int) qrm.y;
419                        if (token < ncols_dst_total) {
420                            val = y[channel*stride_channel_y + token*stride_col_y + col];
421                        }
422                    }
423                    vals[j0] = val;
424                }
425            };
426
427            gather_tile(0, vals_buf[0]);
428
429            int curr_buf = 0;
430            int next_buf = 1;
431#pragma unroll
432            for (int itB = 0; itB < ntB; ++itB) {
433                ggml_cuda_syncwarp();
434#pragma unroll
435                for (int j0 = 0; j0 < tile_B::I; ++j0) {
436                    tile_xy[j0*tile_k_padded + threadIdx.x] = vals_buf[curr_buf][j0];
437                }
438
439                if (itB + 1 < ntB) {
440                    gather_tile(itB + 1, vals_buf[next_buf]);
441                }
442
443                ggml_cuda_syncwarp();
444#pragma unroll
445                for (int k0 = 0; k0 < warp_size; k0 += tile_B::J) {
446                    tile_B B;
447                    load_ldmatrix(B, tile_xy + k0, tile_k_padded);
448#pragma unroll
449                    for (int itA = 0; itA < ntA; ++itA) {
450                        mma(C[itA][itB], A[itA][k0/tile_B::J], B);
451                    }
452                }
453
454                if (itB + 1 < ntB) {
455                    curr_buf ^= 1;
456                    next_buf ^= 1;
457                }
458            }
459        } else if constexpr (std::is_same_v<T, half2> || std::is_same_v<T, nv_bfloat162>) {
460            float2 vals_buf[2][tile_B::I];
461            auto gather_tile = [&](int tile_idx_local, float2 *vals) {
462#pragma unroll
463                for (int j0 = 0; j0 < tile_B::I; ++j0) {
464                    const int j = j0 + tile_idx_local*tile_B::I;
465                    const int global_j = col_base + j;
466                    float2 tmp = make_float2(0.0f, 0.0f);
467                    if (j < cols_per_block && global_j < ncols_expert) {
468                        const int src_entry = ids_src_expert[global_j];
469                        const uint2 qrm = fast_div_modulo((uint32_t) src_entry, sis1_fd);
470                        const int token   = (int) qrm.x;
471                        const int channel = (int) qrm.y;
472                        if (token < ncols_dst_total) {
473                            tmp = *(const float2*) &y[channel*stride_channel_y + 2*(token*stride_col_y + col)];
474                        }
475                    }
476                    vals[j0] = tmp;
477                }
478            };
479
480            if (ntB > 0) {
481                gather_tile(0, vals_buf[0]);
482            }
483
484            int curr_buf = 0;
485            int next_buf = 1;
486#pragma unroll
487            for (int itB = 0; itB < ntB; ++itB) {
488                ggml_cuda_syncwarp();
489#pragma unroll
490                for (int j0 = 0; j0 < tile_B::I; ++j0) {
491                    const float2 tmp = vals_buf[curr_buf][j0];
492                    tile_xy[j0*tile_k_padded + threadIdx.x] = ggml_cuda_cast<T>(tmp);
493                }
494
495                if (itB + 1 < ntB) {
496                    gather_tile(itB + 1, vals_buf[next_buf]);
497                }
498
499                ggml_cuda_syncwarp();
500#pragma unroll
501                for (int k0 = 0; k0 < warp_size; k0 += tile_B::J) {
502                    tile_B B;
503                    load_ldmatrix(B, tile_xy + k0, tile_k_padded);
504#pragma unroll
505                    for (int itA = 0; itA < ntA; ++itA) {
506                        mma(C[itA][itB], A[itA][k0/tile_B::J], B);
507                    }
508                }
509
510                if (itB + 1 < ntB) {
511                    curr_buf ^= 1;
512                    next_buf ^= 1;
513                }
514            }
515        } else {
516            static_assert(std::is_same_v<T, void>, "unsupported type");
517        }
518    }
519
520    float * buf_iw = (float *) compute_base;
521    constexpr int kiw = nwarps*rows_per_block + mmf_get_padding();
522
523    if (nwarps > 1) {
524        __syncthreads();
525    } else {
526        ggml_cuda_syncwarp();
527    }
528#pragma unroll
529    for (int itB = 0; itB < ntB; ++itB) {
530#pragma unroll
531        for (int itA = 0; itA < ntA; ++itA) {
532#pragma unroll
533            for (int l = 0; l < tile_C::ne; ++l) {
534                const int i = threadIdx.y*rows_per_block + itA*tile_C::I + tile_C::get_i(l);
535                const int j = itB*tile_C::J + tile_C::get_j(l);
536                buf_iw[j*kiw + i] = C[itA][itB].x[l];
537            }
538        }
539    }
540
541    if (nwarps > 1) {
542        __syncthreads();
543    } else {
544        ggml_cuda_syncwarp();
545    }
546
547#pragma unroll
548    for (int j0 = 0; j0 < cols_per_block; j0 += nwarps) {
549        const int j = j0 + threadIdx.y;
550
551        if (j0 + nwarps > cols_per_block && j >= cols_per_block) {
552            return;
553        }
554
555        float sum[rows_per_block/warp_size] = {0.0f};
556        static_assert((rows_per_block % warp_size) == 0, "rows_per_block must be a multiple of warp_size.");
557#pragma unroll
558        for (int i0 = 0; i0 < nwarps*rows_per_block; i0 += rows_per_block) {
559#pragma unroll
560            for (int i1 = 0; i1 < sizeof(sum)/sizeof(sum[0]); ++i1) {
561                const int i = i0 + i1*warp_size + threadIdx.x;
562
563                sum[i1] += buf_iw[j * kiw + i];
564            }
565        }
566
567        const int global_j = col_base + j;
568        if (j < cols_per_block && global_j < ncols_expert && nchannels_dst > 0) {
569            const int dst_entry = ids_dst_expert[global_j];
570            const uint2 qrm = fast_div_modulo((uint32_t) dst_entry, nch_fd);
571            const int token = (int) qrm.x;
572            if (token < ncols_dst_total) {
573                const int slot = (int) qrm.y;
574#pragma unroll
575                for (int i0 = 0; i0 < sizeof(sum)/sizeof(sum[0]); ++i0) {
576                    dst[slot * stride_channel_dst + token * stride_col_dst + row0 + i0*warp_size + threadIdx.x] = sum[i0];
577                }
578            }
579        }
580    }
581    }
582#else
583    GGML_UNUSED_VARS(x, y, ids_src_compact, ids_dst_compact, expert_bounds, dst,
584        ncols, ncols_dst_total, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
585        channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
586        sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, sis1_fd, nch_fd);
587    NO_DEVICE_CODE;
588#endif // defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
589}
590
591template<typename T, int rows_per_block, int cols_per_block, int nwarps>
592static inline void mul_mat_f_switch_ids(
593        const T * x, const float * y, const int32_t * ids, float * dst,
594        const int64_t ncols_x, const int64_t ncols_dst, const int64_t nchannels_dst,
595        const int64_t stride_row, const int64_t stride_col_y, const int64_t stride_col_dst,
596        const int64_t stride_col_id, const int64_t stride_row_id,
597        const int64_t channel_ratio, const int64_t stride_channel_x, const int64_t stride_channel_y, const int64_t stride_channel_dst,
598        const int64_t sample_ratio, const int64_t stride_sample_x, const int64_t stride_sample_y, const int64_t stride_sample_dst,
599        const dim3 & block_nums, const dim3 & block_dims, const int nbytes_shared_total, cudaStream_t stream,
600        const mmf_ids_data * ids_data) {
601    const bool has_ids_data = ids_data && ids_data->ids_src_compact;
602
603    // Use the compact-ids kernel only for larger tiles; for small ncols_dst (< 16)
604    // we prefer the normal mul_mat_f path with has_ids=true.
605    if (has_ids_data && ncols_dst > 16) {
606        const int max_tiles = (int) ((ncols_dst + cols_per_block - 1) / cols_per_block);
607        if (max_tiles == 0) {
608            return;
609        }
610        dim3 block_nums_ids(block_nums.x, ids_data->n_experts, max_tiles);
611
612        const uint3 sis1_fd = ids_data->sis1 > 0 ? init_fastdiv_values((uint32_t) ids_data->sis1) : make_uint3(0, 0, 1);
613        const uint3 nch_fd  = init_fastdiv_values((uint32_t) nchannels_dst);
614
615        mul_mat_f_ids<T, rows_per_block, cols_per_block, nwarps><<<block_nums_ids, block_dims, nbytes_shared_total, stream>>>
616            (x, y, ids_data->ids_src_compact, ids_data->ids_dst_compact, ids_data->expert_bounds_dev, dst,
617            ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
618            channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
619            sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst,
620            sis1_fd, nch_fd);
621    } else if (ids) {
622        const int64_t col_tiles = (ncols_dst + cols_per_block - 1) / cols_per_block;
623        dim3 block_nums_ids = block_nums;
624        block_nums_ids.y *= col_tiles;
625
626        mul_mat_f<T, rows_per_block, cols_per_block, nwarps, true><<<block_nums_ids, block_dims, nbytes_shared_total, stream>>>
627            (x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
628             stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
629             sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst);
630    } else {
631        mul_mat_f<T, rows_per_block, cols_per_block, nwarps, false><<<block_nums, block_dims, nbytes_shared_total, stream>>>
632            (x, y, ids, dst, ncols_x, cols_per_block, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
633             stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
634             sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst);
635    }
636}
637
638template <typename T, int rows_per_block, int cols_per_block>
639void mul_mat_f_cuda(
640        const T * x, const float * y, const int32_t * ids, float * dst,
641        const int64_t ncols_x, const int64_t nrows_x, const int64_t ncols_dst,
642        const int64_t stride_row, const int64_t stride_col_y, const int64_t stride_col_dst,
643        const int64_t stride_col_id, const int64_t stride_row_id,
644        const int64_t nchannels_x, const int64_t nchannels_y, const int64_t nchannels_dst,
645        const int64_t stride_channel_x, const int64_t stride_channel_y, const int64_t stride_channel_dst, const int64_t nsamples_x,
646        const int64_t nsamples_dst, const int64_t stride_sample_x, const int64_t stride_sample_y, const int64_t stride_sample_dst,
647        cudaStream_t stream, const mmf_ids_data * ids_data) {
648    typedef tile<16, 8, T>     tile_A_16;
649    typedef tile<32, 8, T>     tile_A_32;
650    typedef tile<16, 8, T>     tile_B_16;
651    typedef tile< 8, 8, T>     tile_B_8;
652
653    GGML_ASSERT(ncols_x      % 2 == 0);
654    GGML_ASSERT(stride_row   % 2 == 0);
655    GGML_ASSERT(stride_col_y % 2 == 0);
656    GGML_ASSERT(ids || nchannels_dst % nchannels_x == 0);
657    GGML_ASSERT(       nsamples_dst  % nsamples_x  == 0);
658    const int64_t channel_ratio = nchannels_dst / nchannels_x;
659    const int64_t sample_ratio  = nsamples_dst  / nsamples_x;
660
661    const int device    = ggml_cuda_get_device();
662    const int cc        = ggml_cuda_info().devices[device].cc;
663    const int warp_size = ggml_cuda_info().devices[device].warp_size;
664
665    int64_t nwarps_best     = 1;
666    int64_t niter_best      = (ncols_x + warp_size*2 - 1) / (warp_size*2);
667    int64_t max_block_size  = mmf_get_max_block_size(cc);
668    for (int64_t nwarps = 2; nwarps <= max_block_size/warp_size; nwarps++) {
669        const int64_t niter = (ncols_x + nwarps*warp_size*2 - 1) / (nwarps*warp_size*2);
670        if (niter < niter_best) {
671            niter_best  = niter;
672            nwarps_best = nwarps;
673        }
674    }
675
676    const int nbytes_shared_iter = nwarps_best * (volta_mma_available(cc) ? tile_A_32::I : tile_A_16::I) * (warp_size + mmf_get_padding(cc)) * 4;
677    const int nbytes_cols_per_block_pad = (amd_wmma_available(cc) || amd_mfma_available(cc)) ? tile_B_16::I : tile_B_8::I;
678    const int nbytes_shared_combine = GGML_PAD(cols_per_block, nbytes_cols_per_block_pad) * (nwarps_best*rows_per_block + mmf_get_padding(cc)) * 4;
679    const int nbytes_shared = std::max(nbytes_shared_iter, nbytes_shared_combine);
680    const int nbytes_slotmap = ids ? GGML_PAD(cols_per_block, 16) * sizeof(int) : 0;
681    const int nbytes_shared_total = nbytes_shared + nbytes_slotmap;
682    const int64_t grid_y = ids ? nchannels_x : nchannels_dst;
683
684    const dim3 block_nums(nrows_x/rows_per_block, grid_y, nsamples_dst);
685    const dim3 block_dims(warp_size, nwarps_best, 1);
686
687    switch (nwarps_best) {
688        case 1: {
689            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 1>(
690                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
691                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
692                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
693                ids_data);
694        } break;
695        case 2: {
696            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 2>(
697                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
698                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
699                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
700                ids_data);
701        } break;
702        case 3: {
703            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 3>(
704                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
705                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
706                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
707                ids_data);
708        } break;
709        case 4: {
710            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 4>(
711                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
712                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
713                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
714                ids_data);
715        } break;
716        case 5: {
717            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 5>(
718                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
719                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
720                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
721                ids_data);
722        } break;
723        case 6: {
724            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 6>(
725                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
726                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
727                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
728                ids_data);
729        } break;
730        case 7: {
731            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 7>(
732                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
733                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
734                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
735                ids_data);
736        } break;
737        case 8: {
738            mul_mat_f_switch_ids<T, rows_per_block, cols_per_block, 8>(
739                x, y, ids, dst, ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
740                stride_col_id, stride_row_id, channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
741                sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, block_nums, block_dims, nbytes_shared_total, stream,
742                ids_data);
743        } break;
744        default: {
745            GGML_ABORT("fatal error");
746        } break;
747    }
748
749    GGML_UNUSED_VARS(nchannels_y);
750}
751
752template <typename T, int rows_per_block>
753static void mul_mat_f_switch_cols_per_block(
754        const T * x, const float * y, const int32_t * ids, float * dst,
755        const int64_t ncols_x, const int64_t nrows_x, const int64_t ncols_dst,
756        const int64_t stride_row, const int64_t stride_col_y, const int64_t stride_col_dst,
757        const int64_t stride_col_id, const int stride_row_id,
758        const int64_t nchannels_x, const int64_t nchannels_y, const int64_t nchannels_dst,
759        const int64_t stride_channel_x, const int64_t stride_channel_y, const int64_t stride_channel_dst, const int64_t nsamples_x,
760        const int64_t nsamples_dst, const int64_t stride_sample_x, const int64_t stride_sample_y, const int64_t stride_sample_dst,
761        cudaStream_t stream, const mmf_ids_data * ids_data) {
762
763    const int ncols_case = (ids && ncols_dst > 16) ? 16 : ncols_dst;
764
765    GGML_ASSERT(ids || ncols_dst <= 16);
766
767    switch (ncols_case) {
768        case  1: {
769            mul_mat_f_cuda<T,  rows_per_block, 1>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
770                stride_col_id, stride_row_id, nchannels_x, nchannels_y, nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
771                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
772        } break;
773        case  2: {
774            mul_mat_f_cuda<T,  rows_per_block, 2>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
775                stride_col_id, stride_row_id, nchannels_x, nchannels_y, nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
776                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
777        } break;
778        case  3: {
779            mul_mat_f_cuda<T,  rows_per_block, 3>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
780                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
781                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
782        } break;
783        case  4: {
784            mul_mat_f_cuda<T,  rows_per_block, 4>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
785                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
786                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
787        } break;
788        case  5: {
789            mul_mat_f_cuda<T,  rows_per_block, 5>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
790                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
791                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y,  stride_sample_dst, stream, ids_data);
792        } break;
793        case  6: {
794            mul_mat_f_cuda<T,  rows_per_block, 6>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
795                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
796                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
797        } break;
798        case  7: {
799            mul_mat_f_cuda<T,  rows_per_block, 7>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
800                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
801                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
802        } break;
803        case  8: {
804            mul_mat_f_cuda<T,  rows_per_block, 8>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
805                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
806                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
807        } break;
808        case  9: {
809            mul_mat_f_cuda<T,  rows_per_block, 9>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
810                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
811                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
812        } break;
813        case 10: {
814            mul_mat_f_cuda<T, rows_per_block, 10>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
815                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
816                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
817        } break;
818        case 11: {
819            mul_mat_f_cuda<T, rows_per_block, 11>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
820                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
821                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
822        } break;
823        case 12: {
824            mul_mat_f_cuda<T, rows_per_block, 12>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
825                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
826                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
827        } break;
828        case 13: {
829            mul_mat_f_cuda<T, rows_per_block, 13>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
830                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
831                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
832        } break;
833        case 14: {
834            mul_mat_f_cuda<T, rows_per_block, 14>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
835                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
836                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
837        } break;
838        case 15: {
839            mul_mat_f_cuda<T, rows_per_block, 15>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
840                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
841                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
842        } break;
843        case 16: {
844            mul_mat_f_cuda<T, rows_per_block, 16>(x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
845                stride_col_id, stride_row_id, nchannels_x, nchannels_y,  nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
846                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
847        } break;
848        default: {
849            GGML_ABORT("fatal error");
850        } break;
851    }
852}
853
854template <typename T>
855static void mul_mat_f_switch_rows_per_block(
856        const int rows_per_block, const T * x, const float * y, const int32_t * ids, float * dst,
857        const int64_t ncols_x, const int64_t nrows_x, const int64_t ncols_dst,
858        const int64_t stride_row, const int64_t stride_col_y, const int64_t stride_col_dst,
859        const int64_t stride_col_id, const int stride_row_id,
860        const int64_t nchannels_x, const int64_t nchannels_y, const int64_t nchannels_dst,
861        const int64_t stride_channel_x, const int64_t stride_channel_y, const int64_t stride_channel_dst, const int64_t nsamples_x,
862        const int64_t nsamples_dst, const int64_t stride_sample_x, const int64_t stride_sample_y, const int64_t stride_sample_dst,
863        cudaStream_t stream, const mmf_ids_data * ids_data) {
864    switch (rows_per_block) {
865        case MMF_ROWS_PER_BLOCK: {
866            mul_mat_f_switch_cols_per_block<T, MMF_ROWS_PER_BLOCK>(
867                x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
868                stride_col_id, stride_row_id, nchannels_x, nchannels_y, nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
869                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
870        } break;
871        case MMF_ROWS_PER_BLOCK_CDNA: {
872            mul_mat_f_switch_cols_per_block<T, MMF_ROWS_PER_BLOCK_CDNA>(
873                x, y, ids, dst, ncols_x, nrows_x, ncols_dst, stride_row, stride_col_y, stride_col_dst,
874                stride_col_id, stride_row_id, nchannels_x, nchannels_y, nchannels_dst, stride_channel_x, stride_channel_y, stride_channel_dst,
875                nsamples_x, nsamples_dst, stride_sample_x, stride_sample_y, stride_sample_dst, stream, ids_data);
876        } break;
877        default:
878            GGML_ABORT("unsupported rows_per_block: %i", rows_per_block);
879    }
880}
881
882#define DECL_MMF_CASE_HELPER(T, nrows_dst, ncols_dst) \
883    template void mul_mat_f_cuda<T, nrows_dst, ncols_dst>( \
884        const T * x, const float * y, const int32_t * ids, float * dst, \
885        const int64_t ncols_x, const int64_t nrows_x, int64_t ncols_dst_total, const int64_t stride_row, const int64_t stride_col_y, const int64_t stride_col_dst, \
886        const int64_t stride_col_id, const int64_t stride_row_id, \
887        const int64_t nchannels_x, const int64_t nchannels_y, const int64_t nchannels_dst, \
888        const int64_t stride_channel_x, const int64_t stride_channel_y, const int64_t stride_channel_dst, const int64_t nsamples_x,\
889        const int64_t nsamples_dst, const int64_t stride_sample_x, const int64_t stride_sample_y, const int64_t stride_sample_dst, \
890        cudaStream_t stream, const mmf_ids_data * ids_data);
891
892#if !defined(GGML_USE_MUSA)
893#define DECL_MMF_CASE_EXTERN(ncols_dst) \
894    extern DECL_MMF_CASE_HELPER(float, MMF_ROWS_PER_BLOCK, ncols_dst) \
895    extern DECL_MMF_CASE_HELPER(half2, MMF_ROWS_PER_BLOCK, ncols_dst) \
896    extern DECL_MMF_CASE_HELPER(nv_bfloat162, MMF_ROWS_PER_BLOCK, ncols_dst) \
897    extern DECL_MMF_CASE_HELPER(float, MMF_ROWS_PER_BLOCK_CDNA, ncols_dst) \
898    extern DECL_MMF_CASE_HELPER(half2, MMF_ROWS_PER_BLOCK_CDNA, ncols_dst) \
899    extern DECL_MMF_CASE_HELPER(nv_bfloat162, MMF_ROWS_PER_BLOCK_CDNA, ncols_dst)
900
901#define DECL_MMF_CASE(ncols_dst) \
902    DECL_MMF_CASE_HELPER(float, MMF_ROWS_PER_BLOCK, ncols_dst) \
903    DECL_MMF_CASE_HELPER(half2, MMF_ROWS_PER_BLOCK, ncols_dst) \
904    DECL_MMF_CASE_HELPER(nv_bfloat162, MMF_ROWS_PER_BLOCK, ncols_dst) \
905    DECL_MMF_CASE_HELPER(float, MMF_ROWS_PER_BLOCK_CDNA, ncols_dst) \
906    DECL_MMF_CASE_HELPER(half2, MMF_ROWS_PER_BLOCK_CDNA, ncols_dst) \
907    DECL_MMF_CASE_HELPER(nv_bfloat162, MMF_ROWS_PER_BLOCK_CDNA, ncols_dst)
908
909DECL_MMF_CASE_EXTERN(1);
910DECL_MMF_CASE_EXTERN(2);
911DECL_MMF_CASE_EXTERN(3);
912DECL_MMF_CASE_EXTERN(4);
913DECL_MMF_CASE_EXTERN(5);
914DECL_MMF_CASE_EXTERN(6);
915DECL_MMF_CASE_EXTERN(7);
916DECL_MMF_CASE_EXTERN(8);
917DECL_MMF_CASE_EXTERN(9);
918DECL_MMF_CASE_EXTERN(10);
919DECL_MMF_CASE_EXTERN(11);
920DECL_MMF_CASE_EXTERN(12);
921DECL_MMF_CASE_EXTERN(13);
922DECL_MMF_CASE_EXTERN(14);
923DECL_MMF_CASE_EXTERN(15);
924DECL_MMF_CASE_EXTERN(16);
925#else
926#define DECL_MMF_CASE(ncols_dst)
927#endif