Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / platform / torch / custom_ops / gdn / triton / utils.py: 0%
198 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +0800
1# Copyright 2026 Huawei Technologies Co., Ltd
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# -*- coding: utf-8 -*-
16# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
18# pylint: disable=no-name-in-module,consider-using-from-import,pointless-string-statement
19# pylint: disable=redefined-outer-name,missing-public-type-hints,missing-public-docstring
20# pylint: disable=unused-argument,no-member,import-outside-toplevel
21# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring
22# pylint: disable=missing-class-docstring,broad-exception-caught,protected-access
24import itertools
25import contextlib
26import os
27import functools
28import warnings
29import logging
30from enum import Enum
31from functools import lru_cache
32from typing import Any, Callable, Optional
33from packaging import version
35import torch
36import triton
37import triton.language as tl
38import triton.language.extra.libdevice as tldevice
39import triton.runtime.driver as driver
41logger = logging.getLogger(__name__)
43FLA_CI_ENV = os.getenv("FLA_CI_ENV") == "1"
46def tensor_cache(fn: Optional[Callable[..., torch.Tensor]] = None, *, maxsize: int = 1) -> Any:
47 """
48 A decorator that caches the most recent results of a function with tensor inputs.
50 This decorator will store the outputs of the decorated function for the most recent
51 set of input tensors, up to `maxsize` entries. If the function is called again with
52 the same input tensors, it will return the cached result.
54 When maxsize=1 (default), the behavior is identical to caching only the most recent result.
55 Can be used as @tensor_cache or @tensor_cache(maxsize=n).
57 Args:
58 fn (Callable[..., torch.Tensor], optional):
59 The function to be decorated when used without parentheses.
60 maxsize (int):
61 Maximum number of input combinations to cache. Default is 1.
63 Returns:
64 Callable[..., torch.Tensor]:
65 A wrapped version of the input function with caching.
66 """
67 if maxsize < 1:
68 raise ValueError("maxsize must be at least 1")
70 def _is_match(a: Any, b: Any) -> bool:
71 if isinstance(a, torch.Tensor) and isinstance(b, torch.Tensor):
72 return a is b
73 try:
74 return a == b
75 except Exception:
76 return a is b
78 def _make_wrapper(fn: Callable[..., torch.Tensor]) -> Callable[..., torch.Tensor]:
79 cache: list = []
81 @functools.wraps(fn)
82 def wrapper(*args: Any, **kwargs: Any) -> Any:
83 for i, (cached_args, cached_kwargs, cached_result) in enumerate(cache):
84 if len(args) == len(cached_args) and len(kwargs) == len(cached_kwargs):
85 if all(_is_match(a, b) for a, b in zip(args, cached_args)) and all(
86 k in cached_kwargs and _is_match(v, cached_kwargs[k]) for k, v in kwargs.items()
87 ):
88 if i != 0:
89 cache.insert(0, cache.pop(i))
90 return cached_result
92 result = fn(*args, **kwargs)
93 cache.insert(0, (args, kwargs, result))
94 if len(cache) > maxsize:
95 cache.pop()
96 return result
98 return wrapper
100 if fn is not None:
101 return _make_wrapper(fn)
102 return _make_wrapper
105@tensor_cache
106def prepare_lens(cu_seqlens: torch.LongTensor) -> torch.LongTensor:
107 return cu_seqlens[1:] - cu_seqlens[:-1]
110@tensor_cache(maxsize=3)
111def prepare_chunk_indices(cu_seqlens: torch.LongTensor, chunk_size: int) -> torch.LongTensor:
112 indices = torch.cat([torch.arange(n) for n in triton.cdiv(prepare_lens(cu_seqlens), chunk_size).tolist()])
113 return torch.stack([indices.eq(0).cumsum(0) - 1, indices], 1).to(cu_seqlens)
116def get_abs_err(x, y):
117 return (x.detach() - y.detach()).flatten().abs().max().item()
120def get_err_ratio(x, y):
121 err = (x.detach() - y.detach()).flatten().square().mean().sqrt().item()
122 base = (x.detach()).flatten().square().mean().sqrt().item()
123 return err / (base + 1e-8)
126def assert_close(prefix, ref, tri, ratio, warning=False, err_atol=1e-6):
127 abs_atol = get_abs_err(ref, tri)
128 msg = f"{prefix:>16} diff: {abs_atol:.6f} ratio: {get_err_ratio(ref, tri):.6f}"
129 logger.info(msg)
130 error_rate = get_err_ratio(ref, tri)
131 if abs_atol <= err_atol:
132 return
133 if warning or (FLA_CI_ENV and (error_rate < 0.01 or abs_atol <= 0.3)):
134 if error_rate > ratio:
135 warnings.warn(msg)
136 else:
137 assert error_rate < ratio, msg
140if hasattr(triton.language, '_experimental_make_tensor_descriptor'):
141 # For Triton 3.3.x
142 make_tensor_descriptor = triton.language._experimental_make_tensor_descriptor
143elif hasattr(triton.language, 'make_tensor_descriptor'):
144 # For Triton 3.4.x and later
145 make_tensor_descriptor = triton.language.make_tensor_descriptor
146else:
147 """
148 Fallback implementation when TMA is not supported.
149 Returns None to indicate TMA descriptors are unavailable.
150 Just make triton compiler happy.
151 """
153 @triton.jit
154 def make_tensor_descriptor(
155 base,
156 shape,
157 strides,
158 block_shape,
159 _builder=None,
160 ):
161 return None
164@lru_cache(maxsize=None)
165def get_available_device() -> str:
166 try:
167 return triton.runtime.driver.active.get_current_target().backend
168 except Exception:
169 _cpu_device_warning()
170 return 'cpu'
173def map_triton_backend_to_torch_device() -> str:
174 backend = get_available_device() # 'cuda' | 'hip' | 'xpu' | 'cpu' | ...
175 return {'cuda': 'cuda', 'hip': 'cuda', 'xpu': 'xpu'}.get(backend, backend)
178device = get_available_device() if get_available_device() != 'hip' else 'cuda'
179device_torch_lib = getattr(torch, device)
180device_platform = get_available_device()
181is_amd = device_platform == 'hip'
182is_nvidia = device_platform == 'cuda'
183is_nvidia_hopper = is_nvidia and (
184 'NVIDIA H' in torch.cuda.get_device_name(0) or torch.cuda.get_device_capability()[0] >= 9
185)
187is_tf32_supported = is_nvidia and torch.cuda.get_device_capability(0)[0] >= 8
188is_tma_supported = (
189 (is_nvidia and torch.cuda.get_device_capability(0)[0] >= 9)
190 and os.environ.get('FLA_NO_USE_TMA', '0') != '1'
191 and (
192 hasattr(triton.language, '_experimental_make_tensor_descriptor')
193 or hasattr(triton.language, 'make_tensor_descriptor')
194 )
195)
197if is_nvidia and not is_tf32_supported:
198 # Make old card happy, since triton will use tf32 by default.
199 # This is a workaround for old nvidia card.
200 os.environ['TRITON_F32_DEFAULT'] = 'ieee'
203@lru_cache(maxsize=None)
204def check_pytorch_version(version_s: str = '2.4') -> bool:
205 return version.parse(torch.__version__) >= version.parse(version_s)
208if check_pytorch_version('2.4'):
209 device = 'cuda' if device == 'cpu' else device
210 autocast_custom_fwd = functools.partial(torch.amp.custom_fwd, device_type=device)
211 autocast_custom_bwd = functools.partial(torch.amp.custom_bwd, device_type=device)
213 def custom_device_ctx(index: int):
214 return device_torch_lib.device(index)
215else:
216 assert device == 'cuda', 'Only cuda device is supported for PyTorch version < 2.4.0.'
217 autocast_custom_fwd = device_torch_lib.amp.custom_fwd
218 autocast_custom_bwd = device_torch_lib.amp.custom_bwd
220 def custom_device_ctx(index: int):
221 return torch.cuda.device(index)
224def input_guard(fn: Callable[..., torch.Tensor]) -> Callable[..., torch.Tensor]:
225 """
226 A decorator to make sure all input tensors are contiguous and set the device based on input tensors.
227 """
229 @functools.wraps(fn)
230 def wrapper(*args, **kwargs):
231 contiguous_args = (i if not isinstance(i, torch.Tensor) else i.contiguous() for i in args)
232 contiguous_kwargs = {k: (v if not isinstance(v, torch.Tensor) else v.contiguous()) for k, v in kwargs.items()}
234 tensor = None
235 for arg in args:
236 if isinstance(arg, torch.Tensor):
237 tensor = arg
238 break
239 if tensor is None:
240 for value in kwargs.values():
241 if isinstance(value, torch.Tensor):
242 tensor = value
243 break
245 if tensor is not None:
246 ctx = custom_device_ctx(tensor.device.index)
247 else:
248 ctx = contextlib.nullcontext()
250 with ctx:
251 return fn(*contiguous_args, **contiguous_kwargs)
253 return wrapper
256def _cpu_device_warning():
257 warnings.warn(('Triton is not supported on current platform, roll back to CPU.'), stacklevel=1)
260@tensor_cache
261def prepare_chunk_offsets(cu_seqlens: torch.LongTensor, chunk_size: int) -> torch.LongTensor:
262 return torch.cat([cu_seqlens.new_tensor([0]), triton.cdiv(prepare_lens(cu_seqlens), chunk_size)]).cumsum(-1)
265if os.environ.get('FLA_USE_FAST_OPS', '0') == '1':
266 exp = tldevice.fast_expf
267 exp2 = tldevice.exp2
268 log = tldevice.fast_logf
269 log2 = tldevice.fast_log2f
270else:
271 exp = tl.exp
272 exp2 = tl.math.exp2
273 log = tl.log
274 log2 = tl.log2
277def get_all_max_shared_mem():
278 try:
279 return [
280 triton.runtime.driver.active.utils.get_device_properties(i)['max_shared_mem']
281 for i in range(device_torch_lib.device_count())
282 ]
283 except Exception:
284 _cpu_device_warning()
285 return [-1]
288class Backend(Enum):
289 ADA = 101376 # RTX 4090
290 AMPERE = 166912 # A100
291 HOPPER = 232448 # H100
292 DEFAULT = 102400 # Default
294 @classmethod
295 def get_shared_memory(cls, arch: str) -> int:
296 try:
297 return cls[arch.upper()].value
298 except KeyError:
299 return cls.DEFAULT.value
302@lru_cache(maxsize=None)
303def check_shared_mem(arch: str = "none", tensor_idx: int = 0) -> bool:
304 try:
305 device_shared_mem_list = get_all_max_shared_mem()
306 max_shared_memory = device_shared_mem_list[tensor_idx]
307 return max_shared_memory >= Backend.get_shared_memory(arch)
308 except Exception:
309 return False
312def get_autotune_config(
313 multibuffer_list: tuple = (False,),
314 unit_flag_list: tuple = (False,),
315 limit_auto_multi_buffer_only_for_local_buffer_list: tuple = (False,),
316 limit_auto_multi_buffer_of_local_buffer_list: tuple = ("no-l0c",),
317 set_workspace_multibuffer_list: tuple = (2, 4),
318 enable_hivm_auto_cv_balance_list: tuple = (True,),
319 tile_mix_vector_loop_num_list: tuple = (2, 4),
320 tile_mix_cube_loop_num_list: tuple = (2, 4),
321):
322 configs = []
323 for (
324 multibuffer,
325 unit_flag,
326 limit_auto_multi_buffer_only_for_local_buffer,
327 limit_auto_multi_buffer_of_local_buffer,
328 ) in itertools.product(
329 list(multibuffer_list),
330 list(unit_flag_list),
331 list(limit_auto_multi_buffer_only_for_local_buffer_list),
332 list(limit_auto_multi_buffer_of_local_buffer_list),
333 ):
334 base_config_dict = {
335 'multibuffer': multibuffer,
336 'unit_flag': unit_flag,
337 'limit_auto_multi_buffer_only_for_local_buffer': limit_auto_multi_buffer_only_for_local_buffer,
338 'limit_auto_multi_buffer_of_local_buffer': limit_auto_multi_buffer_of_local_buffer,
339 }
341 if limit_auto_multi_buffer_only_for_local_buffer:
342 configs.append(triton.Config(base_config_dict))
343 else:
344 for (
345 set_workspace_multibuffer,
346 enable_hivm_auto_cv_balance,
347 tile_mix_vector_loop,
348 tile_mix_cube_loop,
349 ) in itertools.product(
350 list(set_workspace_multibuffer_list),
351 list(enable_hivm_auto_cv_balance_list),
352 list(tile_mix_vector_loop_num_list),
353 list(tile_mix_cube_loop_num_list),
354 ):
355 full_config_dict = base_config_dict.copy()
356 full_config_dict.update(
357 {
358 'set_workspace_multibuffer': set_workspace_multibuffer,
359 'enable_hivm_auto_cv_balance': enable_hivm_auto_cv_balance,
360 'tile_mix_vector_loop': tile_mix_vector_loop,
361 'tile_mix_cube_loop': tile_mix_cube_loop,
362 }
363 )
364 configs.append(triton.Config(full_config_dict))
365 return configs
368def get_npu_properties():
369 return driver.active.utils.get_device_properties(torch.npu.current_device())
372@functools.cache
373def get_vector_num() -> int:
374 import torch_npu
376 current_device = torch_npu.npu.current_device()
377 properties = driver.active.utils.get_device_properties(current_device)
378 return properties["num_vectorcore"]
381@lru_cache
382def is_arch35():
383 try:
384 import torch_npu
386 return "Ascend910_95" in torch_npu.npu.get_device_name() or "Ascend950" in torch_npu.npu.get_device_name()
387 except Exception:
388 return False