Skip to content
KernelIndex
Search⌘K

submission 682060

Aniket Sadashiva · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

No package. Vendor the mirrored source: 945 lines, June 9 Researcher Reciprocity License v1.0.

submission_v706.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-amd-moe-mxfp4-682060?include=source"
interfacepython
Compatibility
measured onAMD Instinct MI355X
declared hardwareAMD Instinct MI355X
architecturesgfx950
dtypesbf16, fp32, fp8_e8m0, int32, mxfp4

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
AMD MXFP4 MoEsuite of 7 cases
AMD Instinct MI355X
122.9µs
#50 of 782
2026-03-31

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:9da7da05eaba8a0d3df90718e5ff1ad015553b736150de42d21ab71141cbe442
license declaredunknown
license concludedunknown
authorsAniket Sadashiva
imported2026-08-15

Techniques

Extracted from the mirrored source by pattern, never inferred. Each row cites its line.

fp4if b_dtype == "fp4":
split-kactivation=ActivationType.Silu, split_k=1, dtype=torch.bfloat16,
tile-n = 32BLOCK_SIZE_M, BLOCK_SIZE_N = 32, 8

Kernel source

submission_v706.py945 lines
"""v706"""
import functools
import inspect
import os
import sys
import traceback
import torch
import triton
_dsv3_path = "/home/runner/aiter/aiter/configs/model_configs/dsv3_fp4_tuned_fmoe.csv"
_flydsl_s3_stage2 = "flydsl_moe2_afp4_wfp4_bf16_t64x256x256_reduce"
try:
    with open(_dsv3_path, "r") as f:
        lines = f.readlines()
    header = lines[0].strip()
    modified_lines = [header + "\n"]
    for line in lines[1:]:
        stripped = line.strip()
        if not stripped:
            continue
        fields = stripped.split(",")
        try:
            token_val = int(fields[1])
            expert_val = int(fields[4])
        except (ValueError, IndexError):
            modified_lines.append(line)
            continue
        if expert_val == 257 and token_val <= 128:
            fields[14] = "2"
            modified_lines.append(",".join(fields) + "\n")
        elif expert_val == 257 and token_val == 512:
            flydsl_fields = list(fields)
            flydsl_fields[19] = _flydsl_s3_stage2
            flydsl_fields[20] = "0.1%"
            if len(flydsl_fields) > 25:
                flydsl_fields[25] = ""
            modified_lines.append(",".join(flydsl_fields) + "\n")
            fallback_fields = list(fields)
            if len(fallback_fields) > 25:
                fallback_fields[25] = "flydsl_fallback"
            else:
                fallback_fields.append("flydsl_fallback")
            modified_lines.append(",".join(fallback_fields) + "\n")
        else:
            modified_lines.append(line)
    with open(_dsv3_path, "w") as f:
        f.writelines(modified_lines)
except Exception as e:
    print(f"[v]{e}", file=sys.stderr)
_csv_header = (
    "cu_num,token,model_dim,inter_dim,expert,topk,act_type,dtype,q_dtype_a,"
    "q_dtype_w,q_type,use_g1u1,doweight_stage1,block_m,ksplit,us1,kernelName1,"
    "err1,us2,kernelName2,err2,us,run_1stage,tflops,bw,_tag"
)
_common = (
    "ActivationType.Silu,torch.bfloat16,torch.float4_e2m1fn_x2,"
    "torch.float4_e2m1fn_x2,QuantType.per_1x32,1,0"
)
_k1_512 = (
    "moe_ck2stages_gemm1_256x32x128x128_1x4_MulABScaleShuffled_v3_"
    "Nswizzle0_Quant3_MulRoutedWeight0_silu_FP4X2_FP4X2_B16"
)
_k2_512 = (
    "moe_ck2stages_gemm2_64x32x32x128_1x1_MulABScaleExpertWeightShuffled_v1_"
    "Nswizzle0_Quant3_MulRoutedWeight1_FP4X2_FP4X2_B16"
)
_k1_2048 = (
    "moe_ck2stages_gemm1_256x128x128x128_1x4_MulABScaleShuffled_v3_"
    "Nswizzle0_Quant3_MulRoutedWeight0_silu_FP4X2_FP4X2_B16"
)
_k2_2048 = (
    "moe_ck2stages_gemm2_256x128x128x128_1x4_MulABScaleExpertWeightShuffled_v3_"
    "Nswizzle0_Quant3_MulRoutedWeight1_FP4X2_FP4X2_B16"
)
_k2_512_flydsl = "flydsl_moe2_afp4_wfp4_bf16_t64x256x256_reduce"
_k2_2048_flydsl = "flydsl_moe2_afp4_wfp4_bf16_t32x128x256_atomic"
_csv_rows = [
    f"256,512,7168,512,33,9,{_common},32,0,0,{_k1_512},0.0%,90.0,{_k2_512_flydsl},0.1%,219.79,0,781.78,2884.18,",
    f"256,512,7168,512,33,9,{_common},32,0,0,{_k1_512},0.0%,0,{_k2_512},0.0%,129.79,0,781.78,2884.18,flydsl_fallback",
    f"256,512,7168,2048,33,9,{_common},64,0,0,{_k1_2048},0.0%,180.0,{_k2_2048_flydsl},0.1%,455.08,0,1475.47,5323.27,",
    f"256,512,7168,2048,33,9,{_common},128,0,0,{_k1_2048},0.0%,0,{_k2_2048},0.0%,275.08,0,1475.47,5323.27,flydsl_fallback",
]
try:
    e33_path = "/home/runner/aiter/aiter/configs/model_configs/e33_fp4_tuned_fmoe.csv"
    with open(e33_path, "w") as f:
        f.write(_csv_header + "\n")
        for row in _csv_rows:
            f.write(row + "\n")
except Exception:
    pass
os.environ["AITER_USE_OPUS_MOE_SORTING"] = "0"
os.environ["AITER_USE_NT"] = "1"
os.environ.pop("FLIR_CK_LDS128", None)
os.environ["FLIR_MOE_STAGE1_SCHED"] = "1"
os.environ["FLIR_MOE_STAGE2_SCHED"] = "1"
os.environ["FLIR_MOE_STAGE2_PERSIST_M"] = "1"
import base64
import zlib
_FLYDSL_PATCH_BLOBS = {
    '/home/runner/aiter/aiter/ops/flydsl/moe_kernels.py': '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',
    '/home/runner/aiter/aiter/ops/flydsl/kernels/mixed_moe_gemm_2stage.py': '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',
}
try:
    for _patch_path, _patch_blob in _FLYDSL_PATCH_BLOBS.items():
        with open(_patch_path, 'wb') as _f:
            _f.write(zlib.decompress(base64.b64decode(_patch_blob)))
except Exception as e:
    print(f"[v]{e}", file=sys.stderr)
from task import input_t, output_t
from aiter import ActivationType, QuantType, dtypes
import aiter
import aiter.fused_moe as fused_moe_mod
import aiter.ops.flydsl.moe_kernels as flydsl_moe_mod
from aiter.ops.triton.quant.fused_mxfp4_quant import _fused_dynamic_mxfp4_quant_moe_sort_kernel
from aiter.ops.flydsl.moe_kernels import flydsl_moe_stage1, flydsl_moe_stage2
def _call_supported_kwargs(fn, **kwargs):
    try:
        params = inspect.signature(fn).parameters
    except (TypeError, ValueError):
        return fn(**kwargs)
    return fn(**{k: v for k, v in kwargs.items() if k in params})
