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
8.9 KiB181 linesraw
1#include "common.cuh"
2#include "mmid.cuh"
3
4// To reduce shared memory use, store "it" and "iex_used" with 22/10 bits each.
5struct mm_ids_helper_store {
6    uint32_t data;
7
8    __device__ mm_ids_helper_store(const uint32_t it, const uint32_t iex_used) {
9        data = (it & 0x003FFFFF) | (iex_used << 22);
10    }
11
12    __device__ uint32_t it() const {
13        return data & 0x003FFFFF;
14    }
15
16    __device__ uint32_t iex_used() const {
17        return data >> 22;
18    }
19};
20static_assert(sizeof(mm_ids_helper_store) == 4, "unexpected size for mm_ids_helper_store");
21
22// the generic path passes 0, which needs no padding since it never groups lanes by token
23template <int n> struct mm_ids_pow2 { static constexpr int value = 2*mm_ids_pow2<(n + 1)/2>::value; };
24template <>      struct mm_ids_pow2<1> { static constexpr int value = 1; };
25template <>      struct mm_ids_pow2<0> { static constexpr int value = 1; };
26
27// Helper function for mul_mat_id, converts ids to a more convenient format.
28// ids_src1 describes how to permute the flattened column indices of src1 in order to get a compact src1 tensor sorted by expert.
29// ids_dst describes the same mapping but for the dst tensor.
30// The upper and lower bounds for the ith expert in the compact src1 tensor are stored in expert_bounds[i:i+1].
31template <int n_expert_used_template>
32__launch_bounds__(ggml_cuda_get_physical_warp_size(), 1)
33static __global__ void mm_ids_helper(
34        const int32_t * __restrict__ ids, int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, int32_t * __restrict__ expert_bounds,
35        const int n_tokens, const int n_expert_used_var, const int nchannels_y, const int si1, const int sis1, const bool write_inverse) {
36    constexpr int warp_size = ggml_cuda_get_physical_warp_size();
37    const int n_expert_used = n_expert_used_template == 0 ? n_expert_used_var : n_expert_used_template;
38    const int expert = blockIdx.x;
39
40    // token slots per warp lane group, padded to a power of 2 so a warp divides evenly
41    constexpr int neu_padded = mm_ids_pow2<n_expert_used_template>::value;
42
43    extern __shared__ char data_mm_ids_helper[];
44    mm_ids_helper_store * store = (mm_ids_helper_store *) data_mm_ids_helper;
45
46    int nex_prev   = 0; // Number of columns for experts with a lower index.
47    int it_compact = 0; // Running index for the compact slice of this expert.
48
49    if constexpr (n_expert_used_template == 0) {
50        // Generic implementation:
51        for (int it = 0; it < n_tokens; ++it) {
52            int iex_used = -1; // The index at which the expert is used, if any.
53            for (int iex = threadIdx.x; iex < n_expert_used; iex += warp_size) {
54                const int expert_used = ids[it*si1 + iex];
55                nex_prev += expert_used < expert;
56                if (expert_used == expert) {
57                    iex_used = iex;
58                }
59            }
60
61            if (iex_used != -1) {
62                store[it_compact] = mm_ids_helper_store(it, iex_used);
63            }
64
65            if (warp_reduce_any<warp_size>(iex_used != -1)) {
66                it_compact++;
67            }
68        }
69    } else {
70        // Implementation optimized for specific numbers of experts used:
71        // a warp holds a whole number of token slots, so the slot count is padded to a power of 2
72        static_assert(neu_padded <= warp_size && warp_size % neu_padded == 0, "bad n_expert_used");
73        for (int it0 = 0; it0 < n_tokens; it0 += warp_size/neu_padded) {
74            const int it = it0 + threadIdx.x / neu_padded;
75
76            const int iex = threadIdx.x % neu_padded; // The index at which the expert is used, if any.
77            const int expert_used = (neu_padded == n_expert_used || iex < n_expert_used) && it < n_tokens ?
78                ids[it*si1 + iex] : INT_MAX;
79            const int iex_used = expert_used == expert ? iex : -1;
80            nex_prev += expert_used < expert;
81
82            // Whether the threads at this token position have used the expert:
83            const int it_compact_add_self = warp_reduce_any<neu_padded>(iex_used != -1);
84
85            // Do a scan over threads at lower token positions in warp to get the correct index for writing data:
86            int it_compact_add_lower = 0;
87#pragma unroll
88            for (int offset = neu_padded; offset < warp_size; offset += neu_padded) {
89                const int tmp = __shfl_up_sync(0xFFFFFFFF, it_compact_add_self, offset, warp_size);
90                if (threadIdx.x >= static_cast<unsigned int>(offset)) {
91                    it_compact_add_lower += tmp;
92                }
93            }
94
95            if (iex_used != -1) {
96                store[it_compact + it_compact_add_lower] = mm_ids_helper_store(it, iex_used);
97            }
98
99            // The thread with the highest index in the warp always has the sum over the whole warp, use it to increment all threads:
100            it_compact += __shfl_sync(0xFFFFFFFF, it_compact_add_lower + it_compact_add_self, warp_size - 1, warp_size);
101        }
102    }
103    nex_prev = warp_reduce_sum<warp_size>(nex_prev);
104    ggml_cuda_syncwarp();
105
106    for (int itc = threadIdx.x; itc < it_compact; itc += warp_size) {
107        const mm_ids_helper_store store_it = store[itc];
108        const int it       = store_it.it();
109        const int iex_used = store_it.iex_used();
110        ids_dst[nex_prev + itc] = it*n_expert_used + iex_used;
111        // ids_src1 holds the forward map, or the inverse map (token slot -> compact row) for quant dedup
112        if (write_inverse) {
113            ids_src1[it*n_expert_used + iex_used] = nex_prev + itc;
114        } else {
115            ids_src1[nex_prev + itc] = it*sis1 + iex_used % nchannels_y;
116        }
117    }
118
119    if (threadIdx.x != 0) {
120        return;
121    }
122
123    expert_bounds[expert] = nex_prev;
124
125    if (expert < static_cast<int>(gridDim.x) - 1) {
126        return;
127    }
128
129    expert_bounds[gridDim.x] = nex_prev + it_compact;
130}
131
132template <int n_expert_used_template>
133static void launch_mm_ids_helper(
134        const int32_t * __restrict__ ids, int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, int32_t * __restrict__ expert_bounds,
135        const int n_experts, const int n_tokens, const int n_expert_used_var, const int nchannels_y, const int si1, const int sis1, const bool write_inverse, cudaStream_t stream) {
136    GGML_ASSERT(n_tokens          < (1 << 22) && "too few bits in mm_ids_helper_store");
137    GGML_ASSERT(n_expert_used_var < (1 << 10) && "too few bits in mm_ids_helper_store");
138
139    const int id = ggml_cuda_get_device();
140    const int warp_size = ggml_cuda_info().devices[id].warp_size;
141    const size_t smpbo = ggml_cuda_info().devices[id].smpbo;
142    CUDA_SET_SHARED_MEMORY_LIMIT(mm_ids_helper<n_expert_used_template>, smpbo);
143
144    const dim3 num_blocks(n_experts, 1, 1);
145    const dim3 block_size(warp_size, 1, 1);
146    const size_t nbytes_shared = n_tokens*sizeof(mm_ids_helper_store);
147    GGML_ASSERT(nbytes_shared <= smpbo);
148    mm_ids_helper<n_expert_used_template><<<num_blocks, block_size, nbytes_shared, stream>>>
149        (ids, ids_src1, ids_dst, expert_bounds, n_tokens, n_expert_used_var, nchannels_y, si1, sis1, write_inverse);
150}
151
152void ggml_cuda_launch_mm_ids_helper(
153        const int32_t * __restrict__ ids, int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, int32_t * __restrict__ expert_bounds,
154        const int n_experts, const int n_tokens, const int n_expert_used, const int nchannels_y, const int si1, const int sis1, const bool write_inverse, cudaStream_t stream) {
155    switch (n_expert_used) {
156        case  2:
157            launch_mm_ids_helper< 2>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
158            break;
159        case  4:
160            launch_mm_ids_helper< 4>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
161            break;
162        case  6:
163            launch_mm_ids_helper< 6>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
164            break;
165        case  8:
166            launch_mm_ids_helper< 8>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
167            break;
168        case 10:
169            launch_mm_ids_helper<10>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
170            break;
171        case 16:
172            launch_mm_ids_helper<16>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
173            break;
174        case 32:
175            launch_mm_ids_helper<32>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
176            break;
177        default:
178            launch_mm_ids_helper< 0>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
179            break;
180    }
181}