3232#include " ggml-cuda/im2col.cuh"
3333#include " ggml-cuda/mmf.cuh"
3434#include " ggml-cuda/mmq.cuh"
35+ #include " ggml-cuda/moe-ffn.cuh"
3536#include " ggml-cuda/mmvf.cuh"
3637#include " ggml-cuda/mmvq.cuh"
3738#include " ggml-cuda/norm.cuh"
@@ -2854,7 +2855,7 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor *
28542855 nb1, nb2, nb3, stream);
28552856}
28562857
2857- static bool ggml_cuda_compute_forward (ggml_backend_cuda_context & ctx, struct ggml_tensor * dst) {
2858+ bool ggml_cuda_compute_forward (ggml_backend_cuda_context & ctx, struct ggml_tensor * dst) {
28582859 switch (dst->op ) {
28592860 case GGML_OP_ARGMAX :
28602861 ggml_cuda_argmax (ctx, dst);
@@ -4032,6 +4033,201 @@ static bool ggml_cuda_can_fuse(const struct ggml_cgraph * cgraph,
40324033 return false ;
40334034}
40344035
4036+ static inline const char * ggml_cuda_moe_wp_trace_tensor_name (const ggml_tensor * t) {
4037+ return t != nullptr && t->name [0 ] != ' \0 ' ? t->name : " -" ;
4038+ }
4039+
4040+ static inline int ggml_cuda_moe_whole_pattern_trace_limit () {
4041+ static const int value = []() {
4042+ const char * s = getenv (" LLAMA_MOE_WHOLE_PATTERN_TRACE" );
4043+ if (s == nullptr || strcmp (s, " 0" ) == 0 ) {
4044+ return 0 ;
4045+ }
4046+ const int parsed = atoi (s);
4047+ return parsed > 0 ? parsed : 128 ;
4048+ }();
4049+
4050+ return value;
4051+ }
4052+
4053+ static inline bool ggml_cuda_moe_whole_pattern_trace_take (std::atomic<int > & counter) {
4054+ const int trace_limit = ggml_cuda_moe_whole_pattern_trace_limit ();
4055+ if (trace_limit <= 0 ) {
4056+ return false ;
4057+ }
4058+
4059+ const int trace_idx = counter.fetch_add (1 , std::memory_order_relaxed);
4060+ return trace_idx < trace_limit;
4061+ }
4062+
4063+ static inline int ggml_cuda_moe_whole_pattern_early_trace_limit () {
4064+ static const int value = []() {
4065+ const char * s = getenv (" LLAMA_MOE_WHOLE_PATTERN_EARLY_TRACE" );
4066+ if (s == nullptr || strcmp (s, " 0" ) == 0 ) {
4067+ return 0 ;
4068+ }
4069+ const int parsed = atoi (s);
4070+ return parsed > 0 ? parsed : 128 ;
4071+ }();
4072+
4073+ return value;
4074+ }
4075+
4076+ static inline bool ggml_cuda_moe_whole_pattern_exec_enabled () {
4077+ static const bool value = []() {
4078+ const char * s = getenv (" LLAMA_MOE_WHOLE_PATTERN_EXEC" );
4079+ return s != nullptr && atoi (s) != 0 ;
4080+ }();
4081+
4082+ return value;
4083+ }
4084+
4085+ static inline int ggml_cuda_moe_whole_pattern_exec_trace_limit () {
4086+ static const int value = []() {
4087+ const char * s = getenv (" LLAMA_MOE_WHOLE_PATTERN_EXEC_TRACE" );
4088+ if (s == nullptr || strcmp (s, " 0" ) == 0 ) {
4089+ return 0 ;
4090+ }
4091+ const int parsed = atoi (s);
4092+ return parsed > 0 ? parsed : 128 ;
4093+ }();
4094+
4095+ return value;
4096+ }
4097+
4098+ static inline bool ggml_cuda_moe_whole_pattern_exec_trace_take (std::atomic<int > & counter) {
4099+ const int trace_limit = ggml_cuda_moe_whole_pattern_exec_trace_limit ();
4100+ if (trace_limit <= 0 ) {
4101+ return false ;
4102+ }
4103+
4104+ const int trace_idx = counter.fetch_add (1 , std::memory_order_relaxed);
4105+ return trace_idx < trace_limit;
4106+ }
4107+
4108+ struct ggml_cuda_moe_whole_pattern {
4109+ const ggml_tensor * gate_up = nullptr ;
4110+ const ggml_tensor * gate = nullptr ;
4111+ const ggml_tensor * up = nullptr ;
4112+ const ggml_tensor * glu = nullptr ;
4113+ const ggml_tensor * down = nullptr ;
4114+ const ggml_tensor * ids = nullptr ;
4115+
4116+ bool view_pair = false ;
4117+ bool ids_match = false ;
4118+ bool swiglu = false ;
4119+ bool supported_type = false ;
4120+ bool supported = false ;
4121+ };
4122+
4123+ static ggml_cuda_moe_whole_pattern ggml_cuda_moe_whole_pattern_detect (const ggml_tensor * glu, const ggml_tensor * down) {
4124+ ggml_cuda_moe_whole_pattern pattern{};
4125+ pattern.glu = glu;
4126+ pattern.down = down;
4127+
4128+ if (glu == nullptr || down == nullptr || glu->op != GGML_OP_GLU || down->op != GGML_OP_MUL_MAT_ID ) {
4129+ return pattern;
4130+ }
4131+
4132+ pattern.gate = glu->src [0 ];
4133+ pattern.up = glu->src [1 ];
4134+ pattern.ids = down->src [2 ];
4135+
4136+ pattern.view_pair = pattern.gate != nullptr && pattern.up != nullptr &&
4137+ pattern.gate ->op == GGML_OP_VIEW && pattern.up ->op == GGML_OP_VIEW &&
4138+ pattern.gate ->view_src != nullptr && pattern.gate ->view_src == pattern.up ->view_src ;
4139+ if (!pattern.view_pair ) {
4140+ return pattern;
4141+ }
4142+
4143+ pattern.gate_up = pattern.gate ->view_src ;
4144+ if (pattern.gate_up == nullptr || pattern.gate_up ->op != GGML_OP_MUL_MAT_ID ) {
4145+ return pattern;
4146+ }
4147+
4148+ pattern.ids_match = pattern.gate_up ->src [2 ] == pattern.ids ;
4149+ pattern.swiglu = ggml_get_glu_op (glu) == GGML_GLU_OP_SWIGLU ;
4150+ pattern.supported_type = down->src [0 ] != nullptr &&
4151+ (down->src [0 ]->type == GGML_TYPE_NVFP4 || down->src [0 ]->type == GGML_TYPE_MXFP4 );
4152+ pattern.supported = pattern.ids_match && pattern.swiglu && pattern.supported_type ;
4153+
4154+ return pattern;
4155+ }
4156+
4157+ static ggml_cuda_moe_whole_pattern ggml_cuda_moe_whole_pattern_detect_early (const ggml_cgraph * cgraph, int i) {
4158+ ggml_cuda_moe_whole_pattern pattern{};
4159+
4160+ if (cgraph == nullptr || i + 4 >= cgraph->n_nodes ) {
4161+ return pattern;
4162+ }
4163+
4164+ const ggml_tensor * gate_up = cgraph->nodes [i + 0 ];
4165+ const ggml_tensor * view0 = cgraph->nodes [i + 1 ];
4166+ const ggml_tensor * view1 = cgraph->nodes [i + 2 ];
4167+ const ggml_tensor * glu = cgraph->nodes [i + 3 ];
4168+ const ggml_tensor * down = cgraph->nodes [i + 4 ];
4169+
4170+ pattern.gate_up = gate_up;
4171+ pattern.glu = glu;
4172+ pattern.down = down;
4173+
4174+ if (gate_up == nullptr || view0 == nullptr || view1 == nullptr || glu == nullptr || down == nullptr ||
4175+ gate_up->op != GGML_OP_MUL_MAT_ID || view0->op != GGML_OP_VIEW || view1->op != GGML_OP_VIEW ||
4176+ glu->op != GGML_OP_GLU || down->op != GGML_OP_MUL_MAT_ID ) {
4177+ return pattern;
4178+ }
4179+
4180+ pattern.view_pair = view0->view_src == gate_up && view1->view_src == gate_up;
4181+ if (!pattern.view_pair ) {
4182+ return pattern;
4183+ }
4184+
4185+ if (glu->src [0 ] == view0 && glu->src [1 ] == view1) {
4186+ pattern.gate = view0;
4187+ pattern.up = view1;
4188+ } else if (glu->src [0 ] == view1 && glu->src [1 ] == view0) {
4189+ pattern.gate = view1;
4190+ pattern.up = view0;
4191+ } else {
4192+ return pattern;
4193+ }
4194+
4195+ if (down->src [1 ] != glu) {
4196+ return pattern;
4197+ }
4198+
4199+ pattern.ids = down->src [2 ];
4200+ pattern.ids_match = gate_up->src [2 ] == pattern.ids ;
4201+ pattern.swiglu = ggml_get_glu_op (glu) == GGML_GLU_OP_SWIGLU ;
4202+ pattern.supported_type = down->src [0 ] != nullptr &&
4203+ (down->src [0 ]->type == GGML_TYPE_NVFP4 || down->src [0 ]->type == GGML_TYPE_MXFP4 );
4204+ pattern.supported = pattern.ids_match && pattern.swiglu && pattern.supported_type ;
4205+
4206+ return pattern;
4207+ }
4208+
4209+ static bool ggml_cuda_moe_whole_pattern_exec_proof (
4210+ ggml_backend_cuda_context * cuda_ctx,
4211+ const ggml_cuda_moe_whole_pattern & pattern) {
4212+ GGML_ASSERT (cuda_ctx != nullptr );
4213+ GGML_ASSERT (pattern.supported );
4214+ GGML_ASSERT (pattern.gate_up != nullptr );
4215+ GGML_ASSERT (pattern.glu != nullptr );
4216+ GGML_ASSERT (pattern.down != nullptr );
4217+
4218+ if (!ggml_cuda_compute_forward (*cuda_ctx, const_cast <ggml_tensor *>(pattern.gate_up ))) {
4219+ return false ;
4220+ }
4221+ if (!ggml_cuda_compute_forward (*cuda_ctx, const_cast <ggml_tensor *>(pattern.glu ))) {
4222+ return false ;
4223+ }
4224+ if (!ggml_cuda_compute_forward (*cuda_ctx, const_cast <ggml_tensor *>(pattern.down ))) {
4225+ return false ;
4226+ }
4227+
4228+ return true ;
4229+ }
4230+
40354231// try and fuse nodes and return the number of nodes to skip
40364232static int ggml_cuda_try_fuse (ggml_backend_cuda_context * cuda_ctx, ggml_cgraph * cgraph, int i) {
40374233
@@ -4042,6 +4238,112 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
40424238
40434239 ggml_tensor * node = cgraph->nodes [i];
40444240
4241+ static std::atomic<int > moe_whole_pattern_early_trace_count{0 };
4242+ const bool routed_ffn_poc = ggml_cuda_moe_routed_ffn_poc_enabled ();
4243+ const bool whole_pattern_exec = ggml_cuda_moe_whole_pattern_exec_enabled ();
4244+ const int whole_pattern_early_trace_limit = ggml_cuda_moe_whole_pattern_early_trace_limit ();
4245+ if (node->op == GGML_OP_MUL_MAT_ID &&
4246+ (routed_ffn_poc || whole_pattern_exec || whole_pattern_early_trace_limit > 0 )) {
4247+ const ggml_cuda_moe_whole_pattern pattern = ggml_cuda_moe_whole_pattern_detect_early (cgraph, i);
4248+ if (pattern.view_pair ) {
4249+ const int trace_idx = moe_whole_pattern_early_trace_count.fetch_add (1 , std::memory_order_relaxed);
4250+ if (trace_idx < whole_pattern_early_trace_limit) {
4251+ const ggml_tensor * down_w = pattern.down != nullptr ? pattern.down ->src [0 ] : nullptr ;
4252+ const ggml_tensor * down_x = pattern.down != nullptr ? pattern.down ->src [1 ] : nullptr ;
4253+ fprintf (stderr,
4254+ " [LLAMA_MOE_WHOLE_PATTERN_EARLY] supported=%d skip_ready=%d gate_up=%s gate=%s up=%s glu=%s down=%s ids=%s type=%s"
4255+ " n_tokens=%" PRId64 " n_used=%" PRId64 " experts=%" PRId64
4256+ " n_embd=%" PRId64 " n_ff=%" PRId64
4257+ " ids_match=%d swiglu=%d\n " ,
4258+ pattern.supported ? 1 : 0 ,
4259+ pattern.supported ? 4 : 0 ,
4260+ ggml_cuda_moe_wp_trace_tensor_name (pattern.gate_up ),
4261+ ggml_cuda_moe_wp_trace_tensor_name (pattern.gate ),
4262+ ggml_cuda_moe_wp_trace_tensor_name (pattern.up ),
4263+ ggml_cuda_moe_wp_trace_tensor_name (pattern.glu ),
4264+ ggml_cuda_moe_wp_trace_tensor_name (pattern.down ),
4265+ ggml_cuda_moe_wp_trace_tensor_name (pattern.ids ),
4266+ down_w != nullptr ? ggml_type_name (down_w->type ) : " -" ,
4267+ down_x != nullptr ? down_x->ne [2 ] : 0 ,
4268+ pattern.ids != nullptr ? pattern.ids ->ne [0 ] : 0 ,
4269+ down_w != nullptr ? down_w->ne [2 ] : 0 ,
4270+ down_w != nullptr ? down_w->ne [1 ] : 0 ,
4271+ down_w != nullptr ? down_w->ne [0 ] : 0 ,
4272+ pattern.ids_match ? 1 : 0 ,
4273+ pattern.swiglu ? 1 : 0 );
4274+ }
4275+ }
4276+
4277+ const int cc = ggml_cuda_info ().devices [ggml_cuda_get_device ()].cc ;
4278+ const bool poc_supported = routed_ffn_poc && ggml_cuda_moe_routed_ffn_poc_should_engage (
4279+ pattern.gate_up , pattern.gate , pattern.up , pattern.glu , pattern.down , pattern.ids , cc);
4280+
4281+ if ((poc_supported || (whole_pattern_exec && pattern.supported ))) {
4282+ const bool ok = poc_supported ?
4283+ ggml_cuda_moe_routed_ffn_poc (
4284+ *cuda_ctx,
4285+ const_cast <ggml_tensor *>(pattern.gate_up ),
4286+ const_cast <ggml_tensor *>(pattern.gate ),
4287+ const_cast <ggml_tensor *>(pattern.up ),
4288+ const_cast <ggml_tensor *>(pattern.glu ),
4289+ const_cast <ggml_tensor *>(pattern.down )) :
4290+ ggml_cuda_moe_whole_pattern_exec_proof (cuda_ctx, pattern);
4291+ GGML_ASSERT (ok);
4292+
4293+ static std::atomic<int > moe_whole_pattern_exec_trace_count{0 };
4294+ if (ggml_cuda_moe_whole_pattern_exec_trace_take (moe_whole_pattern_exec_trace_count)) {
4295+ const ggml_tensor * down_w = pattern.down != nullptr ? pattern.down ->src [0 ] : nullptr ;
4296+ const ggml_tensor * down_x = pattern.down != nullptr ? pattern.down ->src [1 ] : nullptr ;
4297+ fprintf (stderr,
4298+ " [LLAMA_MOE_WHOLE_PATTERN_EXEC] skip=4 gate_up=%s glu=%s down=%s ids=%s"
4299+ " n_tokens=%" PRId64 " n_used=%" PRId64 " experts=%" PRId64 " \n " ,
4300+ ggml_cuda_moe_wp_trace_tensor_name (pattern.gate_up ),
4301+ ggml_cuda_moe_wp_trace_tensor_name (pattern.glu ),
4302+ ggml_cuda_moe_wp_trace_tensor_name (pattern.down ),
4303+ ggml_cuda_moe_wp_trace_tensor_name (pattern.ids ),
4304+ down_x != nullptr ? down_x->ne [2 ] : 0 ,
4305+ pattern.ids != nullptr ? pattern.ids ->ne [0 ] : 0 ,
4306+ down_w != nullptr ? down_w->ne [2 ] : 0 );
4307+ }
4308+
4309+ return 4 ;
4310+ }
4311+ }
4312+
4313+ if (node->op == GGML_OP_GLU && i + 1 < cgraph->n_nodes && cgraph->nodes [i + 1 ]->op == GGML_OP_MUL_MAT_ID ) {
4314+ static std::atomic<int > moe_whole_pattern_trace_count{0 };
4315+ const bool whole_trace = ggml_cuda_moe_whole_pattern_trace_take (moe_whole_pattern_trace_count);
4316+
4317+ if (whole_trace) {
4318+ const ggml_tensor * down = cgraph->nodes [i + 1 ];
4319+ const ggml_cuda_moe_whole_pattern pattern = ggml_cuda_moe_whole_pattern_detect (node, down);
4320+
4321+ const ggml_tensor * down_w = pattern.down != nullptr ? pattern.down ->src [0 ] : nullptr ;
4322+ const ggml_tensor * down_x = pattern.down != nullptr ? pattern.down ->src [1 ] : nullptr ;
4323+ fprintf (stderr,
4324+ " [LLAMA_MOE_WHOLE_PATTERN] supported=%d gate_up=%s gate=%s up=%s glu=%s down=%s ids=%s type=%s"
4325+ " n_tokens=%" PRId64 " n_used=%" PRId64 " experts=%" PRId64
4326+ " n_embd=%" PRId64 " n_ff=%" PRId64
4327+ " view_pair=%d ids_match=%d swiglu=%d\n " ,
4328+ pattern.supported ? 1 : 0 ,
4329+ ggml_cuda_moe_wp_trace_tensor_name (pattern.gate_up ),
4330+ ggml_cuda_moe_wp_trace_tensor_name (pattern.gate ),
4331+ ggml_cuda_moe_wp_trace_tensor_name (pattern.up ),
4332+ ggml_cuda_moe_wp_trace_tensor_name (pattern.glu ),
4333+ ggml_cuda_moe_wp_trace_tensor_name (pattern.down ),
4334+ ggml_cuda_moe_wp_trace_tensor_name (pattern.ids ),
4335+ down_w != nullptr ? ggml_type_name (down_w->type ) : " -" ,
4336+ down_x != nullptr ? down_x->ne [2 ] : 0 ,
4337+ pattern.ids != nullptr ? pattern.ids ->ne [0 ] : 0 ,
4338+ down_w != nullptr ? down_w->ne [2 ] : 0 ,
4339+ down_w != nullptr ? down_w->ne [1 ] : 0 ,
4340+ down_w != nullptr ? down_w->ne [0 ] : 0 ,
4341+ pattern.view_pair ? 1 : 0 ,
4342+ pattern.ids_match ? 1 : 0 ,
4343+ pattern.swiglu ? 1 : 0 );
4344+ }
4345+ }
4346+
40454347 // topk-moe
40464348 if (cgraph->nodes [i]->op == GGML_OP_UNARY || cgraph->nodes [i]->op == GGML_OP_SOFT_MAX ||
40474349 cgraph->nodes [i]->op == GGML_OP_ARGSORT ) {
0 commit comments