Repository navigation
[CK] [FlyDSL] gfx942 a16wmix a16w16: honour swiglu_limit for BF16 SiLU and SwiGLU - #6552
Conversation
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
One backend per PR: PR title tags & labels: |
|
P1 vs Triton (same exclusive node/image as the PR body). Still draft. TritonExperts does apply the clamp (same vs-clamped error as a16wmix to printed precision). UNTUNED a16wmix median µs vs TritonExperts (clamped) vs CK unclamped SOL (invalid): 279/201/222 (t=1), 280/217/228 (t=4), 284/216/232 (t=32), 317/219/235 (t=256). gfx942 has no K=32 bf16 MFMA; no tuned rows. Losing while untuned is not a lane verdict. No accuracy run (no checkpoint). FP8 Family A not in this PR. |
41e73a9 to
25ae6e3
Compare
|
Rebased onto current |
a601f57 to
5c98a2e
Compare
|
Rebased onto current
|
5c98a2e to
ed8b656
Compare
|
Rebased onto current
vLLM has said they will review vllm#61508 after this PR and #6335 are merged and in an aiter release. |
|
gfx942 stage-2 atomic: the Int32 byte offset wraps at 2^30 elements; suggest dropping
This commit does more than swap the atomic API: it also changes the address arithmetic. Before, the gfx942 branch offset a typed bf16 pointer by elements ( Example: M=131073, model_dim=8192 gives an output of 2,147,500,032 bytes. That is just over 2 GiB but still below the 4 GiB The kernel is shared, so this affects every gfx942 atomic stage 2 of the a16w-mix port, not only the new a16w16 path:
I'd also suggest not landing this commit as part of this PR:
Suggested fix: drop out_elems = global_typed_ptr(arg_out, T.bf16, align=4)
...
fx.atomic_add(
fx.recast_iter(out_elems.type, out_elems + out_off),
pk,
syncscope=fx.rocdl.SyncScope.Agent,
)If 0.3.2 support is really needed, please move it to a separate PR, widen the offset to Int64 before scaling, and validate the a16w4 / a16wi4 gfx942 paths there, ideally with a case whose output crosses 2 GiB (for example the M=131073, model_dim=8192 shape above). Side note (pre-existing, not introduced here): |
ed8b656 to
6bf3c53
Compare
|
@Bernard-Liu Dropped. gfx942 a16wmix stage 2 is That commit was only there so the kernel would also compile on FlyDSL 0.3.2. aiter pins Rebased onto current |
|
@Bernard-Liu The |
|
@coderfeli @xytpai You are on the requested-reviewer list for this fused_moe dispatch change ( Head |
|
This PR adds a new test file. Are all of these tests needed? If some of them are not necessary, could you drop them? Also, could the remaining tests go into an existing test script instead of a new one? |
|
@Bernard-Liu Dropped the extra file. The remaining cases are in Kept: FlyDSL name/f16 gate, FORCE_REDUCE stays atomic, reroute sentinels + arch, Dropped:
|
|
@Bernard-Liu @coderfeli Aiter Test Gate is red because Standard Test shards 0 and 1 died at Shards 2–7 passed, including |
|
@xytpai Follow-up: still blocked on a Re-run failed jobs of https://github.com/ROCm/aiter/actions/runs/41413362449 (shards 0/1 Docker Hub |
|
@coderfeli Afternoon ping: still need Re-run failed jobs on https://github.com/ROCm/aiter/actions/runs/41413362449 (shards 0/1 Docker Hub |
…our swiglu_limit. The kernel already clamped SwiGLU; compile/registry never emitted the bf16-W variant, so Cohere-style QuantType.No fell through to CK and dropped the bound. Untuned heuristic selects this port when a finite limit is set. gfx942 stage2 keeps main's fx.atomic_add. SiLU is unchanged: its epilogue still ignores the limit. Signed-off-by: jatseng-ai <jatseng@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Copy mixed_moe's clamp-then-silu_mul_batch into the a16wmix epilogue; do not put the clamp inside the shared helper. With the a16w16 dtype gate already open, a finite SiLU limit now routes to this kernel instead of CK. Co-authored-by: Cursor <cursoragent@cursor.com>
…16wmix. get_2stage_cfgs logged the CK/1-stage choice before the a16wmix return, so a limit-set bf16 call printed "using 2stage default" and then launched FlyDSL. Compute the reroute decision once, skip that line only when the reroute will return (it logs its own kernel names), and reuse the same flag for the return. Every other path logs exactly as before. Co-authored-by: Cursor <cursoragent@cursor.com>
gen_flydsl_2stages_task returned early for anything but per_1x32 fp4/fp8
weights, so no tuned row could name the bf16 a16w16 kernels the runtime
selects for BF16 QuantType.No with an active swiglu_limit.
- Admit BF16 QuantType.No SiLU/SwiGLU (g1u1, no doweight) and enumerate
get_flydsl_stage{1,2}_kernels_a16w16 through the existing a16w-mix branch.
- Time stage1 with waves_per_eu=None, which is what the runtime passes for
these registry entries (the tuner default of 3 would time another build).
- Stage2 data: build the sorted bf16 intermediate for a16w16 as for a16w4.
CK stage2 gets the same a2_qt/a2_scale as before.
- Post-process: CK also enumerates these keys, and a16w16 stage1 writes a
sorted intermediate CK stage2 cannot read. Add an a16w16 marker to the
dedup and stage1/stage2 merge keys (like v2) so pairs stay homogeneous and
dedup cannot leave only a mixed pair. Dropped before the CSV.
Co-authored-by: Cursor <cursoragent@cursor.com>
…oute. The bind tests ran at H=256 I=128 E=4 top-2. Add kernel tests at the per-rank shape the clamped server ran (32 tokens, t16 tile): - limit=7 vs clamped / unclamped references, with the bound shown reachable (routed gate and |up| > 2x limit) and limit=1e9 matching the unclamped ref; - gate -25 at limit 1.0 matches the asymmetric reference, not symmetric. Sentinels (None / 0.0 / +inf) now assert ck_moe_stage1 and run_1stage=False rather than only "not FlyDSL". Black-format the file. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
bf16 stage2 has no reduce epilogue, so those names atomic-scatter into an unzeroed buffer and fail accuracy.
BF16 stage2 has no reduce epilogue. Treating it as honoring the runtime override skipped the output zero-init and the atomic added into stale memory. Signed-off-by: jatseng-ai <jatseng@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Keep sentinels on CK with no AITER_* override. Reject a caller block_size_M that disagrees with the kernel tile before stage1 runs on the wrong layout. Register bf16 a16w16 names only, and give AOT a QuantType.No bf16 recipe so PREBUILD_KERNELS=1 survives the first tuned row. Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the extra test module. Keep the dispatch gates, one small-shape bind per activation, the Command A shape, and the host tile mismatch. Co-authored-by: Cursor <cursoragent@cursor.com>
f088069 to
a2e3318
Compare
|
Rebased onto current Conflict was |
|
Rebased head |
|
@Bernard-Liu thanks for the approval on Merge is still blocked on remaining requested reviews: @coderfeli @xytpai / TEAM_AITER. Required CI is green (Black/Ruff/deps + Aiter Test Gate, all eight Standard Test shards). This is a merge-commit PR (please do not squash — the dtype-gate vs SiLU-epilogue split is intentional). |
|
Morning ping: @coderfeli @xytpai / TEAM_AITER — Bernard approved overnight on |
Resolve conflicts with ROCm#6552 (a16wmix SiLU honours swiglu_limit), ROCm#7294 (gemm2 use_valid_token_count) and the a8w8 literal-file CI case: - gemm1 epilogue: plain silu keeps ROCm#6552's clamp_gate_up; the optional activation_bf16 rounding is applied before the activation. situ_params is now built for every activation, as on main. - gemm2 and flydsl_a16w4_gemm2: keep both reuse_lds and use_valid_token_count; their kernel-name tags stay distinct. - moe_kernels: keep the silu_clamped/activation_bf16 validation with main's bf16-weight comment. - aiter_test.sh: keep the pytest case for test_flydsl_moe_a16wfp4.py next to main's test_gemm_a8w8.py case. silu_clamped and ROCm#6552's clamped silu share the same clamp: gate is upper-bounded and up is clamped to [-limit, limit] before silu(gate) * up. silu_clamped additionally requires a positive limit and is the only mode that accepts activation_bf16. Assisted-by: GitHub Copilot Signed-off-by: yinfengLiu <199041580+LiuYinfeng01@users.noreply.github.com>
…942-fp8-mfma Conflicts in the a16wmix port, resolved by keeping both sides. The fp8 kernels are separate definitions, so two main changes are carried into them: - gemm1 (ROCm#6552): SiLU clamps before silu_mul_batch, so the fp8 kernel builds situ_params for every activation, as the bf16 kernel now does. The fp8 dequant still runs before the activation. - gemm2 (ROCm#7294): the fp8 kernel bounds scatter rows by min(cumsum[1], M) under use_valid_token_count, as the bf16 kernel does.
main gained the FlyDSL a16w16 path (#6552), the explicit clamped SiLU with an activation_bf16 stage-1 argument (#6995) and the mxfp4 a8w4 replacement (#6464), which were added in the same places as this branch's gfx942 a8w4 path: the kernel registry, the stage-1 signatures and the MoE tuner. Both sides are kept everywhere: the registry registers the _g942 and a16w16 names, stage 1 takes in_dtype, x_layout and activation_bf16, and the tuner picks candidates in the order a16w16, gfx942 a8w4, other, with the bf16-intermediate stage 2 for both a16w-mix and gfx942 a8w4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What this does
On gfx942, a BF16
QuantType.Nofused MoE with SiLU or SwiGLU and a setswiglu_limitlands on CK two-stage. CK has noswiglu_limitparameter, so the bound is silently dropped (AITERKER-247). This PR routes that case to the existing FlyDSL a16wmix port and makes the port apply the limit.There are two separable changes, in separate commits:
3ad2b21a4). The a16wmix port already handled bf16 A × {mxfp4, int4} W. This adds the a16w16 (bf16 W) registry, compile, and dispatch entries, plus the host heuristic_can_reroute_bf16_to_a16wmix. The port's SwiGLU epilogue already clamped, so SwiGLU is covered by this commit alone.f84139457). SiLU now runsclamp_gate_up, then the unchanged, unclampedsilu_mul_batch. The shared helper is deliberately not modified, because mixed_moe already clamps before calling it.The remaining commits are supporting:
a4ecba2c4: stop loggingusing 2stage defaultwhen the a16wmix reroute is about to return. That line named the CK choice being discarded. The reroute's ownuntuned fallback (kn1=..., kn2=...)warning names the kernel that actually runs. All other paths log as before.cb2ed1316: make the path tunable. See Tuner.db1a740c5: tests at the qualified shape, and a stricter sentinel test.7af3c2341:dict(...)→ literals in the test helpers (ruff 0.16 C408, which CI'sCheck Code Style with Ruffenforces).230d6b1c8: the tuner stops enumerating a16w16 stage-2*_reducekernels. bf16 stage 2 has no reduce epilogue, so those names atomic-scattered into an unzeroed buffer and failed accuracy (#6724).02cb2b718:AITER_FLYDSL_FORCE_REDUCE=1no longer treats bf16×bf16 as honoring the override (same as bf16×fp4/int4). Sorting still zero-inits; stage2 stays on the atomic epilogue.42e98fa14: gate the reroute to gfx942/gfx950, reject a mismatched callerblock_size_Mbefore stage1, register bf16 a16w16 names only, and give AOT aQuantType.Nobf16×bf16 recipe. NoAITER_*debug knob.a2e3318a9: fold the clamp/dispatch tests intoop_tests/test_moe_a16w16_default_dispatch.pyand drop the extra file.Clamp semantics, both activations: gate
min(g, L)(upper bound only), upclamp(u, -L, L), thensilu(g)*uor SwiGLU-OAIg*sigmoid(1.702g)*(u+1).Blast radius
The route is taken only when
swiglu_limitis positive and finite.None,0.0and+infkeep the existing CK two-stage path (ck_moe_stage1,run_1stage=False) and its numerics.0.0matters because vLLM's ROCm adapter sends it whenever the model config has no limit. Every existing caller, none of which sets a limit, is unaffected.The reroute also requires all of the following; otherwise behavior is unchanged:
get_gfx_runtime()); gfx90a and RDNA stay on CKQuantType.No, SiLU or SwiGLUuse_g1u1, preshuffled weights, no stage-1 bias, nodoweight_stage1model_dim % 128 == 0,inter_dim % 128 == 0A tuned row whose
kernelName1starts withflydsl_is respected. A CK or asm row is overridden only when the limit is active, because neither can clamp.There is no
AITER_*override. To time the same a16wmix kernel without a binding clamp, pass a huge finite limit (1e9); sentinels stay on CK. A callerblock_size_Mthat disagrees with the selected tile is rejected at the host. Registry/AOT/tuner emit bf16 a16w16 names only.Evidence
Kernel accuracy and routing were measured on gfx942, 304 CU, FlyDSL 0.4.1, torch 2.14.1+rocm7.2. The earlier server-side clamp-bind numbers used image
vllm/vllm-openai-rocm:v0.30.0.Kernel selected inside a vLLM server (Command A geometry, TP8, AITER MoE on,
swiglu_limit=7, no tuned rows):run_1stagekernelName1_flydsl_stage1_wrapperflydsl_moe1_abf16_wbf16_bf16_t16x128x128_flydsl_stage1_wrapperflydsl_moe1_abf16_wbf16_bf16_t32x128x128This covers both decode and prefill. With the limit unset (adapter sends
0.0), every one of those padded counts tookck_moe_stage1.The clamp binds in the server. Weights were scaled so the pre-activation gate is in the hundreds against a bound of 7. Layer-0 routed-MoE output absmax was 12–17 with the clamp, versus 1728–2352 on the unset (CK) server.
Kernel level, at the server's per-rank shape (H=4096, I=512, E=128, top-8, limit 7). The table gives max-abs error against each reference:
Asymmetry. Gate fixed at −25 with limit 1.0: the error is
0.021against the asymmetric reference and20.7against a symmetric clamp. The negative side is not floored.This PR's tests (
op_tests/test_moe_a16w16_default_dispatch.pya16w16 clamp cases, 17 passed). The server-shape tests (32 tokens, t16 tile) report:0.64against the clamped reference, whose own absmax is 59.5. The error against the unclamped reference is3014.1e9gives a different output from limit 7, and matches the unclamped reference to26.9(that reference's absmax is 3037).0.0024against the asymmetric reference,1.07against symmetric.Kernel latency, same box, head
42e98fa14(kernel unchanged ina2e3318a9). gfx942 304 CU, torch 2.14.1+rocm7.2, HIP 7.2.53211,AITER_BYPASS_TUNE_CONFIG=1, CUDA-event median of 50 iters after 10 warmup. Command A per-rank shape (H=4096, I=512, E=128, top-8, SiLU). Arms: CK (swiglu_limit=0.0, the pre-patch path) vs a16wmix with the clamp (7.0) vs the same a16wmix kernel with a no-op limit (1e9).The clamp itself is free (L=7 matches L=1e9 to 1%). Decode (t=1/4) is 15–28% slower than CK on the untuned t16 fallback. t=32 matches CK. Prefill (t=4096) is 1.62× CK because CK uses
block_m=128while the fallback is still t32; that is a tile/heuristic gap, not clamp cost. No tuned a16w16 CSV row is in this PR.Tuner
Before this PR,
TUNE_ONLY=flydslreturned nothing for BF16QuantType.No, becausegen_flydsl_2stages_taskadmitted onlyper_1x32fp4/fp8 weights. No tuned row could name this kernel.flydslv2is the fused-quant layout-v2 family (per_1x32fp4/fp8 only), so it does not cover bf16 either.cb2ed1316does four things:get_flydsl_stage{1,2}_kernels_a16w16through the existing a16w-mix branch. That is 84 candidates (76 stage 1, 8 stage 2) at the Command A shape, against 0 before. It was 92 until230d6b1c8dropped the 8 stage-2*_reducenames.waves_per_eu=None, which is what the runtime passes for these entries. The tuner's default of 3 would time a different build.a16w16marker now sits in the dedup and join keys, likev2, and is dropped before the CSV.To check enumeration, a regex-limited tuner run on one Command A row produced a complete row naming
flydsl_moe1_abf16_wbf16_bf16_t16x128x128+..._t16x128x128_atomic(errors 0.0% / 0.6%). With CK candidates included atblock_m=32, the row it picked was a homogeneous pair. This was not a sweep, and no tuned CSV row is committed.Tuning notes:
swiglu_limit. The tuning key has no limit column, so one tuned row serves clamped and unclamped callers of the same shape. If a CK row wins, the runtime still overrides it with the untuned a16wmix fallback when a limit is active. To get a tuned a16wmix row for a clamped model, tune withTUNE_ONLY=flydsl.--run_configcan check the clamp end to end. SetAITER_MXFP4_TUNE_SWIGLU_LIMIT(despite the name it applies to all quant types). The check only means something if its inputs exceed the bound.Known limitations
tuned_fmoe.csvrow iscu_num=80. The server takes the fallback tile shown above. Prefill 1.62× vs CK is that fallback, not the clamp.fused_moeCUDA-event time, CK vs this path. A TPOT comparison against TritonExperts is not in this PR.main. Max abs difference is3.6e-7–9.5e-7, about one bf16 ulp. A second launch of the same#6552binary against the first differs by the same amount (2.4e-7–9.5e-7). This is CK stage-1 run-to-run nondeterminism, not the clamp leaking into the sentinel path. Routing is unchanged: sentinels selectck_moe_stage1withrun_1stage=False, now asserted by the tests.fused_moe_gfx942.py), the asm 1-stage path for non-g1u1 bf16, and other CK paths. #6335 is the fail-loud guard for those.Base and relation to #6335
Ten commits on
upstream/mainatf1965cd26. Rebase was clean (no conflict hunks). None of #6335's commits are in this branch, so it can merge on its own. gfx942 a16wmix stage 2 is unchanged frommain:fx.atomic_addon a typed bf16 pointer. Thellvm.AtomicRMWOpcommit was dropped after review. It scaled the byte offset in Int32, and it existed only for FlyDSL 0.3.2, which this repo does not pin. #6335's guard already treats_flydsl_stage1_wrapperas clamp-applying, so it does not raise on this route.If both land, the second must drop its copy of
_swiglu_limit_is_active. Both PRs define the function identically and the texts merge without conflict, but ruff F811 (redefinition) would fail.Test plan
op_tests/test_moe_a16w16_default_dispatch.pya16w16 clamp/dispatch cases on gfx942 / 304 CU: 17 passed (folded out of a dedicated file). Routing, sentinels, gfx90a stays on CK, block_size_M mismatch, SiLU + SwiGLU bind at 4 tokens, Command A shape bind and asymmetry.QuantType.No(0 → 84 a16w16 candidates; asm andflydslv20), plus a regex-limited end-to-end tuner runblack26.10 andruff0.16.0 (the versions CI pins) clean on changed filesPR Self-Review
Reviewed revision
a2e3318
Behavior and intent
CK two-stage silently drops
swiglu_limitfor BF16QuantType.NoSiLU/SwiGLU. This PR opens the a16wmix a16w16 (bf16×bf16) dtype gate and clamps SiLU in that port so a finite limit binds. Sentinels (None/0.0/+inf) stay on CK, so existing unclamped callers keep the previous path.Validation and unverified scope
python -m pytest op_tests/test_moe_a16w16_default_dispatch.py -k 'not ck_default and not qwen_mtp and not gfx950'on gfx942 304 CU, heada2e3318a9: 17 passed.fused_moetiming at H=4096 I=512 E=128 top-8 SiLU,AITER_BYPASS_TUNE_CONFIG=1: clamp vs no-op a16wmix is 1.00×; vs CK the untuned fallback is 1.00× at 32 tokens and 1.62× at 4096 tokens (table in Evidence).PR-specific question
The changed kernel path is selected only when all of these hold: live
get_gfx_runtime()is gfx942 or gfx950, bf16×bf16QuantType.No, SiLU or SwiGLU, g1u1, preshuffled weights, no stage-1 bias, nodoweight_stage1, no pad,model_dimandinter_dimdivisible by 128, andswiglu_limitpositive and finite._can_reroute_bf16_to_a16wmixreturns false otherwise, so gfx90a/RDNA, sentinels, and every existing unclamped caller stay on CK. A callerblock_size_Mthat disagrees with the a16w16 tile is rejected before launch. Registry, tuner, and AOT emit bf16 names only.Changed op tests
op_tests/test_moe_a16w16_default_dispatch.pyalready covers CK a16w16 default dispatch. The clamp/reroute cases live there now (no extra file).test_moe_2stage.pyis a CLI harness and_effective_swiglu_limitreturns None for QuantType.No, so it cannot catch a silent CK drop of the bound, a gfx90a compile-path leak, an f16 name alias, or ablock_size_Mvs tile mismatch. Dropped: silu_mul_batch source grep, cartesian sentinel×activation, tokens=256 bind, and the small-shape asymmetric duplicate of the Command A case.