Add an opt-in a4w4 SiTUv2 MoE path and fix three SiTUv2 tuner defects - #4463
Merged
Conversation
Contributor
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
|
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an opt-in SiTUv2 MoE activation-quantization route (a4w4) and aligns the SiTUv2 tuner’s reference numerics with the kernel launch by consistently forwarding situ_beta / situ_linear_beta.
Changes:
- Adds Kimi-K3 SiTUv2 beta parameters to both FlyDSL stage1 launches and torch reference calls in the MoE tuner.
- Updates
fused_moe_dispatch to prioritize SiTUv2 activation routing (including newAITER_SITUV2_A4W4) ahead of the Swiglu/INTERLEAVE branch. - Adds new Kimi-K3 a4w4 tuned/untuned CSV configs for MoE 2-stage dispatch.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| csrc/ck_gemm_moe_2stages_codegen/gemm_moe_tune.py | Threads SiTUv2 beta parameters into tuner reference + kernel launch to avoid mismatched activation functions. |
| aiter/fused_moe.py | Adds opt-in a4w4 SiTUv2 activation dtype routing and adjusts dispatch ordering so SiTUv2 isn’t claimed by the INTERLEAVE branch. |
| aiter/configs/model_configs/kimik3_a4w4_untuned_fmoe.csv | Introduces untuned rows for the new a4w4 SiTUv2 path. |
| aiter/configs/model_configs/kimik3_a4w4_tuned_fmoe.csv | Introduces tuned kernel selections/latencies for the new a4w4 SiTUv2 path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
XiaobingSuper
force-pushed
the
k3/situv2-a4w4-moe
branch
from
July 30, 2026 14:08
ff648bd to
00b0059
Compare
1 task
zufayu
self-requested a review
July 31, 2026 01:18
XiaobingSuper
force-pushed
the
k3/situv2-a4w4-moe
branch
from
July 31, 2026 02:02
00b0059 to
72728fd
Compare
lalala-sh
reviewed
Jul 31, 2026
SiTUv2 (Kimi-K3) only had a16w4 and a8w4. Add a4w4 behind AITER_SITUV2_A4W4=1,
alongside the existing AITER_SITUV2_A8W4, with its own tuned config. The
default stays a16w4, so nothing changes unless the flag is set. Select SiTUv2
before the Swiglu/INTERLEAVE branch so that branch cannot claim it and pick the
activation dtype itself.
Tuning this shape needed three fixes in the tuner, each of which silently
mis-scored candidates rather than failing loudly:
1. situ_beta / situ_linear_beta were passed to neither the torch reference nor
the kernel, so torch_moe_stage1 used its (2.0, 1.5) defaults while the
FlyDSL kernels used (1.0, 1.0) -- the two sides were evaluating different
activations. Every SiTUv2 stage1 candidate came back at ~35% err and was
dropped by --errRatio, leaving stage1 with zero valid candidates and
aborting with "stage1 and stage2 should be valid together". Pass Kimi-K3's
(4.0, 25.0) to both; err1 goes 33.4% -> 0.0%. This also applies to the
existing a16w4 config, whose err1 column reads ~34.7%.
2. s1_compare_fn was only set for fp8, so fused fp4 stage1 fell back to the
generic comparison, which reads a 1-byte dtype as raw bytes. fp4x2 packs two
e2m1 codes per byte, so that rejected candidates which were >99% code
identical -- all 4352 fused a4w4 candidates scored 100% err and none was
ever selected.
3. cosine_diff_compare starts with ref.double(), and fp4x2.double() faults the
queue (HIP unspecified launch failure) -- a 64-element tensor is enough.
Enabling it for fp4 in (2) therefore crashed workers and restarted the pool
instead of scoring anything. Decode the nibbles into their represented
values instead, which also avoids comparing two packed codes as one byte.
Also skip k_batch / k_wave splits that do not divide the K axis at enumeration
time. compile_mixed_moe_gemm1 rejects them anyway, but only after dispatch, and
on some shapes the launch faults the queue rather than raising.
kimik3_a4w4_{un,}tuned_fmoe.csv cover the TP8 shape (3584 x 384, 896 experts,
topk 16) over 17 token tiers, 1 through 32768. All 17 select the fused stage1
kernel and err1/err2 are <=0.3% everywhere. The 32768 tier matters because
get_padded_M maps everything in (16384, 32768] onto that key, and the tier
fallback only triggers above it, so without the row those batches drop to the
hardcoded heuristic.
Against the existing configs (us1+us2, us):
token a16w4 a8w4 a4w4 vs a16 vs a8
1 28.3 20.1 20.0 +29.2% +0.4%
16 107.3 93.0 91.2 +15.0% +1.9%
256 374.7 329.8 325.3 +13.2% +1.4%
2048 651.3 475.8 428.2 +34.2% +10.0%
16384 3600.3 2074.8 1716.5 +52.3% +17.3%
Small tokens are weight-bandwidth bound and the weights are fp4 either way, so
a4w4 and a8w4 land together there; the gap opens up once the A traffic and the
MFMA rate start to matter.
Verified on gfx950 TP8: all 17 tiers hit the tuned rows at runtime with no
heuristic fallback, and GSM8K 3-shot under the CI settings gives
0.9484 / 0.9484 / 0.9522 (baseline 0.95, threshold 0.94).
XiaobingSuper
force-pushed
the
k3/situv2-a4w4-moe
branch
from
July 31, 2026 04:06
72728fd to
86308bc
Compare
lalala-sh
previously approved these changes
Jul 31, 2026
1 task
Mirror fused_moe's AITER_SITUV2_A4W4 routing in the test helper and route SiTUv2 through the existing CSV runtime-mode skip so kimik3_a4w4 tuned rows are not validated against the default a16w4 dispatch in CI. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep AITER_SITUV2_A4W4 routing only in _runtime_situv2_mxfp4_q_dtype_a, matching how AITER_SITUV2_A8W4 was handled originally. Co-authored-by: Cursor <cursoragent@cursor.com>
Route Situv2 through _runtime_situv2_mxfp4_q_dtype_a in _iter_csv_cases so opt-in a4w4/a8w4 tuned rows are not validated against the default a16w4 dispatch, without overloading the Swiglu runtime helper. Co-authored-by: Cursor <cursoragent@cursor.com>
_runtime_situv2_mxfp4_q_dtype_a checked gate_mode == INTERLEAVE before the
AITER_SITUV2_A8W4 / AITER_SITUV2_A4W4 opt-ins, mirroring the Swiglu helper it
was derived from. fused_moe tests SiTUv2 ahead of the Swiglu/INTERLEAVE branch
precisely so that branch cannot claim it, so the two disagreed whenever the
inferred gate mode was INTERLEAVE: the helper answered bf16/fp8 while dispatch
selected fp4x2, and _iter_csv_cases dropped those rows as a mode mismatch. The
opt-in a4w4/a8w4 rows were then skipped instead of validated, and CI stayed
green without having exercised them.
Test SiTUv2 first, as fused_moe does. gate_mode and token no longer enter into
it, so drop both parameters -- gate_mode is doubly unwelcome here because it is
inferred from the dtype layout rather than read from the CSV.
Checked against fused_moe's logic over
{default, A4W4=1, A8W4=1} x {SEPARATED, INTERLEAVE} x {token 64, 4096}:
4 of 12 combinations disagreed before, 0 after.
The branch-order fix in 674f144 started running SiTUv2 rows under gate_mode=INTERLEAVE, which had never executed before. All 32 a16w4 rows fail by ~99%: _effective_gate_mode requests INTERLEAVE while get_flydsl_stage1_kernels binds gate_mode="separated" for every non-fp8 activation. Forcing SEPARATED does not help either, so leave these rows out rather than claim a16w4 is validated; the a8w4 and a4w4 coverage the ordering fix is for is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
XiaobingSuper
force-pushed
the
k3/situv2-a4w4-moe
branch
from
August 3, 2026 02:01
f91c336 to
9f14f0c
Compare
lalala-sh
approved these changes
Aug 3, 2026
yanboshao
added a commit
that referenced
this pull request
Aug 4, 2026
…to updated main Reapply the gfx1250 fused-MoE ep_scatter feature (combine_mode="scatter_fused": gemm2's TDM epilogue P2P-writes each route-weighted output row into peers' comb_inp, so combine just sums -- no gather-reduce) onto the updated origin/main, which meanwhile landed #4482 (real SiTUv2 + >512-expert chunked psum scan), #4527 (LDS API refactor: lds_*_raw -> make_lds_copy_ops) and #4463 (fused_moe SiTUv2 path). Squashed to a single commit. Per-file resolution: - dispatch_combine_v2/*: vendored cco-LSA v2 intranode dispatch/combine op-layer. - mxfp4_preshuffle_gfx1250_tdm.py: on upstream's make_lds_copy_ops LDS API + SiTUv2 epilogue, add the TDM gather-store ep epilogue (tdm_scatter with in-kernel global_view/lds_view). tdm_scatter is vendored locally (tdm_gather_shim.py, on the stock FlyDSL wheel's low-level TDM intrinsics) so this branch needs no FlyDSL-side patch; route weight hoisted per wm row (_wf_rows). - moe_contiguous_psum.py: adopt upstream's chunked scan (E>512 correct) for the non-EP remap; keep the multi-block grid-stride remap + ep_rowmap kernels for EP. - grouped_moe_gfx1250.py: keep upstream SiTUv2 (stage1_act=3, situ_beta); add the ep_scatter dispatch wiring (ep_rowmap build, _ep_gemm2_kwargs, ep_scatter return). - batched_gemm_mxfp4.py / fused_moe.py: thread both situ_* and ep_* params. - tuned_grouped_fmoe.csv: tuning points (99 rows). Dropped only the full-subtile PF prefetch pipeline (da8d794): loaders and lds_addr_keepalive stay at upstream. lalala-sh's ds-read hoist is kept. gfx1250-only; not run in this environment. Compile-verify the LDS-API migration + ep/SiTUv2 epilogue and re-run test_mega_moe --combine on hardware. Co-authored-by: lalala-sh <Jiaxing.Wen@amd.com> Co-authored-by: zhimding <zhimding@amd.com>
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a third activation dtype for SiTUv2 (Kimi-K3) MoE — a4w4, behind
AITER_SITUV2_A4W4=1— next to the existingAITER_SITUV2_A8W4. The default is unchanged (a16w4), so this is inert unless the flag is set.The SiTUv2 test is hoisted above the Swiglu/INTERLEAVE branch so that branch cannot claim it and pick the activation dtype itself. All existing a16w4 routing and configs are left untouched.
Performance
TP8 shape (3584 x 384, 896 experts, topk 16), tuner
us1 + us2:a4w4 is ahead of a16w4 at every tier. Against a8w4 the small tiers land together — they are weight-bandwidth bound and the weights are fp4 either way, so the activation dtype only moves a few KB against tens of MB of weights — and the gap opens up once the A traffic and the MFMA rate start to matter.
Numbers are isolated kernel timings from the tuner, not end-to-end throughput.
Three tuner defects
Each of these mis-scored candidates silently rather than failing loudly, and each had to be fixed before this shape could be tuned at all.
1. SiTUv2 activation params never reached either side.
torch_moe_stage1fell back to its(2.0, 1.5)defaults while the FlyDSL kernels used(1.0, 1.0)— reference and kernel were evaluating different activation functions. Every SiTUv2 stage1 candidate came back at ~35% err, all were dropped by--errRatio, and the run aborted with:Passing Kimi-K3's
(4.0, 25.0)to both takes err1 from 33.4% → 0.0%. This is not a4w4-specific: the existingkimik3_fp4_tuned_fmoe.csv(a16w4) carrieserr1 ~34.7%for the same reason.2.
s1_compare_fnwas only set for fp8. Fused fp4 stage1 fell through to the generic comparison, which reads a 1-byte dtype as raw bytes.fp4x2packs two e2m1 codes per byte, so a low-nibble difference moves the byte by 1 and a high-nibble one by 16. All 4352 fused a4w4 candidates scored 100% err and none was ever selected — the tuner had been picking non-fused stage1 for a4w4 and paying for a separate quant kernel.3.
cosine_diff_comparefaults the GPU on fp4x2. It starts withref.double(), and64 elements is enough. Enabling that comparison for fp4 in (2) therefore crashed workers and restarted the pool instead of scoring anything. Fixed by decoding the nibbles into their represented values, which also avoids the packed-byte comparison from (2).
Also: skip
k_batch/k_wavesplits that do not divide the K axis at enumeration time.compile_mixed_moe_gemm1rejects them anyway, but only after dispatch, and on some shapes the launch faults the queue rather than raising.Config
kimik3_a4w4_{un,}tuned_fmoe.csv, 17 token tiers (1 … 32768). All 17 select the fused stage1 kernel (previously 0, because of defect 2), anderr1/err2are ≤0.3% everywhere.The 32768 tier is included because
get_padded_Mmaps everything in(16384, 32768]onto that key, and the tier fallback only fires strictly above it — without the row those batches silently drop to the hardcoded heuristic.Validation
gfx950 TP8. All 17 token tiers hit the tuned rows at runtime, and the server log shows the fused (
_fp4) kernel names on every tier with no heuristic fallback.GSM8K 3-shot under the CI settings (baseline 0.95, threshold 0.94), three runs: 0.9484 / 0.9484 / 0.9522.