Skip to content

Commit 1edddc8

Browse files
committed
feat(paged): whole-pattern MoE matcher + routed-FFN fused NVFP4-quant down MMQ
Add the routed-FFN fused-quant line for the paged NVFP4 MoE decode step, all default-off and md5-clean, gating a fused SwiGLU-to-NVFP4-quant plus a raw pre-quantized down-projection MMQ that skips the intermediate F32 materialize + re-quantize of the standard gate_up -> SwiGLU -> down chain. Pieces (all guarded, no effect unless explicitly enabled): - Whole-pattern MoE matcher + executor hook in ggml_cuda_try_fuse (LLAMA_MOE_WHOLE_PATTERN_EXEC and the *_TRACE/_EARLY_TRACE diagnostics). Detects the gate_up(MUL_MAT_ID) -> view/view -> SwiGLU(GLU) -> down(MUL_MAT_ID) sub-graph early in the fusion pass and, when engaged, runs the whole chain through a single executor instead of node-by-node. - Routed-FFN PoC scaffold ggml/src/ggml-cuda/moe-ffn.{cu,cuh} + a narrow hook (LLAMA_MOE_ROUTED_FFN_POC). ggml_cuda_compute_forward is de-static-ed so the executor translation unit can drive the standard op path for the fallback legs. The executor tries the fused-quant path first, else falls back to the stock compute_forward for glu + down (bit-identical to default). - Fused SwiGLU-to-NVFP4-quant + raw down MMQ (LLAMA_MOE_ROUTED_FFN_FUSED_QUANT): moe_swiglu_nvfp4_quant_kernel writes block_fp4_mmq activations directly, then ggml_cuda_mul_mat_q_moe_quantized (with the local ggml_cuda_mmq_ids_meta refactor of the expert-sorted ids/bounds prep) runs the down GEMM on the pre-quantized rows. Native FP4 (Blackwell) only; NVFP4 down weights only. Gated on GB10 (sm_121a), before/after this commit: - Canonical default-path greedy md5 unchanged: MoE q36-35b-a3b-nvfp4 8cb0ce23777bf55f92f63d0292c756b0, dense q36-27b-nvfp4 5951a5b4d624ce891e22ab5fca9bc439. - md5-clean opt-in: LLAMA_MOE_ROUTED_FFN_POC=1 LLAMA_MOE_ROUTED_FFN_FUSED_QUANT=1 keeps the MoE md5 byte-identical (8cb0ce23...). - test-backend-ops: MUL_MAT 1146/1146, MUL_MAT_ID 806/806, GATED_DELTA_NET 46/46, and the MoE sentinels MOE_SWIGLU_DOWN 7/7 + MUL_MAT_ID_RAGGED_MOE 6/6 pass both default and opt-in. Opt-in emits exactly six route=mmq_moe_quantized_raw markers with zero mmq_moe_sorted_raw launches (fused path provably engaged). - Serving effect is flat-to-slightly-positive and not a shipped default: decode agg 326.9 -> 332.7 t/s, mmq_nvfp4 6009 -> 5915 ms, aggregate flat (~/bench/phase135_routed_ffn_fused_quant_serving/20260702_082102). The rejected/neutral neighbours of this line (Phase133 sorted-F32 down, Phase134 fused-SWIGLU-only, Phase138 finalize/weighted-combine fusion, the W4A16 grouped-tile pack/tune/pad line, GPU-sort, boundary/layout/quant traces) are deliberately excluded and carry no markers in this tree. Assisted-by: Claude:opus-4.8 [Claude Code] Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
1 parent f1d976f commit 1edddc8

5 files changed

Lines changed: 796 additions & 1 deletion

File tree

ggml/src/ggml-cuda/ggml-cuda.cu

Lines changed: 303 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
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
40364232
static 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

Comments
 (0)