A hot kernel that is an **int4 (W4A16 GPTQ/AWQ) or fp8 (blockscale) quantized GEMM / fused-MoE grouped GEMM** on MI300X (gfx942), where the live path is a Triton `@triton.jit` body (or a closed `.co`) and Triton body micro-opts have hit their ceiling. The Triton int4-unpack loop is latency-bound at ~15–20% HBM, so body-only tuning caps at ~1.1×; a full **FlyDSL rewrite** of the candidate reaches ~60–65% HBM at decode and ~3.6–6.2× over the Triton golden (speedup grows with M). This is the lever
Scanned 9/3/2026
Install to Claude Code
npx -y skills add AMD-AGI/GEAK --skill flydsl_rewrite_quantized_moe --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Flydsl Rewrite Quantized Moe?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/amd-agi-flydsl-rewrite-quantized-moe)More formats (shields.io, HTML) on the badges page.
---
id: flydsl_rewrite_quantized_moe
title: Rewrite quantized GEMM / fused-MoE kernels into FlyDSL (Triton->FlyDSL, int4 W4A16 / fp8 blockscale)
kind: expert_skill
authors:
- geak
scope: kernel
match:
operator: fused_moe_grouped_gemm
arch_class:
- '*'
gens:
- gfx942
dtypes:
- int4_w4a16
- fp8_e4m3_fnuz
regimes:
- prefill
- decode
from_backend: triton
to_backend: flydsl
profile_signature:
op_name_regex: ''
min_pct_gpu: 0.0
expects:
isolated_speedup_min: 1.5
e2e_delta_min_pct: 1.0
parity: required
validation:
status: validated
last_verified: '2026-06-17'
gpu: gfx942/MI300X
model: Kimi-K2.6-int4-W4A16
measured:
isolated: 3.576
e2e_pct: ''
parity: pass
artifact: '' # external validation artifact (machine-specific; not shipped in-repo)
role: advisory_prior
supersedes: []
---
## When to use
A hot kernel that is an **int4 (W4A16 GPTQ/AWQ) or fp8 (blockscale) quantized GEMM / fused-MoE grouped
GEMM** on MI300X (gfx942), where the live path is a Triton `@triton.jit` body (or a closed `.co`) and
Triton body micro-opts have hit their ceiling. The Triton int4-unpack loop is latency-bound at ~15–20%
HBM, so body-only tuning caps at ~1.1×; a full **FlyDSL rewrite** of the candidate reaches ~60–65% HBM at
decode and ~3.6–6.2× over the Triton golden (speedup grows with M). This is the lever when more
config/body tuning won't move the needle and the win requires a code-level authored core.
## Memory contract (MANDATORY — an isolated win that OOMs e2e is a REJECT, not a win)
The isolated bench has **no KV cache competing for HBM**, so a large stage-2/activation buffer looks free
there but starves the serving KV pool and OOMs at e2e — the single most common way a *verified* isolated
FlyDSL MoE win fails to transfer. (Observed on Kimi-K2.6 int4 TP=4/mem-frac 0.95: a +896 MiB stage-2
warmup transient did not fit → e2e **REJECT, 0 tok/s**, despite 1.85× isolated + parity PASS. The prior
+61.8% run at the SAME config succeeded only because its stage-2 output was `[M,hidden]`.)
- **Stage-2 output MUST be `[M, hidden]`** — fold the top-k `moe_sum` into the stage-2 **in-kernel
atomic/`reduce` accumulate** (`compile_moe_gemm2(accumulate=True)` or `compile_moe_gemm2_ex(mode=REDUCE)`).
- **DO NOT** ship the expanded `[M·top_k, hidden]` output — the `accumulate=False` + host-side `moe_sum` +
`A2[m]=A[m//top_k]` pre-gather path materialises ~`M·top_k·hidden·2 B` (~940 MiB at M=8192, top_k=8,
hidden=7168). **That transient IS the +896 MiB e2e OOM.** It is acceptable ONLY as a decode-only
(`M ≤ 64`) fast path, never for the prefill buckets that must serve at ISL≥2048.
- This is a **hard requirement, NOT an optional follow-up**: any candidate that will be applied back to
live serving (`apply_flydsl_moe_to_vllm`, learned card `flydsl-moe-applyback-gfx942`) must have its peak
footprint verified to fit the accepted `mem-fraction` BEFORE it can be claimed a win. Isolated speedup
alone is necessary but NOT sufficient.
## Mechanism
Triton's per-element int4 unpack + dequant loop streams weight bytes inefficiently and stalls latency-bound
(~15–20% HBM), which is why body micro-opts cap ~1.1×. A FlyDSL kernel maps the same math onto a tuned
`compile_*` MFMA primitive (aiter's SOTA DSL on gfx942) that feeds HBM/MFMA far better — 60–65% HBM at
decode, crossing to compute-bound (AI > ridge) at large M. The numeric key that makes the port exact: for
int4 W4A16, FlyDSL int4 is **symmetric** `w_signed·scale` with `w_signed∈[−8,7]`, and GPTQ no-zp is
`(uint4−8)·scale`; since `w_signed = uint4−8`, **folding the constant zp=8 into the weight makes the two
identical**. Per-group zero-points aren't expressible symmetrically, so they split out as a small additive
correction. The win transfers to any quantized grouped-GEMM whose math reconciles onto an existing FlyDSL
primitive.
## Procedure
Deliver the rewrite by **editing `candidate.py`** (a MODIFIABLE file): `make_candidate(inp)` builds +
`flyc.compile(...)`s the FlyDSL kernel once (untimed setup) and returns a zero-arg callable that only
launches it (timed). No `@triton.jit` kernel, no harness edits; golden + inputs are unchanged so
correctness is judged identically.
1. **Locate the target**: dispatch path, golden, harness. Triton kernel → golden = the installed-original
kernel (Triton-vs-Triton, identical inputs). `.co` kernel → golden = upstream fp32 PyTorch ref (see the
sibling `rewrite-co-kernel-to-flydsl` skill). Record exact dispatch signature, real shapes per regime
(decode small-M, prefill large-M), quant format (int4 W4A16 group_size? fp8 blockscale?), zero-point y/n.
2. **Deterministic correctness harness**: reuse the original input builder + golden, fixed seed. Oracle by
golden type — Triton-vs-Triton → `torch.allclose(rtol=atol=2e-2)` AND cosine ≈ 0.999994; fp32 ref →
cosine distance < 0.01 (NOT allclose). Mirror the 4-mode contract so the candidate is a drop-in.
3. **Same-session baseline**: measure original and candidate back-to-back in one process, best-of-3 ×
100-iter (CUDA events / `run_perftest`), min over repeats. `speedup = original_ms / candidate_ms`. Never
trust stale baselines (GPU drifts ~5% run-to-run).
4. **Map the math onto an existing FlyDSL primitive (the crux — do NOT write from scratch)**: closest
`compile_*` in `${FLYDSL_ROOT}/kernels/`.
- int4 W4A16 (GPTQ/AWQ) → `moe_gemm_2stage.compile_moe_gemm2` (`in_dtype="int4_bf16"`,
`group_size=32`); fold zp=8 into the weight (see Mechanism). Per-group has_zp → split
`(uint4−zp)·scale = (uint4−8)·scale + (8−zp)·scale` and add the correction as a small
`[tokens,G]×[E,G,N]` torch grouped-matmul (document as not-fully-fused).
- fp8 blockscale → `moe_blockscale_2stage.compile_moe_blockscale_gemm{1,2}`.
- plain grouped GEMM (no SiLU/reduce) → reuse the MoE down-stage as a generic GEMM: `kernel_topk=1`,
`kernel_tokens=M·top_k`, `doweight=True`, `accumulate=False`, pre-gather `A2[m]=A[m//top_k]`.
**⚠ For a real fused-MoE that will be applied back to serving, the `accumulate=False`+pre-gather
expansion violates the Memory contract above (→ +896 MiB e2e OOM). Use it only for the decode-only
(`M ≤ 64`) fast path; prefill MUST use the `accumulate=True`/`mode=REDUCE` `[M,hidden]` accumulate.**
- `mixed_moe_gemm_2stage` is **mxfp4** (symmetric `val·2^scale`), NOT GPTQ — don't use it for W4A16+zp.
5. **Wire layout + compile**: unpack packed int4 (even k = low nibble) → fold zp → int8 → `shuffle_weight`
→ `_pack_shuffled_int8_to_packed_int4_no_perm` (fp8: `shuffle_weight(w, layout=(16,16))`). Transpose
scales to the kernel's layout (GPTQ `[E,N,G]`→`[E,G,N]` f32). Preallocate all buffers, bind at
`flyc.compile`, reuse every call; use `torch.cuda.current_stream()` at call time (CUDA-graph safe); bump
an ABI tag in `module_name` when signatures change so the JIT cache doesn't serve stale binary. If aiter
is also imported, put the new FlyDSL first on `sys.path` and hide `flydsl` from `find_spec` *during*
`import aiter`, else aiter can silently drop its HIP ops. Self-bootstrap the import path at the top of
`candidate.py` from `${FLYDSL_ROOT}` (repo `kernels/`+`tests/`, py3.12 `build-fly/python_packages` — not
a stale py3.10 build); correctness must PASS (cosine ≈ 0.999994) BEFORE benchmarking.
6. **Validate + bench**: recompute the oracle on ALL regimes after every change (reject regressions), then
same-session bench decode (M=64) and prefill (M=2048…8192). Expect speedup to grow with M.
7. **Roofline with real HW counters**: `ProfilingAnalyzer(profiling_type="roofline")` (wraps
`rocprof-compute profile --roof-only` + `analyze -b 4`), parse `hbm_util_emp_pct`, `perf_gflops`,
`ai_hbm`, `bound`. Trust HW counters, not an analytic byte/FLOP model (analytic gave ~90% where rocprof
showed ~65%).
## Knobs & pitfalls
- `group_size` (int4) must match the checkpoint (GPTQ/AWQ commonly 32/128). `in_dtype="int4_bf16"` is the
symmetric path; the zp=8 fold is what makes no-zp GPTQ exact.
- The topk=1 generic-GEMM framing streams more weight bytes than `moe_align` → reported speedup is
**conservative** vs a fully-fused MoE.
- FlyDSL kernels need the **repo source** checkout (new `value_attrs` API), not just `pip install flydsl`;
a stale py3.10 build won't load under py3.12. **To obtain/build it, run the single-source-of-truth
bootstrap `perf_knowledge/expert_skills/skills/ensure_flydsl/ensure_flydsl.sh`** (version-gated
reuse-or-build PIN into container-internal `/opt/flydsl`, flock + hip-cmake/patchelf fixes) then
`source "${FLYDSL_ROOT:-/opt/flydsl/FlyDSL}/flydsl_env.sh"` — do NOT hand-roll clone/build here.
- ABI/JIT-cache staleness: bump `module_name` on signature change or you'll silently run an old binary.
## Do-no-harm notes
- Quote **same-session** ratios only; never a stale/hardcoded baseline (GPU drifts ~5%). A fast-but-wrong
candidate scores 0 — correctness gates every change.
- Per-group has_zp correction and one-time pre-gather may be untimed / torch-side — **state which**, and
that full fusion needs a FlyDSL kernel edit; don't claim a fully-fused number you didn't measure.
- Synthetic uniform routing overstates gains vs the decode-skewed real workload (ordering holds, absolute
is optimistic) — report it.
- When not triggered (non-quantized op, non-MoE/GEMM, or no FlyDSL primitive reconciles the math), the
skill is inert — the workflow falls back to the generic Triton path, no regression.
## Sources
- Recipe (canonical author copy in PerfSkills): `workflows/knowledge/rewrite_kernel_to_flydsl.md`
(PerfSkills `flydsl-rewrite` branch, commit `d7e8df1`); role directions in
`workflows/roles/engineer.md` (FlyDSL rewrite directions) and `workflows/roles/tech_lead.md`
(FlyDSL full-rewrite direction).
- Worked example — Triton→FlyDSL int4 **W4A16** GPTQ/AWQ fused-MoE (`fused_moe_kernel_gptq_awq`, Kimi-K2.6):
`${FLYDSL_EXAMPLES}/int4_w4a16_moe/` — `cand_flydsl_k1.py` (GPTQ→FlyDSL conversion + generic-GEMM trick),
`kernel/test_harness_flydsl.py`, `run_roofline_k1.py`, `DELIVERY_FLYDSL.md` (3.6–6.2× vs Triton golden).
- Worked example — `.co`→FlyDSL fp8 **blockscale** fused-MoE (`fmoe_fp8_blockscale_g1u1`, Qwen3.5-122B):
`${FLYDSL_EXAMPLES}/fp8_blockscale_moe/`; deep dive in sibling skill `rewrite-co-kernel-to-flydsl`.
- Related GEAK skill: `flydsl_fp8_gemm_playbook` (e2e down-proj bare-core bind) — this skill is its
kernel-scope, broader-operator counterpart.
- Validation eval dir (artifact): an external, machine-specific eval dir (not shipped in-repo) — K1 `fused_moe_kernel_gptq_awq`
(int4 W4A16, Kimi-K2.6, MI300X gfx942). Deployment-env same-session A/B: **geomean 3.62×** (5 UT cases),
5.15× prefill, 5/5 correctness PASS (cosine ≈ 0.999994) — `result_flydsl.json`, `DELIVERY_FLYDSL.md`,
rocprof roofline (FlyDSL 60–65% HBM vs Triton 13–18%) in `roofline_hw/`.
- Independent on-box reproduction (GEAK docker, py3.10 + FlyDSL `build-fly.py310stray`; golden reconstructed
from the verbatim local `kernel/kernel_jit.orig.py` + a pure-torch `moe_align` shim, no vLLM install):
5/5 correctness PASS, cosine 0.999994 (exact), candidate ms within ~1.6% of the recorded run; measured
**geomean 8.75×** against this docker's **triton 3.6.0** golden — `onbox_golden_ab.py` / `onbox_golden_ab.json`
/ `onbox_verify_geak.py` / `VALIDATION_STATUS.md`. The win is robust and direction-consistent across triton
versions; the *absolute* ratio depends on the Triton baseline, so 3.62× (deployment-representative) is the
conservative figure recorded in `validation.measured.isolated`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!