char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
6703d7894
char/aliceai-t5-moe
1#include "fusion.hpp" 2#include "binbcast.hpp" 3 4#include <algorithm> 5 6// mul_mat(gate) + mul_mat(up) + GLU: graph shape and tensor properties only. Backend state 7// (weight layout, split buffers, DMMV) is checked by ggml_sycl_mul_mat_glu_mmvq_fused(). 8static bool ggml_sycl_should_fuse_mul_mat_glu (const ggml_tensor * gate ,const ggml_tensor * up , 9const ggml_tensor * glu ) { 10// the fused epilogue implements these two; the rest fall back to the standalone GLU kernels 11const ggml_glu_op glu_op = ggml_get_glu_op (glu ); 12if (glu_op != GGML_GLU_OP_SWIGLU && glu_op != GGML_GLU_OP_GEGLU ) { 13return false; 14 } 15 16// the kernel always treats src[0] as the activated operand and src[1] as the multiplier 17if (ggml_get_op_params_i32 (glu ,1 )/* swapped */ ) { 18return false; 19 } 20 21const ggml_tensor * wu = up -> src [0 ]; 22const ggml_tensor * wg = gate -> src [0 ]; 23const ggml_tensor * act = up -> src [1 ]; 24 25// one set of block offsets and one quantized activation must serve both weights 26if (wu -> type != wg -> type || !ggml_are_same_shape (wu ,wg )|| !ggml_are_same_stride (wu ,wg )) { 27return false; 28 } 29if (act != gate -> src [1 ]) { 30return false; 31 } 32 33// only q4_K has a fused reorder GEMV so far, and it walks whole super-blocks 34if (wu -> type != GGML_TYPE_Q4_K || wu -> ne [0 ] %QK_K != 0 ) { 35return false; 36 } 37 38// one 2D reorder-layout matrix in, a plain column stride out: no broadcast or padding 39if (!ggml_is_contiguous (wu )|| !ggml_is_contiguous (wg )|| !ggml_is_contiguous (act )|| 40 !ggml_is_contiguous (glu )) { 41return false; 42 } 43if (act -> type != GGML_TYPE_F32 || glu -> type != GGML_TYPE_F32 ) { 44return false; 45 } 46if (act -> ne [2 ]!= 1 || act -> ne [3 ]!= 1 || wu -> ne [2 ]!= 1 || wu -> ne [3 ]!= 1 ) { 47return false; 48 } 49// the kernel writes rows [0, wu->ne[1]) of each glu column, strided by glu->ne[0] 50if (glu -> ne [0 ]!= wu -> ne [1 ]|| glu -> ne [1 ]!= act -> ne [1 ]) { 51return false; 52 } 53// mat-vec only: one column per decoded token, up to the batch the reorder kernels cover 54if (act -> ne [1 ]> MMVQ_MAX_BATCH_SIZE ) { 55return false; 56 } 57 58return true; 59} 60 61bool ggml_sycl_can_fuse (const ggml_cgraph * cgraph ,int node_idx , std::initializer_list < enum ggml_op > ops , 62 std::initializer_list < enum ggml_unary_op > unary_ops ) { 63#ifndef NDEBUG 64const size_t num_unary = std::count (ops .begin (),ops .end (),GGML_OP_UNARY ); 65GGML_ASSERT (unary_ops .size ()== num_unary ); 66#endif 67 68if (!g_ggml_sycl_enable_fusion ) { 69return false; 70 } 71 72// gate and up are siblings, not a chain, so ggml_can_fuse cannot express this: use the 73// subgraph form with the GLU as the only materialised output. 74if (ops .size ()== 3 && ops .begin ()[0 ]== GGML_OP_MUL_MAT && ops .begin ()[1 ]== GGML_OP_MUL_MAT && 75ops .begin ()[2 ]== GGML_OP_GLU ) { 76if (!ggml_can_fuse_subgraph (cgraph ,node_idx ,ops , {node_idx + 2 })) { 77return false; 78 } 79 80const ggml_tensor * glu = cgraph -> nodes [node_idx + 2 ]; 81const ggml_tensor * gate = glu -> src [0 ]; 82const ggml_tensor * up = glu -> src [1 ]; 83 84// don't assume which of the two mat-muls is the gate; infer it from the GLU's operands 85const bool ok = (gate == cgraph -> nodes [node_idx ]&& up == cgraph -> nodes [node_idx + 1 ])|| 86 (gate == cgraph -> nodes [node_idx + 1 ]&& up == cgraph -> nodes [node_idx ]); 87if (!ok ) { 88return false; 89 } 90 91return ggml_sycl_should_fuse_mul_mat_glu (gate ,up ,glu ); 92 } 93 94if (!ggml_can_fuse (cgraph ,node_idx ,ops )) { 95return false; 96 } 97 98if ((ops .size ()== 2 || ops .size ()== 3 )&& ops .begin ()[0 ]== GGML_OP_RMS_NORM && ops .begin ()[1 ]== GGML_OP_MUL ) { 99if (ops .size ()== 3 && ops .begin ()[2 ]!= GGML_OP_ADD ) { 100return false; 101 } 102 103const ggml_tensor * rms_norm = cgraph -> nodes [node_idx ]; 104const ggml_tensor * mul = cgraph -> nodes [node_idx + 1 ]; 105const ggml_tensor * add = ops .size ()== 3 ?cgraph -> nodes [node_idx + 2 ] :nullptr ; 106 107GGML_ASSERT (rms_norm -> src [0 ]-> type == GGML_TYPE_F32 ); 108GGML_ASSERT (rms_norm -> type == GGML_TYPE_F32 ); 109 110if (mul -> src [0 ]-> type != GGML_TYPE_F32 || 111mul -> src [1 ]-> type != GGML_TYPE_F32 || 112mul -> type != GGML_TYPE_F32 ) { 113return false; 114 } 115 116// if rms norm is the B operand, then we don't handle broadcast 117if (rms_norm == mul -> src [1 ]&& !ggml_are_same_shape (mul -> src [0 ],rms_norm )) { 118return false; 119 } 120 121const ggml_tensor * mul_w = (mul -> src [0 ]== rms_norm ) ?mul -> src [1 ] :mul -> src [0 ]; 122// the fused kernel indexes the weight as mul[col], so it must span ncols contiguously 123if (mul_w -> ne [0 ]!= rms_norm -> ne [0 ]|| mul_w -> nb [0 ]!= ggml_type_size (mul_w -> type )) { 124return false; 125 } 126 127if (!ggml_is_contiguous_rows (mul -> src [0 ])|| !ggml_is_contiguous_rows (mul -> src [1 ])) { 128return false; 129 } 130 131if (add != nullptr ) { 132if (add -> src [0 ]-> type != GGML_TYPE_F32 || 133add -> src [1 ]-> type != GGML_TYPE_F32 || 134add -> type != GGML_TYPE_F32 ) { 135return false; 136 } 137 138// the fused kernel indexes the residual as add[col] and does not broadcast it 139const ggml_tensor * add_w = (add -> src [0 ]== mul ) ?add -> src [1 ] :add -> src [0 ]; 140if (!ggml_are_same_shape (add_w ,add )) { 141return false; 142 } 143 144if (!ggml_is_contiguous (add -> src [0 ])|| !ggml_is_contiguous_rows (add -> src [1 ])) { 145return false; 146 } 147 } 148 149return true; 150 } 151 152if (ops .size ()== 2 && ops .begin ()[0 ]== GGML_OP_ADD && ops .begin ()[1 ]== GGML_OP_ADD ) { 153const ggml_tensor * add0 = cgraph -> nodes [node_idx ]; 154const ggml_tensor * add1 = cgraph -> nodes [node_idx + 1 ]; 155// ggml_can_fuse already guarantees add1 consumes add0 and that add0 has a single use. 156// Keep the CUDA association: the running sum is src0 of the next ADD so the fused 157// float fold matches two sequential add() launches. 158if (add1 -> src [0 ]!= add0 ) { 159return false; 160 } 161 162const ggml_tensor * c = add1 -> src [1 ]; 163if (!ggml_sycl_add_kernel_supports (add0 -> src [0 ]-> type ,add0 -> src [1 ]-> type ,add0 -> type )|| 164 !ggml_sycl_add_kernel_supports (add0 -> type ,c -> type ,add1 -> type )) { 165return false; 166 } 167 168return true; 169 } 170 171if (ops .size ()== 2 && ops .begin ()[0 ]== GGML_OP_UNARY && ops .begin ()[1 ]== GGML_OP_MUL && 172unary_ops .size ()== 1 ) { 173const ggml_tensor * unary = cgraph -> nodes [node_idx ]; 174const ggml_tensor * mul = cgraph -> nodes [node_idx + 1 ]; 175 176const ggml_unary_op unary_op = ggml_get_unary_op (unary ); 177if (unary_op != unary_ops .begin ()[0 ]) { 178return false; 179 } 180 181// the ops ggml_sycl_op_unary_mul_fused() has a kernel for 182if (unary_op != GGML_UNARY_OP_SILU && unary_op != GGML_UNARY_OP_SIGMOID && 183unary_op != GGML_UNARY_OP_SOFTPLUS ) { 184return false; 185 } 186 187if (unary -> type != GGML_TYPE_F32 && unary -> type != GGML_TYPE_F16 ) { 188return false; 189 } 190 191const ggml_tensor * other = (mul -> src [0 ]== unary ) ?mul -> src [1 ] :mul -> src [0 ]; 192if (other -> type != unary -> type ) { 193return false; 194 } 195 196// one row stride per source comes from nb[1], so rows must be contiguous and equally 197// shaped; the destination is written flat, so it must be fully contiguous 198if (!ggml_is_contiguous_1 (unary -> src [0 ])|| !ggml_is_contiguous_1 (other )|| 199 !ggml_are_same_shape (other ,unary )|| !ggml_is_contiguous (mul )) { 200return false; 201 } 202 203// the 32-bit fastdiv is inexact past 2^31; decline, the unfused path handles it 204if (ggml_nelements (mul ) >= ((int64_t )1 <<31 )) { 205return false; 206 } 207 208return true; 209 } 210 211return false; 212}