Skip to content

[FlyDSL] [CI] megamoe tune kimi-k3 - #6259

Merged
Yaowu-Xiong merged 2 commits into
mainfrom
xyw/megamoe_tune_k3
Oct 10, 2026
Merged

Yaowu-Xiong merged 2 commits into
mainfrom
xyw/megamoe_tune_k3

Conversation

@Yaowu-Xiong

@Yaowu-Xiong Yaowu-Xiong commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Add Kimi-K3 A8W4 support to the FlyDSL MegaMoE V2 operator.

Kimi-K3 is a wide MoE — 112 experts/rank (896 global), model_dim=3584,
inter_dim=3072, topk=16 — and uses the SiTUv2 activation
(situ(g)=β·tanh(g/β)·σ(g), up=linear_β·tanh(u/linear_β), β=4.0,
linear_β=25.0). The generic MegaMoE config (sized for 32-expert ranks, SiLU
only) leaves Kimi-K3 badly under-fed on the dispatch side and cannot express its
activation, so a dedicated config + a situv2 epilogue are required. This change
also reconciles the Kimi-K3 path with the current mainline MegaMoE kernels.

Technical Details

  • SiTUv2 activation baked into the Stage1 GEMM1 epilogue (SiluQuantEpilogue._act_mul), keyed into the kernel name so it never collides with the SiLU cache.
  • Stage2 b2stage pipeline enabled for bs ≥ 256 (BM64+BN256 for 256–2048, BN256 for ≥4096); neutral for small buckets so bs ≤ 128 keep plain BN256.
  • Kimi-K3 dispatch tuning: num_dispatch_cu = 96/160 (vs generic 32) across the buckets, plus persist_cu and persist_strided for the large buckets.
  • fp8 blockwise P2P quant on the Stage2 combine wire, optional Stage1 hardware bounds-checking (AITER_MEGA_MOE_BOUNDS_CHECK, off by default), and a ready-order tile-readiness fix on the compact dispatch path (wait + acquire before reading per-tile metadata, previously skipped on the ready-order path).
  • Scope is A8W4 ; the a_dtype/out_dtype plumbing is retained so A4W4 can be wired back without re-threading signatures.

Test Plan

op_tests/multigpu_tests/test_mega_moe_v2.py --network kimi_k3_route --quant a8w4
on gfx950 (MI355X), EP8, seed 123, uniform routing, mtpr=8192 for
bs ≤ 8192 and mtpr=bs for bs > 8192, bs 1…32768:

  • Correctness: relL2 of the fused op vs an fp32 reference (checked for bs ≤ 128).
  • Perf A — K3 tuning value: tuned config vs the pre-tuning generic baseline (runs_kimi_tune, same session, uniform).
  • Perf B — fused vs serial: MegaMoE V2 vs a mori serial reference (EpDispatchCombine dispatch + grouped expert GEMM + combine, 3 separate launches) via bench_mega_moe_v2.py.

Test Result

Correctness — relL2 ≈ 0.0569 across all checked buckets (bs 1…128), graph-replay PASS, p2p_quant=fp8_blockwise_1x32.

Perf A — Kimi-K3 tuning vs pre-tuning baseline (uniform, mtpr=8192, inter_dim=3072, e2e ms):

bs baseline (pre-tune) tuned gain
1 0.2518 0.2270 +9.8%
8 0.4591 0.4250 +7.4%
32 0.5983 0.5501 +8.1%
64 0.6602 0.5914 +10.4%
128 0.7221 0.6345 +12.1%
256 0.8667 0.7454 +14.0%
512 1.1917 0.9994 +16.1%
1024 1.6813 1.4401 +14.3%
2048 2.7403 2.2803 +16.8%
4096 4.3079 3.8401 +10.9%
8192 8.3756 7.1067 +15.2%