def _install_stage1_sorted_scale_compat():
    if "sorted_scale" in inspect.signature(flydsl_moe_mod._get_compiled_stage1).parameters:
        return
    _orig_compile_stage1 = flydsl_moe_mod.compile_flydsl_moe_stage1
    def _compat_compile_flydsl_moe_stage1(
        model_dim,
        inter_dim,
        experts,
        topk,
        tile_m,
        tile_n,
        tile_k,
        doweight_stage1,
        a_dtype,
        b_dtype,
        out_dtype,
        lds128=None,
        use_scheduler=None,
        sorted_scale=False,
    ):
        if b_dtype == "fp4":
            from aiter.ops.flydsl.kernels.mixed_moe_gemm_2stage import compile_mixed_moe_gemm1
            return _call_supported_kwargs(
                compile_mixed_moe_gemm1,
                model_dim=model_dim,
                inter_dim=inter_dim,
                experts=experts,
                topk=topk,
                tile_m=tile_m,
                tile_n=tile_n,
                tile_k=tile_k,
                doweight_stage1=doweight_stage1,
                a_dtype=a_dtype,
                b_dtype=b_dtype,
                out_dtype=out_dtype,
                use_cshuffle_epilog=(out_dtype == "fp8"),
                lds128=lds128,
                use_scheduler=use_scheduler,
                sorted_scale=sorted_scale,
            )
        return _call_supported_kwargs(
            _orig_compile_stage1,
            model_dim=model_dim,
            inter_dim=inter_dim,
            experts=experts,
            topk=topk,
            tile_m=tile_m,
            tile_n=tile_n,
            tile_k=tile_k,
            doweight_stage1=doweight_stage1,
            a_dtype=a_dtype,
            b_dtype=b_dtype,
            out_dtype=out_dtype,
            lds128=lds128,
            use_scheduler=use_scheduler,
        )
    @functools.cache
    def _compat_get_compiled_stage1(
        model_dim,
        inter_dim,
        experts,
        topk,
        tile_m,
        tile_n,
        tile_k,
        doweight,
        a_dtype,
        b_dtype,
        out_dtype,
        lds128=None,
        use_scheduler=None,
        sorted_scale=False,
    ):
        exe = _compat_compile_flydsl_moe_stage1(
            model_dim=model_dim,
            inter_dim=inter_dim,
            experts=experts,
            topk=topk,
            tile_m=tile_m,
            tile_n=tile_n,
            tile_k=tile_k,
            doweight_stage1=doweight,
            a_dtype=a_dtype,
            b_dtype=b_dtype,
            out_dtype=out_dtype,
            lds128=lds128,
            use_scheduler=use_scheduler,
            sorted_scale=sorted_scale,
        )
        is_fp4 = b_dtype == "fp4"
        _n_in = inter_dim * 2 if is_fp4 else inter_dim
        _k_in = model_dim
        def tensor_api(
            out,
            out_scale,
            a,
            w,
            a_scale,
            w_scale,
            sorted_ids,
            sorted_expert_ids,
            topk_weights,
            num_valid_ids,
            token_num,
            size_expert_ids_in,
        ):
            if is_fp4:
                empty_bias = torch.empty(0, device=a.device, dtype=torch.float32)
                exe(
                    out,
                    out_scale,
                    a,
                    w,
                    a_scale,
                    w_scale,
                    sorted_ids,
                    sorted_expert_ids,
                    topk_weights,
                    num_valid_ids,
                    empty_bias,
                    token_num,
                    _n_in,
                    _k_in,
                    size_expert_ids_in,
                    0,
                )
            else:
                exe(
                    out,
                    out_scale,
                    a,
                    w,
                    a_scale,
                    w_scale,
                    sorted_ids,
                    sorted_expert_ids,
                    topk_weights,
                    num_valid_ids,
                    token_num,
                    _n_in,
                    _k_in,
                    size_expert_ids_in,
                )
        return tensor_api
    flydsl_moe_mod.compile_flydsl_moe_stage1 = _compat_compile_flydsl_moe_stage1
    flydsl_moe_mod._get_compiled_stage1 = _compat_get_compiled_stage1
_install_stage1_sorted_scale_compat()
_SORT_BUFS = {}
def _cached_moe_sorting_impl(
    topk_ids, topk_weights, num_experts, model_dim, moebuf_dtype,
    block_size, expert_mask, num_local_tokens, dispatch_policy, use_opus,
):
    device = topk_ids.device
    M, topk = topk_ids.shape
    key = (M, num_experts, block_size, model_dim)
    if key not in _SORT_BUFS:
        max_num_tokens_padded = int(M * topk + num_experts * block_size - topk)
        max_num_m_blocks = int(
            (max_num_tokens_padded + block_size - 1) // block_size
        )
        _SORT_BUFS[key] = (
            torch.empty(max_num_tokens_padded, dtype=dtypes.i32, device=device),
            torch.empty(max_num_tokens_padded, dtype=dtypes.fp32, device=device),
            torch.empty(max_num_m_blocks, dtype=dtypes.i32, device=device),
            torch.empty(2, dtype=dtypes.i32, device=device),
            torch.empty((M, model_dim), dtype=moebuf_dtype, device=device),
        )
    sid, sw, sei, nvi, mb = _SORT_BUFS[key]
    fwd = aiter.moe_sorting_opus_fwd if use_opus else aiter.moe_sorting_fwd
    fwd(
        topk_ids, topk_weights, sid, sw, sei, nvi, mb,
        num_experts, int(block_size), expert_mask, num_local_tokens,
        dispatch_policy,
    )
    return sid, sw, sei, nvi, mb
fused_moe_mod._moe_sorting_impl = _cached_moe_sorting_impl
_ORIGINAL_GET_2STAGE_CFGS = fused_moe_mod.get_2stage_cfgs
_CKTILE_BUFS = {}
def _cached_cktile_moe_stage1(
    hidden_states, w1, w2,
    sorted_token_ids, sorted_expert_ids, num_valid_ids,
    out, topk, block_m,
    a1_scale, w1_scale, sorted_weights=None,
    n_pad_zeros=0, k_pad_zeros=0, bias1=None,
    activation=ActivationType.Silu, split_k=1, dtype=torch.bfloat16,
):
    token_num = hidden_states.shape[0]
    _, n1, k1 = w1.shape
    _, k2, n2 = w2.shape
    D = n2 if k2 == k1 else n2 * 2
    if w1.dtype is torch.uint32:
        D = D * 8
    buf_key = (token_num, topk, D, w1.shape[1], split_k, hidden_states.device)
    if buf_key not in _CKTILE_BUFS:
        _CKTILE_BUFS[buf_key] = (
            torch.empty((token_num, topk, D), dtype=dtype, device=hidden_states.device),
            torch.zeros(
                (token_num, topk, w1.shape[1]), dtype=hidden_states.dtype,
                device=hidden_states.device,
            ) if split_k > 1 else None,
        )
    out_buf, tmp_buf = _CKTILE_BUFS[buf_key]
    if split_k > 1:
        tmp_buf.zero_()
        aiter.moe_cktile2stages_gemm1(
            hidden_states, w1, tmp_buf,
            sorted_token_ids, sorted_expert_ids, num_valid_ids,
            topk, n_pad_zeros, k_pad_zeros,
            sorted_weights, a1_scale, w1_scale, bias1,
            activation, block_m, split_k,
        )
        aiter.silu_and_mul(out_buf, tmp_buf)
    else:
        aiter.moe_cktile2stages_gemm1(
            hidden_states, w1, out_buf,
            sorted_token_ids, sorted_expert_ids, num_valid_ids,
            topk, n_pad_zeros, k_pad_zeros,
            sorted_weights, a1_scale, w1_scale, bias1,
            activation, block_m, split_k,
        )
    return out_buf
def _make_cktile_metadata(hidden_pad, intermediate_pad, use_g1u1, activation, split_k=2):
    return fused_moe_mod.MOEMetadata(
        functools.partial(
            _cached_cktile_moe_stage1,
            n_pad_zeros=intermediate_pad // 64 * 64 * (2 if use_g1u1 else 1),
            k_pad_zeros=hidden_pad // 128 * 128,
            activation=activation,
            split_k=split_k,
        ),
        functools.partial(
            fused_moe_mod.cktile_moe_stage2,
            n_pad_zeros=hidden_pad // 64 * 64,
            k_pad_zeros=intermediate_pad // 128 * 128,
            activation=activation,
        ),
        16, split_k, False, False, True,
    )
@functools.lru_cache(maxsize=2048)
def _patched_get_2stage_cfgs(
    token, model_dim, inter_dim, expert, topk, dtype,
    q_dtype_a, q_dtype_w, q_type, use_g1u1, activation,
    doweight_stage1, hidden_pad, intermediate_pad, is_shuffled=True,
):
    if model_dim==7168 and topk==9 and dtype==dtypes.bf16 and q_dtype_a==q_dtype_w==dtypes.fp4x2 and q_type==QuantType.per_1x32 and use_g1u1 and is_shuffled and not doweight_stage1 and ((expert==33 and inter_dim==512 and token<=128) or (expert==257 and inter_dim==256 and token in (16,128))):return _make_cktile_metadata(hidden_pad,intermediate_pad,use_g1u1,activation,4 if token==128 else 2)
    return _ORIGINAL_GET_2STAGE_CFGS(
        token, model_dim, inter_dim, expert, topk, dtype,
        q_dtype_a, q_dtype_w, q_type, use_g1u1, activation,
        doweight_stage1, hidden_pad, intermediate_pad, is_shuffled,
    )
fused_moe_mod.get_2stage_cfgs=_patched_get_2stage_cfgs
_DIRECT_BUFS={}
def _get_split_k(E, M, inter_dim): return E==33 and inter_dim==512 and M==128
def _direct_cktile_pipeline(
    hidden_states, w1, w2, w1_scale, w2_scale,
    topk_ids, topk_weights, E, M, topk, model_dim,
    inter_dim, hidden_pad, intermediate_pad, split_k,
):
    block_m = 16
    sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
        topk_ids, topk_weights, E, model_dim, torch.bfloat16,
        block_m, None, None, 0, True,
    )
    _, n1, k1 = w1.shape
    _, k2, n2 = w2.shape
    D = n2 if k2 == k1 else n2 * 2
    if w1.dtype is torch.uint32:
        D = D * 8
    n_pad1 = intermediate_pad // 64 * 64 * 2
    k_pad1 = hidden_pad // 128 * 128
    n_pad2 = hidden_pad // 64 * 64
    k_pad2 = intermediate_pad // 128 * 128
    buf_key = (M, topk, D, w1.shape[1], split_k)
    if buf_key not in _DIRECT_BUFS:
        dev = hidden_states.device
        out = torch.empty((M, topk, D), dtype=torch.bfloat16, device=dev)
        tmp = (
            torch.zeros((M, topk, w1.shape[1]), dtype=torch.bfloat16, device=dev)
            if split_k > 1 else None
        )
        _DIRECT_BUFS[buf_key] = (out, tmp)
    out_buf, tmp_buf = _DIRECT_BUFS[buf_key]
    w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
    w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
    if split_k > 1:
        tmp_buf.zero_()
        aiter.moe_cktile2stages_gemm1(
            hidden_states, w1, tmp_buf,
            sid, sei, nvi,
            topk, n_pad1, k_pad1,
            None, None, w1_scale_e8m0, None,
            ActivationType.Silu, block_m, split_k,
        )
        aiter.silu_and_mul(out_buf, tmp_buf)
    else:
        aiter.moe_cktile2stages_gemm1(
            hidden_states, w1, out_buf,
            sid, sei, nvi,
            topk, n_pad1, k_pad1,
            None, None, w1_scale_e8m0, None,
            ActivationType.Silu, block_m, 1,
        )
    aiter.moe_cktile2stages_gemm2(
        out_buf, w2, mb,
        sid, sei, nvi,
        topk, n_pad2, k_pad2,
        sw, None, w2_scale_e8m0, None,
        ActivationType.Silu, block_m,
    )
    return mb
