nvmath-python 1.0.0__cp314-cp314t-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nvmath/__init__.pxd +0 -0
- nvmath/__init__.py +45 -0
- nvmath/_internal/__init__.py +0 -0
- nvmath/_internal/attribute_ifc_factory.py +330 -0
- nvmath/_internal/layout.py +70 -0
- nvmath/_internal/templates.py +130 -0
- nvmath/_internal/threadsafe.py +106 -0
- nvmath/_internal/utils.py +43 -0
- nvmath/_internal/workspace.py +490 -0
- nvmath/_utils.py +147 -0
- nvmath/bindings/__init__.py +60 -0
- nvmath/bindings/_internal/__init__.pxd +0 -0
- nvmath/bindings/_internal/__init__.py +0 -0
- nvmath/bindings/_internal/common_types.pxd +31 -0
- nvmath/bindings/_internal/cublas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cublas.pxd +530 -0
- nvmath/bindings/_internal/cublasLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cublasLt.pxd +59 -0
- nvmath/bindings/_internal/cublasMp.pxd +52 -0
- nvmath/bindings/_internal/cudss.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cudss.pxd +54 -0
- nvmath/bindings/_internal/cufft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cufft.pxd +70 -0
- nvmath/bindings/_internal/cufftMp.pxd +77 -0
- nvmath/bindings/_internal/curand.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/curand.pxd +42 -0
- nvmath/bindings/_internal/cusolver.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolver.pxd +15 -0
- nvmath/bindings/_internal/cusolverDn.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolverDn.pxd +406 -0
- nvmath/bindings/_internal/cusolverMp.pxd +71 -0
- nvmath/bindings/_internal/cusolverSp.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolverSp.pxd +75 -0
- nvmath/bindings/_internal/cusparse.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusparse.pxd +471 -0
- nvmath/bindings/_internal/cusparseLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusparseLt.pxd +48 -0
- nvmath/bindings/_internal/cutensor.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cutensor.pxd +58 -0
- nvmath/bindings/_internal/mathdx.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/mathdx.pxd +116 -0
- nvmath/bindings/_internal/nvshmem.pxd +29 -0
- nvmath/bindings/_internal/utils.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/utils.pxd +174 -0
- nvmath/bindings/_internal/utils.pyi +10 -0
- nvmath/bindings/cublas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cublas.pxd +558 -0
- nvmath/bindings/cublas.pyi +812 -0
- nvmath/bindings/cublasLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cublasLt.pxd +109 -0
- nvmath/bindings/cublasLt.pyi +1461 -0
- nvmath/bindings/cublasMp.pxd +85 -0
- nvmath/bindings/cublasMp.pyi +267 -0
- nvmath/bindings/cudss.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cudss.pxd +98 -0
- nvmath/bindings/cudss.pyi +443 -0
- nvmath/bindings/cufft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cufft.pxd +118 -0
- nvmath/bindings/cufft.pyi +301 -0
- nvmath/bindings/cufftMp.pxd +124 -0
- nvmath/bindings/cufftMp.pyi +326 -0
- nvmath/bindings/curand.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/curand.pxd +71 -0
- nvmath/bindings/curand.pyi +189 -0
- nvmath/bindings/cusolver.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusolver.pxd +62 -0
- nvmath/bindings/cusolver.pyi +320 -0
- nvmath/bindings/cusolverDn.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusolverDn.pxd +430 -0
- nvmath/bindings/cusolverDn.pyi +422 -0
- nvmath/bindings/cusolverMp.pxd +98 -0
- nvmath/bindings/cusolverMp.pyi +114 -0
- nvmath/bindings/cusolverSp.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusolverSp.pxd +95 -0
- nvmath/bindings/cusolverSp.pyi +70 -0
- nvmath/bindings/cusparse.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusparse.pxd +546 -0
- nvmath/bindings/cusparse.pyi +1017 -0
- nvmath/bindings/cusparseLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusparseLt.pxd +99 -0
- nvmath/bindings/cusparseLt.pyi +252 -0
- nvmath/bindings/cutensor.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cutensor.pxd +98 -0
- nvmath/bindings/cutensor.pyi +324 -0
- nvmath/bindings/cycublas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycublas.pxd +664 -0
- nvmath/bindings/cycublasLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycublasLt.pxd +1045 -0
- nvmath/bindings/cycublasMp.pxd +171 -0
- nvmath/bindings/cycudss.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycudss.pxd +277 -0
- nvmath/bindings/cycufft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycufft.pxd +333 -0
- nvmath/bindings/cycufftMp.pxd +342 -0
- nvmath/bindings/cycurand.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycurand.pxd +141 -0
- nvmath/bindings/cycusolver.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolver.pxd +137 -0
- nvmath/bindings/cycusolverDn.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolverDn.pxd +443 -0
- nvmath/bindings/cycusolverMp.pxd +107 -0
- nvmath/bindings/cycusolverSp.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolverSp.pxd +93 -0
- nvmath/bindings/cycusparse.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusparse.pxd +679 -0
- nvmath/bindings/cycusparseLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusparseLt.pxd +135 -0
- nvmath/bindings/cycutensor.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycutensor.pxd +189 -0
- nvmath/bindings/cymathdx.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cymathdx.pxd +552 -0
- nvmath/bindings/cynvshmem.pxd +118 -0
- nvmath/bindings/mathdx.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/mathdx.pxd +182 -0
- nvmath/bindings/mathdx.pyi +1562 -0
- nvmath/bindings/nvpl/__init__.pxd +0 -0
- nvmath/bindings/nvpl/__init__.py +13 -0
- nvmath/bindings/nvpl/_internal/__init__.pxd +0 -0
- nvmath/bindings/nvpl/_internal/__init__.py +0 -0
- nvmath/bindings/nvpl/_internal/blas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/_internal/blas.pxd +237 -0
- nvmath/bindings/nvpl/_internal/fft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/_internal/fft.pxd +36 -0
- nvmath/bindings/nvpl/blas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/blas.pxd +131 -0
- nvmath/bindings/nvpl/blas.pyi +168 -0
- nvmath/bindings/nvpl/cyblas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/cyblas.pxd +280 -0
- nvmath/bindings/nvpl/cyfft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/cyfft.pxd +93 -0
- nvmath/bindings/nvpl/fft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/fft.pxd +100 -0
- nvmath/bindings/nvpl/fft.pyi +168 -0
- nvmath/bindings/nvshmem.pxd +54 -0
- nvmath/bindings/nvshmem.pyi +191 -0
- nvmath/device/__init__.py +38 -0
- nvmath/device/_deprecated.py +33 -0
- nvmath/device/common.py +315 -0
- nvmath/device/common_backend.py +131 -0
- nvmath/device/common_cuda.py +201 -0
- nvmath/device/common_numba.py +300 -0
- nvmath/device/common_numba_cuda_mlir.py +202 -0
- nvmath/device/common_opaque_tensor.py +201 -0
- nvmath/device/cublasdx.py +1606 -0
- nvmath/device/cublasdx_backend.py +860 -0
- nvmath/device/cublasdx_numba.py +1534 -0
- nvmath/device/cublasdx_numba_cuda_mlir.py +208 -0
- nvmath/device/cufftdx.py +373 -0
- nvmath/device/cufftdx_backend.py +220 -0
- nvmath/device/cufftdx_numba.py +140 -0
- nvmath/device/cufftdx_numba_cuda_mlir.py +79 -0
- nvmath/device/curand_kernel.py +9147 -0
- nvmath/device/cusolverdx.py +2708 -0
- nvmath/device/cusolverdx_backend.py +440 -0
- nvmath/device/cusolverdx_numba.py +567 -0
- nvmath/device/cusolverdx_numba_cuda_mlir.py +604 -0
- nvmath/device/cusolverdx_overload_backend.py +1029 -0
- nvmath/device/llvm_array.py +29 -0
- nvmath/device/random.py +441 -0
- nvmath/device/random_helpers.py +23 -0
- nvmath/device/random_states.py +187 -0
- nvmath/device/types.py +138 -0
- nvmath/device/vector_types_numba.py +259 -0
- nvmath/distributed/__init__.py +200 -0
- nvmath/distributed/_internal/__init__.py +0 -0
- nvmath/distributed/_internal/nccl.py +86 -0
- nvmath/distributed/_internal/nvshmem.py +307 -0
- nvmath/distributed/_internal/symmetric_memory.py +35 -0
- nvmath/distributed/_internal/tensor_ifc.py +70 -0
- nvmath/distributed/_internal/tensor_ifc_cupy.py +68 -0
- nvmath/distributed/_internal/tensor_ifc_host_device.py +172 -0
- nvmath/distributed/_internal/tensor_ifc_numpy.py +46 -0
- nvmath/distributed/_internal/tensor_ifc_torch.py +162 -0
- nvmath/distributed/_internal/tensor_wrapper.py +81 -0
- nvmath/distributed/_utils.py +167 -0
- nvmath/distributed/distribution/__init__.py +30 -0
- nvmath/distributed/distribution/_configuration.py +39 -0
- nvmath/distributed/distribution/distributions.py +1024 -0
- nvmath/distributed/distribution/redistribute.py +1284 -0
- nvmath/distributed/fft/__init__.py +7 -0
- nvmath/distributed/fft/_configuration.py +82 -0
- nvmath/distributed/fft/fft.py +2742 -0
- nvmath/distributed/linalg/__init__.py +22 -0
- nvmath/distributed/linalg/_internal/__init__.py +3 -0
- nvmath/distributed/linalg/_internal/epilog_protocol.py +586 -0
- nvmath/distributed/linalg/_internal/matmul_desc_ifc.py +28 -0
- nvmath/distributed/linalg/advanced/__init__.py +8 -0
- nvmath/distributed/linalg/advanced/_configuration.py +171 -0
- nvmath/distributed/linalg/advanced/matmulmod.py +3573 -0
- nvmath/distributed/linalg/generic/__init__.py +8 -0
- nvmath/distributed/linalg/generic/_caching.py +66 -0
- nvmath/distributed/linalg/generic/_configuration.py +61 -0
- nvmath/distributed/linalg/generic/_factorization.py +172 -0
- nvmath/distributed/linalg/generic/_initialization.py +966 -0
- nvmath/distributed/linalg/generic/_problem_spec.py +511 -0
- nvmath/distributed/linalg/generic/solvermod.py +1368 -0
- nvmath/distributed/process_group.py +408 -0
- nvmath/fft/__init__.py +7 -0
- nvmath/fft/_configuration.py +189 -0
- nvmath/fft/_exec_utils.py +82 -0
- nvmath/fft/_helpers.py +237 -0
- nvmath/fft/fft.py +3122 -0
- nvmath/internal/__init__.pxd +3 -0
- nvmath/internal/__init__.py +10 -0
- nvmath/internal/_bindings.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/_bindings.pxd +18 -0
- nvmath/internal/_device_utils.py +45 -0
- nvmath/internal/_layout/__init__.pxd +3 -0
- nvmath/internal/_layout/__init__.py +7 -0
- nvmath/internal/_layout/_layout.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/_layout/_layout.pxd +1303 -0
- nvmath/internal/_layout/_layout.pyi +1145 -0
- nvmath/internal/enum_utils.py +142 -0
- nvmath/internal/formatters.py +87 -0
- nvmath/internal/mem_limit.py +51 -0
- nvmath/internal/memory.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/memory.pxd +13 -0
- nvmath/internal/memory.pyi +50 -0
- nvmath/internal/ndbuffer/__init__.pxd +3 -0
- nvmath/internal/ndbuffer/__init__.py +9 -0
- nvmath/internal/ndbuffer/_copy_kernel.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/ndbuffer/_copy_kernel.pxd +10 -0
- nvmath/internal/ndbuffer/_jit.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/ndbuffer/_jit.pxd +7 -0
- nvmath/internal/ndbuffer/_ndbuffer.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/ndbuffer/_ndbuffer.pxd +40 -0
- nvmath/internal/ndbuffer/_ndbuffer.pyi +463 -0
- nvmath/internal/ndbuffer/copy_kernel/args.h +34 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/array_view.h +52 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/elementwise.h +68 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/grid_indexer.h +69 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/transposed.h +242 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/type_utils.h +39 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/utils.h +132 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/vec.h +159 -0
- nvmath/internal/ndbuffer/copy_kernel/elementwise.h +53 -0
- nvmath/internal/ndbuffer/copy_kernel/transposed.h +58 -0
- nvmath/internal/package_ifc.py +168 -0
- nvmath/internal/package_ifc_cuda.py +57 -0
- nvmath/internal/package_ifc_cupy.py +67 -0
- nvmath/internal/package_ifc_torch.py +69 -0
- nvmath/internal/package_wrapper.py +14 -0
- nvmath/internal/tensor_ifc.py +179 -0
- nvmath/internal/tensor_ifc_cupy.py +234 -0
- nvmath/internal/tensor_ifc_ndbuffer.py +147 -0
- nvmath/internal/tensor_ifc_numpy.py +184 -0
- nvmath/internal/tensor_ifc_torch.py +178 -0
- nvmath/internal/tensor_wrapper.py +160 -0
- nvmath/internal/typemaps.py +113 -0
- nvmath/internal/utils.py +805 -0
- nvmath/linalg/__init__.py +56 -0
- nvmath/linalg/_internal/__init__.py +3 -0
- nvmath/linalg/_internal/algo_cap_ifc.py +82 -0
- nvmath/linalg/_internal/algo_config_ifc.py +43 -0
- nvmath/linalg/_internal/batch.py +234 -0
- nvmath/linalg/_internal/enum_to_tuples.py +64 -0
- nvmath/linalg/_internal/epilog_protocol.py +766 -0
- nvmath/linalg/_internal/layout.py +624 -0
- nvmath/linalg/_internal/matmul_desc_ifc.py +28 -0
- nvmath/linalg/_internal/matmul_pref_ifc.py +27 -0
- nvmath/linalg/_internal/matrix_layout_ifc.py +26 -0
- nvmath/linalg/_internal/solver_utils.py +432 -0
- nvmath/linalg/_internal/typemaps.py +144 -0
- nvmath/linalg/_internal/utils.py +157 -0
- nvmath/linalg/advanced/__init__.py +8 -0
- nvmath/linalg/advanced/_algorithmmod.py +170 -0
- nvmath/linalg/advanced/_configuration.py +351 -0
- nvmath/linalg/advanced/helpers/__init__.py +5 -0
- nvmath/linalg/advanced/helpers/matmul.py +1316 -0
- nvmath/linalg/advanced/matmulmod.py +3734 -0
- nvmath/linalg/generic/__init__.py +53 -0
- nvmath/linalg/generic/_configuration/__init__.py +39 -0
- nvmath/linalg/generic/_configuration/layout.py +263 -0
- nvmath/linalg/generic/_configuration/match.py +734 -0
- nvmath/linalg/generic/_configuration/qualifiers.py +493 -0
- nvmath/linalg/generic/_configuration/solver_configuration.py +59 -0
- nvmath/linalg/generic/_configuration/wrap.py +217 -0
- nvmath/linalg/generic/_dtype.py +15 -0
- nvmath/linalg/generic/matmulmod.py +2094 -0
- nvmath/linalg/generic/solvermod.py +1301 -0
- nvmath/memory.py +279 -0
- nvmath/sparse/__init__.py +38 -0
- nvmath/sparse/_internal/__init__.py +21 -0
- nvmath/sparse/_internal/common_utils.py +147 -0
- nvmath/sparse/_internal/cudss_config_ifc.py +702 -0
- nvmath/sparse/_internal/cudss_data_ifc.py +399 -0
- nvmath/sparse/_internal/cudss_utils.py +506 -0
- nvmath/sparse/_internal/cusparse_utils.py +382 -0
- nvmath/sparse/_internal/sparse_bsc_ifc.py +303 -0
- nvmath/sparse/_internal/sparse_bsr_ifc.py +305 -0
- nvmath/sparse/_internal/sparse_coo_ifc.py +256 -0
- nvmath/sparse/_internal/sparse_csc_ifc.py +268 -0
- nvmath/sparse/_internal/sparse_csr_ifc.py +288 -0
- nvmath/sparse/_internal/sparse_dia_ifc.py +242 -0
- nvmath/sparse/_internal/sparse_format_helpers.py +601 -0
- nvmath/sparse/_internal/sparse_tensor_ifc.py +133 -0
- nvmath/sparse/_internal/sparse_ust_ifc.py +141 -0
- nvmath/sparse/_internal/utils.py +56 -0
- nvmath/sparse/advanced/__init__.py +7 -0
- nvmath/sparse/advanced/_configuration.py +227 -0
- nvmath/sparse/advanced/direct_solver.py +2069 -0
- nvmath/sparse/generic/__init__.py +7 -0
- nvmath/sparse/generic/_configuration.py +129 -0
- nvmath/sparse/generic/_helpers.py +137 -0
- nvmath/sparse/generic/_thunks.py +21 -0
- nvmath/sparse/generic/matmulmod.py +2353 -0
- nvmath/sparse/ust/__init__.py +7 -0
- nvmath/sparse/ust/_converters.py +422 -0
- nvmath/sparse/ust/_cpp.py +28 -0
- nvmath/sparse/ust/_drawer.py +565 -0
- nvmath/sparse/ust/_emitter.py +1033 -0
- nvmath/sparse/ust/_jit.py +188 -0
- nvmath/sparse/ust/_kernel.py +282 -0
- nvmath/sparse/ust/_utils.py +149 -0
- nvmath/sparse/ust/interfaces/__init__.py +0 -0
- nvmath/sparse/ust/interfaces/torch_interface.py +476 -0
- nvmath/sparse/ust/tensor.py +1016 -0
- nvmath/sparse/ust/tensor_format.py +957 -0
- nvmath/tensor/__init__.py +6 -0
- nvmath/tensor/_configuration.py +120 -0
- nvmath/tensor/_internal/__init__.py +3 -0
- nvmath/tensor/_internal/cutensor_config_ifc.py +279 -0
- nvmath/tensor/_internal/cutensor_utils.py +230 -0
- nvmath/tensor/_internal/data.py +43 -0
- nvmath/tensor/_internal/einsum_parser.py +444 -0
- nvmath/tensor/_internal/typemaps.py +96 -0
- nvmath/tensor/contract.py +1900 -0
- nvmath_python-1.0.0.dist-info/METADATA +134 -0
- nvmath_python-1.0.0.dist-info/RECORD +332 -0
- nvmath_python-1.0.0.dist-info/WHEEL +5 -0
- nvmath_python-1.0.0.dist-info/licenses/LICENSE +177 -0
- nvmath_python-1.0.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,1033 @@
|
|
|
1
|
+
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""This module defines an emitter for the universal sparse tensor (UST)."""
|
|
6
|
+
|
|
7
|
+
__all__ = []
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from io import StringIO
|
|
12
|
+
from itertools import permutations
|
|
13
|
+
from sys import stdout
|
|
14
|
+
|
|
15
|
+
from nvmath.sparse.ust.tensor_format import (
|
|
16
|
+
Add,
|
|
17
|
+
Dimension,
|
|
18
|
+
Divide,
|
|
19
|
+
LevelExpr,
|
|
20
|
+
LevelFormat,
|
|
21
|
+
Modulo,
|
|
22
|
+
Sequence,
|
|
23
|
+
Subtract,
|
|
24
|
+
is_unique,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from ._cpp import prolog_decl
|
|
28
|
+
from ._utils import as_external_tensor, type_str
|
|
29
|
+
|
|
30
|
+
###############
|
|
31
|
+
# Definitions #
|
|
32
|
+
###############
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Backend(Enum):
|
|
36
|
+
CUDA = 1
|
|
37
|
+
OMP = 2
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Index(Enum):
|
|
41
|
+
LEVEL = 1
|
|
42
|
+
LEVEL_A = 2
|
|
43
|
+
LEVEL_B = 3
|
|
44
|
+
FREE = 4
|
|
45
|
+
FREE_A = 5
|
|
46
|
+
FREE_B = 6
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
ID = {Index.LEVEL: "l", Index.LEVEL_A: "ll", Index.LEVEL_B: "l", Index.FREE: "i", Index.FREE_A: "ii", Index.FREE_B: "i"}
|
|
50
|
+
|
|
51
|
+
CYCLIC = 1024 # cyclic scheduled coarsening
|
|
52
|
+
|
|
53
|
+
INDEX_DONE = 999
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
##################
|
|
57
|
+
# HELPER METHODS #
|
|
58
|
+
##################
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _id0(list):
|
|
62
|
+
return f"{[('i' + str(i)) for i in list]}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _id1(list):
|
|
66
|
+
return f"{[(ID[t] + str(i)) for t, i in list]}"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _base(C):
|
|
70
|
+
return f"idxC{len(C) - 1}" if len(C) > 0 else "0"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _fmt(fmt):
|
|
74
|
+
if isinstance(fmt, tuple):
|
|
75
|
+
fmt, _ = fmt
|
|
76
|
+
return fmt
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _is_unique(fmt):
|
|
80
|
+
if isinstance(fmt, Sequence):
|
|
81
|
+
_, prop = fmt
|
|
82
|
+
return is_unique(prop)
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _collapse_type(ftypes, A, fmtA, k):
|
|
87
|
+
"""Returns loop type 'par' or 'red' depending on indexing."""
|
|
88
|
+
if isinstance(k, Dimension):
|
|
89
|
+
return ftypes[A[fmtA.dimensions.index(k)]]
|
|
90
|
+
elif isinstance(k, LevelExpr) and (
|
|
91
|
+
_collapse_type(ftypes, A, fmtA, k.expression1) == "red" or _collapse_type(ftypes, A, fmtA, k.expression2) == "red"
|
|
92
|
+
):
|
|
93
|
+
return "red"
|
|
94
|
+
return "par"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _max_grid(indices, fmts):
|
|
98
|
+
"""Returns the maximum grid (up to 3-D) that can be supported."""
|
|
99
|
+
maxg = 0
|
|
100
|
+
for t, i in indices:
|
|
101
|
+
if t == Index.FREE or _fmt(fmts[i]) in (LevelFormat.DENSE, LevelFormat.BATCH, LevelFormat.COMPRESSED):
|
|
102
|
+
maxg += 1
|
|
103
|
+
continue
|
|
104
|
+
break
|
|
105
|
+
return min(3, maxg)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _lvl2dim(fmtA, A):
|
|
109
|
+
"""Returns the mapping of assignments that will implement lvl2dim."""
|
|
110
|
+
|
|
111
|
+
def _mapi(k):
|
|
112
|
+
return A[fmtA.dimensions.index(k)]
|
|
113
|
+
|
|
114
|
+
def _find_range(litems, expr1, expr2):
|
|
115
|
+
for lvl, (k, v) in enumerate(litems):
|
|
116
|
+
if _fmt(v) == LevelFormat.RANGE and k in (expr1, expr2):
|
|
117
|
+
return lvl, k
|
|
118
|
+
raise AssertionError(f"Cannot find {expr1} or {expr2} in levels")
|
|
119
|
+
|
|
120
|
+
lmap = []
|
|
121
|
+
litems = fmtA.levels.items()
|
|
122
|
+
for lvl, (k, _) in enumerate(litems):
|
|
123
|
+
if isinstance(k, Dimension):
|
|
124
|
+
i = _mapi(k)
|
|
125
|
+
stmt = f"const CRD i{i} = l{lvl};"
|
|
126
|
+
need, gen = [lvl], [i]
|
|
127
|
+
elif isinstance(k.operator, Add):
|
|
128
|
+
rl, re = _find_range(litems, k.expression1, k.expression2)
|
|
129
|
+
if re == k.expression2:
|
|
130
|
+
i = _mapi(k.expression1)
|
|
131
|
+
stmt = f"const CRD i{i} = l{lvl} - l{rl};"
|
|
132
|
+
need, gen = [lvl, rl], [i]
|
|
133
|
+
else:
|
|
134
|
+
assert re == k.expression1
|
|
135
|
+
i = _mapi(k.expression2)
|
|
136
|
+
stmt = f"const CRD i{i} = l{lvl} - l{rl};"
|
|
137
|
+
need, gen = [lvl, rl], [i]
|
|
138
|
+
elif isinstance(k.operator, Subtract):
|
|
139
|
+
rl, re = _find_range(litems, k.expression1, k.expression2)
|
|
140
|
+
if re == k.expression2:
|
|
141
|
+
i = _mapi(k.expression1)
|
|
142
|
+
stmt = f"const CRD i{i} = l{rl} + l{lvl};"
|
|
143
|
+
need, gen = [rl, lvl], [i]
|
|
144
|
+
else:
|
|
145
|
+
assert re == k.expression1
|
|
146
|
+
i = _mapi(k.expression2)
|
|
147
|
+
stmt = f"const CRD i{i} = l{rl} - l{lvl};"
|
|
148
|
+
need, gen = [rl, lvl], [i]
|
|
149
|
+
elif isinstance(k.operator, Divide):
|
|
150
|
+
i = _mapi(k.expression1)
|
|
151
|
+
stmt = f"const CRD b{i} = l{lvl} * {k.expression2};"
|
|
152
|
+
need, gen = [lvl], [] # i not ready yet!
|
|
153
|
+
elif isinstance(k.operator, Modulo):
|
|
154
|
+
i = _mapi(k.expression1)
|
|
155
|
+
stmt = f"const CRD i{i} = b{i} + l{lvl};"
|
|
156
|
+
need, gen = [lvl], [i] # i ready!
|
|
157
|
+
else:
|
|
158
|
+
raise AssertionError(f"Unsupported: {k}")
|
|
159
|
+
|
|
160
|
+
lmap.append((need, stmt, gen))
|
|
161
|
+
|
|
162
|
+
return lmap
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
###################
|
|
166
|
+
# EMITTER METHODS #
|
|
167
|
+
###################
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _emit_linearize(name, subs, geni, stream=stdout, indent=0):
|
|
171
|
+
"""Emits linearized address computations for a dense tensor."""
|
|
172
|
+
for n, i in enumerate(subs):
|
|
173
|
+
if i == INDEX_DONE:
|
|
174
|
+
continue
|
|
175
|
+
elif i in geni:
|
|
176
|
+
sub = f"(idx{name}{n - 1} + i{i})" if n > 0 else f"i{i}"
|
|
177
|
+
if n == len(subs) - 1: # last one
|
|
178
|
+
print(f"{'':>{indent}}const CRD idx{name}{n} = {sub};", file=stream)
|
|
179
|
+
if name == "C":
|
|
180
|
+
print(f"{'':>{indent}}CTP vC = 0;", file=stream)
|
|
181
|
+
else:
|
|
182
|
+
assert name == "B"
|
|
183
|
+
print(f"{'':>{indent}}const CTP vB = prolog_b(static_cast<CTP>(B[idxB{n}]));", file=stream)
|
|
184
|
+
else:
|
|
185
|
+
print(f"{'':>{indent}}const CRD idx{name}{n} = {sub} * N{subs[n + 1]};", file=stream)
|
|
186
|
+
subs[n] = INDEX_DONE
|
|
187
|
+
else:
|
|
188
|
+
return # next subscript must wait
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _emit_ready(lmap, A, B, C, P, genl, geni, stream=stdout, indent=0):
|
|
192
|
+
"""Emits all variables that have their required inputs generated."""
|
|
193
|
+
# Indices ready?
|
|
194
|
+
for m in range(len(lmap)):
|
|
195
|
+
if lmap[m] is not None:
|
|
196
|
+
need, stmt, gen = lmap[m]
|
|
197
|
+
if all(n in genl for n in need):
|
|
198
|
+
print(f"{'':>{indent}}{stmt}", file=stream)
|
|
199
|
+
geni.extend(gen)
|
|
200
|
+
lmap[m] = None
|
|
201
|
+
# A ready? Note that linearization is already done by sparse emitter.
|
|
202
|
+
if all(i in geni for i in A):
|
|
203
|
+
print(f"{'':>{indent}}const CTP vA = prolog_a(static_cast<CTP>(Aval[{P}]));", file=stream)
|
|
204
|
+
A[0] = INDEX_DONE
|
|
205
|
+
# B, C ready?
|
|
206
|
+
_emit_linearize("B", B, geni, stream=stream, indent=indent)
|
|
207
|
+
_emit_linearize("C", C, geni, stream=stream, indent=indent)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _emit_types(ctp, vtp, itp, gtp, stream=stdout, backend=Backend.CUDA):
|
|
211
|
+
"""Emits type definitions with required includes."""
|
|
212
|
+
if backend == Backend.CUDA:
|
|
213
|
+
if vtp in ["__nv_fp8_e4m3", "__nv_fp8_e5m2"]:
|
|
214
|
+
print("#include <cuda_fp8.h>\n", file=stream)
|
|
215
|
+
elif vtp in ["__half"]:
|
|
216
|
+
print("#include <cuda_fp16.h>\n", file=stream)
|
|
217
|
+
elif vtp in ["__nv_bfloat16"]:
|
|
218
|
+
print("#include <cuda_bf16.h>\n", file=stream)
|
|
219
|
+
elif vtp.startswith("cuda::std::complex"):
|
|
220
|
+
print("#include <cuda/std/complex>\n", file=stream)
|
|
221
|
+
elif backend == Backend.OMP:
|
|
222
|
+
if vtp in ["__nv_fp8_e4m3", "__nv_fp8_e5m2", "__half", "__nv_bfloat16"]:
|
|
223
|
+
raise NotImplementedError()
|
|
224
|
+
else:
|
|
225
|
+
raise AssertionError(f"Unsupported backend: {backend}")
|
|
226
|
+
print(f"using CTP = {ctp};", file=stream)
|
|
227
|
+
print(f"using VAL = {vtp};", file=stream)
|
|
228
|
+
print(f"using POS = {itp};", file=stream)
|
|
229
|
+
print(f"using CRD = {itp};", file=stream)
|
|
230
|
+
print(f"using GRD = {gtp};\n", file=stream)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _emit_atomic(C, vtp, stream=stdout, indent=0, backend=Backend.CUDA):
|
|
234
|
+
"""Emits atomic operation."""
|
|
235
|
+
if backend == Backend.CUDA:
|
|
236
|
+
if not vtp.startswith("cuda::std::complex"):
|
|
237
|
+
print(f"{'':>{indent}}atomicAdd(&C[{_base(C)}], static_cast<VAL>(vC));", file=stream)
|
|
238
|
+
else:
|
|
239
|
+
atp = re.search(r"<(.+?)>", vtp).group(1)
|
|
240
|
+
print(f"{'':>{indent}}{atp}* addr = reinterpret_cast<{atp}*>(&C[{_base(C)}]);", file=stream)
|
|
241
|
+
print(f"{'':>{indent}}const VAL c = static_cast<VAL>(vC);", file=stream)
|
|
242
|
+
print(f"{'':>{indent}}atomicAdd(addr + 0, c.real());", file=stream)
|
|
243
|
+
print(f"{'':>{indent}}atomicAdd(addr + 1, c.imag());", file=stream)
|
|
244
|
+
elif backend == Backend.OMP:
|
|
245
|
+
print(f"{'':>{indent}}omp_atomic_add(&C[{_base(C)}], static_cast<VAL>(vC));", file=stream)
|
|
246
|
+
else:
|
|
247
|
+
raise AssertionError(f"Unsupported backend: {backend}")
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _emit_shuffle_down(C, vtp, stream=stdout, indent=0, backend=Backend.CUDA):
|
|
251
|
+
if backend != Backend.CUDA:
|
|
252
|
+
raise NotImplementedError(f"Unsupported backend for shuffle down: {backend}")
|
|
253
|
+
print(f"{'':>{indent}}// WARP REDUCTION (to reduce number of atomics)", file=stream)
|
|
254
|
+
print(f"{'':>{indent}}assert((blockDim.x & 31) == 0);", file=stream)
|
|
255
|
+
print(f"{'':>{indent}}#pragma unroll", file=stream)
|
|
256
|
+
print(f"{'':>{indent}}for (int offset = 16; offset > 0; offset >>= 1) {{", file=stream)
|
|
257
|
+
print(f"{'':>{indent}} vC += __shfl_down_sync(0xffffffffu, vC, offset);", file=stream)
|
|
258
|
+
print(f"{'':>{indent}}}}", file=stream)
|
|
259
|
+
print(f"{'':>{indent}}int lane = threadIdx.x & 31;", file=stream)
|
|
260
|
+
print(f"{'':>{indent}}if (lane == 0) {{", file=stream)
|
|
261
|
+
_emit_atomic(C, vtp, stream=stream, indent=indent + 2, backend=backend)
|
|
262
|
+
print(f"{'':>{indent}}}}", file=stream)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _emit_accumulate(C, vtp, reduc, stream=stdout, indent=0, backend=Backend.CUDA):
|
|
266
|
+
isAtomicRed, isWarpRed, _ = reduc
|
|
267
|
+
if isAtomicRed:
|
|
268
|
+
if isWarpRed:
|
|
269
|
+
_emit_shuffle_down(C, vtp, stream=stream, indent=indent, backend=backend)
|
|
270
|
+
else:
|
|
271
|
+
_emit_atomic(C, vtp, stream=stream, indent=indent, backend=backend)
|
|
272
|
+
else:
|
|
273
|
+
print(f"{'':>{indent}}C[{_base(C)}] += static_cast<VAL>(vC);", file=stream)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _emit_cleanup(N, C, vtp, reduc, isGrid=True, stream=stdout, indent=0, backend=Backend.CUDA):
|
|
277
|
+
# OMP uses tightly nested parallel loops for grid set up, so the accumulation
|
|
278
|
+
# must end and the inner loop nest level.
|
|
279
|
+
_, _, cd = reduc
|
|
280
|
+
if isGrid and backend == Backend.OMP and cd is not None:
|
|
281
|
+
cd = N - 1
|
|
282
|
+
# Loop nest closing.
|
|
283
|
+
for i in range(N - 1, -1, -1):
|
|
284
|
+
if cd == i:
|
|
285
|
+
_emit_accumulate(C, vtp, reduc, stream=stream, indent=indent, backend=backend)
|
|
286
|
+
indent -= 2
|
|
287
|
+
print(f"{'':>{indent}}}}", file=stream)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _emit_thread_grid_setup(
|
|
291
|
+
indices, types, A, B, C, fmtA, lmap, genl, geni, endian, stream=stdout, indent=0, backend=Backend.CUDA
|
|
292
|
+
):
|
|
293
|
+
"""Emits a G-dim grid for a kernel."""
|
|
294
|
+
G = len(indices)
|
|
295
|
+
assert len(types) == G
|
|
296
|
+
|
|
297
|
+
print(f"{'':>{indent}}// {G}-D GRID: {_id1(indices)} {types}", file=stream)
|
|
298
|
+
|
|
299
|
+
# Grid setup.
|
|
300
|
+
if backend == Backend.CUDA:
|
|
301
|
+
if G == 1:
|
|
302
|
+
print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
|
|
303
|
+
# print(f"{'':>{indent}}const POS t = x;", file=stream)
|
|
304
|
+
grid = ["x"] # any endian
|
|
305
|
+
elif G == 2:
|
|
306
|
+
print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
|
|
307
|
+
print(f"{'':>{indent}}const GRD y = blockIdx.y * blockDim.y + threadIdx.y;", file=stream)
|
|
308
|
+
# print(f"{'':>{indent}}const GRD w = gridDim.x * blockDim.x;", file=stream)
|
|
309
|
+
# print(f"{'':>{indent}}const GRD t = y * w + x;", file=stream)
|
|
310
|
+
grid = ["x", "y"] if endian else ["y", "x"]
|
|
311
|
+
elif G == 3:
|
|
312
|
+
print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
|
|
313
|
+
print(f"{'':>{indent}}const GRD y = blockIdx.y * blockDim.y + threadIdx.y;", file=stream)
|
|
314
|
+
print(f"{'':>{indent}}const GRD z = blockIdx.z * blockDim.z + threadIdx.z;", file=stream)
|
|
315
|
+
# print(f"{'':>{indent}}const GRD w = gridDim.x * blockDim.x;", file=stream)
|
|
316
|
+
# print(f"{'':>{indent}}const GRD h = gridDim.y * blockDim.y;", file=stream)
|
|
317
|
+
# print(f"{'':>{indent}}const GRD t = z * w * h + y * w + x;", file=stream)
|
|
318
|
+
grid = ["x", "y", "z"] if endian else ["z", "y", "x"]
|
|
319
|
+
else:
|
|
320
|
+
raise AssertionError(f"unexpected {G}-D grid")
|
|
321
|
+
elif backend == Backend.OMP:
|
|
322
|
+
assert endian
|
|
323
|
+
grid = ["x", "y", "z"][:G]
|
|
324
|
+
print(f"{'':>{indent}}#pragma omp parallel for", file=stream)
|
|
325
|
+
for i in range(G):
|
|
326
|
+
grid_index = grid[i]
|
|
327
|
+
grid_bound = f"G{grid_index}"
|
|
328
|
+
print(f"{'':>{indent}}for (GRD {grid_index} = 0; {grid_index} < {grid_bound}; {grid_index}++) {{", file=stream)
|
|
329
|
+
indent += 2
|
|
330
|
+
else:
|
|
331
|
+
raise AssertionError(f"Unsupported backend: {backend}")
|
|
332
|
+
|
|
333
|
+
# Grid translation into loops.
|
|
334
|
+
P = "0"
|
|
335
|
+
isAtomicRed = False
|
|
336
|
+
cd = None
|
|
337
|
+
fmts = list(fmtA.levels.values())
|
|
338
|
+
for g in range(G):
|
|
339
|
+
t, a = indices[g]
|
|
340
|
+
if t == Index.FREE or t == Index.FREE_A:
|
|
341
|
+
if t == Index.FREE:
|
|
342
|
+
ii, cnst = f"i{a}", "const "
|
|
343
|
+
geni.append(a)
|
|
344
|
+
else:
|
|
345
|
+
ii, cnst = f"ii{a}", ""
|
|
346
|
+
print(f"{'':>{indent}}{cnst}CRD {ii} = {grid[g]};", file=stream)
|
|
347
|
+
if backend == Backend.CUDA:
|
|
348
|
+
print(f"{'':>{indent}}if ({ii} < N{a}) {{", file=stream)
|
|
349
|
+
indent += 2
|
|
350
|
+
else:
|
|
351
|
+
print(f"{'':>{indent}}if ({ii} >= N{a}) continue;", file=stream)
|
|
352
|
+
elif t == Index.LEVEL or t == Index.LEVEL_A:
|
|
353
|
+
isAtomicRed = isAtomicRed or types[g] == "red" or not _is_unique(fmts[a])
|
|
354
|
+
if t == Index.LEVEL:
|
|
355
|
+
pp, ll = f"p{a}", f"l{a}"
|
|
356
|
+
genl.append(a)
|
|
357
|
+
else:
|
|
358
|
+
pp, ll = f"pp{a}", f"ll{a}"
|
|
359
|
+
if _fmt(fmts[a]) in (LevelFormat.DENSE, LevelFormat.BATCH):
|
|
360
|
+
print(f"{'':>{indent}}const POS {pp} = {P} * L{a} + {grid[g]};", file=stream)
|
|
361
|
+
print(f"{'':>{indent}}const CRD {ll} = {grid[g]};", file=stream)
|
|
362
|
+
if backend == Backend.CUDA:
|
|
363
|
+
print(f"{'':>{indent}}if ({ll} < L{a}) {{", file=stream)
|
|
364
|
+
indent += 2
|
|
365
|
+
else:
|
|
366
|
+
print(f"{'':>{indent}}if ({ll} >= L{a}) continue;", file=stream)
|
|
367
|
+
elif _fmt(fmts[a]) == LevelFormat.COMPRESSED:
|
|
368
|
+
# TODO: batching?
|
|
369
|
+
print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{P}];", file=stream)
|
|
370
|
+
print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{P} + 1];", file=stream)
|
|
371
|
+
print(f"{'':>{indent}}const POS {pp} = lo{a} + {grid[g]};", file=stream)
|
|
372
|
+
if backend == Backend.CUDA:
|
|
373
|
+
print(f"{'':>{indent}}if ({pp} < hi{a}) {{", file=stream)
|
|
374
|
+
indent += 2
|
|
375
|
+
else:
|
|
376
|
+
print(f"{'':>{indent}}if ({pp} >= hi{a}) continue;", file=stream)
|
|
377
|
+
if t == Index.LEVEL:
|
|
378
|
+
print(f"{'':>{indent}}const CRD {ll} = Acrd{a}[{pp}];", file=stream)
|
|
379
|
+
else:
|
|
380
|
+
raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[a])}")
|
|
381
|
+
P = pp
|
|
382
|
+
else:
|
|
383
|
+
raise AssertionError(f"Unexpected index type: {t}")
|
|
384
|
+
_emit_ready(lmap, A, B, C, P, genl, geni, stream=stream, indent=indent)
|
|
385
|
+
if cd is None and all(i == INDEX_DONE for i in C):
|
|
386
|
+
cd = g
|
|
387
|
+
|
|
388
|
+
return P, isAtomicRed, cd
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def _emit_loop_structure(
|
|
392
|
+
indices, types, A, B, C, fmtA, vtp, N, lmap, genl, geni, P, reduc, stream=stdout, indent=0, backend=Backend.CUDA
|
|
393
|
+
):
|
|
394
|
+
"""Emits computational loop nest for a kernel."""
|
|
395
|
+
D = len(indices)
|
|
396
|
+
assert D < N and len(types) == D
|
|
397
|
+
|
|
398
|
+
def _find_add_sub(litems, r):
|
|
399
|
+
for lvl, (k, _) in enumerate(litems):
|
|
400
|
+
if isinstance(k, LevelExpr) and isinstance(k.operator, (Add, Subtract)):
|
|
401
|
+
if k.expression1 == r:
|
|
402
|
+
return lvl, k, False, k.expression2
|
|
403
|
+
if k.expression2 == r:
|
|
404
|
+
return lvl, k, True, k.expression1
|
|
405
|
+
raise AssertionError(f"Cannot find {r} in levels")
|
|
406
|
+
|
|
407
|
+
print(f"{'':>{indent}}// LOOPS: {_id1(indices)} {types}", file=stream)
|
|
408
|
+
|
|
409
|
+
# Loop nest opening.
|
|
410
|
+
isAtomicRed, isWarpRed, cd = reduc
|
|
411
|
+
cd = None if cd is None else -1
|
|
412
|
+
fmts = list(fmtA.levels.values())
|
|
413
|
+
litems = fmtA.levels.items()
|
|
414
|
+
batch = sum(_fmt(f) == LevelFormat.BATCH for f in fmts)
|
|
415
|
+
for d in range(D):
|
|
416
|
+
t, a = indices[d]
|
|
417
|
+
if t == Index.FREE:
|
|
418
|
+
print(f"{'':>{indent}}for (CRD i{a} = 0; i{a} < N{a}; i{a}++) {{", file=stream)
|
|
419
|
+
geni.append(a)
|
|
420
|
+
elif t == Index.FREE_B:
|
|
421
|
+
print(f"{'':>{indent}}for (CRD i{a} = ii{a}; i{a} < N{a}; i{a}+={CYCLIC}) {{", file=stream)
|
|
422
|
+
geni.append(a)
|
|
423
|
+
elif t == Index.LEVEL:
|
|
424
|
+
if _fmt(fmts[a]) in (LevelFormat.DENSE, LevelFormat.BATCH):
|
|
425
|
+
print(f"{'':>{indent}}POS p{a} = {P} * L{a};", file=stream)
|
|
426
|
+
print(f"{'':>{indent}}for (CRD l{a} = 0; l{a} < L{a}; l{a}++, p{a}++) {{", file=stream)
|
|
427
|
+
P = f"p{a}"
|
|
428
|
+
genl.append(a)
|
|
429
|
+
elif _fmt(fmts[a]) == LevelFormat.COMPRESSED:
|
|
430
|
+
if batch > 0:
|
|
431
|
+
# Batched uncompressed.
|
|
432
|
+
cidx = f"p{batch - 1} * (L{batch} + 1) + l{a - 1}"
|
|
433
|
+
hidx = f"p{batch - 1} * (L{batch} + 1) + L{batch}"
|
|
434
|
+
print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{cidx}];", file=stream)
|
|
435
|
+
print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{cidx} + 1];", file=stream)
|
|
436
|
+
print(f"{'':>{indent}}const POS no{a} = p{a - 2} * Apos{a}[{hidx}];", file=stream)
|
|
437
|
+
print(f"{'':>{indent}}for (POS p{a} = lo{a} + no{a}; p{a} < hi{a} + no{a}; p{a}++) {{", file=stream)
|
|
438
|
+
batch = 0
|
|
439
|
+
else:
|
|
440
|
+
# Regular uncompressed.
|
|
441
|
+
print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{P}];", file=stream)
|
|
442
|
+
print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{P} + 1];", file=stream)
|
|
443
|
+
print(f"{'':>{indent}}for (POS p{a} = lo{a}; p{a} < hi{a}; p{a}++) {{", file=stream)
|
|
444
|
+
print(f"{'':>{indent}} const CRD l{a} = Acrd{a}[p{a}];", file=stream)
|
|
445
|
+
P = f"p{a}"
|
|
446
|
+
genl.append(a)
|
|
447
|
+
elif _fmt(fmts[a]) == LevelFormat.SINGLETON:
|
|
448
|
+
print(f"{'':>{indent}}const CRD l{a} = Acrd{a}[{P}];", file=stream)
|
|
449
|
+
print(f"{'':>{indent}}{{", file=stream)
|
|
450
|
+
genl.append(a)
|
|
451
|
+
elif _fmt(fmts[a]) == LevelFormat.RANGE:
|
|
452
|
+
q, _ = list(litems)[a]
|
|
453
|
+
la, add, isI, r = _find_add_sub(litems, q)
|
|
454
|
+
di, dj = fmtA.dimensions.index(q), fmtA.dimensions.index(r)
|
|
455
|
+
szi, szj = f"N{di}", f"N{dj}"
|
|
456
|
+
if isinstance(add.operator, Add):
|
|
457
|
+
print(f"{'':>{indent}}const CRD of{a} = l{la};", file=stream)
|
|
458
|
+
print(f"{'':>{indent}}const CRD lo{a} = llmax(0, of{a} - {szj} + 1);", file=stream)
|
|
459
|
+
print(f"{'':>{indent}}const CRD hi{a} = llmin({szi}, of{a} + 1);", file=stream)
|
|
460
|
+
else:
|
|
461
|
+
print(f"{'':>{indent}}const CRD of{a} = {'-' if isI else ''}l{la};", file=stream)
|
|
462
|
+
print(f"{'':>{indent}}const CRD lo{a} = llmax(0, of{a});", file=stream)
|
|
463
|
+
print(f"{'':>{indent}}const CRD hi{a} = llmin({szi}, {szj} + of{a});", file=stream)
|
|
464
|
+
print(f"{'':>{indent}}CRD p{a} = {P} * {szi} + lo{a};", file=stream)
|
|
465
|
+
print(f"{'':>{indent}}for (CRD l{a} = lo{a}; l{a} < hi{a}; l{a}++, p{a}++) {{", file=stream)
|
|
466
|
+
P = f"p{a}"
|
|
467
|
+
genl.append(a)
|
|
468
|
+
elif _fmt(fmts[a]) == LevelFormat.DELTA:
|
|
469
|
+
print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{P}];", file=stream)
|
|
470
|
+
print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{P} + 1];", file=stream)
|
|
471
|
+
print(f"{'':>{indent}}CRD l{a} = 0; // start running sequence", file=stream)
|
|
472
|
+
print(f"{'':>{indent}}for (POS p{a} = lo{a}; p{a} < hi{a}; p{a}++, l{a}++) {{", file=stream)
|
|
473
|
+
print(f"{'':>{indent}} l{a} += Acrd{a}[p{a}]; // adjust by delta", file=stream)
|
|
474
|
+
P = f"p{a}"
|
|
475
|
+
genl.append(a)
|
|
476
|
+
elif _fmt(fmts[a]) == LevelFormat.STRUCTURED:
|
|
477
|
+
n = fmts[a][1]
|
|
478
|
+
print(f"{'':>{indent}}const POS lo{a} = {n} * {P};", file=stream)
|
|
479
|
+
print(f"{'':>{indent}}const POS hi{a} = lo{a} + {n};", file=stream)
|
|
480
|
+
print(f"{'':>{indent}}for (POS p{a} = lo{a}; p{a} < hi{a}; p{a}++) {{", file=stream)
|
|
481
|
+
print(f"{'':>{indent}} const CRD l{a} = Acrd{a}[p{a}];", file=stream)
|
|
482
|
+
P = f"p{a}"
|
|
483
|
+
genl.append(a)
|
|
484
|
+
else:
|
|
485
|
+
raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[a])}")
|
|
486
|
+
elif t == Index.LEVEL_B:
|
|
487
|
+
if _fmt(fmts[a]) in (LevelFormat.DENSE, LevelFormat.BATCH):
|
|
488
|
+
print(f"{'':>{indent}}POS p{a} = {P};", file=stream)
|
|
489
|
+
print(f"{'':>{indent}}for (CRD l{a} = ll{a}; l{a} < L{a}; l{a}+={CYCLIC}, p{a}+={CYCLIC}) {{", file=stream)
|
|
490
|
+
P = f"p{a}"
|
|
491
|
+
genl.append(a)
|
|
492
|
+
elif _fmt(fmts[a]) == LevelFormat.COMPRESSED:
|
|
493
|
+
assert batch == 0 # TODO: batching?
|
|
494
|
+
print(f"{'':>{indent}}for (POS p{a} = {P}; p{a} < hi{a}; p{a}+={CYCLIC}) {{", file=stream)
|
|
495
|
+
print(f"{'':>{indent}} const CRD l{a} = Acrd{a}[p{a}];", file=stream)
|
|
496
|
+
P = f"p{a}"
|
|
497
|
+
genl.append(a)
|
|
498
|
+
else:
|
|
499
|
+
raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[a])}")
|
|
500
|
+
else:
|
|
501
|
+
raise AssertionError(f"Unexpected index type: {t}")
|
|
502
|
+
indent += 2
|
|
503
|
+
_emit_ready(lmap, A, B, C, P, genl, geni, stream=stream, indent=indent)
|
|
504
|
+
if cd is None and all(i == INDEX_DONE for i in C):
|
|
505
|
+
cd = d
|
|
506
|
+
|
|
507
|
+
# Loop body.
|
|
508
|
+
if B == [] and C == []:
|
|
509
|
+
pars = ", ".join([f"i{i}" for i in range(fmtA.num_dimensions)])
|
|
510
|
+
print(f"{'':>{indent}}Aval[{P}] = static_cast<VAL>(apply(vA, {pars}));", file=stream)
|
|
511
|
+
elif C == []:
|
|
512
|
+
print(f"{'':>{indent}}const CTP vC = vA * vB;", file=stream)
|
|
513
|
+
else:
|
|
514
|
+
print(f"{'':>{indent}}vC += vA * vB;", file=stream)
|
|
515
|
+
|
|
516
|
+
# Loop nest closing.
|
|
517
|
+
reduc = isAtomicRed, isWarpRed, cd
|
|
518
|
+
_emit_cleanup(D, C, vtp, reduc, isGrid=False, stream=stream, indent=indent, backend=backend)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def _emit_A_parameters(fmtA, constA=True, stream=stdout, indent=0, backend=Backend.CUDA):
|
|
522
|
+
"""Emit kernel parameters for a UST."""
|
|
523
|
+
for lvl, (_, v) in enumerate(fmtA.levels.items()):
|
|
524
|
+
# Handle level format.
|
|
525
|
+
if _fmt(v) in (LevelFormat.DENSE, LevelFormat.BATCH, LevelFormat.RANGE):
|
|
526
|
+
pass
|
|
527
|
+
elif _fmt(v) in (LevelFormat.COMPRESSED, LevelFormat.DELTA):
|
|
528
|
+
print(f"{'':>{indent}}const POS* __restrict__ Apos{lvl},", file=stream)
|
|
529
|
+
print(f"{'':>{indent}}const CRD* __restrict__ Acrd{lvl},", file=stream)
|
|
530
|
+
elif _fmt(v) in (LevelFormat.SINGLETON, LevelFormat.STRUCTURED):
|
|
531
|
+
print(f"{'':>{indent}}const CRD* __restrict__ Acrd{lvl},", file=stream)
|
|
532
|
+
else:
|
|
533
|
+
raise AssertionError(f"Unsupported: {_fmt(v)}")
|
|
534
|
+
cnst = "const " if constA else ""
|
|
535
|
+
print(f"{'':>{indent}}{cnst}VAL* __restrict__ Aval,", file=stream)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def _emit_BC_parameters(stream=stdout, indent=0):
|
|
539
|
+
"""Emits kernel parameters for dense B and C (shortcut for UST)."""
|
|
540
|
+
print(f"{'':>{indent}}const VAL* __restrict__ B,", file=stream)
|
|
541
|
+
print(f"{'':>{indent}}VAL* __restrict__ C,", file=stream)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def _emit_size_parameters(Q, L, stream=stdout, indent=0):
|
|
545
|
+
"""Emits kernel parameters for dimension and level sizes."""
|
|
546
|
+
pars = ",\n ".join([f"const GRD N{i}" for i in range(Q)] + [f"const GRD L{i}" for i in range(L)])
|
|
547
|
+
print(f"{'':>{indent}}{pars}", file=stream)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _emit_apply_kernel(
|
|
551
|
+
indices, fmtA, endian, ctp, vtp, itp, gtp, with_indices, G, stream=stdout, indent=0, backend=Backend.CUDA
|
|
552
|
+
):
|
|
553
|
+
"""Emits a G-dim "apply" kernel for the given tensor format."""
|
|
554
|
+
if backend != Backend.CUDA:
|
|
555
|
+
raise NotImplementedError(f"Unsupported backend for apply kernel: {backend}")
|
|
556
|
+
Q = fmtA.num_dimensions
|
|
557
|
+
L = fmtA.num_levels
|
|
558
|
+
|
|
559
|
+
print(f"\n// TENSOR FORMAT : {fmtA.name}\n", file=stream)
|
|
560
|
+
|
|
561
|
+
_emit_types(ctp, vtp, itp, gtp, stream=stream, backend=backend)
|
|
562
|
+
print("#define prolog_a(a) (a)\n", file=stream)
|
|
563
|
+
|
|
564
|
+
dim = Q if with_indices else 0
|
|
565
|
+
print(f'{"":>{indent}}extern "C" __device__ CTP apply(CTP{", CRD" * dim});\n', file=stream)
|
|
566
|
+
print(f'{"":>{indent}}extern "C" __global__ void apply_kernel(', file=stream)
|
|
567
|
+
indent += 2
|
|
568
|
+
|
|
569
|
+
if with_indices:
|
|
570
|
+
_emit_A_parameters(fmtA, constA=False, stream=stream, indent=indent)
|
|
571
|
+
_emit_size_parameters(Q, L, stream=stream, indent=indent)
|
|
572
|
+
print(") {", file=stream)
|
|
573
|
+
types = ["par" for i in range(L)]
|
|
574
|
+
A = list(range(Q)) # in terms of dim
|
|
575
|
+
lmap, genl, geni = _lvl2dim(fmtA, A), [], []
|
|
576
|
+
P, _, _ = _emit_thread_grid_setup(
|
|
577
|
+
indices[:G], types[:G], A, [], [], fmtA, lmap, genl, geni, endian, stream=stream, indent=indent, backend=backend
|
|
578
|
+
)
|
|
579
|
+
indent += G * 2
|
|
580
|
+
reduc = False, False, -1
|
|
581
|
+
_emit_loop_structure(
|
|
582
|
+
indices[G:], types[G:], A, [], [], fmtA, vtp, L, lmap, genl, geni, P, reduc, stream=stream, indent=indent
|
|
583
|
+
)
|
|
584
|
+
_emit_cleanup(G, [], vtp, reduc, isGrid=True, stream=stream, indent=indent, backend=backend)
|
|
585
|
+
indent -= G * 2
|
|
586
|
+
else:
|
|
587
|
+
print(f"{'':>{indent}}VAL* __restrict__ Aval,\n{'':>{indent}}POS Anse) {{", file=stream)
|
|
588
|
+
print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
|
|
589
|
+
print(f"{'':>{indent}}if (x < Anse) {{", file=stream)
|
|
590
|
+
print(f"{'':>{indent}} const CTP vA = prolog_a(static_cast<CTP>(Aval[x]));", file=stream)
|
|
591
|
+
print(f"{'':>{indent}} Aval[x] = static_cast<VAL>(apply(vA));", file=stream)
|
|
592
|
+
print(f"{'':>{indent}}}}", file=stream)
|
|
593
|
+
|
|
594
|
+
indent -= 2
|
|
595
|
+
print(f"{'':>{indent}}}}", file=stream)
|
|
596
|
+
|
|
597
|
+
return indices[:G], endian
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def _emit_matmul_kernel(
|
|
601
|
+
indices, types, Q, G, A, B, C, fmtA, endian, ctp, vtp, itp, gtp, stream=stdout, indent=0, backend=Backend.CUDA
|
|
602
|
+
):
|
|
603
|
+
"""Emits a G-dim "matmul" kernel for the given tensor index expression."""
|
|
604
|
+
N = len(indices)
|
|
605
|
+
L = fmtA.num_levels
|
|
606
|
+
assert len(types) == N and G <= N and L <= N
|
|
607
|
+
|
|
608
|
+
print(f"// TENSOR ITERATION : {_id0(list(range(Q)))}", file=stream)
|
|
609
|
+
print(f"// TENSOR EXPRESSION : C{_id0(C)} += A{_id0(A)} * B{_id0(B)}", file=stream)
|
|
610
|
+
print(f"// TENSOR FORMAT : {fmtA.name}\n", file=stream)
|
|
611
|
+
|
|
612
|
+
_emit_types(ctp, vtp, itp, gtp, stream=stream, backend=backend)
|
|
613
|
+
if backend == Backend.CUDA:
|
|
614
|
+
print(prolog_decl, file=stream)
|
|
615
|
+
else:
|
|
616
|
+
# OMP does not inject user-functions.
|
|
617
|
+
print("#define prolog_a(a) (a)", file=stream)
|
|
618
|
+
print("#define prolog_b(b) (b)\n", file=stream)
|
|
619
|
+
|
|
620
|
+
print(f"{'':>{indent}}// MATMUL KERNEL: {_id1(indices)} {types}", file=stream)
|
|
621
|
+
if backend == Backend.CUDA:
|
|
622
|
+
print(f'{"":>{indent}}extern "C" __global__ void matmul_kernel(', file=stream)
|
|
623
|
+
elif backend == Backend.OMP:
|
|
624
|
+
print(f'{"":>{indent}}extern "C" void matmul_kernel(', file=stream)
|
|
625
|
+
else:
|
|
626
|
+
raise AssertionError(f"Unsupported backend for matmul kernel: {backend}")
|
|
627
|
+
_emit_A_parameters(fmtA, stream=stream, indent=indent + 2)
|
|
628
|
+
_emit_BC_parameters(stream=stream, indent=indent + 2)
|
|
629
|
+
_emit_size_parameters(Q, L, stream=stream, indent=indent + 2)
|
|
630
|
+
if backend == Backend.OMP:
|
|
631
|
+
# For OMP, grid dimensions are passed as parameters
|
|
632
|
+
print(f"{'':>{indent + 2}}, GRD Gx, GRD Gy, GRD Gz", file=stream)
|
|
633
|
+
print(f"{'':>{indent}}) {{", file=stream)
|
|
634
|
+
indent += 2
|
|
635
|
+
|
|
636
|
+
lmap, genl, geni = _lvl2dim(fmtA, A), [], []
|
|
637
|
+
P, isAtomicRed, cd = _emit_thread_grid_setup(
|
|
638
|
+
indices[:G], types[:G], A, B, C, fmtA, lmap, genl, geni, endian, stream=stream, indent=indent, backend=backend
|
|
639
|
+
)
|
|
640
|
+
isWarpRed = not endian and types[G:] == [] and (types[:G] == ["par", "red"] or types[:G] == ["par", "par", "red"])
|
|
641
|
+
reduc = isAtomicRed, isWarpRed, cd
|
|
642
|
+
indent += G * 2
|
|
643
|
+
_emit_loop_structure(
|
|
644
|
+
indices[G:], types[G:], A, B, C, fmtA, vtp, N, lmap, genl, geni, P, reduc, stream=stream, indent=indent, backend=backend
|
|
645
|
+
)
|
|
646
|
+
_emit_cleanup(G, C, vtp, reduc, isGrid=True, stream=stream, indent=indent, backend=backend)
|
|
647
|
+
indent -= G * 2
|
|
648
|
+
|
|
649
|
+
indent -= 2
|
|
650
|
+
print(f"{'':>{indent}}}}", file=stream)
|
|
651
|
+
|
|
652
|
+
assert all(i is None for i in lmap) # all consumed
|
|
653
|
+
|
|
654
|
+
return indices[:G], endian
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def _emit_search_space(A, B, C, fmtA, ctp, vtp, itp, gtp, kernel=None, stream=stdout, backend=Backend.CUDA):
|
|
658
|
+
"""Searches the state space of kernels for the given tensor index expression."""
|
|
659
|
+
Q = len(set(A) | set(B) | set(C))
|
|
660
|
+
L = fmtA.num_levels
|
|
661
|
+
|
|
662
|
+
def _filter_order(indices):
|
|
663
|
+
"""Returns `True` when index order satisfies constraints."""
|
|
664
|
+
level = 0
|
|
665
|
+
for t, i in indices:
|
|
666
|
+
if t == Index.LEVEL or t == Index.LEVEL_A or t == Index.LEVEL_B:
|
|
667
|
+
if i < level:
|
|
668
|
+
return False
|
|
669
|
+
elif t == Index.LEVEL_A:
|
|
670
|
+
level = i
|
|
671
|
+
else:
|
|
672
|
+
level = i + 1
|
|
673
|
+
return True # all seen, no conflict
|
|
674
|
+
|
|
675
|
+
# Convert iteration space [i0,i1,...] into iteration space [l0,l1,..,free].
|
|
676
|
+
ftypes = ["par" if i in C else "red" for i in range(Q)]
|
|
677
|
+
levels = [(Index.LEVEL, i) for i in range(L)]
|
|
678
|
+
free = [(Index.FREE, i) for i in range(Q) if i not in A]
|
|
679
|
+
fmts = list(fmtA.levels.values())
|
|
680
|
+
llist = list(fmtA.levels)
|
|
681
|
+
|
|
682
|
+
# Generate all valid iteration space permutations.
|
|
683
|
+
count = 0
|
|
684
|
+
space1 = levels + free
|
|
685
|
+
for perm1 in permutations(space1):
|
|
686
|
+
indices1 = list(perm1)
|
|
687
|
+
if _filter_order(indices1):
|
|
688
|
+
maxg = _max_grid(indices1, fmts)
|
|
689
|
+
# Generate all legal 1D, 2D, 3D grid versions.
|
|
690
|
+
for G in range(1, maxg + 1):
|
|
691
|
+
E = [True] if (G == 1 or backend == Backend.OMP) else [True, False]
|
|
692
|
+
# Generate the version for this grid without thread coarsening.
|
|
693
|
+
for endian in E:
|
|
694
|
+
if count == kernel:
|
|
695
|
+
types1 = [
|
|
696
|
+
ftypes[i]
|
|
697
|
+
if t == Index.FREE or t == Index.FREE_A or t == Index.FREE_B
|
|
698
|
+
else _collapse_type(ftypes, A, fmtA, llist[i])
|
|
699
|
+
for t, i in indices1
|
|
700
|
+
]
|
|
701
|
+
print(f"// UNCOARSENED KERNEL: K={kernel} G={G} E={endian}", file=stream)
|
|
702
|
+
return _emit_matmul_kernel(
|
|
703
|
+
indices1, types1, Q, G, A, B, C, fmtA, endian, ctp, vtp, itp, gtp, stream=stream, backend=backend
|
|
704
|
+
)
|
|
705
|
+
count += 1
|
|
706
|
+
# Construct valid thread coarsening of the level and free indices.
|
|
707
|
+
for gg in range(G):
|
|
708
|
+
if gg < G - 1 and all(t != Index.FREE for t, i in indices1[gg : G - 1]):
|
|
709
|
+
continue
|
|
710
|
+
prefix = []
|
|
711
|
+
suffix = []
|
|
712
|
+
for g in range(0, gg):
|
|
713
|
+
prefix.append(indices1[g])
|
|
714
|
+
for g in range(gg, G):
|
|
715
|
+
t, i = indices1[g]
|
|
716
|
+
if t == Index.LEVEL:
|
|
717
|
+
if g == G - 1:
|
|
718
|
+
prefix.append((Index.LEVEL_A, i))
|
|
719
|
+
suffix.append((Index.LEVEL_B, i))
|
|
720
|
+
else:
|
|
721
|
+
prefix.append((Index.LEVEL, i))
|
|
722
|
+
elif t == Index.FREE:
|
|
723
|
+
prefix.append((Index.FREE_A, i))
|
|
724
|
+
suffix.append((Index.FREE_B, i))
|
|
725
|
+
else:
|
|
726
|
+
raise AssertionError(f"Unexpected coarsening: {t}")
|
|
727
|
+
assert len(suffix) != 0
|
|
728
|
+
# Generate the other versions for this grid with thread coarsening.
|
|
729
|
+
space2 = suffix + indices1[G:]
|
|
730
|
+
for perm2 in permutations(space2):
|
|
731
|
+
indices2 = prefix + list(perm2)
|
|
732
|
+
if _filter_order(indices2):
|
|
733
|
+
for endian in E:
|
|
734
|
+
if count == kernel:
|
|
735
|
+
types2 = [
|
|
736
|
+
ftypes[i]
|
|
737
|
+
if t == Index.FREE or t == Index.FREE_A or t == Index.FREE_B
|
|
738
|
+
else _collapse_type(ftypes, A, fmtA, llist[i])
|
|
739
|
+
for t, i in indices2
|
|
740
|
+
]
|
|
741
|
+
print(f"// COARSENED KERNEL : K={kernel} G={G} E={endian}", file=stream)
|
|
742
|
+
return _emit_matmul_kernel(
|
|
743
|
+
indices2,
|
|
744
|
+
types2,
|
|
745
|
+
Q,
|
|
746
|
+
G,
|
|
747
|
+
A,
|
|
748
|
+
B,
|
|
749
|
+
C,
|
|
750
|
+
fmtA,
|
|
751
|
+
endian,
|
|
752
|
+
ctp,
|
|
753
|
+
vtp,
|
|
754
|
+
itp,
|
|
755
|
+
gtp,
|
|
756
|
+
stream=stream,
|
|
757
|
+
backend=backend,
|
|
758
|
+
)
|
|
759
|
+
count += 1
|
|
760
|
+
|
|
761
|
+
assert kernel is None
|
|
762
|
+
return count
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
##############
|
|
766
|
+
# POPULATION #
|
|
767
|
+
##############
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
#
|
|
771
|
+
# NVIDIA RTX A6000
|
|
772
|
+
#
|
|
773
|
+
# Threads per warp : 32
|
|
774
|
+
# Max threads per block : 1024
|
|
775
|
+
# Max thread dimensions : (1024, 1024, 64)
|
|
776
|
+
# Max grid dimensions : (2147483647, 65535, 65535)
|
|
777
|
+
#
|
|
778
|
+
# thread dims == per axis maxima, and joint product cap 1024
|
|
779
|
+
# grid dims == per axis maxima, but no joint product cap
|
|
780
|
+
#
|
|
781
|
+
# NOTE
|
|
782
|
+
# grid ( gridDim.xyz ) -> blockIdx.xyz
|
|
783
|
+
# block ( blockDim.xyz ) -> threadIdx.xyz
|
|
784
|
+
#
|
|
785
|
+
def _compute_grid_block(grid_xyz, endian):
|
|
786
|
+
"""Computes grid and block dimension for given grid.
|
|
787
|
+
|
|
788
|
+
Note that we always keep X a multiple of 32 to get full warps,
|
|
789
|
+
which means that X = min(x, 32) simply become X=32.
|
|
790
|
+
"""
|
|
791
|
+
if len(grid_xyz) == 1:
|
|
792
|
+
x = grid_xyz[0] # any endian
|
|
793
|
+
# X = min(x, 1024) -> 1024 max
|
|
794
|
+
X = (min(x, 1024) + 31) // 32 * 32 # keep multiple of 32
|
|
795
|
+
grid_dim = ((x + X - 1) // X,)
|
|
796
|
+
block_dim = (X,)
|
|
797
|
+
elif len(grid_xyz) == 2:
|
|
798
|
+
x, y = (grid_xyz[0], grid_xyz[1]) if endian else (grid_xyz[1], grid_xyz[0])
|
|
799
|
+
# X, Y = min(x, 32), min(y, 32) -> 32*32=1024 max
|
|
800
|
+
X, Y = 32, min(y, 32)
|
|
801
|
+
grid_dim = ((x + X - 1) // X, (y + Y - 1) // Y)
|
|
802
|
+
block_dim = (X, Y)
|
|
803
|
+
else: # truncate to first three
|
|
804
|
+
x, y, z = (grid_xyz[0], grid_xyz[1], grid_xyz[2]) if endian else (grid_xyz[2], grid_xyz[1], grid_xyz[0])
|
|
805
|
+
# X, Y, Z = min(x, 32), min(y, 4), min(z, 8) -> 32*4*8=1024 max
|
|
806
|
+
X, Y, Z = 32, min(y, 4), min(z, 8)
|
|
807
|
+
grid_dim = ((x + X - 1) // X, (y + Y - 1) // Y, (z + Z - 1) // Z)
|
|
808
|
+
block_dim = (X, Y, Z)
|
|
809
|
+
# print(f"{grid_xyz} -> {grid_dim} {block_dim}")
|
|
810
|
+
return grid_dim, block_dim
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
def _max_pos_diff(pos_tensor, stream_holder):
|
|
814
|
+
pos_tensor = as_external_tensor(pos_tensor, stream_holder)
|
|
815
|
+
return (pos_tensor[1:] - pos_tensor[:-1]).max().item()
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _populate_grid(tensorA, ti_grid_iter, sizes, stream_holder):
|
|
819
|
+
"""Populates grid x,y,z values given the tensor format and grid iteration."""
|
|
820
|
+
fmts = list(tensorA.tensor_format.levels.values())
|
|
821
|
+
grid_xyz = []
|
|
822
|
+
for t, i in ti_grid_iter:
|
|
823
|
+
if t == Index.FREE_A or t == Index.LEVEL_A:
|
|
824
|
+
grid_xyz.append(CYCLIC)
|
|
825
|
+
elif t == Index.FREE:
|
|
826
|
+
grid_xyz.append(sizes[i])
|
|
827
|
+
elif t == Index.LEVEL:
|
|
828
|
+
if _fmt(fmts[i]) == LevelFormat.DENSE or _fmt(fmts[i]) == LevelFormat.BATCH:
|
|
829
|
+
grid_xyz.append(tensorA.levels[i])
|
|
830
|
+
elif _fmt(fmts[i]) == LevelFormat.COMPRESSED:
|
|
831
|
+
pos_tensor = tensorA._pos[i].tensor
|
|
832
|
+
grid_xyz.append(_max_pos_diff(pos_tensor, stream_holder))
|
|
833
|
+
else:
|
|
834
|
+
raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[i])}")
|
|
835
|
+
else:
|
|
836
|
+
raise AssertionError(f"Unexpected index type: {t}")
|
|
837
|
+
return grid_xyz
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
def _populate_data_parameters(tensor):
|
|
841
|
+
"""Populates parameters for given UST."""
|
|
842
|
+
params = []
|
|
843
|
+
for lvl, (_, fmt) in enumerate(tensor.tensor_format.levels.items()):
|
|
844
|
+
f = _fmt(fmt)
|
|
845
|
+
if f in (LevelFormat.DENSE, LevelFormat.BATCH, LevelFormat.RANGE):
|
|
846
|
+
pass
|
|
847
|
+
elif f in (LevelFormat.COMPRESSED, LevelFormat.DELTA):
|
|
848
|
+
params.append(tensor._pos.get(lvl).data_ptr)
|
|
849
|
+
params.append(tensor._crd.get(lvl).data_ptr)
|
|
850
|
+
elif f in (LevelFormat.SINGLETON, LevelFormat.STRUCTURED):
|
|
851
|
+
params.append(tensor._crd.get(lvl).data_ptr)
|
|
852
|
+
else:
|
|
853
|
+
raise AssertionError(f"Unknown level format: {f}")
|
|
854
|
+
params.append(tensor._val.data_ptr)
|
|
855
|
+
return params
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
#################
|
|
859
|
+
# ENTRY METHODS #
|
|
860
|
+
#################
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
def emit_apply_kernel(tensorA, with_indices):
|
|
864
|
+
"""
|
|
865
|
+
Emits the kernel for an apply operation (with or without indices).
|
|
866
|
+
|
|
867
|
+
Returns source_code, grid_iter
|
|
868
|
+
"""
|
|
869
|
+
stream = StringIO()
|
|
870
|
+
fmtA = tensorA.tensor_format
|
|
871
|
+
ctp = type_str(tensorA.dtype)
|
|
872
|
+
vtp = ctp
|
|
873
|
+
itp = type_str(tensorA.index_type)
|
|
874
|
+
gtp = "int"
|
|
875
|
+
indices = [(Index.LEVEL, i) for i in range(tensorA.num_levels)]
|
|
876
|
+
fmts = list(fmtA.levels.values())
|
|
877
|
+
G = _max_grid(indices, fmts) # always max, with endian=True
|
|
878
|
+
grid_iter = _emit_apply_kernel(indices, fmtA, True, ctp, vtp, itp, gtp, with_indices, G, stream=stream)
|
|
879
|
+
return stream.getvalue(), grid_iter
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def populate_apply_parameters(tensorA, with_indices, grid_iter, stream_holder):
|
|
883
|
+
"""
|
|
884
|
+
Populates the parameters for an apply operation (with or without indices).
|
|
885
|
+
|
|
886
|
+
Returns (grid_dim, block_dim), params
|
|
887
|
+
"""
|
|
888
|
+
with stream_holder.ctx:
|
|
889
|
+
ti_grid_iter, endian = grid_iter
|
|
890
|
+
if with_indices:
|
|
891
|
+
# Set grid and parameters.
|
|
892
|
+
grid_xyz = _populate_grid(tensorA, ti_grid_iter, [], stream_holder)
|
|
893
|
+
params = _populate_data_parameters(tensorA)
|
|
894
|
+
for i in range(tensorA.num_dimensions):
|
|
895
|
+
params.append(tensorA.extents[i])
|
|
896
|
+
for i in range(tensorA.num_levels):
|
|
897
|
+
params.append(tensorA.levels[i])
|
|
898
|
+
else:
|
|
899
|
+
# Set trivial grid and parameters.
|
|
900
|
+
grid_xyz = [tensorA.nse]
|
|
901
|
+
params = [tensorA._val.data_ptr, tensorA.nse]
|
|
902
|
+
return _compute_grid_block(grid_xyz, endian), params
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def emit_matmul_kernel(tensorA, tensorB, tensorC, ctp, transpose_a=False, transpose_b=False, kernel=0, backend=Backend.CUDA):
|
|
906
|
+
"""
|
|
907
|
+
Emits the kernel for a generic C += AxB matmul operation.
|
|
908
|
+
|
|
909
|
+
Returns source_code, grid_iter
|
|
910
|
+
"""
|
|
911
|
+
stream = StringIO()
|
|
912
|
+
vtp = type_str(tensorA.dtype)
|
|
913
|
+
itp = type_str(tensorA.index_type)
|
|
914
|
+
gtp = "int"
|
|
915
|
+
|
|
916
|
+
# Restrictions on dense formats of B and C.
|
|
917
|
+
if tensorB.tensor_format.name not in ["DenseVector", "DensedRight", "DensedLeft", "Dense3D-0-1-2", "Dense4D-0-1-2-3"]:
|
|
918
|
+
raise NotImplementedError(f"Unsupported format {tensorB.tensor_format.name} for B operand.")
|
|
919
|
+
if tensorC.tensor_format.name not in ["Scalar", "DenseVector", "DensedRight", "Dense3D-0-1-2", "Dense4D-0-1-2-3"]:
|
|
920
|
+
raise NotImplementedError(f"Unsupported format {tensorC.tensor_format.name} for C operand.")
|
|
921
|
+
|
|
922
|
+
# Construct tensor index expression.
|
|
923
|
+
A, B, C = None, None, None
|
|
924
|
+
if tensorA.num_dimensions == 0:
|
|
925
|
+
# A is a scalar.
|
|
926
|
+
raise NotImplementedError("Unsupported matmul scalar A")
|
|
927
|
+
elif tensorA.num_dimensions == 1:
|
|
928
|
+
# A is a vector.
|
|
929
|
+
if tensorB.num_dimensions == 1 and tensorC.num_dimensions == 0:
|
|
930
|
+
A, B, C = [0], [0], [] # DOT: c = a(i) b(i)
|
|
931
|
+
elif tensorB.num_dimensions == 2 and tensorC.num_dimensions == 1:
|
|
932
|
+
A, B, C = [0], [0, 1], [1] # VM: c(j) = a(i) B(i,j)
|
|
933
|
+
else:
|
|
934
|
+
# A is a matrix or tensor.
|
|
935
|
+
if tensorA.num_dimensions == 2 and tensorB.num_dimensions == 1 and tensorC.num_dimensions == 1:
|
|
936
|
+
A, B, C = [0, 1], [1], [0] # MV: c(i) = A(i,j) b(j)
|
|
937
|
+
elif (
|
|
938
|
+
tensorB.num_dimensions == 2 or tensorB.num_dimensions == tensorA.num_dimensions
|
|
939
|
+
) and tensorC.num_dimensions == tensorA.num_dimensions:
|
|
940
|
+
bi = tensorA.num_dimensions - 2 # (B)M(B)M: C([bi*],i,k) = A([bi*],i,j) B({[bi*]},j,k)
|
|
941
|
+
prefix = list(range(bi))
|
|
942
|
+
prefixb = [] if tensorB.num_dimensions == 2 else prefix
|
|
943
|
+
A, B, C = (
|
|
944
|
+
prefix + [bi, bi + 1],
|
|
945
|
+
prefixb + [bi + 1, bi + 2],
|
|
946
|
+
prefix + [bi, bi + 2],
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
if A is None or B is None or C is None:
|
|
950
|
+
raise NotImplementedError("Unsupported matmul situation in sparse emitter")
|
|
951
|
+
|
|
952
|
+
# Transposition is on last two dimensions.
|
|
953
|
+
if tensorB.tensor_format.name == "DensedLeft":
|
|
954
|
+
transpose_b = not transpose_b
|
|
955
|
+
if transpose_a:
|
|
956
|
+
A[-2], A[-1] = A[-1], A[-2]
|
|
957
|
+
if transpose_b:
|
|
958
|
+
B[-2], B[-1] = B[-1], B[-2]
|
|
959
|
+
|
|
960
|
+
grid_iter = _emit_search_space(
|
|
961
|
+
A, B, C, tensorA.tensor_format, ctp, vtp, itp, gtp, kernel=kernel, stream=stream, backend=backend
|
|
962
|
+
)
|
|
963
|
+
return stream.getvalue(), grid_iter
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def count_matmul_kernels(tensorA, tensorB, tensorC, ctp, transpose_a=False, transpose_b=False, backend=Backend.CUDA):
|
|
967
|
+
"""Returns the number of kernels for a generic C += AxB matmul operation."""
|
|
968
|
+
_, G = emit_matmul_kernel(tensorA, tensorB, tensorC, ctp, transpose_a, transpose_b, kernel=None, backend=backend)
|
|
969
|
+
return G
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
def populate_matmul_parameters(
|
|
973
|
+
tensorA,
|
|
974
|
+
tensorB,
|
|
975
|
+
tensorC,
|
|
976
|
+
grid_iter,
|
|
977
|
+
stream_holder,
|
|
978
|
+
transpose_a=False,
|
|
979
|
+
transpose_b=False,
|
|
980
|
+
backend=Backend.CUDA,
|
|
981
|
+
):
|
|
982
|
+
"""
|
|
983
|
+
Populates the parameters for a generic C += AxB matmul operation.
|
|
984
|
+
|
|
985
|
+
Returns (grid_dim, block_dim), params
|
|
986
|
+
"""
|
|
987
|
+
with stream_holder.ctx:
|
|
988
|
+
# Handle transposition. Because transposition can be either forced by caller
|
|
989
|
+
# or due to "Left" layout of B, make sure to test "levels", not "extents".
|
|
990
|
+
if tensorB.tensor_format.name == "DensedLeft":
|
|
991
|
+
transpose_b = not transpose_b
|
|
992
|
+
ai, aj = (1, 0) if transpose_a else (0, 1)
|
|
993
|
+
bi, bj = (1, 0) if transpose_b else (0, 1)
|
|
994
|
+
# Validate and extract sizes from tensor index expression.
|
|
995
|
+
if tensorA.num_dimensions == 1:
|
|
996
|
+
if tensorB.num_dimensions == 1 and tensorC.num_dimensions == 0:
|
|
997
|
+
assert tensorA.extents[0] == tensorB.extents[0]
|
|
998
|
+
sizes = [tensorA.extents[0]] # DOT: c = a(i) b(i)
|
|
999
|
+
else:
|
|
1000
|
+
assert tensorA.extents[0] == tensorB.levels[bi]
|
|
1001
|
+
assert tensorB.levels[bj] == tensorC.extents[0]
|
|
1002
|
+
sizes = [tensorA.extents[0], tensorC.extents[0]] # VM: c(j) = a(i) B(i,j)
|
|
1003
|
+
elif tensorA.num_dimensions == 2 and tensorB.num_dimensions == 1 and tensorC.num_dimensions == 1:
|
|
1004
|
+
assert tensorA.extents[ai] == tensorC.extents[0]
|
|
1005
|
+
assert tensorA.extents[aj] == tensorB.extents[0]
|
|
1006
|
+
sizes = [tensorA.extents[ai], tensorA.extents[aj]] # MV: c(i) = A(i,j) b(j)
|
|
1007
|
+
else:
|
|
1008
|
+
b = tensorA.num_dimensions - 2 # (B)M(B)M: C([bi*],i,k) = A([bi*],i,j) B({[bi*]},j,k)
|
|
1009
|
+
bb = 0 if tensorB.num_dimensions == 2 else b
|
|
1010
|
+
sizes = []
|
|
1011
|
+
for i in range(b):
|
|
1012
|
+
assert tensorA.extents[i] == tensorC.extents[i]
|
|
1013
|
+
assert bb == 0 or tensorA.extents[i] == tensorB.extents[i]
|
|
1014
|
+
sizes.append(tensorA.extents[i])
|
|
1015
|
+
assert tensorA.extents[b + ai] == tensorC.extents[b]
|
|
1016
|
+
assert tensorA.extents[b + aj] == tensorB.levels[bb + bi]
|
|
1017
|
+
assert tensorB.levels[bb + bj] == tensorC.extents[b + 1]
|
|
1018
|
+
sizes.append(tensorA.extents[b + ai])
|
|
1019
|
+
sizes.append(tensorA.extents[b + aj])
|
|
1020
|
+
sizes.append(tensorC.extents[b + 1])
|
|
1021
|
+
# Set grid and parameters.
|
|
1022
|
+
ti_grid_iter, endian = grid_iter
|
|
1023
|
+
grid_xyz = _populate_grid(tensorA, ti_grid_iter, sizes, stream_holder)
|
|
1024
|
+
params = _populate_data_parameters(tensorA)
|
|
1025
|
+
params.append(tensorB._val.data_ptr)
|
|
1026
|
+
params.append(tensorC._val.data_ptr)
|
|
1027
|
+
params.extend(sizes)
|
|
1028
|
+
for i in range(tensorA.num_levels):
|
|
1029
|
+
params.append(tensorA.levels[i])
|
|
1030
|
+
if backend == Backend.OMP:
|
|
1031
|
+
# For OMP, grid dimensions are passed as parameters
|
|
1032
|
+
params.extend(grid_xyz)
|
|
1033
|
+
return _compute_grid_block(grid_xyz, endian), params
|