The dominant lever is the dispatch-CU widening (K3's 112-expert dispatch is
starved at the generic num_dispatch_cu=32); the Stage2 b2stage pipeline adds
a further +6–16% Stage2 / +2.4–5% e2e at bs ≥ 256 (on/off A/B, same tree).
Reconciling with mainline left Kimi-K3 e2e unchanged-to-faster across all buckets
(−0.2…−3.0% vs the pre-rebase branch, bs 1…32768).

Submission Checklist

@Yaowu-Xiong
Yaowu-Xiong requested a review from a team October 8, 2026 10:34
@github-actions github-actions Bot changed the title megamoe tune kimi-k3 [FlyDSL] [CI] megamoe tune kimi-k3 Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every PR:

  • ✅ Pre-checks (submodule verification, code formatting)
  • ✅ Aiter op tests (gfx942 + gfx950)
  • ✅ Triton tests on MI35X (only when aiter/ops/triton/** or related paths are changed)

Extended tests (opt-in via labels):

Label Tests
ci:gfx1250-ffm-triton Run the five-shard gfx1250 FFM Triton test suite
ci:triton-300x Run an additional Triton test job on MI300X in PRs (added automatically when gfx942 configs change); main branch always runs both MI35X and MI300X
ci:triton-355 Run the full Triton test suite on MI35X, not only the tests the change affects
ci:multi-gpu Aiter multi-GPU tests on the 8-GPU runner
ci:sglang SGLang integration tests: DeepSeek-R1-MXFP4 accuracy, Qwen 3.5 accuracy
ci:atom ATOM benchmark: DeepSeek-R1-0528, GPT-OSS-120B
ci:atom_full ATOM accuracy suite for PR and main models from ATOM models_accuracy.json
ci:vllm vLLM benchmark: GPT-OSS-120B, DeepSeek-R1-0528, Kimi-K2.5
ci:all All standard extended tests (excludes ci:atom_full)

Only add ci:atom_full for FlyDSL or Triton upgrades.
Add labels via the sidebar or gh pr edit 6259 --add-label <label>

One backend per PR:
A PR changes one kernel backend: [Triton/Gluon] (Triton and Gluon count as one), [HIP], [ASM], [CK], [OPUS] or [FlyDSL]. If the title ends up with two backend tags, split the PR -- as stacked pull requests when one part cannot merge without the other.

PR title tags & labels:
Component tags ([Triton/Gluon], [HIP], [CK], [ASM], ...) are added to the PR title and as PR labels automatically from the changed files and re-synced on every push — change-type tags like [fix]/[Perf], op tags like [MLA], and human labels (ci:*) are left untouched. Add the no-auto-title label to stop the title rewrites; labels stay in sync either way.

@Yaowu-Xiong Yaowu-Xiong added the ci:multi-gpu Trigger multi-GPU op tests on PR label Oct 9, 2026
@zufayu
zufayu requested a review from yadaish October 9, 2026 01:55
Reconcile the Kimi-K3 (112 experts/rank) A8W4 path with mainline MegaMoE V2:
- SiTUv2 activation baked into the Stage1 GEMM1 epilogue
- Stage2 b2stage pipeline (enabled for bs>=256) with deep_a plumbing
- Kimi-K3 config tuning: dispatch CU 96/160, persist_cu, BN256/BM64
- fp8 blockwise P2P quant and optional Stage1 bounds checking
- ready-order tile-readiness fix on the compact dispatch path

Scope is A8W4 (FP8 activation, FP4 weight); the a_dtype/out_dtype
plumbing is retained so A4W4 can be wired back later.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add test_mega_moe_v2.py to skip_tests and remove its now-dead case block
(v4_pro + kimi_k3_route a8w4 coverage).

Co-authored-by: Cursor <cursoragent@cursor.com>
@Yaowu-Xiong Yaowu-Xiong removed the ci:multi-gpu Trigger multi-GPU op tests on PR label Oct 10, 2026
@Yaowu-Xiong
Yaowu-Xiong merged commit 882ea79 into main Oct 10, 2026
@Yaowu-Xiong
Yaowu-Xiong deleted the xyw/megamoe_tune_k3 branch October 10, 2026 08:06
Duyi-Wang added a commit to Duyi-Wang/aiter that referenced this pull request Oct 10, 2026
…eduler block

ROCm#6259 fixed a race on the compact ready-order path (a consumer could
read per-tile metadata before the planner's store was visible) by making every
compact work item run tid0 wait(tile_ready) + barrier + fence_system_acquire.
On the ready-order path that is a second barrier + system acquire per work
item, on top of the ones the scheduler block already does after popping the
ready queue.

Do the tile_ready wait in the scheduler block instead (tid0, right after
reading the queued tile, before the existing barrier and system acquire), and
keep the per-work-item wait only for the sequential path. The ordering
guarantee is unchanged: every thread's system acquire still follows tid0
observing tile_ready for the tile it is about to read.

DSv4-Pro EP8 a8w4, MTPR 8192, 392 experts / topk 7, skewed routing:
1024 tok/rank 1334.7 -> 1271.7 us (41630d0: 1269.4 us); 2x skew
64/256/1024 tok/rank +4.6/+5.8/+6.1% -> -1.3/+0.3/-0.6% vs 41630d0.
Uniform routing unchanged; outputs identical. test_mega_moe_v2.py
(v4_pro compact/fixed-slot/uneven ranks/burst 2000, kimi_k3_route incl.
--force-fanout-boundary + burst 2000) passes.
AMD-yanfeiwang added a commit to AMD-yanfeiwang/aiter that referenced this pull request Oct 12, 2026
ROCm#6259 fixed a race on the compact ready-order path (a consumer could
read per-tile metadata before the planner's store was visible) by making every
compact work item run tid0 wait(tile_ready) + barrier + fence_system_acquire.
On the ready-order path that is a second barrier + system acquire per work
item, on top of the ones the scheduler block already does after popping the
ready queue.

Do the tile_ready wait in the scheduler block instead (tid0, right after
reading the queued tile, before the existing barrier and system acquire), and
keep the per-work-item wait only for the sequential path. The ordering
guarantee is unchanged: every thread's system acquire still follows tid0
observing tile_ready for the tile it is about to read.

DSv4-Pro EP8 a8w4, MTPR 8192, 392 experts / topk 7, skewed routing:
1024 tok/rank 1334.7 -> 1271.7 us (41630d0: 1269.4 us); 2x skew
64/256/1024 tok/rank +4.6/+5.8/+6.1% -> -1.3/+0.3/-0.6% vs 41630d0.
Uniform routing unchanged; outputs identical. test_mega_moe_v2.py
(v4_pro compact/fixed-slot/uneven ranks/burst 2000, kimi_k3_route incl.
--force-fanout-boundary + burst 2000) passes.

Co-authored-by: Duyi-Wang <duyi.wang@amd.com>
Duyi-Wang added a commit to Duyi-Wang/sglang that referenced this pull request Oct 12, 2026
…iter#6259 skew regression

ROCm/aiter#6259 fixed a race on MegaMoEV2 stage1's compact ready-order path by
running tid0 wait(tile_ready) + barrier + fence_system_acquire for every compact
work item.  The ready-order scheduler block already does a barrier + system
acquire after popping the ready queue, so under skewed routing (ready-order is
selected when a hot expert holds >= 25% of the tiles) DSv4-Pro EP8 a8w4 prefill
pays a second round per work item and is ~5-6% slower (1024 tok/rank).

Carry the fix (Duyi-Wang/aiter a5d49321ac: do the tile wait in the scheduler
block, before its barrier + acquire; same ordering guarantee, bit-identical
output) in-tree until it is upstream:

* layers/moe/aiter_mega_moe_intree/mega_moe_stage1.py: aiter's stage1 module
  with the fix, absolute aiter imports, an `_sglrof` kernel-name suffix and a
  closed-over cache tag (copy version + sha of the aiter MegaMoE sources it
  compiles against), so it never shares a FlyDSL JIT/AOT cache entry with
  aiter's kernels.
* install_mega_moe_stage1_fix(), called from _import_aiter_megamoe() before the
  first MegaMoEV2 is built, replaces the five stage1 entry points of aiter's
  module.  It only patches when aiter's mega_moe_stage1.py has the exact sha256
  of the unfixed file (ROCm/aiter b0c4209d4d .. 02575f1c3a); if aiter has the fix
  or predates sgl-project#6259 it is a no-op, if aiter changed the file otherwise it warns
  and leaves aiter alone.
* SGLANG_AITER_INTREE_MEGA_STAGE1_FIX (default 1); 0 = pinned aiter as is.

TODO(aiter#6259): remove once the regression fix is in the aiter pin.
Duyi-Wang added a commit to Duyi-Wang/sglang that referenced this pull request Oct 12, 2026
…iter#6259 skew regression

ROCm/aiter#6259 fixed a race on MegaMoEV2 stage1's compact ready-order path by
running tid0 wait(tile_ready) + barrier + fence_system_acquire for every compact
work item.  The ready-order scheduler block already does a barrier + system
acquire after popping the ready queue, so under skewed routing (ready-order is
selected when a hot expert holds >= 25% of the tiles) DSv4-Pro EP8 a8w4 prefill
pays a second round per work item and is ~5-6% slower (1024 tok/rank).

Carry the fix (Duyi-Wang/aiter a5d49321ac: do the tile wait in the scheduler
block, before its barrier + acquire; same ordering guarantee, bit-identical
output) in-tree until it is upstream:

* layers/moe/aiter_mega_moe_intree/mega_moe_stage1.py: aiter's stage1 module
  with the fix, absolute aiter imports, an `_sglrof` kernel-name suffix and a
  closed-over cache tag (copy version + sha of the aiter MegaMoE sources it
  compiles against), so it never shares a FlyDSL JIT/AOT cache entry with
  aiter's kernels.
* install_mega_moe_stage1_fix(), called from _import_aiter_megamoe() before the
  first MegaMoEV2 is built, replaces the five stage1 entry points of aiter's
  module.  It only patches when aiter's mega_moe_stage1.py has the exact sha256
  of the unfixed file (ROCm/aiter b0c4209d4d .. 02575f1c3a); if aiter has the fix
  or predates sgl-project#6259 it is a no-op, if aiter changed the file otherwise it warns
  and leaves aiter alone.
* SGLANG_AITER_INTREE_MEGA_STAGE1_FIX (default 1); 0 = pinned aiter as is.

TODO(aiter#6259): remove once the regression fix is in the aiter pin.
coderfeli pushed a commit that referenced this pull request Oct 13, 2026
…he scheduler block (#6431)

* MegaMoE stage1: fold the ready-order tile wait into the scheduler block

#6259 fixed a race on the compact ready-order path (a consumer could
read per-tile metadata before the planner's store was visible) by making every
compact work item run tid0 wait(tile_ready) + barrier + fence_system_acquire.
On the ready-order path that is a second barrier + system acquire per work
item, on top of the ones the scheduler block already does after popping the
ready queue.

Do the tile_ready wait in the scheduler block instead (tid0, right after
reading the queued tile, before the existing barrier and system acquire), and
keep the per-work-item wait only for the sequential path. The ordering
guarantee is unchanged: every thread's system acquire still follows tid0
observing tile_ready for the tile it is about to read.

DSv4-Pro EP8 a8w4, MTPR 8192, 392 experts / topk 7, skewed routing:
1024 tok/rank 1334.7 -> 1271.7 us (41630d0: 1269.4 us); 2x skew
64/256/1024 tok/rank +4.6/+5.8/+6.1% -> -1.3/+0.3/-0.6% vs 41630d0.
Uniform routing unchanged; outputs identical. test_mega_moe_v2.py
(v4_pro compact/fixed-slot/uneven ranks/burst 2000, kimi_k3_route incl.
--force-fanout-boundary + burst 2000) passes.

Co-authored-by: Duyi-Wang <duyi.wang@amd.com>

* MegaMoE stage1: trim the ready-order wait comments

---------

Co-authored-by: Duyi-Wang <duyi.wang@amd.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants