Repository navigation
[FlyDSL] [CI] megamoe tune kimi-k3 - #6259
Conversation
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
One backend per PR: PR title tags & labels: |
1eed482 to
07cced7
Compare
07cced7 to
6513fa1
Compare
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>
6513fa1 to
578d8b8
Compare
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>
…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.
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>
…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.
…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.
…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>
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
SiluQuantEpilogue._act_mul), keyed into the kernel name so it never collides with the SiLU cache.b2stagepipeline enabled forbs ≥ 256(BM64+BN256 for 256–2048, BN256 for ≥4096); neutral for small buckets sobs ≤ 128keep plain BN256.num_dispatch_cu = 96/160(vs generic 32) across the buckets, pluspersist_cuandpersist_stridedfor the large buckets.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).a_dtype/out_dtypeplumbing 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 a8w4on gfx950 (MI355X), EP8, seed 123, uniform routing,
mtpr=8192forbs ≤ 8192andmtpr=bsforbs > 8192, bs 1…32768:bs ≤ 128).runs_kimi_tune, same session, uniform).bench_mega_moe_v2.py.Test Result
Correctness — relL2 ≈
0.0569across 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):
The dominant lever is the dispatch-CU widening (K3's 112-expert dispatch is
starved at the generic
num_dispatch_cu=32); the Stage2b2stagepipeline addsa 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