Skip to content

Commit cb5e32f

Browse files
authored
[ExecuTorch][llm] Fuse w1+w3 into single GEMM in quantized_moe_ffn (pytorch#21538)
This PR was created by the merge bot to help merge the original PR into the main branch. ghstack PR number: pytorch#21124 by @digantdesai ^ Please use this as the source of truth for the PR details, comments, and reviews ghstack PR base: https://github.com/pytorch/executorch/tree/gh/digantdesai/74/base ghstack PR head: https://github.com/pytorch/executorch/tree/gh/digantdesai/74/head Merge bot PR base: https://github.com/pytorch/executorch/tree/gh/digantdesai/73/orig Merge bot PR head: https://github.com/pytorch/executorch/tree/gh/digantdesai/74/orig Differential Revision: [D102799854](https://our.internmc.facebook.com/intern/diff/D102799854/) @diff-train-skip-merge Co-authored-by: Digant Desai <digantdesai@meta.com>
1 parent 24ff58a commit cb5e32f

6 files changed

Lines changed: 131 additions & 166 deletions

File tree

‎examples/models/llama/source_transformation/moe.py‎

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,15 @@ class QuantizedMoEFFN(nn.Module):
104104
Buffers (registered, not parameters):
105105
gate_weight [E, D] fp32 — copied from `MOEFeedForward.gate.weight`
106106
expert_bias [E] or [0] fp32 — empty when `use_expert_bias=False`
107-
packed_w1 [E, packed_bytes_w1] uint8 — torchao opaque blobs
108-
packed_w3 [E, packed_bytes_w3] uint8
109-
packed_w2 [E, packed_bytes_w2] uint8
107+
packed_w13 [E, packed_bytes_w13] uint8 — fused w1+w3 torchao blobs
108+
packed_w2 [E, packed_bytes_w2] uint8
110109
"""
111110

112111
def __init__(
113112
self,
114113
gate_weight: torch.Tensor,
115114
expert_bias: torch.Tensor | None,
116-
packed_w1: torch.Tensor,
117-
packed_w3: torch.Tensor,
115+
packed_w13: torch.Tensor,
118116
packed_w2: torch.Tensor,
119117
*,
120118
num_experts: int,
@@ -149,8 +147,7 @@ def __init__(
149147

150148
# torchao packed blobs are int8 tensors; reinterpret the bytes as
151149
# uint8 (no value conversion) for the op schema.
152-
self.register_buffer("packed_w1", packed_w1.view(torch.uint8))
153-
self.register_buffer("packed_w3", packed_w3.view(torch.uint8))
150+
self.register_buffer("packed_w13", packed_w13.view(torch.uint8))
154151
self.register_buffer("packed_w2", packed_w2.view(torch.uint8))
155152
self.shared_expert: nn.Module | None = None
156153

@@ -178,8 +175,7 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:
178175
x_flat,
179176
self.gate_weight,
180177
self.expert_bias,
181-
self.packed_w1,
182-
self.packed_w3,
178+
self.packed_w13,
183179
self.packed_w2,
184180
self.num_activated_experts,
185181
self.num_experts,
@@ -261,18 +257,15 @@ def _build_quantized_moe_ffn_from_eager(
261257
_torchao_pack_int4_weight if weight_nbit == 4 else _torchao_pack_int8_weight
262258
)
263259

264-
packed_w1_list: list[torch.Tensor] = []
265-
packed_w3_list: list[torch.Tensor] = []
260+
packed_w13_list: list[torch.Tensor] = []
266261
packed_w2_list: list[torch.Tensor] = []
267262
for ei in range(e):
268-
# w1, w3 share the [F, D] shape, group along K=D.
269-
packed_w1_list.append(pack_fn(w1[ei], group_size))
270-
packed_w3_list.append(pack_fn(w3[ei], group_size))
271-
# w2 packed shape is [D, F], group along K=F.
263+
# Fuse w1 and w3 into a single [2F, D] matrix before packing.
264+
w13 = torch.cat([w1[ei], w3[ei]], dim=0) # [2F, D]
265+
packed_w13_list.append(pack_fn(w13, group_size))
272266
packed_w2_list.append(pack_fn(w2_packed_in[ei], group_size))
273267

274-
packed_w1 = _stack_per_expert_packed(packed_w1_list)
275-
packed_w3 = _stack_per_expert_packed(packed_w3_list)
268+
packed_w13 = _stack_per_expert_packed(packed_w13_list)
276269
packed_w2 = _stack_per_expert_packed(packed_w2_list)
277270

278271
# `MOEFeedForward` registers `expert_bias` as a buffer that is None unless
@@ -284,8 +277,7 @@ def _build_quantized_moe_ffn_from_eager(
284277
replacement = QuantizedMoEFFN(
285278
gate_weight=moe.gate.weight.detach().clone(),
286279
expert_bias=expert_bias,
287-
packed_w1=packed_w1,
288-
packed_w3=packed_w3,
280+
packed_w13=packed_w13,
289281
packed_w2=packed_w2,
290282
num_experts=e,
291283
num_activated_experts=moe.num_activated_experts,

‎extension/llm/custom_ops/custom_ops.py‎

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,7 @@ def _validate_quantized_moe_ffn_params(
168168
x,
169169
gate_weight,
170170
expert_bias,
171-
packed_w1,
172-
packed_w3,
171+
packed_w13,
173172
packed_w2,
174173
num_activated_experts,
175174
num_experts,
@@ -202,18 +201,14 @@ def _validate_quantized_moe_ffn_params(
202201
), f"expert_bias must be float32, got {expert_bias.dtype}"
203202

204203
for name, t in (
205-
("packed_w1", packed_w1),
206-
("packed_w3", packed_w3),
204+
("packed_w13", packed_w13),
207205
("packed_w2", packed_w2),
208206
):
209207
assert (
210208
t.dim() == 2 and t.size(0) == num_experts
211209
), f"{name} must be [E={num_experts}, packed_bytes], got {list(t.size())}"
212210
assert t.dtype == torch.uint8, f"{name} must be uint8, got {t.dtype}"
213211

214-
assert packed_w1.size(1) == packed_w3.size(
215-
1
216-
), "packed_w1 and packed_w3 per-expert blob sizes must match"
217212
assert (
218213
0 < num_activated_experts <= num_experts
219214
), f"num_activated_experts ({num_activated_experts}) out of range [1, {num_experts}]"
@@ -241,8 +236,7 @@ def quantized_moe_ffn_meta(
241236
x,
242237
gate_weight,
243238
expert_bias,
244-
packed_w1,
245-
packed_w3,
239+
packed_w13,
246240
packed_w2,
247241
num_activated_experts,
248242
num_experts,
@@ -257,8 +251,7 @@ def quantized_moe_ffn_meta(
257251
x,
258252
gate_weight,
259253
expert_bias,
260-
packed_w1,
261-
packed_w3,
254+
packed_w13,
262255
packed_w2,
263256
num_activated_experts,
264257
num_experts,

0 commit comments

Comments
 (0)