char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
73a43d1f6
char/aliceai-t5-moe
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}