Skip to content

Commit 5853ebd

Browse files
committed
vendor vllm fused_moe
Signed-off-by: Ceng23333 <441651826@qq.com>
1 parent 5a1b634 commit 5853ebd

17 files changed

Lines changed: 2468 additions & 31 deletions

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ dependencies = []
1111
requires-python = ">=3.8"
1212

1313
[project.optional-dependencies]
14-
# Same interpreter as InfiniCore built with ``--aten=y`` (see vllm_kernel_reuse_evaluation.md).
14+
# Optional vLLM pip stack for benchmark/compare scripts only (MoE fused path is vendored in-tree).
1515
vllm = ["vllm==0.19.0"]
16-
# MiniCPM5 fused stub + jiuge: vLLM fused MoE + helper deps (flash-attn install separately; see InfiniLM/examples/FLASH_ATTN_AND_VLLM_FUSED_MOE.md).
17-
vllm-fused-moe = ["vllm==0.19.0", "janus>=1.0.0", "xxhash>=3.0.0"]
16+
# MiniCPM5 fused MoE + jiuge: Triton + helpers (flash-attn install separately; see InfiniLM/examples/FLASH_ATTN_AND_VLLM_FUSED_MOE.md).
17+
vllm-fused-moe = ["triton>=3.0.0", "janus>=1.0.0", "xxhash>=3.0.0"]
1818

1919
classifiers = [
2020
"Programming Language :: Python :: 3",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Namespace for vendored third-party slices (see subpackages).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
This directory vendors vLLM fused MoE Triton kernels for MiniCPM / InfiniLM.
2+
3+
Upstream: https://github.com/vllm-project/vllm (release 0.19.x)
4+
License: Apache-2.0 (see SPDX headers in individual files)
5+
6+
InfiniLM changes:
7+
- Register torch.ops.infinilm.outplace_fused_experts (no vllm PyPI import).
8+
- Replace vLLM-only CUDA helpers (moe_align_block_size, moe_sum) with PyTorch.
9+
- Replace silu_and_mul custom op with pure torch SwiGLU.
10+
- Config search honors INFINILM_TUNED_CONFIG_FOLDER (and VLLM_TUNED_CONFIG_FOLDER).
11+
12+
Tuned configs:
13+
``configs/`` is intentionally empty in-repo (kernel falls back to defaults). To use
14+
vLLM-style JSON tuning, copy ``E=...,N=...,device_name=....json`` files from upstream
15+
``vllm/model_executor/layers/fused_moe/configs/`` into ``configs/``, or point
16+
``INFINILM_TUNED_CONFIG_FOLDER`` at such a directory.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
"""vLLM-derived fused MoE (Triton) registered as ``torch.ops.infinilm.*``."""
4+
5+
from __future__ import annotations
6+
7+
# Import for side effects: registers torch.library fragments on ``infinilm``.
8+
from . import fused_moe as _fused_moe # noqa: F401
9+
from .activation import MoEActivation
10+
from .fused_moe import fused_experts, get_config_file_name
11+
12+
__all__ = ["MoEActivation", "fused_experts", "get_config_file_name"]
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Internal one-shot helper (not imported at runtime): regenerate fused_moe.py from vLLM.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from __future__ import annotations
5+
6+
from pathlib import Path
7+
8+
9+
def main() -> None:
10+
repo = Path(__file__).resolve().parents[5]
11+
src = (
12+
repo
13+
/ ".venv-vllm/lib/python3.12/site-packages/vllm/model_executor/layers/fused_moe/fused_moe.py"
14+
)
15+
dst = Path(__file__).resolve().parent / "fused_moe.py"
16+
lines = src.read_text(encoding="utf-8").splitlines(keepends=True)
17+
out = "".join(lines[:1896])
18+
19+
reps: list[tuple[str, str]] = [
20+
("import vllm.envs as envs", "from . import envs"),
21+
(
22+
"import vllm.model_executor.layers.fused_moe.modular_kernel as mk\n",
23+
"",
24+
),
25+
("from vllm import _custom_ops as ops\n", "from . import ops_shim as ops\n"),
26+
("from vllm.logger import init_logger", "from .logging_utils import init_logger"),
27+
(
28+
"from vllm.model_executor.layers.fused_moe.activation import",
29+
"from .activation import",
30+
),
31+
(
32+
"from vllm.model_executor.layers.fused_moe.config import (\n"
33+
" FUSED_MOE_UNQUANTIZED_CONFIG,\n"
34+
" FusedMoEConfig,\n"
35+
" FusedMoEParallelConfig,\n"
36+
" FusedMoEQuantConfig,\n"
37+
" _get_config_dtype_str,\n)",
38+
"from .config_light import (\n"
39+
" FUSED_MOE_UNQUANTIZED_CONFIG,\n"
40+
" FusedMoEQuantConfig,\n"
41+
" _get_config_dtype_str,\n)",
42+
),
43+
(
44+
"from vllm.model_executor.layers.fused_moe.moe_align_block_size import",
45+
"from .moe_align_block_size import",
46+
),
47+
(
48+
"from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (\n"
49+
" TopKWeightAndReduceNoOP,\n)\n",
50+
"",
51+
),
52+
(
53+
"from vllm.model_executor.layers.fused_moe.utils import",
54+
"from .utils_moe import",
55+
),
56+
(
57+
"from vllm.model_executor.layers.quantization.utils.mxfp4_utils import dequant_mxfp4\n",
58+
"",
59+
),
60+
(
61+
"from vllm.model_executor.layers.quantization.utils.mxfp6_utils import dequant_mxfp6\n",
62+
"",
63+
),
64+
(
65+
"from vllm.model_executor.layers.quantization.utils.quant_utils import (\n"
66+
" QuantKey,\n"
67+
" kFp8Dynamic128Sym,\n"
68+
" kFp8DynamicTensorSym,\n"
69+
" kFp8DynamicTokenSym,\n"
70+
" kFp8Static128BlockSym,\n"
71+
" kFp8StaticChannelSym,\n"
72+
" kFp8StaticTensorSym,\n)\n",
73+
"",
74+
),
75+
("from vllm.platforms import current_platform", "from .platform_ctx import current_platform"),
76+
("from vllm.triton_utils import tl, triton", "import triton\nimport triton.language as tl"),
77+
(
78+
"from vllm.utils.torch_utils import direct_register_custom_op",
79+
"from .torch_register import direct_register_custom_op, infinilm_fused_lib",
80+
),
81+
]
82+
for a, b in reps:
83+
if a not in out:
84+
raise SystemExit(f"missing pattern fragment:\n{a[:200]}")
85+
out = out.replace(a, b)
86+
87+
out = out.replace(
88+
"activation_out_dim = mk.FusedMoEExpertsModular.adjust_N_for_activation(\n"
89+
" N, activation_enum\n"
90+
" )",
91+
"activation_out_dim = N if not activation_enum.is_gated else N // 2",
92+
)
93+
94+
out = out.replace(
95+
" from vllm.model_executor.layers.fused_moe import get_config\n\n"
96+
" override_config = get_config()",
97+
" override_config = None",
98+
)
99+
100+
out = out.replace("torch.ops.vllm.", "torch.ops.infinilm.")
101+
102+
out = out.replace(
103+
"direct_register_custom_op(\n op_name=\"inplace_fused_experts\",",
104+
"direct_register_custom_op(\n op_name=\"inplace_fused_experts\",\n"
105+
" target_lib=infinilm_fused_lib,",
106+
)
107+
out = out.replace(
108+
"direct_register_custom_op(\n op_name=\"outplace_fused_experts\",",
109+
"direct_register_custom_op(\n op_name=\"outplace_fused_experts\",\n"
110+
" target_lib=infinilm_fused_lib,",
111+
)
112+
113+
needle = "def dispatch_fused_moe_kernel(\n"
114+
idx = out.find(needle)
115+
if idx == -1:
116+
raise SystemExit("dispatch_fused_moe_kernel not found")
117+
insert_at = idx + len(needle)
118+
# After opening def line + newline, insert guard at body start
119+
guard = (
120+
" if (use_int8_w8a16 or use_int4_w4a16) and (\n"
121+
" block_shape is not None and block_shape[1] > 0\n"
122+
" ):\n"
123+
" raise NotImplementedError(\n"
124+
" \"InfiniLM vendor fused_moe: INT4/INT8 WNA16 CUDA path requires vLLM native ops.\"\n"
125+
" )\n\n"
126+
)
127+
# Find first line after def that's already indented (skip docstring? none)
128+
# Insert right after `) -> None:` line end - actually after `):`
129+
line_end = out.find(") -> None:", idx)
130+
if line_end == -1:
131+
line_end = out.find("):", idx)
132+
body_start = out.find("\n", line_end) + 1
133+
out = out[:body_start] + guard + out[body_start:]
134+
135+
ocp_start = out.find(" if ocp_mx_scheme is not None:\n # TODO: On platforms")
136+
if ocp_start == -1:
137+
raise SystemExit("ocp_mx block not found")
138+
ocp_end = out.find("\n qhidden_states, a1q_scale = moe_kernel_quantize_input(", ocp_start)
139+
if ocp_end == -1:
140+
raise SystemExit("ocp_mx block end not found")
141+
out = (
142+
out[:ocp_start]
143+
+ " if ocp_mx_scheme is not None:\n"
144+
" raise NotImplementedError(\n"
145+
" \"InfiniLM vendor fused_moe: ocp_mx_scheme is unsupported.\"\n"
146+
" )\n\n"
147+
+ out[ocp_end + 1 :]
148+
)
149+
150+
hdr = (
151+
'# SPDX-License-Identifier: Apache-2.0\n# SPDX-FileCopyrightText: Copyright contributors to the vLLM project\n"""Fused MoE Triton kernels (InfiniLM vendor; see NOTICE)."""\n\n'
152+
)
153+
# Docstring already in file - replace opening docstring line
154+
out = out.replace(
155+
'"""Fused MoE Triton kernels."""\n\n',
156+
'"""Fused MoE Triton kernels (InfiniLM vendor; see NOTICE)."""\n\n',
157+
1,
158+
)
159+
160+
dst.write_text(out, encoding="utf-8")
161+
print("wrote", dst)
162+
163+
164+
if __name__ == "__main__":
165+
main()
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
4+
from __future__ import annotations
5+
6+
from enum import Enum
7+
8+
import torch
9+
import torch.nn.functional as F
10+
11+
12+
class MoEActivation(Enum):
13+
SILU = "silu"
14+
GELU = "gelu"
15+
RELU2 = "relu2"
16+
SWIGLUOAI = "swigluoai"
17+
SWIGLUSTEP = "swiglustep"
18+
SILU_NO_MUL = "silu_no_mul"
19+
GELU_NO_MUL = "gelu_no_mul"
20+
RELU2_NO_MUL = "relu2_no_mul"
21+
22+
@property
23+
def is_gated(self) -> bool:
24+
return not self.value.endswith("_no_mul")
25+
26+
@classmethod
27+
def from_str(cls, s: str) -> MoEActivation:
28+
for member in cls:
29+
if member.value == s:
30+
return member
31+
valid = [m.value for m in cls]
32+
raise ValueError(f"Unknown MoE activation: {s!r}. Valid activations: {valid}")
33+
34+
35+
def apply_moe_activation(
36+
activation: MoEActivation,
37+
output: torch.Tensor,
38+
input: torch.Tensor,
39+
) -> torch.Tensor:
40+
assert input.dim() == 2, "Input must be 2D"
41+
assert output.dim() == 2, "Output must be 2D"
42+
if activation.is_gated:
43+
assert output.size(-1) * 2 == input.size(-1), (
44+
f"{activation.value} expects 2x ratio: "
45+
f"{output.size(-1) * 2} vs {input.size(-1)}"
46+
)
47+
else:
48+
assert output.size(-1) == input.size(-1), (
49+
f"{activation.value} expects equal sizes: "
50+
f"{output.size(-1)} vs {input.size(-1)}"
51+
)
52+
53+
if activation == MoEActivation.SILU:
54+
gate, up = input.chunk(2, dim=-1)
55+
torch.mul(F.silu(gate), up, out=output)
56+
elif activation == MoEActivation.GELU:
57+
gate, up = input.chunk(2, dim=-1)
58+
torch.mul(F.gelu(gate), up, out=output)
59+
elif activation in (MoEActivation.SWIGLUOAI, MoEActivation.SWIGLUSTEP):
60+
raise NotImplementedError(f"{activation} requires vLLM Triton ops in upstream.")
61+
elif activation == MoEActivation.SILU_NO_MUL:
62+
output.copy_(F.silu(input))
63+
elif activation == MoEActivation.GELU_NO_MUL:
64+
output.copy_(F.gelu(input))
65+
elif activation == MoEActivation.RELU2_NO_MUL:
66+
tmp = F.relu(input)
67+
torch.square(tmp, out=output)
68+
else:
69+
raise ValueError(f"Unsupported FusedMoe activation: {activation}")
70+
71+
return output
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
4+
from __future__ import annotations
5+
6+
from dataclasses import dataclass
7+
8+
import torch
9+
10+
11+
def _get_config_dtype_str(
12+
dtype: torch.dtype,
13+
use_fp8_w8a8: bool = False,
14+
use_fp8_w8a16: bool = False,
15+
use_int8_w8a16: bool = False,
16+
use_int4_w4a16: bool = False,
17+
ocp_mx_scheme: str | None = None,
18+
) -> str | None:
19+
if use_fp8_w8a8:
20+
return "fp8_w8a8"
21+
elif use_fp8_w8a16:
22+
return "fp8_w8a16"
23+
elif use_int8_w8a16:
24+
return "int8_w8a16"
25+
elif use_int4_w4a16:
26+
return "int4_w4a16"
27+
elif ocp_mx_scheme is not None:
28+
return None
29+
elif dtype == torch.float:
30+
return "float32"
31+
return None
32+
33+
34+
@dataclass
35+
class FusedMoEQuantConfig:
36+
"""Minimal quant bundle for fused_experts (unquantized MoE only in this vendor)."""
37+
38+
use_fp8_w8a8: bool = False
39+
use_int8_w8a8: bool = False
40+
use_int8_w8a16: bool = False
41+
use_int4_w4a16: bool = False
42+
ocp_mx_scheme: str | None = None
43+
per_act_token_quant: bool = False
44+
block_shape: list[int] | None = None
45+
w1_scale: torch.Tensor | None = None
46+
w2_scale: torch.Tensor | None = None
47+
w1_zp: torch.Tensor | None = None
48+
w2_zp: torch.Tensor | None = None
49+
a1_scale: torch.Tensor | None = None
50+
a2_scale: torch.Tensor | None = None
51+
w1_bias: torch.Tensor | None = None
52+
w2_bias: torch.Tensor | None = None
53+
54+
def config_name(self, dtype: torch.dtype) -> str | None:
55+
return _get_config_dtype_str(
56+
use_fp8_w8a8=self.use_fp8_w8a8,
57+
use_fp8_w8a16=False,
58+
use_int8_w8a16=self.use_int8_w8a16,
59+
use_int4_w4a16=self.use_int4_w4a16,
60+
ocp_mx_scheme=self.ocp_mx_scheme,
61+
dtype=dtype,
62+
)
63+
64+
65+
FUSED_MOE_UNQUANTIZED_CONFIG = FusedMoEQuantConfig()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
4+
from __future__ import annotations
5+
6+
import os
7+
8+
# Prefer InfiniLM env; keep VLLM_* as fallback for existing workflows.
9+
INFINILM_TUNED_CONFIG_FOLDER = os.environ.get("INFINILM_TUNED_CONFIG_FOLDER")
10+
VLLM_TUNED_CONFIG_FOLDER = INFINILM_TUNED_CONFIG_FOLDER or os.environ.get(
11+
"VLLM_TUNED_CONFIG_FOLDER"
12+
)
13+
14+
VLLM_BATCH_INVARIANT = os.environ.get("VLLM_BATCH_INVARIANT", "0") == "1"

0 commit comments

Comments
 (0)