_A2_BUFS = {}
_S3_CK_K1 = (
    "moe_ck2stages_gemm1_64x32x32x128_1x1_MulABScaleShuffled_v3_"
    "Nswizzle0_Quant3_MulRoutedWeight0_silu_FP4X2_FP4X2_B16"
)
_DIRECT_CK_CONFIGS = {
    (257, 256): (_S3_CK_K1, 32, 16, 256, 256, "reduce"),
    (33, 2048): (_k1_2048, 64, 16, 256, 256, "atomic"),
}
_QUANT_BUFS = {}
_FLYDSL_EMPTY_BUFS = {}
_FLYDSL_FP4_STAGE1_BUFS = {}
def _cached_quant(x, sorted_ids, num_valid_ids, token_num, topk, block_size=32):
    M, N = x.shape
    MXFP4_QUANT_BLOCK_SIZE = 32
    scaleN = triton.cdiv(N, MXFP4_QUANT_BLOCK_SIZE)
    BLOCK_SIZE_Mx = 128
    BLOCK_SIZE_M, BLOCK_SIZE_N = 32, 8
    BLOCK_SIZE_M_u32, BLOCK_SIZE_N_u32 = 16, 4
    M_o = sorted_ids.shape[0]
    N_o = scaleN
    key = (M, N, M_o)
    if key not in _QUANT_BUFS:
        x_fp4 = torch.empty((M, N // 2), dtype=torch.uint8, device=x.device)
        blockscale = torch.empty(
            (
                triton.cdiv(M_o, BLOCK_SIZE_M),
                triton.cdiv(N_o, BLOCK_SIZE_N),
                BLOCK_SIZE_N_u32,
                BLOCK_SIZE_M_u32,
                4,
            ),
            dtype=torch.uint8,
            device=x.device,
        )
        _QUANT_BUFS[key] = (
            x_fp4,
            blockscale,
            x_fp4.view(dtypes.fp4x2),
            blockscale.view(dtypes.fp8_e8m0).view(-1, N_o),
        )
    x_fp4, blockscale, x_fp4_view, blockscale_view = _QUANT_BUFS[key]
    M_i, N_i = M, scaleN
    num_pid = triton.cdiv(M, BLOCK_SIZE_Mx) * scaleN + triton.cdiv(
        M_o, BLOCK_SIZE_M
    ) * triton.cdiv(N_i, BLOCK_SIZE_N)
    _fused_dynamic_mxfp4_quant_moe_sort_kernel[(num_pid,)](
        x,
        x_fp4,
        sorted_ids,
        num_valid_ids,
        blockscale,
        M,
        N,
        scaleN,
        *x.stride(),
        *x_fp4.stride(),
        *blockscale.stride(),
        token_num=token_num,
        M_i=M_i,
        N_i=N_i,
        MXFP4_QUANT_BLOCK_SIZE=MXFP4_QUANT_BLOCK_SIZE,
        BLOCK_SIZE_Mx=BLOCK_SIZE_Mx,
        BLOCK_SIZE_M=BLOCK_SIZE_M // 2,
        BLOCK_SIZE_N=BLOCK_SIZE_N // 2,
        TOPK=topk,
    )
    return x_fp4_view, blockscale_view
def _cached_flydsl_empty(device):
    key = (device.type, device.index)
    if key not in _FLYDSL_EMPTY_BUFS:
        _FLYDSL_EMPTY_BUFS[key] = (
            torch.empty(0, dtype=torch.uint8, device=device),
            torch.empty(0, dtype=torch.float32, device=device),
        )
    return _FLYDSL_EMPTY_BUFS[key]
def _cached_flydsl_stage1_fp4_bufs(device, token_num, topk, inter_dim):
    key = (device.type, device.index, token_num, topk, inter_dim)
    if key not in _FLYDSL_FP4_STAGE1_BUFS:
        out_u8 = torch.empty(
            (token_num, topk, inter_dim // 2), dtype=torch.uint8, device=device
        )
        scale_u8 = torch.empty(
            (token_num, topk, inter_dim // 32), dtype=torch.uint8, device=device
        )
        _FLYDSL_FP4_STAGE1_BUFS[key] = (
            out_u8,
            scale_u8,
            out_u8.view(dtypes.fp4x2),
            scale_u8.view(dtypes.fp8_e8m0).view(token_num, topk, inter_dim // 32),
        )
    return _FLYDSL_FP4_STAGE1_BUFS[key]
def _flydsl_stage1_direct(
    *,
    a,
    w1,
    sorted_token_ids,
    sorted_expert_ids,
    num_valid_ids,
    topk,
    tile_m,
    tile_n,
    tile_k,
    a_dtype,
    b_dtype,
    out_dtype,
    w1_scale,
    a1_scale,
    sorted_weights=None,
    out=None,
):
    token_num = a.shape[0]
    experts = w1.shape[0]
    inter_dim = w1.shape[1] // 2
    model_dim = a.shape[1]
    if a_dtype == "fp4":
        model_dim *= 2
    empty_u8, empty_f32 = _cached_flydsl_empty(a.device)
    flat_a_scale = a1_scale.view(-1) if a1_scale is not None else empty_f32
    flat_w_scale = w1_scale.view(-1) if w1_scale is not None else empty_f32
    sw = sorted_weights if sorted_weights is not None else empty_f32
    codegen_opts = flydsl_moe_mod._default_stage1_codegen_opts(
        token_num=token_num,
        model_dim=model_dim,
        inter_dim=inter_dim,
        experts=experts,
        tile_m=tile_m,
        tile_n=tile_n,
        tile_k=tile_k,
        a_dtype=a_dtype,
        b_dtype=b_dtype,
    )
    tensor_api = flydsl_moe_mod._get_compiled_stage1(
        model_dim=model_dim,
        inter_dim=inter_dim,
        experts=experts,
        topk=topk,
        tile_m=tile_m,
        tile_n=tile_n,
        tile_k=tile_k,
        doweight=(sorted_weights is not None),
        a_dtype=a_dtype,
        b_dtype=b_dtype,
        out_dtype=out_dtype,
        lds128=codegen_opts["lds128"],
        use_scheduler=codegen_opts["use_scheduler"],
        sorted_scale=(out_dtype == "fp4"),
    )
    if out_dtype == "fp4":
        out_u8, out_scale_u8, out_view, out_scale_view = _cached_flydsl_stage1_fp4_bufs(
            a.device, token_num, topk, inter_dim
        )
        tensor_api(
            out_u8.view(-1),
            out_scale_u8.view(-1),
            a.view(-1),
            w1.view(-1),
            flat_a_scale,
            flat_w_scale,
            sorted_token_ids,
            sorted_expert_ids,
            sw,
            num_valid_ids,
            token_num,
            sorted_expert_ids.shape[0],
        )
        return out_view, out_scale_view
    tensor_api(
        out.view(-1),
        empty_u8,
        a.view(-1),
        w1.view(-1),
        flat_a_scale,
        flat_w_scale,
        sorted_token_ids,
        sorted_expert_ids,
        sw,
        num_valid_ids,
        token_num,
        sorted_expert_ids.shape[0],
    )
    return out
def _flydsl_stage2_direct(
    *,
    inter_states,
    w2,
    sorted_token_ids,
    sorted_expert_ids,
    num_valid_ids,
    out,
    topk,
    tile_m,
    tile_n,
    tile_k,
    a_dtype,
    b_dtype,
    out_dtype,
    mode,
    w2_scale,
    a2_scale,
    sorted_weights,
):
    token_num = inter_states.shape[0]
    experts = w2.shape[0]
    model_dim = w2.shape[1]
    inter_dim = inter_states.shape[2]
    if a_dtype == "fp4":
        inter_dim *= 2
    persist_m_env = os.environ.get("FLIR_MOE_STAGE2_PERSIST_M")
    if persist_m_env not in (None, ""):
        persist_m = int(persist_m_env)
    else:
        persist_m = 4 if int(sorted_expert_ids.numel()) > 256 else 1
    codegen_opts = flydsl_moe_mod._default_stage2_codegen_opts(
        token_num=token_num,
        model_dim=model_dim,
        inter_dim=inter_dim,
        experts=experts,
        tile_m=tile_m,
        tile_n=tile_n,
        tile_k=tile_k,
        a_dtype=a_dtype,
        b_dtype=b_dtype,
        mode=mode,
    )
    tensor_api = flydsl_moe_mod._get_compiled_stage2(
        model_dim=model_dim,
        inter_dim=inter_dim,
        experts=experts,
        topk=topk,
        tile_m=tile_m,
        tile_n=tile_n,
        tile_k=tile_k,
        doweight=(sorted_weights is not None),
        a_dtype=a_dtype,
        b_dtype=b_dtype,
        out_dtype=out_dtype,
        accumulate=(mode != "reduce"),
        persist_m=persist_m,
        lds128=codegen_opts["lds128"],
        use_scheduler=codegen_opts["use_scheduler"],
    )
    tensor_api(
        out,
        inter_states,
        w2,
        a2_scale,
        w2_scale,
        sorted_token_ids,
        sorted_expert_ids,
        sorted_weights,
        num_valid_ids,
        token_num,
        int(sorted_expert_ids.numel()),
    )
    return out
def _direct_ck_flydsl_pipeline(
    hidden_states, w1, w2, w1_scale, w2_scale,
    topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
    ck_k1, block_m, fly_tm, fly_tn, fly_tk, fly_mode = _DIRECT_CK_CONFIGS[(E, inter_dim)]
    sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
        topk_ids, topk_weights, E, model_dim, torch.bfloat16,
        block_m, None, None, 0, True,
    )
    a1, a1_scale = _cached_quant(
        hidden_states, sorted_ids=sid, num_valid_ids=nvi,
        token_num=M, topk=1, block_size=block_m,
    )
    buf_key = (M, topk, inter_dim)
    if buf_key not in _A2_BUFS:
        _A2_BUFS[buf_key] = torch.empty(
            (M, topk, inter_dim), dtype=torch.bfloat16, device=hidden_states.device,
        )
    a2 = _A2_BUFS[buf_key]
    w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
    aiter.ck_moe_stage1_fwd(
        a1, w1, w2, sid, sei, nvi, a2, topk,
        ck_k1, w1_scale_e8m0, a1_scale, block_m,
        None, QuantType.per_1x32, ActivationType.Silu, 0, True,
        torch.bfloat16,
    )
    a2_flat = a2.view(-1, inter_dim)
    a2_quant, a2_scale = _cached_quant(
        a2_flat, sorted_ids=sid, num_valid_ids=nvi,
        token_num=M, topk=topk, block_size=block_m,
    )
    a2_quant = a2_quant.view(M, topk, -1)
    w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
    _flydsl_stage2_direct(
        inter_states=a2_quant, w2=w2,
        sorted_token_ids=sid, sorted_expert_ids=sei,
        num_valid_ids=nvi, out=mb, topk=topk,
        tile_m=fly_tm, tile_n=fly_tn, tile_k=fly_tk,
        a_dtype="fp4", b_dtype="fp4", out_dtype="bf16",
        mode=fly_mode,
        w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
        sorted_weights=sw,
    )
    return mb
_FLYDSL_S1_CONFIGS = {
    (257, 256): (32, 128, 256, 256, 64, 256, 256),
    (33, 512): (32, 64, 256, 256, 16, 256, 256),
}
def _direct_flydsl_bf16_pipeline(
    hidden_states, w1, w2, w1_scale, w2_scale,
    topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
    block_m, fly_tm, fly_tn, fly_tk, s2_tm, s2_tn, s2_tk = _FLYDSL_S1_CONFIGS[(E, inter_dim)]
    fly_mode = "reduce"
    sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
        topk_ids, topk_weights, E, model_dim, torch.bfloat16,
        block_m, None, None, 0, True,
    )
    a1, a1_scale = _cached_quant(
        hidden_states, sorted_ids=sid, num_valid_ids=nvi,
        token_num=M, topk=1, block_size=block_m,
    )
    buf_key = (M, topk, inter_dim, "fly")
    if buf_key not in _A2_BUFS:
        _A2_BUFS[buf_key] = torch.empty(
            (M, topk, inter_dim), dtype=torch.bfloat16, device=hidden_states.device,
        )
    a2 = _A2_BUFS[buf_key]
    w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
    _flydsl_stage1_direct(
        a=a1, w1=w1,
        sorted_token_ids=sid, sorted_expert_ids=sei,
        num_valid_ids=nvi, out=a2, topk=topk,
        tile_m=fly_tm, tile_n=fly_tn, tile_k=fly_tk,
        a_dtype="fp4", b_dtype="fp4", out_dtype="bf16",
        w1_scale=w1_scale_e8m0, a1_scale=a1_scale,
        sorted_weights=None,
    )
    a2_flat = a2.view(-1, inter_dim)
    a2_quant, a2_scale = _cached_quant(
        a2_flat, sorted_ids=sid, num_valid_ids=nvi,
        token_num=M, topk=topk, block_size=block_m,
    )
    a2_quant = a2_quant.view(M, topk, -1)
    w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
    _flydsl_stage2_direct(
        inter_states=a2_quant, w2=w2,
        sorted_token_ids=sid, sorted_expert_ids=sei,
        num_valid_ids=nvi, out=mb, topk=topk,
        tile_m=s2_tm, tile_n=s2_tn, tile_k=s2_tk,
        a_dtype="fp4", b_dtype="fp4", out_dtype="bf16",
        mode=fly_mode,
        w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
        sorted_weights=sw,
    )
    return mb
def _direct_flydsl_fp4_pipeline(
    hidden_states, w1, w2, w1_scale, w2_scale,
    topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
    block_m, fly_tm, fly_tn, fly_tk, s2_tm, s2_tn, s2_tk = _FLYDSL_S1_CONFIGS[(E, inter_dim)]
    fly_mode = "reduce"
    sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
        topk_ids, topk_weights, E, model_dim, torch.bfloat16,
        block_m, None, None, 0, True,
    )
    a1, a1_scale = _cached_quant(
        hidden_states, sorted_ids=sid, num_valid_ids=nvi,
        token_num=M, topk=1, block_size=block_m,
    )
    w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
    try:
        a2_quant, a2_scale = _flydsl_stage1_direct(
            a=a1, w1=w1,
            sorted_token_ids=sid, sorted_expert_ids=sei,
            num_valid_ids=nvi, out=None, topk=topk,
            tile_m=fly_tm, tile_n=fly_tn, tile_k=fly_tk,
            a_dtype="fp4", b_dtype="fp4", out_dtype="fp4",
            w1_scale=w1_scale_e8m0, a1_scale=a1_scale,
            sorted_weights=None,
        )
    except BaseException as e:
        print(
            (
                f"[v]{type(e).__name__}:{e!r};"
                f"E={E} M={M} topk={topk} model_dim={model_dim} inter_dim={inter_dim} "
                f"block_m={block_m} s1=({fly_tm},{fly_tn},{fly_tk})"
            ),
            file=sys.stderr,
        )
        traceback.print_exc(file=sys.stderr)
        raise
    w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
    _flydsl_stage2_direct(
        inter_states=a2_quant, w2=w2,
        sorted_token_ids=sid, sorted_expert_ids=sei,
        num_valid_ids=nvi, out=mb, topk=topk,
        tile_m=s2_tm, tile_n=s2_tn, tile_k=s2_tk,
        a_dtype="fp4", b_dtype="fp4", out_dtype="bf16",
        mode=fly_mode,
        w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
        sorted_weights=sw,
    )
    return mb
def _direct_flydsl_s3_stage2_dualsort_pipeline(
    hidden_states, w1, w2, w1_scale, w2_scale,
    topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
    block_m, fly_tm, fly_tn, fly_tk, s2_tm, s2_tn, s2_tk = _FLYDSL_S1_CONFIGS[(E, inter_dim)]
    fly_mode = "reduce"
    sid_s1, _, sei_s1, nvi_s1, _ = _cached_moe_sorting_impl(
        topk_ids, topk_weights, E, model_dim, torch.bfloat16,
        block_m, None, None, 0, True,
    )
    sid_s2, sw_s2, sei_s2, nvi_s2, mb_s2 = _cached_moe_sorting_impl(
        topk_ids, topk_weights, E, model_dim, torch.bfloat16,
        s2_tm, None, None, 0, True,
    )
    a1, a1_scale = _cached_quant(
        hidden_states, sorted_ids=sid_s1, num_valid_ids=nvi_s1,
        token_num=M, topk=1, block_size=block_m,
    )
    buf_key = (M, topk, inter_dim, "fly")
    if buf_key not in _A2_BUFS:
        _A2_BUFS[buf_key] = torch.empty(
            (M, topk, inter_dim), dtype=torch.bfloat16, device=hidden_states.device,
        )
    a2 = _A2_BUFS[buf_key]
    w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
    flydsl_moe_stage1(
        a=a1, w1=w1,
        sorted_token_ids=sid_s1, sorted_expert_ids=sei_s1,
        num_valid_ids=nvi_s1, out=a2, topk=topk,
        tile_m=fly_tm, tile_n=fly_tn, tile_k=fly_tk,
        a_dtype="fp4", b_dtype="fp4", out_dtype="bf16",
        w1_scale=w1_scale_e8m0, a1_scale=a1_scale,
        sorted_weights=None,
    )
    a2_flat = a2.view(-1, inter_dim)
    a2_quant, a2_scale = _cached_quant(
        a2_flat, sorted_ids=sid_s2, num_valid_ids=nvi_s2,
        token_num=M, topk=topk, block_size=s2_tm,
    )
    a2_quant = a2_quant.view(M, topk, -1)
    w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
    flydsl_moe_stage2(
        inter_states=a2_quant, w2=w2,
        sorted_token_ids=sid_s2, sorted_expert_ids=sei_s2,
        num_valid_ids=nvi_s2, out=mb_s2, topk=topk,
        tile_m=s2_tm, tile_n=s2_tn, tile_k=s2_tk,
        a_dtype="fp4", b_dtype="fp4", out_dtype="bf16",
        mode=fly_mode,
        w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
        sorted_weights=sw_s2,
    )
    return mb_s2
def custom_kernel(data: input_t) -> output_t:
    (
        hidden_states, gate_up_weight, down_weight,
        gate_up_weight_scale, down_weight_scale,
        gate_up_weight_shuffled, down_weight_shuffled,
        gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
        topk_weights, topk_ids, config,
    ) = data
    hidden_pad = config["d_hidden_pad"] - config["d_hidden"]
    intermediate_pad = config["d_expert_pad"] - config["d_expert"]
    M = hidden_states.shape[0]
    E = config["n_routed_experts"] + config["n_shared_experts"]
    inter_dim = config["d_expert"]
    model_dim = hidden_states.shape[1]
    topk = topk_ids.shape[1]
    split_k = _get_split_k(E, M, inter_dim)
    if split_k > 0 and model_dim == 7168 and topk == 9:
        return _direct_cktile_pipeline(
            hidden_states,
            gate_up_weight_shuffled, down_weight_shuffled,
            gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
            topk_ids, topk_weights, E, M, topk, model_dim,
            inter_dim, hidden_pad, intermediate_pad, split_k,
        )
    if M == 512 and model_dim == 7168 and topk == 9 and (E, inter_dim) in _FLYDSL_S1_CONFIGS:
        if (E, inter_dim) == (257, 256):
            return _direct_flydsl_bf16_pipeline(
                hidden_states,
                gate_up_weight_shuffled, down_weight_shuffled,
                gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
                topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
            )
        if (E, inter_dim) == (33, 512):
            return _direct_flydsl_fp4_pipeline(
                hidden_states,
                gate_up_weight_shuffled, down_weight_shuffled,
                gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
                topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
            )
        return _direct_flydsl_bf16_pipeline(
            hidden_states,
            gate_up_weight_shuffled, down_weight_shuffled,
            gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
            topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
        )
    if M == 512 and model_dim == 7168 and topk == 9 and (E, inter_dim) in _DIRECT_CK_CONFIGS:
        return _direct_ck_flydsl_pipeline(
            hidden_states,
            gate_up_weight_shuffled, down_weight_shuffled,
            gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
            topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
        )
    return fused_moe_mod.fused_moe(
        hidden_states, gate_up_weight_shuffled, down_weight_shuffled,
        topk_weights, topk_ids,
        expert_mask=None, activation=ActivationType.Silu,
        quant_type=QuantType.per_1x32, doweight_stage1=False,
        w1_scale=gate_up_weight_scale_shuffled,
        w2_scale=down_weight_scale_shuffled,
        a1_scale=None, a2_scale=None,
        hidden_pad=hidden_pad, intermediate_pad=intermediate_pad,
    )
scrolls · 945 lines total

Source code from GPU Mode and the KernelBot dataset · June 9 Researcher Reciprocity License v1.0

Changes from previous submission

Against this author's previous submission submission 628145.

- """v607"""
+ """v706"""
import functools
+ import inspect
import os
import sys
import traceback
-
import torch
import triton
-
- from dataclasses import replace
-
_dsv3_path = "/home/runner/aiter/aiter/configs/model_configs/dsv3_fp4_tuned_fmoe.csv"
_flydsl_s3_stage2 = "flydsl_moe2_afp4_wfp4_bf16_t64x256x256_reduce"
try:
⋯ 33 unchanged lines
with open(_dsv3_path, "w") as f:
f.writelines(modified_lines)
except Exception as e:
- print(f"[v607] dsv3 error: {e}", file=sys.stderr)
-
+ print(f"[v]{e}", file=sys.stderr)
_csv_header = (
"cu_num,token,model_dim,inter_dim,expert,topk,act_type,dtype,q_dtype_a,"
"q_dtype_w,q_type,use_g1u1,doweight_stage1,block_m,ksplit,us1,kernelName1,"
⋯ 35 unchanged lines
f.write(row + "\n")
except Exception:
pass
-
- os.environ["AITER_USE_OPUS_MOE_SORTING"] = "1"
+ os.environ["AITER_USE_OPUS_MOE_SORTING"] = "0"
os.environ["AITER_USE_NT"] = "1"
os.environ.pop("FLIR_CK_LDS128", None)
os.environ["FLIR_MOE_STAGE1_SCHED"] = "1"
os.environ["FLIR_MOE_STAGE2_SCHED"] = "1"
os.environ["FLIR_MOE_STAGE2_PERSIST_M"] = "1"
-
-
import base64
import zlib
-
_FLYDSL_PATCH_BLOBS = {
'/home/runner/aiter/aiter/ops/flydsl/moe_kernels.py': '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',
'/home/runner/aiter/aiter/ops/flydsl/kernels/mixed_moe_gemm_2stage.py': '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',
⋯ 3 unchanged lines
with open(_patch_path, 'wb') as _f:
_f.write(zlib.decompress(base64.b64decode(_patch_blob)))
except Exception as e:
- print(f"[v607] flydsl backend patch skipped: {e}", file=sys.stderr)
-
+ print(f"[v]{e}", file=sys.stderr)
from task import input_t, output_t
from aiter import ActivationType, QuantType, dtypes
import aiter
import aiter.fused_moe as fused_moe_mod
- from aiter.ops.triton.quant.fused_mxfp4_quant import (
- fused_dynamic_mxfp4_quant_moe_sort,
- _fused_dynamic_mxfp4_quant_moe_sort_kernel,
- )
+ import aiter.ops.flydsl.moe_kernels as flydsl_moe_mod
+ from aiter.ops.triton.quant.fused_mxfp4_quant import _fused_dynamic_mxfp4_quant_moe_sort_kernel
from aiter.ops.flydsl.moe_kernels import flydsl_moe_stage1, flydsl_moe_stage2
- from aiter.utility.fp4_utils import moe_mxfp4_sort
-
-
+ def _call_supported_kwargs(fn, **kwargs):
+ try:
+ params = inspect.signature(fn).parameters
+ except (TypeError, ValueError):
+ return fn(**kwargs)
+ return fn(**{k: v for k, v in kwargs.items() if k in params})
+ def _install_stage1_sorted_scale_compat():
+ if "sorted_scale" in inspect.signature(flydsl_moe_mod._get_compiled_stage1).parameters:
+ return
+ _orig_compile_stage1 = flydsl_moe_mod.compile_flydsl_moe_stage1
+ def _compat_compile_flydsl_moe_stage1(
+ model_dim,
+ inter_dim,
+ experts,
+ topk,
+ tile_m,
+ tile_n,
+ tile_k,
+ doweight_stage1,
+ a_dtype,
+ b_dtype,
+ out_dtype,
+ lds128=None,
+ use_scheduler=None,
+ sorted_scale=False,
+ ):
+ if b_dtype == "fp4":
+ from aiter.ops.flydsl.kernels.mixed_moe_gemm_2stage import compile_mixed_moe_gemm1
+ return _call_supported_kwargs(
+ compile_mixed_moe_gemm1,
+ model_dim=model_dim,
+ inter_dim=inter_dim,
+ experts=experts,
+ topk=topk,
+ tile_m=tile_m,
+ tile_n=tile_n,
+ tile_k=tile_k,
+ doweight_stage1=doweight_stage1,
+ a_dtype=a_dtype,
+ b_dtype=b_dtype,
+ out_dtype=out_dtype,
+ use_cshuffle_epilog=(out_dtype == "fp8"),
+ lds128=lds128,
+ use_scheduler=use_scheduler,
+ sorted_scale=sorted_scale,
+ )
+ return _call_supported_kwargs(
+ _orig_compile_stage1,
+ model_dim=model_dim,
+ inter_dim=inter_dim,
+ experts=experts,
+ topk=topk,
+ tile_m=tile_m,
+ tile_n=tile_n,
+ tile_k=tile_k,
+ doweight_stage1=doweight_stage1,
+ a_dtype=a_dtype,
+ b_dtype=b_dtype,
+ out_dtype=out_dtype,
+ lds128=lds128,
+ use_scheduler=use_scheduler,
+ )
+ @functools.cache
+ def _compat_get_compiled_stage1(
+ model_dim,
+ inter_dim,
+ experts,
+ topk,
+ tile_m,
+ tile_n,
+ tile_k,
+ doweight,
+ a_dtype,
+ b_dtype,
+ out_dtype,
+ lds128=None,
+ use_scheduler=None,
+ sorted_scale=False,
+ ):
+ exe = _compat_compile_flydsl_moe_stage1(
+ model_dim=model_dim,
+ inter_dim=inter_dim,
+ experts=experts,
+ topk=topk,
+ tile_m=tile_m,
+ tile_n=tile_n,
+ tile_k=tile_k,
+ doweight_stage1=doweight,
+ a_dtype=a_dtype,
+ b_dtype=b_dtype,
+ out_dtype=out_dtype,
+ lds128=lds128,
+ use_scheduler=use_scheduler,
+ sorted_scale=sorted_scale,
+ )
+ is_fp4 = b_dtype == "fp4"
+ _n_in = inter_dim * 2 if is_fp4 else inter_dim
+ _k_in = model_dim
+ def tensor_api(
+ out,
+ out_scale,
+ a,
+ w,
+ a_scale,
+ w_scale,
+ sorted_ids,
+ sorted_expert_ids,
+ topk_weights,
+ num_valid_ids,
+ token_num,
+ size_expert_ids_in,
+ ):
+ if is_fp4:
+ empty_bias = torch.empty(0, device=a.device, dtype=torch.float32)
+ exe(
+ out,
+ out_scale,
+ a,
+ w,
+ a_scale,
+ w_scale,
+ sorted_ids,
+ sorted_expert_ids,
+ topk_weights,
+ num_valid_ids,
+ empty_bias,
+ token_num,
+ _n_in,
+ _k_in,
+ size_expert_ids_in,
+ 0,
+ )
+ else:
+ exe(
+ out,
+ out_scale,
+ a,
+ w,
+ a_scale,
+ w_scale,
+ sorted_ids,
+ sorted_expert_ids,
+ topk_weights,
+ num_valid_ids,
+ token_num,
+ _n_in,
+ _k_in,
+ size_expert_ids_in,
+ )
+ return tensor_api
+ flydsl_moe_mod.compile_flydsl_moe_stage1 = _compat_compile_flydsl_moe_stage1
+ flydsl_moe_mod._get_compiled_stage1 = _compat_get_compiled_stage1
+ _install_stage1_sorted_scale_compat()
_SORT_BUFS = {}
-
-
def _cached_moe_sorting_impl(
topk_ids, topk_weights, num_experts, model_dim, moebuf_dtype,
block_size, expert_mask, num_local_tokens, dispatch_policy, use_opus,
⋯ 1 unchanged lines
device = topk_ids.device
M, topk = topk_ids.shape
key = (M, num_experts, block_size, model_dim)
-
if key not in _SORT_BUFS:
max_num_tokens_padded = int(M * topk + num_experts * block_size - topk)
max_num_m_blocks = int(
⋯ 6 unchanged lines
torch.empty(2, dtype=dtypes.i32, device=device),
torch.empty((M, model_dim), dtype=moebuf_dtype, device=device),
)
-
sid, sw, sei, nvi, mb = _SORT_BUFS[key]
fwd = aiter.moe_sorting_opus_fwd if use_opus else aiter.moe_sorting_fwd
fwd(
⋯ 2 unchanged lines
dispatch_policy,
)
return sid, sw, sei, nvi, mb
-
-
fused_moe_mod._moe_sorting_impl = _cached_moe_sorting_impl
-
-
_ORIGINAL_GET_2STAGE_CFGS = fused_moe_mod.get_2stage_cfgs
-
_CKTILE_BUFS = {}
-
-
def _cached_cktile_moe_stage1(
hidden_states, w1, w2,
sorted_token_ids, sorted_expert_ids, num_valid_ids,
⋯ 8 unchanged lines
D = n2 if k2 == k1 else n2 * 2
if w1.dtype is torch.uint32:
D = D * 8
-
buf_key = (token_num, topk, D, w1.shape[1], split_k, hidden_states.device)
if buf_key not in _CKTILE_BUFS:
_CKTILE_BUFS[buf_key] = (
⋯ 3 unchanged lines
device=hidden_states.device,
) if split_k > 1 else None,
)
-
out_buf, tmp_buf = _CKTILE_BUFS[buf_key]
-
if split_k > 1:
tmp_buf.zero_()
aiter.moe_cktile2stages_gemm1(
⋯ 13 unchanged lines
activation, block_m, split_k,
)
return out_buf
-
-
def _make_cktile_metadata(hidden_pad, intermediate_pad, use_g1u1, activation, split_k=2):
return fused_moe_mod.MOEMetadata(
functools.partial(
⋯ 11 unchanged lines
),
16, split_k, False, False, True,
)
-
-
@functools.lru_cache(maxsize=2048)
def _patched_get_2stage_cfgs(
token, model_dim, inter_dim, expert, topk, dtype,
q_dtype_a, q_dtype_w, q_type, use_g1u1, activation,
doweight_stage1, hidden_pad, intermediate_pad, is_shuffled=True,
):
- common = (
- model_dim == 7168 and topk == 9
- and dtype == dtypes.bf16 and q_dtype_a == dtypes.fp4x2
- and q_dtype_w == dtypes.fp4x2 and q_type == QuantType.per_1x32
- and use_g1u1 and not doweight_stage1 and is_shuffled
- )
-
- if not common:
- return _ORIGINAL_GET_2STAGE_CFGS(
- token, model_dim, inter_dim, expert, topk, dtype,
- q_dtype_a, q_dtype_w, q_type, use_g1u1, activation,
- doweight_stage1, hidden_pad, intermediate_pad, is_shuffled,
- )
-
- if expert == 257 and inter_dim == 256 and token == 16:
- return _make_cktile_metadata(
- hidden_pad, intermediate_pad, use_g1u1, activation, split_k=2,
- )
- if expert == 257 and inter_dim == 256 and token == 128:
- return _make_cktile_metadata(
- hidden_pad, intermediate_pad, use_g1u1, activation, split_k=4,
- )
-
- if expert == 33 and inter_dim == 512 and token <= 128:
- return _make_cktile_metadata(
- hidden_pad, intermediate_pad, use_g1u1, activation, split_k=2,
- )
-
+ if model_dim==7168 and topk==9 and dtype==dtypes.bf16 and q_dtype_a==q_dtype_w==dtypes.fp4x2 and q_type==QuantType.per_1x32 and use_g1u1 and is_shuffled and not doweight_stage1 and ((expert==33 and inter_dim==512 and token<=128) or (expert==257 and inter_dim==256 and token in (16,128))):return _make_cktile_metadata(hidden_pad,intermediate_pad,use_g1u1,activation,4 if token==128 else 2)
return _ORIGINAL_GET_2STAGE_CFGS(
token, model_dim, inter_dim, expert, topk, dtype,
q_dtype_a, q_dtype_w, q_type, use_g1u1, activation,
doweight_stage1, hidden_pad, intermediate_pad, is_shuffled,
)
-
-
- fused_moe_mod.get_2stage_cfgs = _patched_get_2stage_cfgs
-
-
- _DIRECT_BUFS = {}
-
-
- def _get_split_k(E, M, inter_dim):
- if E == 257 and inter_dim == 256:
- if M == 16:
- return 2
- if M == 128:
- return 4
- if E == 33 and inter_dim == 512:
- if M == 16:
- return 2
- if M == 128:
- return 1
- return 0
-
-
+ fused_moe_mod.get_2stage_cfgs=_patched_get_2stage_cfgs
+ _DIRECT_BUFS={}
+ def _get_split_k(E, M, inter_dim): return E==33 and inter_dim==512 and M==128
def _direct_cktile_pipeline(
hidden_states, w1, w2, w1_scale, w2_scale,
topk_ids, topk_weights, E, M, topk, model_dim,
inter_dim, hidden_pad, intermediate_pad, split_k,
):
block_m = 16
-
sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
topk_ids, topk_weights, E, model_dim, torch.bfloat16,
block_m, None, None, 0, True,
)
-
_, n1, k1 = w1.shape
_, k2, n2 = w2.shape
D = n2 if k2 == k1 else n2 * 2
if w1.dtype is torch.uint32:
D = D * 8
-
n_pad1 = intermediate_pad // 64 * 64 * 2
k_pad1 = hidden_pad // 128 * 128
n_pad2 = hidden_pad // 64 * 64
k_pad2 = intermediate_pad // 128 * 128
-
buf_key = (M, topk, D, w1.shape[1], split_k)
if buf_key not in _DIRECT_BUFS:
dev = hidden_states.device
⋯ 3 unchanged lines
if split_k > 1 else None
)
_DIRECT_BUFS[buf_key] = (out, tmp)
-
out_buf, tmp_buf = _DIRECT_BUFS[buf_key]
-
w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
-
if split_k > 1:
tmp_buf.zero_()
aiter.moe_cktile2stages_gemm1(
⋯ 12 unchanged lines
None, None, w1_scale_e8m0, None,
ActivationType.Silu, block_m, 1,
)
-
aiter.moe_cktile2stages_gemm2(
out_buf, w2, mb,
sid, sei, nvi,
⋯ 1 unchanged lines
sw, None, w2_scale_e8m0, None,
ActivationType.Silu, block_m,
)
-
return mb
-
-
_A2_BUFS = {}
-
_S3_CK_K1 = (
"moe_ck2stages_gemm1_64x32x32x128_1x1_MulABScaleShuffled_v3_"
"Nswizzle0_Quant3_MulRoutedWeight0_silu_FP4X2_FP4X2_B16"
)
-
_DIRECT_CK_CONFIGS = {
(257, 256): (_S3_CK_K1, 32, 16, 256, 256, "reduce"),
(33, 2048): (_k1_2048, 64, 16, 256, 256, "atomic"),
}
-
-
_QUANT_BUFS = {}
-
-
+ _FLYDSL_EMPTY_BUFS = {}
+ _FLYDSL_FP4_STAGE1_BUFS = {}
def _cached_quant(x, sorted_ids, num_valid_ids, token_num, topk, block_size=32):
M, N = x.shape
MXFP4_QUANT_BLOCK_SIZE = 32
⋯ 3 unchanged lines
BLOCK_SIZE_M_u32, BLOCK_SIZE_N_u32 = 16, 4
M_o = sorted_ids.shape[0]
N_o = scaleN
-
key = (M, N, M_o)
if key not in _QUANT_BUFS:
x_fp4 = torch.empty((M, N // 2), dtype=torch.uint8, device=x.device)
⋯ 14 unchanged lines
x_fp4.view(dtypes.fp4x2),
blockscale.view(dtypes.fp8_e8m0).view(-1, N_o),
)
-
x_fp4, blockscale, x_fp4_view, blockscale_view = _QUANT_BUFS[key]
-
M_i, N_i = M, scaleN
num_pid = triton.cdiv(M, BLOCK_SIZE_Mx) * scaleN + triton.cdiv(
M_o, BLOCK_SIZE_M
⋯ 19 unchanged lines
BLOCK_SIZE_N=BLOCK_SIZE_N // 2,
TOPK=topk,
)
-
return x_fp4_view, blockscale_view
-
-
+ def _cached_flydsl_empty(device):
+ key = (device.type, device.index)
+ if key not in _FLYDSL_EMPTY_BUFS:
+ _FLYDSL_EMPTY_BUFS[key] = (
+ torch.empty(0, dtype=torch.uint8, device=device),
+ torch.empty(0, dtype=torch.float32, device=device),
+ )
+ return _FLYDSL_EMPTY_BUFS[key]
+ def _cached_flydsl_stage1_fp4_bufs(device, token_num, topk, inter_dim):
+ key = (device.type, device.index, token_num, topk, inter_dim)
+ if key not in _FLYDSL_FP4_STAGE1_BUFS:
+ out_u8 = torch.empty(
+ (token_num, topk, inter_dim // 2), dtype=torch.uint8, device=device
+ )
+ scale_u8 = torch.empty(
+ (token_num, topk, inter_dim // 32), dtype=torch.uint8, device=device
+ )
+ _FLYDSL_FP4_STAGE1_BUFS[key] = (
+ out_u8,
+ scale_u8,
+ out_u8.view(dtypes.fp4x2),
+ scale_u8.view(dtypes.fp8_e8m0).view(token_num, topk, inter_dim // 32),
+ )
+ return _FLYDSL_FP4_STAGE1_BUFS[key]
+ def _flydsl_stage1_direct(
+ *,
+ a,
+ w1,
+ sorted_token_ids,
+ sorted_expert_ids,
+ num_valid_ids,
+ topk,
+ tile_m,
+ tile_n,
+ tile_k,
+ a_dtype,
+ b_dtype,
+ out_dtype,
+ w1_scale,
+ a1_scale,
+ sorted_weights=None,
+ out=None,
+ ):
+ token_num = a.shape[0]
+ experts = w1.shape[0]
+ inter_dim = w1.shape[1] // 2
+ model_dim = a.shape[1]
+ if a_dtype == "fp4":
+ model_dim *= 2
+ empty_u8, empty_f32 = _cached_flydsl_empty(a.device)
+ flat_a_scale = a1_scale.view(-1) if a1_scale is not None else empty_f32
+ flat_w_scale = w1_scale.view(-1) if w1_scale is not None else empty_f32
+ sw = sorted_weights if sorted_weights is not None else empty_f32
+ codegen_opts = flydsl_moe_mod._default_stage1_codegen_opts(
+ token_num=token_num,
+ model_dim=model_dim,
+ inter_dim=inter_dim,
+ experts=experts,
+ tile_m=tile_m,
+ tile_n=tile_n,
+ tile_k=tile_k,
+ a_dtype=a_dtype,
+ b_dtype=b_dtype,
+ )
+ tensor_api = flydsl_moe_mod._get_compiled_stage1(
+ model_dim=model_dim,
+ inter_dim=inter_dim,
+ experts=experts,
+ topk=topk,
+ tile_m=tile_m,
+ tile_n=tile_n,
+ tile_k=tile_k,
+ doweight=(sorted_weights is not None),
+ a_dtype=a_dtype,
+ b_dtype=b_dtype,
+ out_dtype=out_dtype,
+ lds128=codegen_opts["lds128"],
+ use_scheduler=codegen_opts["use_scheduler"],
+ sorted_scale=(out_dtype == "fp4"),
+ )
+ if out_dtype == "fp4":
+ out_u8, out_scale_u8, out_view, out_scale_view = _cached_flydsl_stage1_fp4_bufs(
+ a.device, token_num, topk, inter_dim
+ )
+ tensor_api(
+ out_u8.view(-1),
+ out_scale_u8.view(-1),
+ a.view(-1),
+ w1.view(-1),
+ flat_a_scale,
+ flat_w_scale,
+ sorted_token_ids,
+ sorted_expert_ids,
+ sw,
+ num_valid_ids,
+ token_num,
+ sorted_expert_ids.shape[0],
+ )
+ return out_view, out_scale_view
+ tensor_api(
+ out.view(-1),
+ empty_u8,
+ a.view(-1),
+ w1.view(-1),
+ flat_a_scale,
+ flat_w_scale,
+ sorted_token_ids,
+ sorted_expert_ids,
+ sw,
+ num_valid_ids,
+ token_num,
+ sorted_expert_ids.shape[0],
+ )
+ return out
+ def _flydsl_stage2_direct(
+ *,
+ inter_states,
+ w2,
+ sorted_token_ids,
+ sorted_expert_ids,
+ num_valid_ids,
+ out,
+ topk,
+ tile_m,
+ tile_n,
+ tile_k,
+ a_dtype,
+ b_dtype,
+ out_dtype,
+ mode,
+ w2_scale,
+ a2_scale,
+ sorted_weights,
+ ):
+ token_num = inter_states.shape[0]
+ experts = w2.shape[0]
+ model_dim = w2.shape[1]
+ inter_dim = inter_states.shape[2]
+ if a_dtype == "fp4":
+ inter_dim *= 2
+ persist_m_env = os.environ.get("FLIR_MOE_STAGE2_PERSIST_M")
+ if persist_m_env not in (None, ""):
+ persist_m = int(persist_m_env)
+ else:
+ persist_m = 4 if int(sorted_expert_ids.numel()) > 256 else 1
+ codegen_opts = flydsl_moe_mod._default_stage2_codegen_opts(
+ token_num=token_num,
+ model_dim=model_dim,
+ inter_dim=inter_dim,
+ experts=experts,
+ tile_m=tile_m,
+ tile_n=tile_n,
+ tile_k=tile_k,
+ a_dtype=a_dtype,
+ b_dtype=b_dtype,
+ mode=mode,
+ )
+ tensor_api = flydsl_moe_mod._get_compiled_stage2(
+ model_dim=model_dim,
+ inter_dim=inter_dim,
+ experts=experts,
+ topk=topk,
+ tile_m=tile_m,
+ tile_n=tile_n,
+ tile_k=tile_k,
+ doweight=(sorted_weights is not None),
+ a_dtype=a_dtype,
+ b_dtype=b_dtype,
+ out_dtype=out_dtype,
+ accumulate=(mode != "reduce"),
+ persist_m=persist_m,
+ lds128=codegen_opts["lds128"],
+ use_scheduler=codegen_opts["use_scheduler"],
+ )
+ tensor_api(
+ out,
+ inter_states,
+ w2,
+ a2_scale,
+ w2_scale,
+ sorted_token_ids,
+ sorted_expert_ids,
+ sorted_weights,
+ num_valid_ids,
+ token_num,
+ int(sorted_expert_ids.numel()),
+ )
+ return out
def _direct_ck_flydsl_pipeline(
hidden_states, w1, w2, w1_scale, w2_scale,
topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
ck_k1, block_m, fly_tm, fly_tn, fly_tk, fly_mode = _DIRECT_CK_CONFIGS[(E, inter_dim)]
-
sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
topk_ids, topk_weights, E, model_dim, torch.bfloat16,
block_m, None, None, 0, True,
)
-
a1, a1_scale = _cached_quant(
hidden_states, sorted_ids=sid, num_valid_ids=nvi,
token_num=M, topk=1, block_size=block_m,
)
-
buf_key = (M, topk, inter_dim)
if buf_key not in _A2_BUFS:
_A2_BUFS[buf_key] = torch.empty(
(M, topk, inter_dim), dtype=torch.bfloat16, device=hidden_states.device,
)
a2 = _A2_BUFS[buf_key]
-
w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
aiter.ck_moe_stage1_fwd(
a1, w1, w2, sid, sei, nvi, a2, topk,
⋯ 1 unchanged lines
None, QuantType.per_1x32, ActivationType.Silu, 0, True,
torch.bfloat16,
)
-
a2_flat = a2.view(-1, inter_dim)
a2_quant, a2_scale = _cached_quant(
a2_flat, sorted_ids=sid, num_valid_ids=nvi,
token_num=M, topk=topk, block_size=block_m,
)
a2_quant = a2_quant.view(M, topk, -1)
-
w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
- flydsl_moe_stage2(
+ _flydsl_stage2_direct(
inter_states=a2_quant, w2=w2,
sorted_token_ids=sid, sorted_expert_ids=sei,
num_valid_ids=nvi, out=mb, topk=topk,
⋯ 3 unchanged lines
w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
sorted_weights=sw,
)
-
return mb
-
-
_FLYDSL_S1_CONFIGS = {
(257, 256): (32, 128, 256, 256, 64, 256, 256),
(33, 512): (32, 64, 256, 256, 16, 256, 256),
}
-
-
def _direct_flydsl_bf16_pipeline(
hidden_states, w1, w2, w1_scale, w2_scale,
topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
block_m, fly_tm, fly_tn, fly_tk, s2_tm, s2_tn, s2_tk = _FLYDSL_S1_CONFIGS[(E, inter_dim)]
fly_mode = "reduce"
-
sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
topk_ids, topk_weights, E, model_dim, torch.bfloat16,
block_m, None, None, 0, True,
)
-
a1, a1_scale = _cached_quant(
hidden_states, sorted_ids=sid, num_valid_ids=nvi,
token_num=M, topk=1, block_size=block_m,
)
-
buf_key = (M, topk, inter_dim, "fly")
if buf_key not in _A2_BUFS:
_A2_BUFS[buf_key] = torch.empty(
(M, topk, inter_dim), dtype=torch.bfloat16, device=hidden_states.device,
)
a2 = _A2_BUFS[buf_key]
-
w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
- flydsl_moe_stage1(
+ _flydsl_stage1_direct(
a=a1, w1=w1,
sorted_token_ids=sid, sorted_expert_ids=sei,
num_valid_ids=nvi, out=a2, topk=topk,
⋯ 2 unchanged lines
w1_scale=w1_scale_e8m0, a1_scale=a1_scale,
sorted_weights=None,
)
-
a2_flat = a2.view(-1, inter_dim)
a2_quant, a2_scale = _cached_quant(
a2_flat, sorted_ids=sid, num_valid_ids=nvi,
token_num=M, topk=topk, block_size=block_m,
)
a2_quant = a2_quant.view(M, topk, -1)
-
w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
- flydsl_moe_stage2(
+ _flydsl_stage2_direct(
inter_states=a2_quant, w2=w2,
sorted_token_ids=sid, sorted_expert_ids=sei,
num_valid_ids=nvi, out=mb, topk=topk,
⋯ 3 unchanged lines
w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
sorted_weights=sw,
)
-
return mb
-
-
def _direct_flydsl_fp4_pipeline(
hidden_states, w1, w2, w1_scale, w2_scale,
topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
block_m, fly_tm, fly_tn, fly_tk, s2_tm, s2_tn, s2_tk = _FLYDSL_S1_CONFIGS[(E, inter_dim)]
fly_mode = "reduce"
-
sid, sw, sei, nvi, mb = _cached_moe_sorting_impl(
topk_ids, topk_weights, E, model_dim, torch.bfloat16,
block_m, None, None, 0, True,
)
-
a1, a1_scale = _cached_quant(
hidden_states, sorted_ids=sid, num_valid_ids=nvi,
token_num=M, topk=1, block_size=block_m,
)
-
w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
try:
- a2_quant, a2_scale_unsorted = flydsl_moe_stage1(
+ a2_quant, a2_scale = _flydsl_stage1_direct(
a=a1, w1=w1,
sorted_token_ids=sid, sorted_expert_ids=sei,
- num_valid_ids=nvi, out=None, out_scale=None, topk=topk,
+ num_valid_ids=nvi, out=None, topk=topk,
tile_m=fly_tm, tile_n=fly_tn, tile_k=fly_tk,
a_dtype="fp4", b_dtype="fp4", out_dtype="fp4",
w1_scale=w1_scale_e8m0, a1_scale=a1_scale,
⋯ 2 unchanged lines
except BaseException as e:
print(
(
- f"[v607] flydsl_moe_stage1 fp4 failed: {type(e).__name__}: {e!r}; "
+ f"[v]{type(e).__name__}:{e!r};"
f"E={E} M={M} topk={topk} model_dim={model_dim} inter_dim={inter_dim} "
f"block_m={block_m} s1=({fly_tm},{fly_tn},{fly_tk})"
),
⋯ 1 unchanged lines
)
traceback.print_exc(file=sys.stderr)
raise
- a2_scale = moe_mxfp4_sort(
- a2_scale_unsorted,
- sid,
- nvi,
- token_num=M,
- block_size=block_m,
- )
-
w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
- flydsl_moe_stage2(
+ _flydsl_stage2_direct(
inter_states=a2_quant, w2=w2,
sorted_token_ids=sid, sorted_expert_ids=sei,
num_valid_ids=nvi, out=mb, topk=topk,
⋯ 3 unchanged lines
w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
sorted_weights=sw,
)
-
return mb
-
-
def _direct_flydsl_s3_stage2_dualsort_pipeline(
hidden_states, w1, w2, w1_scale, w2_scale,
topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
):
block_m, fly_tm, fly_tn, fly_tk, s2_tm, s2_tn, s2_tk = _FLYDSL_S1_CONFIGS[(E, inter_dim)]
fly_mode = "reduce"
-
sid_s1, _, sei_s1, nvi_s1, _ = _cached_moe_sorting_impl(
topk_ids, topk_weights, E, model_dim, torch.bfloat16,
block_m, None, None, 0, True,
⋯ 2 unchanged lines
topk_ids, topk_weights, E, model_dim, torch.bfloat16,
s2_tm, None, None, 0, True,
)
-
a1, a1_scale = _cached_quant(
hidden_states, sorted_ids=sid_s1, num_valid_ids=nvi_s1,
token_num=M, topk=1, block_size=block_m,
)
-
buf_key = (M, topk, inter_dim, "fly")
if buf_key not in _A2_BUFS:
_A2_BUFS[buf_key] = torch.empty(
(M, topk, inter_dim), dtype=torch.bfloat16, device=hidden_states.device,
)
a2 = _A2_BUFS[buf_key]
-
w1_scale_e8m0 = w1_scale.view(dtypes.fp8_e8m0)
flydsl_moe_stage1(
a=a1, w1=w1,
⋯ 4 unchanged lines
w1_scale=w1_scale_e8m0, a1_scale=a1_scale,
sorted_weights=None,
)
-
a2_flat = a2.view(-1, inter_dim)
a2_quant, a2_scale = _cached_quant(
a2_flat, sorted_ids=sid_s2, num_valid_ids=nvi_s2,
token_num=M, topk=topk, block_size=s2_tm,
)
a2_quant = a2_quant.view(M, topk, -1)
-
w2_scale_e8m0 = w2_scale.view(dtypes.fp8_e8m0)
flydsl_moe_stage2(
inter_states=a2_quant, w2=w2,
⋯ 5 unchanged lines
w2_scale=w2_scale_e8m0, a2_scale=a2_scale,
sorted_weights=sw_s2,
)
-
return mb_s2
-
-
def custom_kernel(data: input_t) -> output_t:
(
hidden_states, gate_up_weight, down_weight,
⋯ 2 unchanged lines
gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
topk_weights, topk_ids, config,
) = data
-
hidden_pad = config["d_hidden_pad"] - config["d_hidden"]
intermediate_pad = config["d_expert_pad"] - config["d_expert"]
-
M = hidden_states.shape[0]
- E = gate_up_weight.shape[0]
+ E = config["n_routed_experts"] + config["n_shared_experts"]
inter_dim = config["d_expert"]
model_dim = hidden_states.shape[1]
topk = topk_ids.shape[1]
-
split_k = _get_split_k(E, M, inter_dim)
-
if split_k > 0 and model_dim == 7168 and topk == 9:
return _direct_cktile_pipeline(
hidden_states,
⋯ 2 unchanged lines
topk_ids, topk_weights, E, M, topk, model_dim,
inter_dim, hidden_pad, intermediate_pad, split_k,
)
-
if M == 512 and model_dim == 7168 and topk == 9 and (E, inter_dim) in _FLYDSL_S1_CONFIGS:
if (E, inter_dim) == (257, 256):
return _direct_flydsl_bf16_pipeline(
⋯ 15 unchanged lines
gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
)
-
if M == 512 and model_dim == 7168 and topk == 9 and (E, inter_dim) in _DIRECT_CK_CONFIGS:
return _direct_ck_flydsl_pipeline(
hidden_states,
⋯ 1 unchanged lines
gate_up_weight_scale_shuffled, down_weight_scale_shuffled,
topk_ids, topk_weights, E, M, topk, model_dim, inter_dim,
)
-
return fused_moe_mod.fused_moe(
hidden_states, gate_up_weight_shuffled, down_weight_shuffled,
topk_weights, topk_ids,
scrolls · 924 diff lines total

Best evidence level for this revision: reported

JSON