Skip to content

Ggml moe gathermatmul gpu#36932

Open
cavusmustafa wants to merge 3 commits into
openvinotoolkit:masterfrom
cavusmustafa:ggml-moe-gathermatmul-gpu
Open

Ggml moe gathermatmul gpu#36932
cavusmustafa wants to merge 3 commits into
openvinotoolkit:masterfrom
cavusmustafa:ggml-moe-gathermatmul-gpu

Conversation

@cavusmustafa

@cavusmustafa cavusmustafa commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Details:

  • Add a GPU-plugin MatcherPass (ov::intel_gpu::ConvertGgmlMoeGatherToGatherMatmul) that captures the ggml-openvino frontend's public-op MoE expert-matmul carrier — MatMul(activations, Reshape([Convert(]Gather(CompressedWeightsBlock[n_expert, m*k], ids)[)]), transpose_b=true) — and rewrites it to the internal ov::op::internal::GatherMatmul.
  • The matched block is rank-2 [n_expert, m*k], but GatherMatmul needs rank-3 [n_expert, N, K]; the pass rebuilds the block's low-bit weight / scale / (optional) zero-point Constants at the grouped rank-4 [n_expert, N, K/group, group] shape over the same data (a bit-identical reinterpret), reshapes to rank-3, and feeds it to GatherMatmul. The downstream stock ConvertGatherMatmulToGatherMatmulCompressed then folds it into a single GatherMatmulCompressed (gather + dequantize-selected-experts + matmul), keeping the low-bit weights compressed.
  • Registered inside the existing supports_immad && use_onednn MoE-compressed pipeline, after ConvertTiledMoeBlockToGatherMatmuls and before ConvertGatherMatmulToGatherMatmulCompressed. Matching a genuine CompressedWeightsBlock (u4/i4/u8/i8) both guarantees the downstream fold can fire and structurally excludes any non-compressed expert matmul.
  • The frontend needs no changes and keeps emitting public ops only: without this pass its rank-2 carrier folds to a plain GatherCompressed; with this pass it is upgraded to GatherMatmulCompressed. No env flag — the fusion is automatic when the plugin is present.
  • Change is purely additive: 2 new pass files + 1 registration line in transformations_pipeline.cpp (0 deletions to existing code).
  • Adds unit tests (convert_ggml_moe_gather_to_gathermatmul_test.cpp, 6 cases): fires on u4/u8 (with zero point) and symmetric i4 (no zero point); end-to-end folds to GatherMatmulCompressed with the u4 weight Constant surviving (weights stay compressed); negative cases confirm it does not fire on a non-compressed f16 expert Gather nor when transpose_b is false.
  • Measured on Arc B390 (gemma-4-26B-A4B Q4_K_M, llama-bench, warmup on): prefill ~1.8x (7.7 -> 13.6 t/s), decode ~1.2x (3.96 -> 4.81 t/s) vs. the non-fused rank-2 path.
image

Tickets:

AI Assistance:

  • AI assistance used: yes
  • The pass implementation, the unit test, and this description were developed with AI (Claude) assistance. Validation performed: built the GPU plugin and the ov_gpu_unit_tests target; the 6 new unit tests all pass; ran the GPU transformation test subset (*Transform*:*Convert*:*Fuse*:*Compress*:*MoE*:*GatherMatmul*:*FullyConnected*:*MatMul* -> 260 passed, 1 pre-existing skip, 0 failed) to confirm no regression; verified end-to-end on the real 26B model (dumped IR shows all 60 expert matmuls rewritten and folded to GatherMatmulCompressed, a before/after constant census confirms no expert weights are materialized), a live 26B GPU run completes (EXIT 0), and the throughput numbers above were measured directly.

cc: @wine99

The ggml-openvino frontend lowers the gemma-style top-k expert matmul
(MUL_MAT_ID) using public ops only, as a rank-2 carrier:

  Reshape( [Convert]( Gather( CompressedWeightsBlock[n_expert, m*k],
                              ids[n_tokens, n_used], axis=0 ) ) )
    -> MatMul(activations, ., transpose_b=true)

The frontend keeps the expert Gather on the rank-2 [n_expert, m*k] compressed
block on purpose: it folds to a plain GatherCompressed on any OpenVINO build
(weights stay compressed, no OOM), so the frontend is correct and memory-safe
with or without this pass.

When this pass is present (GPU systolic + oneDNN MoE pipeline) it does better:
it rewrites the carrier to the internal ov::op::internal::GatherMatmul so the
existing ConvertGatherMatmulToGatherMatmulCompressed folds it into a single
GatherMatmulCompressed (gather + dequantize-selected-experts + matmul in one op).
GatherMatmul needs a rank-3 [n_expert, N, K] weights block, so the pass rebuilds
the block's low-bit / scale / zero-point Constants at the grouped rank-4
[n_expert, N, K/group, group] shape (a bit-identical reinterpret of the same
data) and reshapes to rank-3.

Matching a genuine CompressedWeightsBlock as the Gather's source both guarantees
the downstream fold can fire and structurally excludes any non-compressed expert
matmul. Registered after ConvertTiledMoeBlockToGatherMatmuls and before
ConvertGatherMatmulToGatherMatmulCompressed.
Cover the ggml-openvino MoE expert-matmul fusion pass:
- fires on the rank-2 compressed carrier for u4/u8 (asymmetric, with zero point)
  and symmetric i4 (no zero point), rewriting the expert MatMul to GatherMatmul;
- end-to-end, the produced GatherMatmul folds to GatherMatmulCompressed and the
  low-bit weight Constant survives (weights stay compressed, no f32 materialization);
- negative: does NOT fire on a non-compressed f16 expert Gather, nor when the
  MatMul is not transpose_b.
@cavusmustafa
cavusmustafa marked this pull request as ready for review July 16, 2026 22:55
@cavusmustafa
cavusmustafa requested review from a team as code owners July 16, 2026 22:55
@github-actions github-actions Bot added the category: GPU OpenVINO GPU plugin label Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category: GPU OpenVINO GPU plugin

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant