char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
41ef91f7c
char/aliceai-t5-moe
1#include "ggml-cuda/common.cuh" 2#include "ggml.h" 3#include "topk-moe.cuh" 4 5#include <cmath> 6#include <initializer_list> 7 8// Kernel config struct - passed by value to CUDA kernel 9struct topk_moe_config { 10 bool use_sigmoid; 11 bool use_sqrt_softplus; 12 bool with_norm; 13 bool delayed_softmax; 14}; 15 16// Warp-local softmax used for both the pre-top-k logits and the post-top-k delayed path. 17template <int experts_per_thread, bool use_limit> 18__device__ void softmax_warp_inplace(float (&vals)[experts_per_thread], const int limit, const int lane) { 19 float max_val = -INFINITY; 20 21#pragma unroll 22 for (int i = 0; i < experts_per_thread; i++) { 23 const int idx = lane + i * WARP_SIZE; 24 const bool active = !use_limit || (idx < limit); 25 if (active) { 26 max_val = max(max_val, vals[i]); 27 } 28 } 29 30 max_val = warp_reduce_max(max_val); 31 32 float sum = 0.f; 33 34#pragma unroll 35 for (int i = 0; i < experts_per_thread; i++) { 36 const int idx = lane + i * WARP_SIZE; 37 const bool active = !use_limit || (idx < limit); 38 if (active) { 39 const float val = expf(vals[i] - max_val); 40 vals[i] = val; 41 sum += val; 42 } else { 43 vals[i] = 0.f; 44 } 45 } 46 47 sum = warp_reduce_sum(sum); 48 49 const float inv_sum = 1.0f / sum; 50 51#pragma unroll 52 for (int i = 0; i < experts_per_thread; i++) { 53 const int idx = lane + i * WARP_SIZE; 54 const bool active = !use_limit || (idx < limit); 55 if (active) { 56 vals[i] *= inv_sum; 57 } 58 } 59} 60 61template <int experts_per_thread, bool use_limit> 62__device__ void sigmoid_warp_inplace(float (&vals)[experts_per_thread], const int limit, const int lane) { 63#pragma unroll 64 for (int i = 0; i < experts_per_thread; i++) { 65 const int idx = lane + i * WARP_SIZE; 66 const bool active = !use_limit || (idx < limit); 67 vals[i] = active ? 1.f / (1.f + expf(-vals[i])) : -INFINITY; 68 } 69} 70 71template <int experts_per_thread, bool use_limit> 72__device__ void sqrt_softplus_warp_inplace(float (&vals)[experts_per_thread], const int limit, const int lane) { 73#pragma unroll 74 for (int i = 0; i < experts_per_thread; i++) { 75 const int idx = lane + i * WARP_SIZE; 76 const bool active = !use_limit || (idx < limit); 77 vals[i] = active ? sqrtf(vals[i] > 20.0f ? vals[i] : logf(1.0f + expf(vals[i]))) : -INFINITY; 78 } 79} 80 81/* 82 This kernel does the following: 83 1. optionally softmax over the logits per token [n_experts, n_tokens] 84 2. argmax reduce over the top-k (n_experts_used) logits 85 3. write weights + ids to global memory 86 4. optionally normalize the weights or apply softmax over the selected logits 87 88 It is intended as fusion of softmax->top-k->get_rows pipeline for MoE models 89*/ 90template <int n_experts, bool has_bias> 91__launch_bounds__(TOPK_MOE_ROWS_PER_BLOCK * WARP_SIZE, 1) 92__global__ void topk_moe_cuda(const float * logits, 93 float * weights, 94 int32_t * ids, 95 float * bias, 96 const int n_rows, 97 const int n_expert_used, 98 const float clamp_val, 99 const float scale_val, 100 const topk_moe_config config) { 101 const int row = blockIdx.x * blockDim.y + threadIdx.y; 102 if (row >= n_rows) { 103 return; 104 } 105 106 logits += n_experts * row; 107 weights += n_expert_used * row; 108 ids += n_experts * row; 109 110 constexpr int experts_per_thread = (n_experts > WARP_SIZE) ? n_experts / WARP_SIZE : 1; 111 112 float wt[experts_per_thread]; 113 114 // Initialize all slots to -INFINITY 115#pragma unroll 116 for (int i = 0; i < experts_per_thread; i++) { 117 wt[i] = -INFINITY; 118 } 119 120 ggml_cuda_pdl_sync(); 121#pragma unroll 122 for (int i = 0; i < n_experts; i += WARP_SIZE) { 123 const int expert = i + threadIdx.x; 124 wt[i / WARP_SIZE] = (n_experts % WARP_SIZE == 0 || expert < n_experts) ? logits[expert] : -INFINITY; 125 } 126 127 // Weights and IDs can alias logits, so wait until every row in the block reads its logits. 128 __syncthreads(); 129 130 if (!config.delayed_softmax) { 131 if (config.use_sigmoid) { 132 sigmoid_warp_inplace<experts_per_thread, false>(wt, n_experts, threadIdx.x); 133 } else if (config.use_sqrt_softplus) { 134 sqrt_softplus_warp_inplace<experts_per_thread, false>(wt, n_experts, threadIdx.x); 135 } else { 136 softmax_warp_inplace<experts_per_thread, false>(wt, n_experts, threadIdx.x); 137 } 138 } 139 140 // Sanitize NaN to -FLT_MAX so the iterative argmax produces unique expert IDs. 141 // NaN comparisons always return false, which would cause the same expert to be 142 // selected repeatedly. -FLT_MAX compares normally and is still excluded by the 143 // -INFINITY sentinel used after each selection round. 144 // More relevant for the cuBLAS path. See https://github.com/ggml-org/llama.cpp/issues/19659 145#pragma unroll 146 for (int i = 0; i < experts_per_thread; i++) { 147 if (__isnanf(wt[i])) { 148 wt[i] = -FLT_MAX; 149 } 150 } 151 152 // selection_wt is only needed when bias is present (selection uses wt + bias) 153 // when no bias, we use wt directly for both selection and weight values 154 [[maybe_unused]] float selection_wt[has_bias ? experts_per_thread : 1]; 155 156 if constexpr (has_bias) { 157#pragma unroll 158 for (int i = 0; i < experts_per_thread; i++) { 159 selection_wt[i] = -INFINITY; 160 } 161#pragma unroll 162 for (int i = 0; i < n_experts; i += WARP_SIZE) { 163 const int expert = i + threadIdx.x; 164 selection_wt[i / WARP_SIZE] = 165 (n_experts % WARP_SIZE == 0 || expert < n_experts) ? wt[i / WARP_SIZE] + bias[expert] : -INFINITY; 166 } 167 } 168 169 //at this point, each thread holds either a portion of the softmax distribution 170 //or the raw logits. We do the argmax reduce over n_expert_used, each time marking 171 //the expert weight as -inf to exclude from the next iteration 172 173 float wt_sum = 0.f; 174 175 float output_weights[experts_per_thread]; 176 177#pragma unroll 178 for (int i = 0; i < experts_per_thread; i++) { 179 output_weights[i] = 0.f; 180 } 181 182 ggml_cuda_pdl_lc(); 183 for (int k = 0; k < n_expert_used; k++) { 184 float max_val = wt[0]; 185 int max_expert = threadIdx.x; 186 187 if constexpr (has_bias) { 188 float max_val_s = selection_wt[0]; 189 190#pragma unroll 191 for (int i = 1; i < experts_per_thread; i++) { 192 const int expert = threadIdx.x + i * WARP_SIZE; 193 if ((n_experts % WARP_SIZE == 0 || expert < n_experts) && selection_wt[i] > max_val_s) { 194 max_val = wt[i]; 195 max_val_s = selection_wt[i]; 196 max_expert = expert; 197 } 198 } 199 200#pragma unroll 201 for (int mask = WARP_SIZE / 2; mask > 0; mask /= 2) { 202 const float val = __shfl_xor_sync(0xFFFFFFFF, max_val, mask, WARP_SIZE); 203 const float val_s = __shfl_xor_sync(0xFFFFFFFF, max_val_s, mask, WARP_SIZE); 204 const int expert = __shfl_xor_sync(0xFFFFFFFF, max_expert, mask, WARP_SIZE); 205 if (val_s > max_val_s || (val_s == max_val_s && expert < max_expert)) { 206 max_val = val; 207 max_val_s = val_s; 208 max_expert = expert; 209 } 210 } 211 212 if ((max_expert & (WARP_SIZE - 1)) == threadIdx.x) { 213 selection_wt[max_expert / WARP_SIZE] = -INFINITY; 214 } 215 } else { 216#pragma unroll 217 for (int i = 1; i < experts_per_thread; i++) { 218 const int expert = threadIdx.x + i * WARP_SIZE; 219 if ((n_experts % WARP_SIZE == 0 || expert < n_experts) && wt[i] > max_val) { 220 max_val = wt[i]; 221 max_expert = expert; 222 } 223 } 224 225#pragma unroll 226 for (int mask = WARP_SIZE / 2; mask > 0; mask /= 2) { 227 const float val = __shfl_xor_sync(0xFFFFFFFF, max_val, mask, WARP_SIZE); 228 const int expert = __shfl_xor_sync(0xFFFFFFFF, max_expert, mask, WARP_SIZE); 229 if (val > max_val || (val == max_val && expert < max_expert)) { 230 max_val = val; 231 max_expert = expert; 232 } 233 } 234 235 if ((max_expert & (WARP_SIZE - 1)) == threadIdx.x) { 236 wt[max_expert / WARP_SIZE] = -INFINITY; 237 } 238 } 239 240 if ((k & (WARP_SIZE - 1)) == threadIdx.x) { 241 output_weights[k / WARP_SIZE] = max_val; 242 } 243 244 if ((max_expert & (WARP_SIZE - 1)) == threadIdx.x) { 245 ids[k] = max_expert; 246 if (config.with_norm) { 247 wt_sum += max_val; 248 } 249 } 250 } 251 252 if (config.with_norm) { 253 wt_sum = warp_reduce_sum(wt_sum); 254 wt_sum = max(wt_sum, clamp_val); 255 const float inv_sum = 1.0f / wt_sum; 256 257 for (int i = 0; i < experts_per_thread; i++) { 258 output_weights[i] *= inv_sum; 259 } 260 } 261 262 if (config.delayed_softmax) { 263 softmax_warp_inplace<experts_per_thread, true>(output_weights, n_expert_used, threadIdx.x); 264 } 265 266#pragma unroll 267 for (int i = 0; i < experts_per_thread; i++) { 268 const int idx = i * WARP_SIZE + threadIdx.x; 269 if (idx < n_expert_used) { 270 weights[idx] = output_weights[i] * scale_val; 271 } 272 } 273} 274 275template<bool has_bias> 276static void launch_topk_moe_cuda(ggml_backend_cuda_context & ctx, 277 const float * logits, 278 float * weights, 279 int32_t * ids, 280 float * bias, 281 const int n_rows, 282 const int n_expert, 283 const int n_expert_used, 284 const float clamp_val, 285 const float scale_val, 286 const topk_moe_config config) { 287 GGML_ASSERT(!(config.with_norm && config.delayed_softmax) && 288 "delayed softmax is not supported with weight normalization"); 289 const int rows_per_block = TOPK_MOE_ROWS_PER_BLOCK; 290 dim3 grid_dims((n_rows + rows_per_block - 1) / rows_per_block, 1, 1); 291 dim3 block_dims(WARP_SIZE, rows_per_block, 1); 292 cudaStream_t stream = ctx.stream(); 293 const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(grid_dims, block_dims, 0, stream); 294 295 switch (n_expert) { 296 case 1: 297 ggml_cuda_kernel_launch(topk_moe_cuda<1, has_bias>, launch_params, 298 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 299 break; 300 case 2: 301 ggml_cuda_kernel_launch(topk_moe_cuda<2, has_bias>, launch_params, 302 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 303 break; 304 case 4: 305 ggml_cuda_kernel_launch(topk_moe_cuda<4, has_bias>, launch_params, 306 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 307 break; 308 case 8: 309 ggml_cuda_kernel_launch(topk_moe_cuda<8, has_bias>, launch_params, 310 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 311 break; 312 case 16: 313 ggml_cuda_kernel_launch(topk_moe_cuda<16, has_bias>, launch_params, 314 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 315 break; 316 case 32: 317 ggml_cuda_kernel_launch(topk_moe_cuda<32, has_bias>, launch_params, 318 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 319 break; 320 case 64: 321 ggml_cuda_kernel_launch(topk_moe_cuda<64, has_bias>, launch_params, 322 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 323 break; 324 case 128: 325 ggml_cuda_kernel_launch(topk_moe_cuda<128, has_bias>, launch_params, 326 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 327 break; 328 case 256: 329 ggml_cuda_kernel_launch(topk_moe_cuda<256, has_bias>, launch_params, 330 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 331 break; 332 case 288: // StepFun 3.7 333 ggml_cuda_kernel_launch(topk_moe_cuda<288, has_bias>, launch_params, 334 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 335 break; 336 case 512: 337 ggml_cuda_kernel_launch(topk_moe_cuda<512, has_bias>, launch_params, 338 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 339 break; 340 case 576: 341 ggml_cuda_kernel_launch(topk_moe_cuda<576, has_bias>, launch_params, 342 logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config); 343 break; 344 default: 345 GGML_ASSERT(false && "fatal error"); 346 break; 347 } 348} 349 350void ggml_cuda_op_topk_moe(ggml_backend_cuda_context & ctx, 351 const ggml_tensor * logits, 352 ggml_tensor * weights, 353 ggml_tensor * ids, 354 const ggml_tensor * clamp, 355 const ggml_tensor * scale, 356 const ggml_tensor * bias, 357 const ggml_cuda_topk_moe_args & args) { 358 GGML_ASSERT(logits->type == GGML_TYPE_F32); 359 GGML_ASSERT(weights->type == GGML_TYPE_F32); 360 GGML_ASSERT(ids->type == GGML_TYPE_I32); 361 362 const int n_experts = logits->ne[0]; 363 const int n_rows = logits->ne[1]; 364 365 const float * logits_d = (const float *) logits->data; 366 float * weights_d = (float *) weights->data; 367 int32_t * ids_d = (int32_t *) ids->data; 368 float * bias_d = bias ? (float *) bias->data : nullptr; 369 370 float scale_val = scale ? ggml_get_op_params_f32(scale, 0) : 1.0f; 371 372 GGML_ASSERT(ids->nb[1] / ggml_type_size(ids->type) == (size_t) n_experts); 373 374 const int n_expert_used = weights->ne[1]; 375 376 const bool with_norm = clamp != nullptr; 377 378 float clamp_val = -INFINITY; 379 if (clamp) { 380 clamp_val = ggml_get_op_params_f32(clamp, 0); 381 } 382 383 topk_moe_config config; 384 config.use_sigmoid = args.sigmoid; 385 config.use_sqrt_softplus = args.sqrt_softplus; 386 config.with_norm = with_norm; 387 config.delayed_softmax = args.delayed_softmax; 388 389 if (bias) { 390 launch_topk_moe_cuda<true>(ctx, logits_d, weights_d, ids_d, bias_d, n_rows, n_experts, n_expert_used, clamp_val, 391 scale_val, config); 392 } else { 393 launch_topk_moe_cuda<false>(ctx, logits_d, weights_d, ids_d, bias_d, n_rows, n_experts, n_expert_used, clamp_val, 394 scale_val, config); 395 } 396} 397 398bool ggml_cuda_should_use_topk_moe(const ggml_tensor * gating_op, 399 const ggml_tensor * weights, 400 const ggml_tensor * logits, 401 const ggml_tensor * ids) { 402 // must match an instantiation of launch_topk_moe_cuda: a power of 2 up to 512, 403 // or one of the non-power-of-2 expert counts of supported models 404 const int n_expert = ids->nb[1] / ids->nb[0]; 405 if (((n_expert & (n_expert - 1)) != 0 || n_expert > 512) && n_expert != 288 && n_expert != 576) { 406 return false; 407 } 408 409 if (!ggml_is_contiguous(weights) || !ggml_is_contiguous(logits)) { 410 return false; 411 } 412 413 if (gating_op->op == GGML_OP_SOFT_MAX) { 414 const ggml_tensor * softmax = gating_op; 415 float scale = 1.0f; 416 float max_bias = 0.0f; 417 418 memcpy(&scale, (const float *) softmax->op_params + 0, sizeof(float)); 419 memcpy(&max_bias, (const float *) softmax->op_params + 1, sizeof(float)); 420 421 if (!ggml_is_contiguous(softmax->src[0])) { 422 return false; 423 } 424 425 if (scale != 1.0f || max_bias != 0.0f) { 426 return false; 427 } 428 429 // don't fuse when masks or sinks are present 430 if (softmax->src[1] || softmax->src[2]) { 431 return false; 432 } 433 } else if (gating_op->op == GGML_OP_UNARY) { 434 ggml_unary_op op = ggml_get_unary_op(gating_op); 435 436 if (op != GGML_UNARY_OP_SIGMOID && op != GGML_UNARY_OP_SOFTPLUS) { 437 return false; 438 } 439 } 440 441 return true; 442}