nvmath-python 0.5.0__cp313-cp313-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 +37 -0
- nvmath/_internal/__init__.py +0 -0
- nvmath/_internal/layout.py +58 -0
- nvmath/_utils.py +363 -0
- nvmath/bindings/__init__.pxd +0 -0
- nvmath/bindings/__init__.py +44 -0
- nvmath/bindings/_internal/__init__.pxd +0 -0
- nvmath/bindings/_internal/__init__.py +0 -0
- nvmath/bindings/_internal/cublas.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cublas.pxd +518 -0
- nvmath/bindings/_internal/cublasLt.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cublasLt.pxd +54 -0
- nvmath/bindings/_internal/cudss.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cudss.pxd +44 -0
- nvmath/bindings/_internal/cufft.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cufft.pxd +69 -0
- nvmath/bindings/_internal/cufftMp.pxd +77 -0
- nvmath/bindings/_internal/curand.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/curand.pxd +42 -0
- nvmath/bindings/_internal/cusolver.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolver.pxd +15 -0
- nvmath/bindings/_internal/cusolverDn.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolverDn.pxd +386 -0
- nvmath/bindings/_internal/cusparse.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusparse.pxd +270 -0
- nvmath/bindings/_internal/mathdx.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/mathdx.pxd +79 -0
- nvmath/bindings/_internal/nvshmem.pxd +29 -0
- nvmath/bindings/_internal/utils.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/utils.pxd +186 -0
- nvmath/bindings/cublas.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cublas.pxd +545 -0
- nvmath/bindings/cublas.pyi +18 -0
- nvmath/bindings/cublasLt.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cublasLt.pxd +99 -0
- nvmath/bindings/cudss.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cudss.pxd +83 -0
- nvmath/bindings/cudss.pyi +249 -0
- nvmath/bindings/cufft.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cufft.pxd +114 -0
- nvmath/bindings/cufftMp.pxd +124 -0
- nvmath/bindings/curand.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/curand.pxd +71 -0
- nvmath/bindings/cusolver.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cusolver.pxd +60 -0
- nvmath/bindings/cusolverDn.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cusolverDn.pxd +410 -0
- nvmath/bindings/cusparse.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cusparse.pxd +329 -0
- nvmath/bindings/cycublas.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycublas.pxd +657 -0
- nvmath/bindings/cycublasLt.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycublasLt.pxd +1010 -0
- nvmath/bindings/cycudss.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycudss.pxd +191 -0
- nvmath/bindings/cycufft.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycufft.pxd +320 -0
- nvmath/bindings/cycufftMp.pxd +333 -0
- nvmath/bindings/cycurand.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycurand.pxd +146 -0
- nvmath/bindings/cycusolver.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolver.pxd +143 -0
- nvmath/bindings/cycusolverDn.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolverDn.pxd +414 -0
- nvmath/bindings/cycusparse.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cycusparse.pxd +476 -0
- nvmath/bindings/cymathdx.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/cymathdx.pxd +363 -0
- nvmath/bindings/cynvshmem.pxd +126 -0
- nvmath/bindings/mathdx.cp313-win_amd64.pyd +0 -0
- nvmath/bindings/mathdx.pxd +131 -0
- nvmath/bindings/mathdx.pyi +686 -0
- nvmath/bindings/nvpl/__init__.pxd +0 -0
- nvmath/bindings/nvpl/__init__.py +11 -0
- nvmath/bindings/nvpl/_internal/__init__.pxd +0 -0
- nvmath/bindings/nvpl/_internal/__init__.py +0 -0
- nvmath/bindings/nvpl/_internal/fft.pxd +36 -0
- nvmath/bindings/nvpl/cyfft.pxd +87 -0
- nvmath/bindings/nvpl/fft.pxd +100 -0
- nvmath/bindings/nvshmem.pxd +54 -0
- nvmath/device/__init__.py +23 -0
- nvmath/device/_deprecated.py +33 -0
- nvmath/device/caching.py +51 -0
- nvmath/device/common.py +131 -0
- nvmath/device/common_backend.py +125 -0
- nvmath/device/common_cuda.py +156 -0
- nvmath/device/common_mathdx.py +97 -0
- nvmath/device/common_numba.py +159 -0
- nvmath/device/common_opaque_tensor.py +128 -0
- nvmath/device/cublasdx.py +1085 -0
- nvmath/device/cublasdx_backend.py +552 -0
- nvmath/device/cublasdx_numba.py +528 -0
- nvmath/device/cufftdx.py +551 -0
- nvmath/device/cufftdx_backend.py +194 -0
- nvmath/device/cufftdx_numba.py +123 -0
- nvmath/device/curand_kernel.py +9147 -0
- nvmath/device/nvrtc.py +95 -0
- nvmath/device/patch.py +35 -0
- nvmath/device/random.py +417 -0
- nvmath/device/random_helpers.py +23 -0
- nvmath/device/random_states.py +189 -0
- nvmath/device/types.py +11 -0
- nvmath/device/vector_types_numba.py +203 -0
- nvmath/distributed/__init__.py +117 -0
- nvmath/distributed/_internal/__init__.py +0 -0
- nvmath/distributed/_internal/nvshmem.py +243 -0
- nvmath/distributed/_internal/tensor_ifc_cupy.py +111 -0
- nvmath/distributed/_internal/tensor_ifc_numpy.py +53 -0
- nvmath/distributed/_internal/tensor_ifc_torch.py +108 -0
- nvmath/distributed/_internal/tensor_wrapper.py +59 -0
- nvmath/distributed/_utils.py +152 -0
- nvmath/distributed/fft/__init__.py +6 -0
- nvmath/distributed/fft/_configuration.py +86 -0
- nvmath/distributed/fft/fft.py +1737 -0
- nvmath/distributed/reshape/__init__.py +6 -0
- nvmath/distributed/reshape/_configuration.py +39 -0
- nvmath/distributed/reshape/reshape.py +1249 -0
- nvmath/fft/__init__.py +7 -0
- nvmath/fft/_configuration.py +208 -0
- nvmath/fft/_exec_utils.py +116 -0
- nvmath/fft/_helpers.py +260 -0
- nvmath/fft/fft.py +2346 -0
- nvmath/internal/__init__.py +5 -0
- nvmath/internal/enum_utils.py +142 -0
- nvmath/internal/formatters.py +87 -0
- nvmath/internal/mem_limit.py +51 -0
- nvmath/internal/package_ifc.py +119 -0
- nvmath/internal/package_ifc_cuda.py +43 -0
- nvmath/internal/package_ifc_cupy.py +49 -0
- nvmath/internal/package_ifc_torch.py +31 -0
- nvmath/internal/package_wrapper.py +14 -0
- nvmath/internal/tensor_ifc.py +143 -0
- nvmath/internal/tensor_ifc_cupy.py +185 -0
- nvmath/internal/tensor_ifc_numpy.py +157 -0
- nvmath/internal/tensor_ifc_torch.py +115 -0
- nvmath/internal/tensor_wrapper.py +160 -0
- nvmath/internal/typemaps.py +154 -0
- nvmath/internal/utils.py +634 -0
- nvmath/linalg/__init__.py +11 -0
- nvmath/linalg/_internal/__init__.py +3 -0
- nvmath/linalg/_internal/algo_cap_ifc.py +86 -0
- nvmath/linalg/_internal/algo_config_ifc.py +87 -0
- nvmath/linalg/_internal/enum_to_tuples.py +64 -0
- nvmath/linalg/_internal/epilog_protocol.py +738 -0
- nvmath/linalg/_internal/matmul_desc_ifc.py +72 -0
- nvmath/linalg/_internal/matmul_pref_ifc.py +65 -0
- nvmath/linalg/_internal/matrix_layout_ifc.py +59 -0
- nvmath/linalg/_internal/typemaps.py +135 -0
- nvmath/linalg/_internal/utils.py +96 -0
- nvmath/linalg/advanced/__init__.py +8 -0
- nvmath/linalg/advanced/_algorithmmod.py +142 -0
- nvmath/linalg/advanced/_configuration.py +320 -0
- nvmath/linalg/advanced/helpers/__init__.py +5 -0
- nvmath/linalg/advanced/helpers/matmul.py +173 -0
- nvmath/linalg/advanced/matmulmod.py +2855 -0
- nvmath/memory.py +266 -0
- nvmath/sparse/__init__.py +18 -0
- nvmath/sparse/_internal/__init__.py +21 -0
- nvmath/sparse/_internal/common_utils.py +81 -0
- nvmath/sparse/_internal/cudss_config_ifc.py +584 -0
- nvmath/sparse/_internal/cudss_data_ifc.py +291 -0
- nvmath/sparse/_internal/cudss_utils.py +398 -0
- nvmath/sparse/_internal/sparse_csr_ifc.py +154 -0
- nvmath/sparse/_internal/sparse_format_helpers.py +88 -0
- nvmath/sparse/_internal/sparse_tensor_ifc.py +80 -0
- nvmath/sparse/advanced/__init__.py +7 -0
- nvmath/sparse/advanced/_configuration.py +150 -0
- nvmath/sparse/advanced/direct_solver.py +1705 -0
- nvmath_python-0.5.0.dist-info/METADATA +122 -0
- nvmath_python-0.5.0.dist-info/RECORD +174 -0
- nvmath_python-0.5.0.dist-info/WHEEL +5 -0
- nvmath_python-0.5.0.dist-info/licenses/LICENSE +177 -0
- nvmath_python-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1085 @@
|
|
|
1
|
+
# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
__all__ = ["matmul", "TransposeMode", "BlasOptions"]
|
|
6
|
+
|
|
7
|
+
from functools import cached_property
|
|
8
|
+
import itertools
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
import math
|
|
11
|
+
import re
|
|
12
|
+
from typing import overload
|
|
13
|
+
from warnings import warn
|
|
14
|
+
|
|
15
|
+
from .common import (
|
|
16
|
+
Layout,
|
|
17
|
+
make_binary_tempfile,
|
|
18
|
+
check_in,
|
|
19
|
+
SHARED_DEVICE_DOCSTRINGS,
|
|
20
|
+
pad_or_truncate,
|
|
21
|
+
)
|
|
22
|
+
from .common_backend import MATHDX_TYPES_TO_NP, get_isa_version, get_lto
|
|
23
|
+
from .common_cuda import MAX_SUPPORTED_CC, get_default_code_type, ComputeCapability, Code, CodeType, Dim3
|
|
24
|
+
from .common_numba import NP_TYPES_TO_NUMBA_FE_TYPES
|
|
25
|
+
from .cublasdx_backend import (
|
|
26
|
+
Alignment,
|
|
27
|
+
Arrangement,
|
|
28
|
+
Precision,
|
|
29
|
+
generate_MM,
|
|
30
|
+
generate_code,
|
|
31
|
+
generate_code_tensors,
|
|
32
|
+
generate_copy_wait_lto,
|
|
33
|
+
generate_tensors,
|
|
34
|
+
get_str_trait,
|
|
35
|
+
get_int_traits,
|
|
36
|
+
get_tensor_int_traits,
|
|
37
|
+
validate,
|
|
38
|
+
LeadingDimension,
|
|
39
|
+
TransposeMode,
|
|
40
|
+
validate_alignment,
|
|
41
|
+
validate_execute_api,
|
|
42
|
+
validate_tensor_types,
|
|
43
|
+
MAX_ALIGNMENT, # noqa: F401
|
|
44
|
+
)
|
|
45
|
+
from ._deprecated import deprecated
|
|
46
|
+
from nvmath.internal.utils import docstring_decorator
|
|
47
|
+
|
|
48
|
+
from nvmath.bindings import mathdx
|
|
49
|
+
import numpy
|
|
50
|
+
|
|
51
|
+
CUBLASDX_DOCSTRING = SHARED_DEVICE_DOCSTRINGS.copy()
|
|
52
|
+
CUBLASDX_DOCSTRING.update(
|
|
53
|
+
{
|
|
54
|
+
"size": """\
|
|
55
|
+
A sequence of integers denoting the three dimensions ``(m, n, k)`` for the matrix multiplication
|
|
56
|
+
problem.""".replace("\n", " "),
|
|
57
|
+
#
|
|
58
|
+
"data_type": """\
|
|
59
|
+
The data type of the input matrices, can be either ``'real'`` or ``'complex'``.""".replace("\n", " "),
|
|
60
|
+
#
|
|
61
|
+
"block_size": """\
|
|
62
|
+
The total block size, optional. If not provided or set to ``'suggested'``, will be set to a suggested value for 1D block
|
|
63
|
+
dim. """.replace("\n", " "),
|
|
64
|
+
#
|
|
65
|
+
"block_dim": """\
|
|
66
|
+
The block dimension for launching the CUDA kernel, optional. If not provided or set to ``'suggested'``, will be set to a
|
|
67
|
+
suggested value. Cannot be used when `block_size` is explicitly specified.""".replace("\n", " "),
|
|
68
|
+
#
|
|
69
|
+
"leading_dimension": """\
|
|
70
|
+
The leading dimensions for the input matrices, optional. If not provided, will be set to match the matrix row/column
|
|
71
|
+
dimension. Alternatively, if provided as ``'suggested'``, will be set to a suggested value for optimal performance.
|
|
72
|
+
""".replace("\n", " "),
|
|
73
|
+
#
|
|
74
|
+
"transpose_mode": """\
|
|
75
|
+
The transpose mode for all input matrices ;
|
|
76
|
+
transpose_mode or arrangement must be provided.""".replace("\n", " "),
|
|
77
|
+
#
|
|
78
|
+
"arrangement": """\
|
|
79
|
+
The arrangement for all input matrices ;
|
|
80
|
+
transpose_mode or arrangement must be provided.""".replace("\n", " "),
|
|
81
|
+
#
|
|
82
|
+
"alignment": """\
|
|
83
|
+
The alignment for the input matrices in shared memory.
|
|
84
|
+
Defines the alignments (in bytes) of the input matrices A, B, and C
|
|
85
|
+
(either arrays or wrapped in opaque tensors) that are passed to the
|
|
86
|
+
execute(...) method. Default alignment is equal to an element size of the
|
|
87
|
+
matrix unless used suggested layout. In that case alignment is greater or equal
|
|
88
|
+
than the element size.""".replace("\n", " "),
|
|
89
|
+
#
|
|
90
|
+
"global_memory_alignment": """\
|
|
91
|
+
Same as alignment, but for the global memory. Used to optimize copying between
|
|
92
|
+
shared and global memory.
|
|
93
|
+
""".replace("\n", " "),
|
|
94
|
+
#
|
|
95
|
+
"function": """\
|
|
96
|
+
A string specifying the name of the function. Currently supports ``'MM'`` (default) for matrix
|
|
97
|
+
multiplication.""".replace("\n", " "),
|
|
98
|
+
#
|
|
99
|
+
"execute_api": """\
|
|
100
|
+
A string specifying the signature of the function that handles problems with default or custom/dynamic leading dimensions.
|
|
101
|
+
Could be ``'static_leading_dimensions'`` or ``'dynamic_leading_dimensions'``.""".replace("\n", " "),
|
|
102
|
+
"tensor_types": """\
|
|
103
|
+
A list of strings specifying the tensors being used at execute signature.""".replace("\n", " "),
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
#
|
|
108
|
+
# A set of knobs, potentially in-complete (ie not sufficient to generate a device functions)
|
|
109
|
+
#
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SharedStorageCalc:
|
|
113
|
+
"""
|
|
114
|
+
Helper class to calculate shared storage size.
|
|
115
|
+
|
|
116
|
+
For further details, please refer to `cuBLASDx documentation
|
|
117
|
+
<https://docs.nvidia.com/cuda/cublasdx/>`_.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
_memory: int = 0
|
|
121
|
+
|
|
122
|
+
@overload
|
|
123
|
+
def add(self, alignment: int, matrix_size_bytes: int) -> None: ...
|
|
124
|
+
@overload
|
|
125
|
+
def add(self, alignment: int, elem_size: int, num_elements: int) -> None: ...
|
|
126
|
+
@overload
|
|
127
|
+
def add(self, alignment: int, elem_size: int, layout: Layout) -> None: ...
|
|
128
|
+
def add(self, *args):
|
|
129
|
+
assert len(args) in {2, 3}
|
|
130
|
+
|
|
131
|
+
if len(args) == 2:
|
|
132
|
+
[alignment, matrix_size_bytes] = args
|
|
133
|
+
|
|
134
|
+
assert matrix_size_bytes > 0
|
|
135
|
+
else:
|
|
136
|
+
[alignment, elem_size, num_elements] = args
|
|
137
|
+
|
|
138
|
+
if isinstance(num_elements, Layout):
|
|
139
|
+
num_elements = num_elements.cosize
|
|
140
|
+
|
|
141
|
+
assert elem_size > 0
|
|
142
|
+
assert num_elements > 0
|
|
143
|
+
|
|
144
|
+
matrix_size_bytes = elem_size * num_elements
|
|
145
|
+
|
|
146
|
+
assert alignment > 0
|
|
147
|
+
|
|
148
|
+
self._memory = ((self._memory + alignment - 1) // alignment) * alignment + matrix_size_bytes
|
|
149
|
+
|
|
150
|
+
def get(self):
|
|
151
|
+
return self._memory
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@docstring_decorator(CUBLASDX_DOCSTRING, skip_missing=False)
|
|
155
|
+
class BlasOptions:
|
|
156
|
+
"""
|
|
157
|
+
A class that encapsulates a partial BLAS device function. A partial device function can
|
|
158
|
+
be queried for available or optimal values for some knobs (such as `leading_dimension`
|
|
159
|
+
or `block_dim`). It does not contain a compiled, ready-to-use, device function until
|
|
160
|
+
finalized using :meth:`create`.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
size: {size}
|
|
164
|
+
|
|
165
|
+
precision: {precision}
|
|
166
|
+
|
|
167
|
+
data_type: {data_type}
|
|
168
|
+
|
|
169
|
+
code_type (CodeType): {code_type}
|
|
170
|
+
|
|
171
|
+
block_size (int): {block_size}
|
|
172
|
+
|
|
173
|
+
block_dim (Dim3): {block_dim}
|
|
174
|
+
|
|
175
|
+
leading_dimension (LeadingDimension): {leading_dimension}
|
|
176
|
+
|
|
177
|
+
transpose_mode (TransposeMode): {transpose_mode}
|
|
178
|
+
|
|
179
|
+
arrangement (Arrangement): {arrangement}
|
|
180
|
+
|
|
181
|
+
alignment (Alignment): {alignment}
|
|
182
|
+
|
|
183
|
+
function (str): {function}
|
|
184
|
+
|
|
185
|
+
execution (str): {execution}
|
|
186
|
+
|
|
187
|
+
execute_api:
|
|
188
|
+
.. versionchanged:: 0.5.0
|
|
189
|
+
execute_api is not part of the Blas type. Pass this argument to
|
|
190
|
+
:py:func:`nvmath.device.matmul` instead.
|
|
191
|
+
|
|
192
|
+
tensor_types:
|
|
193
|
+
.. versionchanged:: 0.5.0
|
|
194
|
+
tensor_types is not part of the Blas type. Pass this argument to
|
|
195
|
+
:py:func:`nvmath.device.matmul` instead.
|
|
196
|
+
|
|
197
|
+
See Also:
|
|
198
|
+
The attributes of this class provide a 1:1 mapping with the CUDA C++ cuBLASDx APIs.
|
|
199
|
+
For further details, please refer to `cuBLASDx documentation
|
|
200
|
+
<https://docs.nvidia.com/cuda/cublasdx/>`_.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
def __init__(
|
|
204
|
+
self,
|
|
205
|
+
size,
|
|
206
|
+
precision,
|
|
207
|
+
data_type,
|
|
208
|
+
*,
|
|
209
|
+
code_type=None,
|
|
210
|
+
block_size=None,
|
|
211
|
+
block_dim=None,
|
|
212
|
+
leading_dimension=None,
|
|
213
|
+
transpose_mode=None,
|
|
214
|
+
arrangement=None,
|
|
215
|
+
alignment=None,
|
|
216
|
+
function="MM",
|
|
217
|
+
static_block_dim=False,
|
|
218
|
+
execution="Block",
|
|
219
|
+
):
|
|
220
|
+
if not isinstance(code_type, Sequence) or len(code_type) != 2:
|
|
221
|
+
raise ValueError(f"code_type should be an instance of CodeType or a 2-tuple ; got code_type = {code_type}")
|
|
222
|
+
code_type = CodeType(code_type[0], ComputeCapability(*code_type[1]))
|
|
223
|
+
if code_type.cc.major < 7:
|
|
224
|
+
raise RuntimeError(
|
|
225
|
+
f"Minimal compute capability 7.0 is required by cuBLASDx, got {code_type.cc.major}.{code_type.cc.minor}"
|
|
226
|
+
)
|
|
227
|
+
if (code_type.cc.major, code_type.cc.minor) > MAX_SUPPORTED_CC:
|
|
228
|
+
raise RuntimeError(
|
|
229
|
+
"The maximum compute capability currently supported by device "
|
|
230
|
+
f"APIs is {MAX_SUPPORTED_CC}, "
|
|
231
|
+
f"got {code_type.cc.major}.{code_type.cc.minor}"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if transpose_mode is not None:
|
|
235
|
+
warn(
|
|
236
|
+
"transpose_mode is deprecated and may be removed in future versions. User arrangement instead",
|
|
237
|
+
category=DeprecationWarning,
|
|
238
|
+
)
|
|
239
|
+
if not isinstance(transpose_mode, Sequence) or len(transpose_mode) != 2:
|
|
240
|
+
raise ValueError(
|
|
241
|
+
"transpose_mode should be an instance of TransposeMode or a 2-tuple ; "
|
|
242
|
+
f"got transpose_mode = {transpose_mode}"
|
|
243
|
+
)
|
|
244
|
+
transpose_mode = TransposeMode(*transpose_mode)
|
|
245
|
+
if arrangement is not None:
|
|
246
|
+
if not isinstance(arrangement, Sequence) or len(arrangement) != 3:
|
|
247
|
+
raise ValueError(
|
|
248
|
+
f"arrangement should be an instance of Arrangement or a 3-tuple ; got arrangement = {arrangement}"
|
|
249
|
+
)
|
|
250
|
+
arrangement = Arrangement(*arrangement)
|
|
251
|
+
|
|
252
|
+
if alignment is not None:
|
|
253
|
+
if not isinstance(alignment, Sequence) or len(alignment) != 3:
|
|
254
|
+
raise ValueError(f"alignment should be an instance of Alignment or a 3-tuple ; got alignment = {alignment}")
|
|
255
|
+
alignment = Alignment(*alignment)
|
|
256
|
+
|
|
257
|
+
if leading_dimension is not None and leading_dimension != "suggested":
|
|
258
|
+
if not isinstance(leading_dimension, Sequence) or len(leading_dimension) != 3:
|
|
259
|
+
raise ValueError(
|
|
260
|
+
"leading_dimension should be a 3-tuple, an instance of LeadingDimension, 'suggested' or None ; "
|
|
261
|
+
f"got leading_dimension = {leading_dimension}"
|
|
262
|
+
)
|
|
263
|
+
else:
|
|
264
|
+
leading_dimension = LeadingDimension(*leading_dimension)
|
|
265
|
+
|
|
266
|
+
if isinstance(precision, Sequence):
|
|
267
|
+
if len(precision) != 3:
|
|
268
|
+
raise ValueError(
|
|
269
|
+
"precision should be a 3-len sequence, an instance of Precision, or a single value; "
|
|
270
|
+
f"got precision = {precision}"
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
precision = (precision, precision, precision)
|
|
274
|
+
precision = Precision(*precision)
|
|
275
|
+
|
|
276
|
+
#
|
|
277
|
+
# Check that the knobs are, individually, valid
|
|
278
|
+
#
|
|
279
|
+
|
|
280
|
+
if block_size is not None and block_dim is not None:
|
|
281
|
+
raise ValueError(f"Both block_size ({block_size}) and block_dim ({block_dim}) cannot be specified.")
|
|
282
|
+
if block_size is not None:
|
|
283
|
+
check_in("block_dim", block_dim, [None])
|
|
284
|
+
if block_size == "suggested":
|
|
285
|
+
block_dim = "suggested"
|
|
286
|
+
else:
|
|
287
|
+
block_dim = Dim3(block_size, 1, 1)
|
|
288
|
+
if block_dim is not None and isinstance(block_dim, Sequence) and block_dim != "suggested":
|
|
289
|
+
if len(block_dim) != 3:
|
|
290
|
+
raise ValueError(
|
|
291
|
+
f"block_dim should be a 3-tuple, an instance of Dim3, 'suggested' or None ; got block_dim = {block_dim}"
|
|
292
|
+
)
|
|
293
|
+
else:
|
|
294
|
+
block_dim = Dim3(*block_dim)
|
|
295
|
+
|
|
296
|
+
validate(
|
|
297
|
+
size=size,
|
|
298
|
+
precision=precision,
|
|
299
|
+
data_type=data_type,
|
|
300
|
+
transpose_mode=transpose_mode,
|
|
301
|
+
arrangement=arrangement,
|
|
302
|
+
alignment=alignment,
|
|
303
|
+
code_type=code_type,
|
|
304
|
+
leading_dimension=leading_dimension,
|
|
305
|
+
block_dim=block_dim,
|
|
306
|
+
function=function,
|
|
307
|
+
execution=execution,
|
|
308
|
+
static_block_dim=static_block_dim,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
#
|
|
312
|
+
# Traits set by input
|
|
313
|
+
#
|
|
314
|
+
|
|
315
|
+
self._size = size
|
|
316
|
+
self._precision = precision
|
|
317
|
+
self._data_type = data_type
|
|
318
|
+
self._transpose_mode = transpose_mode
|
|
319
|
+
self._arrangement = arrangement
|
|
320
|
+
self._alignment = alignment
|
|
321
|
+
self._code_type = code_type
|
|
322
|
+
self._block_dim = block_dim
|
|
323
|
+
self._function = function
|
|
324
|
+
self._execution = execution
|
|
325
|
+
self._leading_dimension = leading_dimension
|
|
326
|
+
self._static_block_dim = static_block_dim
|
|
327
|
+
|
|
328
|
+
#
|
|
329
|
+
# Update suggested traits
|
|
330
|
+
#
|
|
331
|
+
|
|
332
|
+
if leading_dimension == "suggested":
|
|
333
|
+
self._leading_dimension = self._suggested_leading_dimension
|
|
334
|
+
|
|
335
|
+
if block_dim == "suggested":
|
|
336
|
+
self._block_dim = self._suggested_block_dim
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def precision(self) -> Precision:
|
|
340
|
+
return self._precision
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def data_type(self) -> str:
|
|
344
|
+
return self._data_type
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def size(self) -> tuple[int, int, int]:
|
|
348
|
+
return self._size
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def execution(self) -> str:
|
|
352
|
+
return self._execution
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
@deprecated("transpose_mode trait is deprecated and may be removed in future versions. Use arrangement instead")
|
|
356
|
+
def transpose_mode(self) -> TransposeMode:
|
|
357
|
+
return self._transpose_mode
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def arrangement(self) -> Arrangement:
|
|
361
|
+
return self._arrangement
|
|
362
|
+
|
|
363
|
+
@property
|
|
364
|
+
def alignment(self) -> Alignment:
|
|
365
|
+
return self._alignment
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def code_type(self):
|
|
369
|
+
return self._code_type
|
|
370
|
+
|
|
371
|
+
@property
|
|
372
|
+
def function(self) -> str:
|
|
373
|
+
return self._function
|
|
374
|
+
|
|
375
|
+
@property
|
|
376
|
+
def block_size(self) -> int:
|
|
377
|
+
return self._block_dim[0] * self._block_dim[1] * self._block_dim[2]
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def block_dim(self) -> Dim3:
|
|
381
|
+
return self._block_dim
|
|
382
|
+
|
|
383
|
+
@property
|
|
384
|
+
def static_block_dim(self) -> bool:
|
|
385
|
+
return self._static_block_dim
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def leading_dimension(self) -> LeadingDimension:
|
|
389
|
+
return self._leading_dimension
|
|
390
|
+
|
|
391
|
+
#
|
|
392
|
+
# Extensions
|
|
393
|
+
#
|
|
394
|
+
|
|
395
|
+
def valid(self, *knobs):
|
|
396
|
+
return itertools.product(*[self._valid(knob) for knob in knobs])
|
|
397
|
+
|
|
398
|
+
def definition(self):
|
|
399
|
+
dd = {
|
|
400
|
+
"size": self.size,
|
|
401
|
+
"precision": self.precision,
|
|
402
|
+
"data_type": self.data_type,
|
|
403
|
+
"transpose_mode": self.transpose_mode,
|
|
404
|
+
"arrangement": self.arrangement,
|
|
405
|
+
"alignment": self.alignment,
|
|
406
|
+
"code_type": self.code_type,
|
|
407
|
+
"block_dim": self.block_dim,
|
|
408
|
+
"static_block_dim": self.static_block_dim,
|
|
409
|
+
"function": self.function,
|
|
410
|
+
"execution": self.execution,
|
|
411
|
+
"leading_dimension": self.leading_dimension,
|
|
412
|
+
}
|
|
413
|
+
return dd
|
|
414
|
+
|
|
415
|
+
def create(self, **kwargs):
|
|
416
|
+
dd = self.definition()
|
|
417
|
+
dd.update(**kwargs)
|
|
418
|
+
return matmul(**dd)
|
|
419
|
+
|
|
420
|
+
#
|
|
421
|
+
# Private implementations
|
|
422
|
+
#
|
|
423
|
+
|
|
424
|
+
def _valid(self, knob):
|
|
425
|
+
if knob == "block_dim":
|
|
426
|
+
return [self._suggested_block_dim]
|
|
427
|
+
else:
|
|
428
|
+
raise ValueError("Unsupported knob")
|
|
429
|
+
|
|
430
|
+
@cached_property
|
|
431
|
+
def _suggested_leading_dimension(self):
|
|
432
|
+
if self.code_type is None:
|
|
433
|
+
raise ValueError("leading_dimension='suggested' require code_type to be set.")
|
|
434
|
+
if self.execution != "Block":
|
|
435
|
+
raise ValueError("leading_dimension='suggested' require execution to be 'Block'.")
|
|
436
|
+
# Generate special PTX for suggested_leading_dimension_of
|
|
437
|
+
descriptor = generate_MM(
|
|
438
|
+
size=self.size,
|
|
439
|
+
function=self.function,
|
|
440
|
+
precision=self.precision,
|
|
441
|
+
data_type=self.data_type,
|
|
442
|
+
code_type=self.code_type,
|
|
443
|
+
transpose_mode=self._transpose_mode,
|
|
444
|
+
arrangement=self._arrangement,
|
|
445
|
+
alignment=self._alignment,
|
|
446
|
+
block_dim=None,
|
|
447
|
+
static_block_dim=self._static_block_dim,
|
|
448
|
+
leading_dimension=None,
|
|
449
|
+
execution=self.execution,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
return LeadingDimension(*get_int_traits(descriptor.descriptor, mathdx.CublasdxTraitType.SUGGESTED_LEADING_DIMENSION, 3))
|
|
453
|
+
|
|
454
|
+
@cached_property
|
|
455
|
+
def _suggested_block_dim(self):
|
|
456
|
+
if self.code_type is None:
|
|
457
|
+
raise ValueError("block_dim='suggested' require code_type to be set.")
|
|
458
|
+
if self.execution != "Block":
|
|
459
|
+
raise ValueError("block_dim='suggested' require execution to be 'Block'.")
|
|
460
|
+
# Generate full PTX
|
|
461
|
+
descriptor = generate_MM(
|
|
462
|
+
size=self.size,
|
|
463
|
+
function=self.function,
|
|
464
|
+
precision=self.precision,
|
|
465
|
+
data_type=self.data_type,
|
|
466
|
+
code_type=self.code_type,
|
|
467
|
+
transpose_mode=self._transpose_mode,
|
|
468
|
+
arrangement=self._arrangement,
|
|
469
|
+
alignment=self._alignment,
|
|
470
|
+
block_dim=None,
|
|
471
|
+
static_block_dim=self._static_block_dim,
|
|
472
|
+
leading_dimension=None,
|
|
473
|
+
execution=self.execution,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
return Dim3(*get_int_traits(descriptor.descriptor, mathdx.CublasdxTraitType.SUGGESTED_BLOCK_DIM, 3))
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
#
|
|
480
|
+
# A complete set of knobs, ie sufficient to generate a device functions and query all traits
|
|
481
|
+
# Not exposed to end users
|
|
482
|
+
#
|
|
483
|
+
class BlasOptionsComplete(BlasOptions):
|
|
484
|
+
def __init__(self, **kwargs):
|
|
485
|
+
super().__init__(**kwargs)
|
|
486
|
+
|
|
487
|
+
if self.code_type is None:
|
|
488
|
+
raise NotImplementedError(f"code_type should be set, but got code_type = {self.code_type}")
|
|
489
|
+
if self.execution != "Block":
|
|
490
|
+
raise NotImplementedError(f"Only execution=Block is implemented ; got execution = {self.execution}")
|
|
491
|
+
|
|
492
|
+
(m, n, k) = self.size
|
|
493
|
+
|
|
494
|
+
h = generate_MM(
|
|
495
|
+
size=self.size,
|
|
496
|
+
function=self.function,
|
|
497
|
+
precision=self.precision,
|
|
498
|
+
data_type=self.data_type,
|
|
499
|
+
code_type=self.code_type,
|
|
500
|
+
transpose_mode=self._transpose_mode,
|
|
501
|
+
arrangement=self.arrangement,
|
|
502
|
+
alignment=self._alignment,
|
|
503
|
+
block_dim=self.block_dim,
|
|
504
|
+
static_block_dim=self._static_block_dim,
|
|
505
|
+
leading_dimension=self._leading_dimension,
|
|
506
|
+
execution=self.execution,
|
|
507
|
+
).descriptor
|
|
508
|
+
|
|
509
|
+
self._value_types = tuple(MATHDX_TYPES_TO_NP[vt] for vt in get_int_traits(h, mathdx.CublasdxTraitType.VALUE_TYPE, 3))
|
|
510
|
+
self._leading_dimension = LeadingDimension(*get_int_traits(h, mathdx.CublasdxTraitType.LEADING_DIMENSION, 3))
|
|
511
|
+
self._block_dim = Dim3(*get_int_traits(h, mathdx.CublasdxTraitType.BLOCK_DIM, 3))
|
|
512
|
+
self._alignment = Alignment(*get_int_traits(h, mathdx.CublasdxTraitType.ALIGNMENT, 3))
|
|
513
|
+
|
|
514
|
+
self._a_dim = (m, k)
|
|
515
|
+
self._b_dim = (k, n)
|
|
516
|
+
self._c_dim = (m, n)
|
|
517
|
+
|
|
518
|
+
if self._transpose_mode is not None:
|
|
519
|
+
if self._transpose_mode.a in {"transposed", "conj_transposed"}:
|
|
520
|
+
self._a_dim = self._a_dim[::-1]
|
|
521
|
+
if self._transpose_mode.b in {"transposed", "conj_transposed"}:
|
|
522
|
+
self._b_dim = self._b_dim[::-1]
|
|
523
|
+
|
|
524
|
+
[self._a_size, self._b_size, self._c_size] = self._calculate_abc_sizes(self._leading_dimension)
|
|
525
|
+
|
|
526
|
+
self._max_threads_per_block = self._block_dim.x * self._block_dim.y * self._block_dim.z
|
|
527
|
+
|
|
528
|
+
def _calculate_abc_sizes(self, ld: LeadingDimension) -> tuple[int, int, int]:
|
|
529
|
+
assert isinstance(ld, LeadingDimension)
|
|
530
|
+
if self._transpose_mode:
|
|
531
|
+
non_ld = (self._a_dim[1], self._b_dim[1], self._c_dim[1])
|
|
532
|
+
elif self._arrangement:
|
|
533
|
+
non_ld = (
|
|
534
|
+
self._a_dim[1 if self._arrangement.a == "col_major" else 0],
|
|
535
|
+
self._b_dim[1 if self._arrangement.b == "col_major" else 0],
|
|
536
|
+
self._c_dim[1 if self._arrangement.c == "col_major" else 0],
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
return tuple(x * y for x, y in zip(ld, non_ld, strict=True))
|
|
540
|
+
|
|
541
|
+
@property
|
|
542
|
+
def a_value_type(self):
|
|
543
|
+
return self._value_types[0]
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def b_value_type(self):
|
|
547
|
+
return self._value_types[1]
|
|
548
|
+
|
|
549
|
+
@property
|
|
550
|
+
def c_value_type(self):
|
|
551
|
+
return self._value_types[2]
|
|
552
|
+
|
|
553
|
+
@property
|
|
554
|
+
@deprecated("value_type trait is deprecated. Please use {a|b|c}_value_type instead")
|
|
555
|
+
def value_type(self):
|
|
556
|
+
if not all(vt == self.a_value_type for vt in self._value_types):
|
|
557
|
+
raise RuntimeError("value_type may be used only if all {a|b|c}_value_type have the same type")
|
|
558
|
+
return self.a_value_type
|
|
559
|
+
|
|
560
|
+
@property
|
|
561
|
+
@deprecated("input_type trait is deprecated. Please use {a|b}_value_type instead")
|
|
562
|
+
def input_type(self):
|
|
563
|
+
if self.a_value_type != self.b_value_type:
|
|
564
|
+
raise RuntimeError("input_type may be used only if A and B input matrix have the same type")
|
|
565
|
+
return self.a_value_type
|
|
566
|
+
|
|
567
|
+
@property
|
|
568
|
+
@deprecated("output_type trait is deprecated. Please use c_value_type instead")
|
|
569
|
+
def output_type(self):
|
|
570
|
+
return self.c_value_type
|
|
571
|
+
|
|
572
|
+
@property
|
|
573
|
+
def a_dim(self):
|
|
574
|
+
return self._a_dim
|
|
575
|
+
|
|
576
|
+
@property
|
|
577
|
+
def b_dim(self):
|
|
578
|
+
return self._b_dim
|
|
579
|
+
|
|
580
|
+
@property
|
|
581
|
+
def c_dim(self):
|
|
582
|
+
return self._c_dim
|
|
583
|
+
|
|
584
|
+
@property
|
|
585
|
+
def leading_dimension(self):
|
|
586
|
+
return self._leading_dimension
|
|
587
|
+
|
|
588
|
+
@property
|
|
589
|
+
def a_size(self):
|
|
590
|
+
return self._a_size
|
|
591
|
+
|
|
592
|
+
@property
|
|
593
|
+
def b_size(self):
|
|
594
|
+
return self._b_size
|
|
595
|
+
|
|
596
|
+
@property
|
|
597
|
+
def c_size(self):
|
|
598
|
+
return self._c_size
|
|
599
|
+
|
|
600
|
+
@property
|
|
601
|
+
@deprecated(
|
|
602
|
+
"shared_memory_size trait is deprecated and will be removed in "
|
|
603
|
+
"future versions. Use get_shared_storage_size instead. Don't "
|
|
604
|
+
"use with Opaque Tensors. Use get_shared_storage_size(...) or"
|
|
605
|
+
"SharedStorageCalc instead"
|
|
606
|
+
)
|
|
607
|
+
def shared_memory_size(self):
|
|
608
|
+
return self.get_shared_storage_size()
|
|
609
|
+
|
|
610
|
+
@property
|
|
611
|
+
def max_threads_per_block(self):
|
|
612
|
+
return self._max_threads_per_block
|
|
613
|
+
|
|
614
|
+
def _get_shared_storage_size(self, *args, ab=False) -> int | None: # type: ignore
|
|
615
|
+
# Complex will be over-aligned (eg: f32x2 complex is aligned on 8B) with
|
|
616
|
+
# this logic (which is what we want - for performance and vectorization)
|
|
617
|
+
item_sizes = tuple(numpy.dtype(vt).itemsize for vt in self._value_types)
|
|
618
|
+
|
|
619
|
+
alignment = self.alignment
|
|
620
|
+
sizes = None
|
|
621
|
+
|
|
622
|
+
if len(args) == 0:
|
|
623
|
+
sizes = (self._a_size, self._b_size, self._c_size)
|
|
624
|
+
elif all(isinstance(arg, int) for arg in args):
|
|
625
|
+
sizes = self._calculate_abc_sizes(LeadingDimension(*pad_or_truncate(list(args), 3)))
|
|
626
|
+
elif all(isinstance(arg, Layout) for arg in args):
|
|
627
|
+
sizes = tuple(arg.cosize for arg in args)
|
|
628
|
+
|
|
629
|
+
if sizes is None:
|
|
630
|
+
return None
|
|
631
|
+
|
|
632
|
+
smem_calc = SharedStorageCalc()
|
|
633
|
+
smem_calc.add(alignment[0], item_sizes[0], sizes[0])
|
|
634
|
+
smem_calc.add(alignment[1], item_sizes[1], sizes[1])
|
|
635
|
+
if not ab:
|
|
636
|
+
smem_calc.add(alignment[2], item_sizes[2], sizes[2])
|
|
637
|
+
return smem_calc.get()
|
|
638
|
+
|
|
639
|
+
@overload
|
|
640
|
+
def get_shared_storage_size(self) -> int: ...
|
|
641
|
+
@overload
|
|
642
|
+
def get_shared_storage_size(self, lda: int, ldb: int, ldc: int) -> int: ...
|
|
643
|
+
@overload
|
|
644
|
+
def get_shared_storage_size(self, matrix_a_layout: Layout, matrix_b_layout: Layout, matrix_c_layout: Layout) -> int: ...
|
|
645
|
+
def get_shared_storage_size(self, *args) -> int: # type: ignore
|
|
646
|
+
value_error = ValueError(
|
|
647
|
+
"get_shared_storage_size() takes either 0 or 3 arguments. If 3 "
|
|
648
|
+
"arguments are provided, they must be either all integers or "
|
|
649
|
+
"all Layout objects."
|
|
650
|
+
)
|
|
651
|
+
if len(args) not in {0, 3}:
|
|
652
|
+
raise value_error
|
|
653
|
+
if any(not isinstance(arg, Layout) for arg in args) and any(not isinstance(arg, int) for arg in args):
|
|
654
|
+
raise value_error
|
|
655
|
+
size = self._get_shared_storage_size(*args, ab=False)
|
|
656
|
+
if size is None:
|
|
657
|
+
raise value_error
|
|
658
|
+
return size
|
|
659
|
+
|
|
660
|
+
@overload
|
|
661
|
+
def get_shared_storage_size_ab(self) -> int: ...
|
|
662
|
+
@overload
|
|
663
|
+
def get_shared_storage_size_ab(self, lda: int, ldb: int) -> int: ...
|
|
664
|
+
@overload
|
|
665
|
+
def get_shared_storage_size_ab(self, matrix_a_layout: Layout, matrix_b_layout: Layout) -> int: ...
|
|
666
|
+
def get_shared_storage_size_ab(self, *args) -> int: # type: ignore
|
|
667
|
+
value_error = ValueError(
|
|
668
|
+
"get_shared_storage_size_ab() takes either 0 or 2 arguments. "
|
|
669
|
+
"If 2 arguments are provided, they must be either all integers "
|
|
670
|
+
"or all Layout objects."
|
|
671
|
+
)
|
|
672
|
+
if len(args) not in {0, 2}:
|
|
673
|
+
raise value_error
|
|
674
|
+
if any(not isinstance(arg, Layout) for arg in args) and any(not isinstance(arg, int) for arg in args):
|
|
675
|
+
raise value_error
|
|
676
|
+
size = self._get_shared_storage_size(*args, ab=True)
|
|
677
|
+
if size is None:
|
|
678
|
+
raise value_error
|
|
679
|
+
return size
|
|
680
|
+
|
|
681
|
+
def get_layout_gmem_a(self, leading_dimension: int | None = None) -> Layout:
|
|
682
|
+
return _BlasLayout(self, "get_layout_gmem_a", leading_dimension)
|
|
683
|
+
|
|
684
|
+
def get_layout_gmem_b(self, leading_dimension: int | None = None) -> Layout:
|
|
685
|
+
return _BlasLayout(self, "get_layout_gmem_b", leading_dimension)
|
|
686
|
+
|
|
687
|
+
def get_layout_gmem_c(self, leading_dimension: int | None = None) -> Layout:
|
|
688
|
+
return _BlasLayout(self, "get_layout_gmem_c", leading_dimension)
|
|
689
|
+
|
|
690
|
+
def get_layout_smem_a(self) -> Layout:
|
|
691
|
+
return _BlasLayout(self, "get_layout_smem_a")
|
|
692
|
+
|
|
693
|
+
def get_layout_smem_b(self) -> Layout:
|
|
694
|
+
return _BlasLayout(self, "get_layout_smem_b")
|
|
695
|
+
|
|
696
|
+
def get_layout_smem_c(self) -> Layout:
|
|
697
|
+
return _BlasLayout(self, "get_layout_smem_c")
|
|
698
|
+
|
|
699
|
+
def suggest_layout_smem_a(self) -> Layout:
|
|
700
|
+
return _BlasLayout(self, "suggest_layout_smem_a")
|
|
701
|
+
|
|
702
|
+
def suggest_layout_smem_b(self) -> Layout:
|
|
703
|
+
return _BlasLayout(self, "suggest_layout_smem_b")
|
|
704
|
+
|
|
705
|
+
def suggest_layout_smem_c(self) -> Layout:
|
|
706
|
+
return _BlasLayout(self, "suggest_layout_smem_c")
|
|
707
|
+
|
|
708
|
+
def suggest_layout_rmem_c(self) -> Layout:
|
|
709
|
+
return _BlasLayout(self, "suggest_layout_rmem_c")
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
#
|
|
713
|
+
# A compiled BLAS device function, with knobs and device function
|
|
714
|
+
#
|
|
715
|
+
class BlasCompiled(BlasOptionsComplete):
|
|
716
|
+
def __init__(self, **kwargs):
|
|
717
|
+
execute_api = kwargs.pop("execute_api", "static_leading_dimensions")
|
|
718
|
+
tensor_types = kwargs.pop("tensor_types", None)
|
|
719
|
+
global_memory_alignment = kwargs.pop("global_memory_alignment", None)
|
|
720
|
+
|
|
721
|
+
# Build set of knobs
|
|
722
|
+
super().__init__(**kwargs)
|
|
723
|
+
|
|
724
|
+
if global_memory_alignment is not None:
|
|
725
|
+
if not isinstance(global_memory_alignment, Sequence) or len(global_memory_alignment) != 3:
|
|
726
|
+
raise ValueError(
|
|
727
|
+
"global_memory_alignment should be an instance of Alignment"
|
|
728
|
+
"or a 3-tuple ; "
|
|
729
|
+
"got global_memory_alignment = {global_memory_alignment}"
|
|
730
|
+
)
|
|
731
|
+
global_memory_alignment = Alignment(*global_memory_alignment)
|
|
732
|
+
|
|
733
|
+
validate_alignment(
|
|
734
|
+
global_memory_alignment,
|
|
735
|
+
self.precision,
|
|
736
|
+
self.data_type,
|
|
737
|
+
gmem=True,
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
validate_execute_api(execute_api)
|
|
741
|
+
tensors_api = execute_api == "tensors"
|
|
742
|
+
if tensors_api:
|
|
743
|
+
validate_tensor_types(tensor_types)
|
|
744
|
+
|
|
745
|
+
self._execute_api = execute_api
|
|
746
|
+
self._tensor_types = tensor_types
|
|
747
|
+
self._global_memory_alignment = global_memory_alignment
|
|
748
|
+
|
|
749
|
+
handle = generate_MM(
|
|
750
|
+
size=self.size,
|
|
751
|
+
function=self.function,
|
|
752
|
+
precision=self.precision,
|
|
753
|
+
data_type=self.data_type,
|
|
754
|
+
code_type=self.code_type,
|
|
755
|
+
transpose_mode=self._transpose_mode,
|
|
756
|
+
arrangement=self.arrangement,
|
|
757
|
+
alignment=self._alignment,
|
|
758
|
+
block_dim=self.block_dim,
|
|
759
|
+
static_block_dim=self._static_block_dim,
|
|
760
|
+
# TODO: find better way to exclude ld operator for dynamic_leading_dimensions
|
|
761
|
+
leading_dimension=self._leading_dimension if self._execute_api == "static_leading_dimensions" else None,
|
|
762
|
+
execution=self._execution,
|
|
763
|
+
execute_api=self._execute_api,
|
|
764
|
+
tensor_types=self._tensor_types,
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
# TODO: remove once MM.files is deprecated
|
|
768
|
+
self._handle = handle
|
|
769
|
+
|
|
770
|
+
# Now compile the LTO device function
|
|
771
|
+
h = handle.descriptor
|
|
772
|
+
|
|
773
|
+
if tensors_api:
|
|
774
|
+
self._declare_tensors(h)
|
|
775
|
+
|
|
776
|
+
code, self._tensor_api_symbols = generate_code_tensors(
|
|
777
|
+
h, self.code_type.cc, self._gmem_tensors, self._target_tensors, rmem_c="rmem" in self._tensor_types[2]
|
|
778
|
+
)
|
|
779
|
+
else:
|
|
780
|
+
code = generate_code(h, self.code_type.cc)
|
|
781
|
+
|
|
782
|
+
# Compile
|
|
783
|
+
lto_fn = get_lto(code.descriptor)
|
|
784
|
+
isa_version = get_isa_version(code.descriptor)
|
|
785
|
+
|
|
786
|
+
self._ltos = [Code(self.code_type, isa_version, lto_fn)]
|
|
787
|
+
self._symbol = get_str_trait(h, mathdx.CublasdxTraitType.SYMBOL_NAME)
|
|
788
|
+
|
|
789
|
+
if self._tensor_types:
|
|
790
|
+
_, copy_wait_lto = generate_copy_wait_lto(self.code_type.cc)
|
|
791
|
+
self._ltos += [Code(self.code_type, isa_version, copy_wait_lto)]
|
|
792
|
+
|
|
793
|
+
def _declare_tensors(self, h):
|
|
794
|
+
# Complex will be over-aligned (eg: f32x2 complex is aligned on 8B) with
|
|
795
|
+
# this logic (which is what we want - for performance and vectorization)
|
|
796
|
+
item_sizes = tuple(numpy.dtype(vt).itemsize for vt in self._value_types)
|
|
797
|
+
|
|
798
|
+
self._gmem_tensors, self._target_tensors = generate_tensors(h, self._tensor_types, self._global_memory_alignment)
|
|
799
|
+
self._target_tensor_sizes = get_tensor_int_traits(self._target_tensors, mathdx.CublasdxTensorTrait.STORAGE_BYTES)
|
|
800
|
+
for ts, _is in zip(self._target_tensor_sizes, item_sizes, strict=True):
|
|
801
|
+
assert ts % _is == 0
|
|
802
|
+
self._target_tensor_sizes = tuple(ts // item_sizes[i] for i, ts in enumerate(self._target_tensor_sizes))
|
|
803
|
+
self._gmem_tensor_uids = get_tensor_int_traits(self._gmem_tensors, mathdx.CublasdxTensorTrait.UID)
|
|
804
|
+
self._target_tensor_uids = get_tensor_int_traits(self._target_tensors, mathdx.CublasdxTensorTrait.UID)
|
|
805
|
+
|
|
806
|
+
def definition(self):
|
|
807
|
+
dd = super().definition()
|
|
808
|
+
dd.update(execute_api=self.execute_api)
|
|
809
|
+
if self.execute_api == "tensors":
|
|
810
|
+
dd.update(tensor_types=self.tensor_types)
|
|
811
|
+
return dd
|
|
812
|
+
|
|
813
|
+
@cached_property
|
|
814
|
+
def _tempfiles(self):
|
|
815
|
+
"""
|
|
816
|
+
Create temporary files for the LTO functions.
|
|
817
|
+
"""
|
|
818
|
+
return [make_binary_tempfile(lto.data, ".ltoir") for lto in self._ltos]
|
|
819
|
+
|
|
820
|
+
@property
|
|
821
|
+
def files(self):
|
|
822
|
+
"""The list of binary files for the lto functions."""
|
|
823
|
+
return [v.name for v in self._tempfiles]
|
|
824
|
+
|
|
825
|
+
@property
|
|
826
|
+
def codes(self):
|
|
827
|
+
"""A list of :class:`Code` objects for all lto functions."""
|
|
828
|
+
return self._ltos
|
|
829
|
+
|
|
830
|
+
@property
|
|
831
|
+
def symbol(self):
|
|
832
|
+
"""The name of the device function."""
|
|
833
|
+
return self._symbol
|
|
834
|
+
|
|
835
|
+
@property
|
|
836
|
+
def execute_api(self) -> str:
|
|
837
|
+
"""
|
|
838
|
+
The API used to execute the function. It defines the signature of the
|
|
839
|
+
LTO function.
|
|
840
|
+
"""
|
|
841
|
+
return self._execute_api
|
|
842
|
+
|
|
843
|
+
@property
|
|
844
|
+
def tensor_types(self) -> tuple[str, str, str]:
|
|
845
|
+
"""
|
|
846
|
+
The tensor types used in the function. Defines types of the tensors for
|
|
847
|
+
the tensors API.
|
|
848
|
+
"""
|
|
849
|
+
if self.execute_api != "tensors":
|
|
850
|
+
raise RuntimeError("tensor_types is only available when execute_api is 'tensors'")
|
|
851
|
+
return self._tensor_types
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
#
|
|
855
|
+
# A compiled BLAS device function, with knobs and device function, to be used with Numba
|
|
856
|
+
#
|
|
857
|
+
class BlasNumba(BlasCompiled):
|
|
858
|
+
"""
|
|
859
|
+
A class that encapsulates a compiled BLAS device function compatible with Numba.
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
"""
|
|
863
|
+
|
|
864
|
+
def __init__(self, **kwargs):
|
|
865
|
+
if "code_type" not in kwargs:
|
|
866
|
+
kwargs["code_type"] = get_default_code_type()
|
|
867
|
+
|
|
868
|
+
# Build LTO device functions
|
|
869
|
+
super().__init__(**kwargs)
|
|
870
|
+
|
|
871
|
+
self._numba_value_types = tuple(NP_TYPES_TO_NUMBA_FE_TYPES[vt] for vt in self._value_types)
|
|
872
|
+
|
|
873
|
+
@property
|
|
874
|
+
def a_value_type(self):
|
|
875
|
+
return self._numba_value_types[0]
|
|
876
|
+
|
|
877
|
+
@property
|
|
878
|
+
def b_value_type(self):
|
|
879
|
+
return self._numba_value_types[1]
|
|
880
|
+
|
|
881
|
+
@property
|
|
882
|
+
def c_value_type(self):
|
|
883
|
+
return self._numba_value_types[2]
|
|
884
|
+
|
|
885
|
+
@deprecated("Calling MM(...) directly is deprecated, please use MM.execute(...) method instead.")
|
|
886
|
+
def __call__(self, *args):
|
|
887
|
+
raise RuntimeError("__call__ should not be called directly outside of a numba.cuda.jit(...) kernel.")
|
|
888
|
+
|
|
889
|
+
def execute(self, *args):
|
|
890
|
+
raise RuntimeError("execute should not be called directly outside of a numba.cuda.jit(...) kernel.")
|
|
891
|
+
|
|
892
|
+
@cached_property
|
|
893
|
+
def _copy_symbols_map(self):
|
|
894
|
+
if self.execute_api != "tensors":
|
|
895
|
+
return {}
|
|
896
|
+
|
|
897
|
+
return {
|
|
898
|
+
(self._gmem_tensor_uids[0], self._target_tensor_uids[0]): self._tensor_api_symbols.copy_a,
|
|
899
|
+
(self._gmem_tensor_uids[1], self._target_tensor_uids[1]): self._tensor_api_symbols.copy_b,
|
|
900
|
+
(self._gmem_tensor_uids[2], self._target_tensor_uids[2]): self._tensor_api_symbols.copy_c,
|
|
901
|
+
(self._target_tensor_uids[2], self._gmem_tensor_uids[2]): self._tensor_api_symbols.copy_c_back,
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
@docstring_decorator(CUBLASDX_DOCSTRING, skip_missing=False)
|
|
906
|
+
def matmul(*, compiler=None, **kwargs):
|
|
907
|
+
"""
|
|
908
|
+
Create an :class:`BlasOptions` object that encapsulates a compiled and ready-to-use
|
|
909
|
+
device function for matrix multiplication.
|
|
910
|
+
|
|
911
|
+
Args:
|
|
912
|
+
size: {size}
|
|
913
|
+
|
|
914
|
+
precision: {precision}
|
|
915
|
+
|
|
916
|
+
data_type: {data_type}
|
|
917
|
+
|
|
918
|
+
compiler: {compiler}
|
|
919
|
+
|
|
920
|
+
code_type (CodeType): {code_type}
|
|
921
|
+
|
|
922
|
+
block_size (int): {block_size}
|
|
923
|
+
|
|
924
|
+
block_dim (Dim3): {block_dim}
|
|
925
|
+
|
|
926
|
+
leading_dimension (LeadingDimension): {leading_dimension}
|
|
927
|
+
|
|
928
|
+
transpose_mode (TransposeMode): {transpose_mode}
|
|
929
|
+
|
|
930
|
+
arrangement (Arrangement): {arrangement}
|
|
931
|
+
|
|
932
|
+
alignment (Alignment): {alignment}
|
|
933
|
+
|
|
934
|
+
function (str): {function}
|
|
935
|
+
|
|
936
|
+
execution (str): {execution}
|
|
937
|
+
|
|
938
|
+
execute_api (str): {execute_api}
|
|
939
|
+
|
|
940
|
+
tensor_types (str): {tensor_types}
|
|
941
|
+
|
|
942
|
+
global_memory_alignment (Alignment): {global_memory_alignment}
|
|
943
|
+
|
|
944
|
+
See Also:
|
|
945
|
+
The attributes of :class:`BlasOptions` provide a 1:1 mapping with the CUDA C++
|
|
946
|
+
cuBLASDx APIs. For further details, please refer to `cuBLASDx documentation
|
|
947
|
+
<https://docs.nvidia.com/cuda/cublasdx/>`_.
|
|
948
|
+
|
|
949
|
+
Examples:
|
|
950
|
+
|
|
951
|
+
>>> from numba import cuda
|
|
952
|
+
>>> from nvmath.device import matmul
|
|
953
|
+
>>> import numpy as np
|
|
954
|
+
>>> m, n, k = 32, 16, 64
|
|
955
|
+
>>> block_size = 256
|
|
956
|
+
|
|
957
|
+
Use :func:`nvmath.device.matmul` to create the compiled matrix multiplication
|
|
958
|
+
object:
|
|
959
|
+
|
|
960
|
+
>>> MM = matmul(
|
|
961
|
+
... size=(m, n, k),
|
|
962
|
+
... precision=np.float32,
|
|
963
|
+
... data_type="real",
|
|
964
|
+
... transpose_mode=("non_transposed", "transposed"),
|
|
965
|
+
... execution="Block",
|
|
966
|
+
... block_size=block_size,
|
|
967
|
+
... compiler="numba",
|
|
968
|
+
... )
|
|
969
|
+
|
|
970
|
+
Pass ``link=MM.files`` to the :func:`numba.cuda.jit` decorator when defining your
|
|
971
|
+
kernel to link with the compiled code.
|
|
972
|
+
|
|
973
|
+
cuBLASDx works on shared memory arrays. It requires column-major (F order) arrays
|
|
974
|
+
but :class:`cuda.shared.array` creates row-major (C order) arrays only. You can
|
|
975
|
+
emulate a column-major array by flipping dimensions. With your shared memory arrays
|
|
976
|
+
ready and filled with actual data, you can run the matrix multiplication by calling
|
|
977
|
+
`MM`
|
|
978
|
+
|
|
979
|
+
>>> a_dim, b_dim, c_dim = MM.a_dim, MM.b_dim, MM.c_dim
|
|
980
|
+
>>> @cuda.jit(link=MM.files)
|
|
981
|
+
... def f():
|
|
982
|
+
... a = cuda.shared.array(shape=(a_dim[1], a_dim[0]), dtype=np.float32)
|
|
983
|
+
... b = cuda.shared.array(shape=(b_dim[1], b_dim[0]), dtype=np.float32)
|
|
984
|
+
... c = cuda.shared.array(shape=(c_dim[1], c_dim[0]), dtype=np.float32)
|
|
985
|
+
... # TODO: Populate the arrays with actual data.
|
|
986
|
+
... alpha, beta = 1.0, 0.0
|
|
987
|
+
... MM(alpha, a, b, beta, c)
|
|
988
|
+
... cuda.syncthreads()
|
|
989
|
+
... # TODO: Copy the result (c) from the shared memory
|
|
990
|
+
>>> f[1, block_size]()
|
|
991
|
+
|
|
992
|
+
Further examples can be found in the `nvmath/examples/device
|
|
993
|
+
<https://github.com/NVIDIA/nvmath-python/tree/main/examples/device>`_ directory.
|
|
994
|
+
"""
|
|
995
|
+
check_in("compiler", compiler, [None, "numba"])
|
|
996
|
+
if compiler is None:
|
|
997
|
+
return BlasCompiled(**kwargs)
|
|
998
|
+
elif compiler == "numba":
|
|
999
|
+
return BlasNumba(**kwargs)
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
def _parse_layout(layout: str) -> tuple[bool, str, str]:
|
|
1003
|
+
"""Parse layout string to extract tensor type and memory type.
|
|
1004
|
+
|
|
1005
|
+
Returns: tuple of (suggest, memory, tensor)
|
|
1006
|
+
suggest: bool, True if the layout is a suggested layout
|
|
1007
|
+
memory: str, memory type ('s' for shared, 'g' for global, 'r' for register)
|
|
1008
|
+
tensor: str, tensor type ('a', 'b', 'c')
|
|
1009
|
+
"""
|
|
1010
|
+
# extracting tensor type from layout
|
|
1011
|
+
pattern = re.compile(r"^(?:(suggest|get)_)?layout_([srg])mem_([abc])$")
|
|
1012
|
+
|
|
1013
|
+
match = pattern.match(layout)
|
|
1014
|
+
|
|
1015
|
+
assert match is not None
|
|
1016
|
+
|
|
1017
|
+
suggest, memory, tensor = match.group(1, 2, 3)
|
|
1018
|
+
|
|
1019
|
+
return suggest == "suggest", memory, tensor
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
class _BlasLayout(Layout):
|
|
1023
|
+
"""BlasLayout for the OpaqueTensor"""
|
|
1024
|
+
|
|
1025
|
+
_size: int
|
|
1026
|
+
_cosize: int
|
|
1027
|
+
|
|
1028
|
+
# Runtime fields for the opaque tensor
|
|
1029
|
+
_uid: int
|
|
1030
|
+
_leading_dimension: int | None
|
|
1031
|
+
|
|
1032
|
+
# Internal fields to recreate the numba layout type
|
|
1033
|
+
_MM: BlasNumba | None
|
|
1034
|
+
_layout: str
|
|
1035
|
+
|
|
1036
|
+
# Cached fields to avoid recomputing
|
|
1037
|
+
_is_register: bool
|
|
1038
|
+
_tensor_index: int
|
|
1039
|
+
|
|
1040
|
+
def __init__(self, MM: BlasOptionsComplete, layout: str, leading_dimension: int | None = None):
|
|
1041
|
+
if not isinstance(MM, BlasCompiled):
|
|
1042
|
+
raise ValueError("MM should be an instance of BlasCompiled, support for BlasOptionsComplete is in progress")
|
|
1043
|
+
if MM.execute_api != "tensors":
|
|
1044
|
+
raise ValueError(f"{layout} is only available for execute_api='tensors'")
|
|
1045
|
+
|
|
1046
|
+
assert MM._tensor_types is not None
|
|
1047
|
+
|
|
1048
|
+
suggested, memory, tensor = _parse_layout(layout)
|
|
1049
|
+
self._tensor_index = ["a", "b", "c"].index(tensor)
|
|
1050
|
+
|
|
1051
|
+
self._size = math.prod((MM.a_dim, MM.b_dim, MM.c_dim)[self._tensor_index])
|
|
1052
|
+
|
|
1053
|
+
if memory == "g":
|
|
1054
|
+
self._uid = MM._gmem_tensor_uids[self._tensor_index]
|
|
1055
|
+
self._cosize = self._size
|
|
1056
|
+
else:
|
|
1057
|
+
tensor_type = f"{memory}mem_{tensor}"
|
|
1058
|
+
|
|
1059
|
+
if suggested:
|
|
1060
|
+
tensor_type = "suggested_" + tensor_type
|
|
1061
|
+
|
|
1062
|
+
if tensor_type not in set(MM._tensor_types):
|
|
1063
|
+
raise ValueError(f"Invalid layout {layout} for tensor {tensor_type}. Available layouts are {MM._tensor_types}")
|
|
1064
|
+
self._uid = MM._target_tensor_uids[self._tensor_index]
|
|
1065
|
+
self._cosize = MM._target_tensor_sizes[self._tensor_index]
|
|
1066
|
+
|
|
1067
|
+
if memory == "r":
|
|
1068
|
+
# for register memory, we are using fragment so it does not have
|
|
1069
|
+
# any gaps and contain only small chank, so dimension production
|
|
1070
|
+
# does not apply
|
|
1071
|
+
self._size = self._cosize
|
|
1072
|
+
|
|
1073
|
+
self._is_register = memory == "r"
|
|
1074
|
+
self._dynamic_ld = memory == "g" # dynamic ld only global memory
|
|
1075
|
+
self._MM = MM if isinstance(MM, BlasNumba) else None
|
|
1076
|
+
self._layout = layout
|
|
1077
|
+
self._leading_dimension = leading_dimension
|
|
1078
|
+
|
|
1079
|
+
@property
|
|
1080
|
+
def size(self) -> int:
|
|
1081
|
+
return self._size
|
|
1082
|
+
|
|
1083
|
+
@property
|
|
1084
|
+
def cosize(self) -> int:
|
|
1085
|
+
return self._cosize
|