Skip to content

Commit 2fed6aa

Browse files
committed
test(paged): cover ragged MoE dispatch
Assisted-by: Claude:opus-4.8 [Claude Code] Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
1 parent a85c1e0 commit 2fed6aa

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

tests/test-backend-ops.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4615,6 +4615,115 @@ struct test_moe_weighted_combine : public test_case {
46154615
}
46164616
};
46174617

4618+
// Ragged 256-expert MoE dispatch gate for serving decode.
4619+
struct test_mul_mat_id_ragged_moe : public test_case {
4620+
const ggml_type type_a;
4621+
const int n_mats;
4622+
const int n_used;
4623+
const int64_t m;
4624+
const int64_t n;
4625+
const int64_t k;
4626+
4627+
std::string vars() override {
4628+
return VARS_TO_STR6(type_a, n_mats, n_used, m, n, k);
4629+
}
4630+
4631+
double max_nmse_err() override {
4632+
return 5e-4;
4633+
}
4634+
4635+
double max_nmse_err(ggml_backend_t backend) override {
4636+
if ((type_a == GGML_TYPE_MXFP4 || type_a == GGML_TYPE_NVFP4) && backend_has_feature(backend, "BLACKWELL_NATIVE_FP4")) {
4637+
return 2e-2;
4638+
}
4639+
return max_nmse_err();
4640+
}
4641+
4642+
uint64_t op_flops(ggml_tensor * t) override {
4643+
GGML_UNUSED(t);
4644+
return 2 * m * k * n * n_used;
4645+
}
4646+
4647+
test_mul_mat_id_ragged_moe(ggml_type type_a = GGML_TYPE_NVFP4, int n_mats = 256, int n_used = 8,
4648+
int64_t m = 768, int64_t n = 128, int64_t k = 2048)
4649+
: type_a(type_a), n_mats(n_mats), n_used(n_used), m(m), n(n), k(k) {
4650+
GGML_ASSERT(n_used <= n_mats);
4651+
}
4652+
4653+
ggml_tensor * build_graph(ggml_context * ctx) override {
4654+
ggml_tensor * as = ggml_new_tensor_3d(ctx, type_a, k, m, n_mats);
4655+
ggml_set_name(as, "as");
4656+
4657+
ggml_tensor * ids = ggml_new_tensor_2d(ctx, GGML_TYPE_I32, n_mats, n);
4658+
ggml_set_name(ids, "ids");
4659+
if (n_used != n_mats) {
4660+
ids = ggml_view_2d(ctx, ids, n_used, n, ids->nb[1], 0);
4661+
ggml_set_name(ids, "view_of_ids");
4662+
}
4663+
4664+
ggml_tensor * b = ggml_new_tensor_3d(ctx, GGML_TYPE_F32, k, n_used, n);
4665+
ggml_set_name(b, "b");
4666+
4667+
ggml_tensor * out = ggml_mul_mat_id(ctx, as, b, ids);
4668+
ggml_set_name(out, "out");
4669+
4670+
return out;
4671+
}
4672+
4673+
void initialize_tensors(ggml_context * ctx) override {
4674+
for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != nullptr; t = ggml_get_next_tensor(ctx, t)) {
4675+
if (ggml_is_view_op(t->op)) {
4676+
continue;
4677+
}
4678+
if (t->type != GGML_TYPE_I32) {
4679+
init_tensor_uniform(t);
4680+
continue;
4681+
}
4682+
4683+
std::vector<int32_t> data(t->ne[0]);
4684+
for (int64_t token = 0; token < ggml_nrows(t); ++token) {
4685+
for (int64_t r = 0; r < t->ne[0]; ++r) {
4686+
data[r] = (int32_t) ((token * 17 + r * 31) % n_mats);
4687+
}
4688+
4689+
if (n_used >= 8) {
4690+
// Skew rank 0 heavily to expert 0, exercise max expert id,
4691+
// leave many experts empty, and preserve unique top-k ids.
4692+
std::vector<bool> used(n_mats, false);
4693+
const int64_t seeds[8] = {
4694+
0,
4695+
1 + token % 4,
4696+
4 + (token * 3) % 8,
4697+
n_mats - 1,
4698+
token * 5 + 7,
4699+
token * 7 + 11,
4700+
token * 13 + 19,
4701+
token * 29 + 23,
4702+
};
4703+
4704+
for (int64_t r = 0; r < 8; ++r) {
4705+
int32_t id = (int32_t) (seeds[r] % n_mats);
4706+
while (used[id]) {
4707+
id = (id + 1) % n_mats;
4708+
}
4709+
data[r] = id;
4710+
used[id] = true;
4711+
}
4712+
}
4713+
4714+
ggml_backend_tensor_set(t, data.data(), token * t->nb[1], t->ne[0] * sizeof(int32_t));
4715+
}
4716+
}
4717+
}
4718+
4719+
bool run_whole_graph() override { return true; }
4720+
4721+
std::string op_desc(ggml_tensor * t) override {
4722+
GGML_UNUSED(t);
4723+
return "MUL_MAT_ID_RAGGED_MOE";
4724+
}
4725+
};
4726+
46184727
// GGML_OP_OUT_PROD
46194728
struct test_out_prod : public test_case {
46204729
const ggml_type type_a;
@@ -8941,6 +9050,15 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
89419050
test_cases.emplace_back(new test_moe_weighted_combine(GGML_TYPE_NVFP4, 128, 8, 768, n, 2048));
89429051
}
89439052

9053+
// [paged Phase 8] Ragged 256-expert MoE dispatch gate for live serving decode.
9054+
// Deterministic ids skew many tokens into a few hot experts, include expert 255,
9055+
// and leave many experts empty. n=1 covers single-token decode; n=257 crosses
9056+
// the MMVQ/MMID batch cutoff while preserving top-8 routing.
9057+
test_cases.emplace_back(new test_mul_mat_id_ragged_moe(GGML_TYPE_F32, 16, 8, 32, 8, 64));
9058+
for (int n : {1, 8, 33, 128, 257}) {
9059+
test_cases.emplace_back(new test_mul_mat_id_ragged_moe(GGML_TYPE_NVFP4, 256, 8, 768, n, 2048));
9060+
}
9061+
89449062
// [paged P0 / track B] NVFP4/MXFP4 dense decode-shape mmq_y-down bit-exact gate.
89459063
// The dense FP4 weight GEMM is the track-B target; P1 lowers mmq_y (the weight-row tile) on the
89469064
// NVFP4 decode path to raise resident-CTA occupancy. mmq_y is a pure N-row tiling knob, so a

0 commit comments

Comments
 (0)