Skip to content

Commit bdd8ae2

Browse files
committed
[KernelGen][MThreads] Add log2_ Moore Threads specialized operator
1 parent 4350421 commit bdd8ae2

2 files changed

Lines changed: 116 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
@@ -35,6 +35,7 @@
3535
from .index_select import index_select
3636
from .linalg_cholesky import linalg_cholesky
3737
from .log import log
38+
from .log2_ import log2_
3839
from .log10 import log10, log10_, log10_out
3940
from .log_normal_ import log_normal_
4041
from .log_softmax import (
@@ -111,6 +112,7 @@
111112
"log10",
112113
"log10_",
113114
"log10_out",
115+
"log2_",
114116
"log_normal_",
115117
"log_softmax",
116118
"log_softmax_backward",
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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.log2 import log2_ as default_log2_
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 _log2_kernel(x_ptr, n_elements, BLOCK_SIZE: tl.constexpr):
35+
pid = tl.program_id(axis=0)
36+
offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
37+
mask = offsets < n_elements
38+
x = tl.load(x_ptr + offsets, mask=mask, other=1.0)
39+
# Compute in fp32 (matches reference: torch.log2(x.float()).to(x.dtype)),
40+
# then round back to the input dtype.
41+
x = x.to(tl.float32)
42+
x = tl.log2(x)
43+
x = x.to(x_ptr.dtype.element_ty)
44+
tl.store(x_ptr + offsets, x, mask=mask)
45+
46+
47+
@libentry()
48+
@triton.jit
49+
def _log2_kernel_even(x_ptr, BLOCK_SIZE: tl.constexpr):
50+
# Unmasked specialization for tensors whose size is an exact multiple of
51+
# BLOCK_SIZE; probes and eval show it ~1% faster than the masked path for
52+
# large 16-bit tensors on this backend.
53+
pid = tl.program_id(axis=0)
54+
offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
55+
x = tl.load(x_ptr + offsets)
56+
x = x.to(tl.float32)
57+
x = tl.log2(x)
58+
x = x.to(x_ptr.dtype.element_ty)
59+
tl.store(x_ptr + offsets, x)
60+
61+
62+
def _use_triton_kernel(x: torch.Tensor) -> bool:
63+
if not isinstance(x, torch.Tensor):
64+
return False
65+
if x.device.type != "musa" or x.dtype not in _SUPPORTED_DTYPES:
66+
return False
67+
if not x.is_contiguous() or x.numel() == 0:
68+
return False
69+
return True
70+
71+
72+
def _launch_log2_(x: torch.Tensor):
73+
n_elements = x.numel()
74+
x_flat = x.view(-1)
75+
with torch_device_fn.device(x.device):
76+
if n_elements <= 32768:
77+
# Small tensors are dispatch-bound: tiny blocks with 2 warps measured
78+
# ~10% lower latency than BLOCK 512/4 (min 2.52us vs 2.89us, avg
79+
# 2.68us vs 3.42us on the 4096-element workload, replicated across
80+
# two shapes and three probe runs).
81+
# Rule 21: hardcoded BLOCK_SIZE with rationale.
82+
block = 64
83+
grid = (triton.cdiv(n_elements, block),)
84+
_log2_kernel[grid](x_flat, n_elements, BLOCK_SIZE=block, num_warps=2)
85+
elif x.element_size() == 2 and n_elements % 2048 == 0:
86+
# Large 16-bit tensors: 16 elems/thread (2x128-bit in flight) and no
87+
# bounds mask; measured fastest on this backend.
88+
# Rule 21: hardcoded BLOCK_SIZE with rationale.
89+
grid = (n_elements // 2048,)
90+
_log2_kernel_even[grid](x_flat, BLOCK_SIZE=2048)
91+
else:
92+
# Large 32-bit tensors. 16 elems/thread with 2 warps (4x128-bit in
93+
# flight) measured faster on the 16.7M-element tensor (0.10472ms vs
94+
# 0.10506ms) but slower on the 4.2M-element one, so split by size.
95+
# Rule 21: hardcoded BLOCK_SIZE with rationale.
96+
block = 1024
97+
grid = (triton.cdiv(n_elements, block),)
98+
_log2_kernel[grid](
99+
x_flat,
100+
n_elements,
101+
BLOCK_SIZE=block,
102+
num_warps=2 if n_elements > 8388608 else 4,
103+
)
104+
return x
105+
106+
107+
def log2_(x):
108+
logger.debug("GEMS_MTHREADS LOG2_")
109+
if not _use_triton_kernel(x):
110+
return default_log2_(x)
111+
return _launch_log2_(x)
112+
113+
114+
__all__ = ["log2_"]

0 commit comments

Comments
 (0)