Skip to content

Commit 23ff92c

Browse files
committed
[KernelGen][MThreads] Add channel_shuffle Moore Threads specialized operator
1 parent b44d2fd commit 23ff92c

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

src/flag_gems/runtime/backend/_mthreads/ops/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from .batch_norm import batch_norm, batch_norm_backward
2323
from .bucketize import bucketize
2424
from .celu import celu
25+
from .channel_shuffle import channel_shuffle
2526
from .conv2d import conv2d
2627
from .div import (
2728
div_mode,
@@ -110,6 +111,7 @@
110111
"bucketize",
111112
"celu",
112113
# "celu_",
114+
"channel_shuffle",
113115
"conv2d",
114116
"dropout",
115117
"dropout_backward",
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright 2026 FlagOS Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import logging
16+
17+
import torch
18+
import triton
19+
import triton.language as tl
20+
21+
from flag_gems.ops.channel_shuffle import channel_shuffle as default_channel_shuffle
22+
from flag_gems.runtime import torch_device_fn
23+
from flag_gems.utils import libentry
24+
25+
logger = logging.getLogger(
26+
f'flag_gems.runtime.backend._mthreads.ops.{__name__.split(".")[-1]}'
27+
)
28+
29+
_SUPPORTED_DTYPES = {torch.float16, torch.bfloat16, torch.float32}
30+
31+
32+
@libentry()
33+
@triton.jit
34+
def channel_shuffle_kernel(
35+
x_ptr,
36+
out_ptr,
37+
total,
38+
HW: tl.constexpr,
39+
C: tl.constexpr,
40+
G: tl.constexpr,
41+
BLOCK: tl.constexpr,
42+
):
43+
pid = tl.program_id(0)
44+
offs = pid * BLOCK + tl.arange(0, BLOCK)
45+
mask = offs < total
46+
# Decompose flat output index (contiguous NCHW layout):
47+
# offs = n*C*HW + c*HW + sp, where sp in [0, HW)
48+
c = (offs // HW) % C
49+
# channel_shuffle: output channel c reads input channel
50+
# c_in = (c % G) * (C // G) + (c // G)
51+
c_in = (c % G) * (C // G) + (c // G)
52+
read_idx = offs + (c_in - c) * HW
53+
val = tl.load(x_ptr + read_idx, mask=mask)
54+
tl.store(out_ptr + offs, val, mask=mask)
55+
56+
57+
def _use_triton_kernel(x: torch.Tensor, groups) -> bool:
58+
if not isinstance(x, torch.Tensor):
59+
return False
60+
if x.device.type != "musa" or x.dtype not in _SUPPORTED_DTYPES:
61+
return False
62+
if x.ndim != 4 or not x.is_contiguous() or x.numel() == 0:
63+
return False
64+
try:
65+
g = int(groups)
66+
except Exception:
67+
return False
68+
c = x.shape[1]
69+
if g <= 0 or c % g != 0:
70+
return False
71+
return True
72+
73+
74+
def channel_shuffle(x: torch.Tensor, groups: int):
75+
logger.debug("GEMS_MTHREADS CHANNEL_SHUFFLE")
76+
if not _use_triton_kernel(x, groups):
77+
return default_channel_shuffle(x, groups)
78+
79+
N, C, H, W = x.shape
80+
HW = H * W
81+
numel = x.numel()
82+
G = int(groups)
83+
BLOCK = min(1024, triton.next_power_of_2(numel))
84+
grid = (triton.cdiv(numel, BLOCK),)
85+
with torch_device_fn.device(x.device):
86+
out = torch.empty_like(x)
87+
channel_shuffle_kernel[grid](x, out, numel, HW=HW, C=C, G=G, BLOCK=BLOCK)
88+
return out

0 commit comments

Comments
 (0)