xformers 0.0.35.dev1146__py3-none-any.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.
Files changed (72) hide show
  1. xformers/__init__.py +73 -0
  2. xformers/_cpp_lib.py +138 -0
  3. xformers/_deprecation_warning.py +12 -0
  4. xformers/attn_bias_utils.py +12 -0
  5. xformers/benchmarks/__init__.py +4 -0
  6. xformers/benchmarks/benchmark_attn_decoding.py +467 -0
  7. xformers/benchmarks/benchmark_indexing.py +191 -0
  8. xformers/benchmarks/benchmark_mem_eff_attention.py +373 -0
  9. xformers/benchmarks/benchmark_merge_attentions.py +92 -0
  10. xformers/benchmarks/benchmark_sequence_parallel_fused.py +473 -0
  11. xformers/benchmarks/benchmark_tiled_matmul.py +122 -0
  12. xformers/benchmarks/utils.py +767 -0
  13. xformers/checkpoint.py +592 -0
  14. xformers/flash_attn_3/__init__.py +4 -0
  15. xformers/fwbw_overlap.py +536 -0
  16. xformers/info.py +75 -0
  17. xformers/ops/__init__.py +138 -0
  18. xformers/ops/_triton/__init__.py +19 -0
  19. xformers/ops/_triton/k_index_select_cat.py +184 -0
  20. xformers/ops/_triton/k_scaled_index_add.py +365 -0
  21. xformers/ops/_triton/matmul_perf_model.py +250 -0
  22. xformers/ops/_triton/rmsnorm_kernels.py +159 -0
  23. xformers/ops/_triton/rope_padded_kernels.py +224 -0
  24. xformers/ops/_triton/tiled_matmul_kernels.py +436 -0
  25. xformers/ops/common.py +64 -0
  26. xformers/ops/differentiable_collectives.py +187 -0
  27. xformers/ops/fmha/__init__.py +105 -0
  28. xformers/ops/fmha/_triton/__init__.py +4 -0
  29. xformers/ops/fmha/_triton/splitk_kernels.py +13 -0
  30. xformers/ops/fmha/attn_bias.py +49 -0
  31. xformers/ops/fmha/ck.py +6 -0
  32. xformers/ops/fmha/ck_splitk.py +16 -0
  33. xformers/ops/fmha/common.py +20 -0
  34. xformers/ops/fmha/cutlass.py +6 -0
  35. xformers/ops/fmha/cutlass_blackwell.py +10 -0
  36. xformers/ops/fmha/dispatch.py +10 -0
  37. xformers/ops/fmha/flash.py +24 -0
  38. xformers/ops/fmha/flash3.py +36 -0
  39. xformers/ops/fmha/merge_training.py +10 -0
  40. xformers/ops/fmha/torch_attention_compat.py +9 -0
  41. xformers/ops/fmha/triton_splitk.py +65 -0
  42. xformers/ops/indexing.py +237 -0
  43. xformers/ops/modpar_layers.py +165 -0
  44. xformers/ops/rmsnorm.py +113 -0
  45. xformers/ops/rope_padded.py +300 -0
  46. xformers/ops/seqpar.py +365 -0
  47. xformers/ops/sequence_parallel_fused_ops.py +847 -0
  48. xformers/ops/swiglu_op.py +423 -0
  49. xformers/ops/tiled_matmul.py +318 -0
  50. xformers/ops/tree_attention.py +14 -0
  51. xformers/ops/unbind.py +129 -0
  52. xformers/profiler/__init__.py +15 -0
  53. xformers/profiler/api.py +92 -0
  54. xformers/profiler/device_limits.py +113 -0
  55. xformers/profiler/find_slowest.py +169 -0
  56. xformers/profiler/profile_analyzer.py +235 -0
  57. xformers/profiler/profiler.py +375 -0
  58. xformers/profiler/profiler_dcgm.py +45 -0
  59. xformers/profiler/profiler_dcgm_impl.py +216 -0
  60. xformers/sparse/__init__.py +6 -0
  61. xformers/sparse/blocksparse_tensor.py +278 -0
  62. xformers/sparse/utils.py +123 -0
  63. xformers/test.py +4 -0
  64. xformers/triton/__init__.py +4 -0
  65. xformers/triton/importing.py +31 -0
  66. xformers/utils.py +145 -0
  67. xformers/version.py +2 -0
  68. xformers-0.0.35.dev1146.dist-info/METADATA +32 -0
  69. xformers-0.0.35.dev1146.dist-info/RECORD +72 -0
  70. xformers-0.0.35.dev1146.dist-info/WHEEL +5 -0
  71. xformers-0.0.35.dev1146.dist-info/licenses/LICENSE +35 -0
  72. xformers-0.0.35.dev1146.dist-info/top_level.txt +1 -0
xformers/__init__.py ADDED
@@ -0,0 +1,73 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
2
+ #
3
+ # This source code is licensed under the BSD license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import logging
7
+ import os
8
+
9
+ import torch
10
+
11
+ from . import _cpp_lib
12
+ from .checkpoint import ( # noqa: E402, F401
13
+ checkpoint,
14
+ get_optimal_checkpoint_policy,
15
+ list_operators,
16
+ selective_checkpoint_wrapper,
17
+ )
18
+
19
+ try:
20
+ from .version import __version__ # noqa: F401
21
+ except ImportError:
22
+ __version__ = "0.0.0"
23
+
24
+
25
+ logger = logging.getLogger("xformers")
26
+
27
+ _has_cpp_library: bool = _cpp_lib._has_cpp_library
28
+
29
+ _is_opensource: bool = True
30
+
31
+
32
+ def compute_once(func):
33
+ value = None
34
+
35
+ def func_wrapper():
36
+ nonlocal value
37
+ if value is None:
38
+ value = func()
39
+ return value
40
+
41
+ return func_wrapper
42
+
43
+
44
+ @compute_once
45
+ def _is_triton_available():
46
+ if os.environ.get("XFORMERS_ENABLE_TRITON", "0") == "1":
47
+ return True
48
+ if not torch.cuda.is_available():
49
+ return False
50
+ if os.environ.get("XFORMERS_FORCE_DISABLE_TRITON", "0") == "1":
51
+ return False
52
+ # We have many errors on V100 with recent triton versions
53
+ # Let's just drop support for triton kernels below A100
54
+ if torch.cuda.get_device_capability("cuda") < (8, 0):
55
+ return False
56
+ try:
57
+ import triton # noqa
58
+
59
+ return True
60
+ except (ImportError, AttributeError):
61
+ logger.warning(
62
+ "A matching Triton is not available, some optimizations will not be enabled",
63
+ exc_info=True,
64
+ )
65
+ return False
66
+
67
+
68
+ @compute_once
69
+ def get_python_lib():
70
+ return torch.library.Library("xformers_python", "DEF")
71
+
72
+
73
+ # end of file
xformers/_cpp_lib.py ADDED
@@ -0,0 +1,138 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
2
+ #
3
+ # This source code is licensed under the BSD license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import dataclasses
7
+ import json
8
+ import logging
9
+ import os
10
+ import platform
11
+ from typing import Any, Dict, Optional
12
+
13
+ import torch
14
+
15
+ logger = logging.getLogger("xformers")
16
+
17
+ UNAVAILABLE_FEATURES_MSG = " SwiGLU, sparse and more won't be available."
18
+
19
+
20
+ @dataclasses.dataclass
21
+ class _BuildInfo:
22
+ metadata: Dict[str, Any]
23
+
24
+ @property
25
+ def cuda_version(self) -> Optional[int]:
26
+ return self.metadata["version"]["cuda"]
27
+
28
+ @property
29
+ def hip_version(self) -> Optional[int]:
30
+ return self.metadata["version"]["hip"]
31
+
32
+ @property
33
+ def torch_version(self) -> str:
34
+ return self.metadata["version"]["torch"]
35
+
36
+ @property
37
+ def python_version(self) -> str:
38
+ return self.metadata["version"]["python"]
39
+
40
+ @property
41
+ def flash_version(self) -> str:
42
+ return self.metadata["version"].get("flash", "0.0.0")
43
+
44
+ @property
45
+ def use_torch_flash(self) -> bool:
46
+ return self.metadata["version"].get("use_torch_flash", False)
47
+
48
+ @property
49
+ def build_env(self) -> Dict[str, Any]:
50
+ return self.metadata["env"]
51
+
52
+
53
+ class xFormersWasNotBuiltException(Exception):
54
+ def __str__(self) -> str:
55
+ return (
56
+ "Need to compile C++ extensions to use all xFormers features.\n"
57
+ " Please install xformers properly "
58
+ "(see https://github.com/facebookresearch/xformers#installing-xformers)\n"
59
+ + UNAVAILABLE_FEATURES_MSG
60
+ )
61
+
62
+
63
+ class xFormersInvalidLibException(Exception):
64
+ def __init__(self, build_info: Optional[_BuildInfo]) -> None:
65
+ self.build_info = build_info
66
+
67
+ def __str__(self) -> str:
68
+ if self.build_info is None:
69
+ msg = "xFormers was built for a different version of PyTorch or Python."
70
+ else:
71
+ msg = f"""xFormers was built for:
72
+ PyTorch {self.build_info.torch_version} with CUDA {self.build_info.cuda_version} (you have {torch.__version__})
73
+ Python {self.build_info.python_version} (you have {platform.python_version()})"""
74
+ return (
75
+ "xFormers can't load C++/CUDA extensions. "
76
+ + msg
77
+ + "\n Please reinstall xformers "
78
+ "(see https://github.com/facebookresearch/xformers#installing-xformers)\n"
79
+ + UNAVAILABLE_FEATURES_MSG
80
+ )
81
+
82
+
83
+ def _register_extensions():
84
+ import importlib
85
+ import os
86
+
87
+ import torch
88
+
89
+ # load the custom_op_library and register the custom ops
90
+ lib_dir = os.path.dirname(__file__)
91
+
92
+ loader_details = (
93
+ importlib.machinery.ExtensionFileLoader,
94
+ importlib.machinery.EXTENSION_SUFFIXES,
95
+ )
96
+
97
+ extfinder = importlib.machinery.FileFinder(lib_dir, loader_details)
98
+ if torch.version.hip and not hasattr(torch.version, "git_version"):
99
+ ext_specs = extfinder.find_spec("_C_hip")
100
+ else:
101
+ ext_specs = extfinder.find_spec("_C")
102
+ cpp_lib_json = os.path.join(lib_dir, "cpp_lib.json")
103
+ if ext_specs is None and not os.path.exists(cpp_lib_json):
104
+ # Nothing was compiled, and nothing was meant to be: this is the
105
+ # pure-Python build. Everything that needs a kernel now lives in
106
+ # separate packages, such as mslk.
107
+ return None
108
+ if ext_specs is None:
109
+ raise xFormersWasNotBuiltException()
110
+ with open(cpp_lib_json, "r") as fp:
111
+ build_metadata = _BuildInfo(json.load(fp))
112
+ try:
113
+ torch.ops.load_library(ext_specs.origin)
114
+ except OSError as exc:
115
+ raise xFormersInvalidLibException(build_metadata) from exc
116
+ return build_metadata
117
+
118
+
119
+ _cpp_library_load_exception = None
120
+ _build_metadata: Optional[_BuildInfo] = None
121
+ _has_cpp_library: bool = False
122
+
123
+ try:
124
+ _build_metadata = _register_extensions()
125
+ _has_cpp_library = _build_metadata is not None
126
+ except (xFormersInvalidLibException, xFormersWasNotBuiltException) as e:
127
+ ENV_VAR_FOR_DETAILS = "XFORMERS_MORE_DETAILS"
128
+ if os.environ.get(ENV_VAR_FOR_DETAILS, False):
129
+ logger.warning(f"WARNING[XFORMERS]: {e}", exc_info=e)
130
+ else:
131
+ logger.warning(
132
+ f"WARNING[XFORMERS]: {e}\n Set {ENV_VAR_FOR_DETAILS}=1 for more details"
133
+ )
134
+ _cpp_library_load_exception = e
135
+
136
+ _built_with_cuda = (
137
+ _build_metadata is not None and _build_metadata.cuda_version is not None
138
+ )
@@ -0,0 +1,12 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
2
+ #
3
+ # This source code is licensed under the BSD license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import warnings
7
+
8
+
9
+ def deprecated_function(self):
10
+ name = repr(self) # self.__name__
11
+ msg = f"{name} is deprecated and is not maintained anymore. It might be removed in a future version of xFormers"
12
+ warnings.warn(msg, FutureWarning, stacklevel=2)
@@ -0,0 +1,12 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
2
+ #
3
+ # This source code is licensed under the BSD license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # flake8: noqa
6
+
7
+ from mslk.attention.fmha.attn_bias_utils import (
8
+ create_attn_bias,
9
+ pack_kv_cache,
10
+ ref_attention,
11
+ ref_attention_bmhk,
12
+ )
@@ -0,0 +1,4 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
2
+ #
3
+ # This source code is licensed under the BSD license found in the
4
+ # LICENSE file in the root directory of this source tree.
@@ -0,0 +1,467 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
2
+ #
3
+ # This source code is licensed under the BSD license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ # Run with --omit-baselines to skip slow baselines.
7
+ # See other CLI arguments in benchmark_main_helper in utils.py.
8
+
9
+ import sys
10
+ from typing import Any, Dict, Type
11
+
12
+ import pytest
13
+ import torch
14
+
15
+ import xformers.ops as xops
16
+ from xformers.attn_bias_utils import create_attn_bias
17
+ from xformers.benchmarks.utils import benchmark_main_helper2, NotSupportedInputError
18
+
19
+ min_run_time = 0.5
20
+ device = torch.device("cuda")
21
+
22
+
23
+ CASES = [
24
+ dict(
25
+ B=max(1, 2 ** (16 - i)),
26
+ Mq=1,
27
+ Mkv=2**i,
28
+ Hq=16,
29
+ Hkv=hkv,
30
+ K=128,
31
+ attn_bias_type=xops.fmha.attn_bias.BlockDiagonalCausalWithOffsetPaddedKeysMask,
32
+ )
33
+ for i in range(8, 18)
34
+ for hkv in (1, 2)
35
+ ]
36
+
37
+
38
+ def quantize_kv_int4(k: torch.Tensor, num_groups: int = 1) -> torch.Tensor:
39
+ """
40
+ Auxiliary int4 row quantization function used for benchmarking and tests.
41
+ Matches the behaviour of torch.ops.llama_cpp.dequantize_int4_cache -
42
+ quantization parameters (scale and offset) of each row along the last
43
+ dimension of the tensor are assumed to be packed into two float16 values
44
+ at the beginning of the row.
45
+ """
46
+ # Scale and shift are such that quantization linearly maps int4 values range [0..15]
47
+ # to input values range min(k)..max(k) individually for every row
48
+ k = k.reshape(*k.shape[:-1], num_groups, k.shape[-1] // num_groups)
49
+ # print(f"k_reshape = {k.shape}")
50
+ max_vals = torch.max(k, dim=-1, keepdim=True).values
51
+ min_vals = torch.min(k, dim=-1, keepdim=True).values
52
+ scale_k: torch.Tensor = (max_vals - min_vals) / 15
53
+ # print(f"scale_k_shape = {scale_k.shape}")
54
+
55
+ shift_k = torch.min(k, dim=-1, keepdim=True).values
56
+ scale_k = scale_k.to(torch.float16)
57
+ shift_k = shift_k.to(torch.float16)
58
+ in_bytes = ((k - shift_k.expand(k.shape)) / scale_k.expand(k.shape)) + 0.5
59
+ in_bytes = in_bytes.to(torch.uint8)
60
+ in_int4 = in_bytes & 0xF
61
+ in_int4_packed = in_int4[..., ::2] + (in_int4[..., 1::2] << 4)
62
+ scale_shift = torch.concat(
63
+ [scale_k.view(torch.uint8), shift_k.view(torch.uint8)], dim=-1
64
+ )
65
+ k_quant = torch.concat(
66
+ [
67
+ scale_shift.flatten(start_dim=-2),
68
+ in_int4_packed.flatten(start_dim=-2),
69
+ ],
70
+ dim=-1,
71
+ ).view(torch.int16)
72
+ return k_quant
73
+
74
+
75
+ class AttentionDecodingBase:
76
+ OP: Any = None
77
+
78
+ def __init__(
79
+ self,
80
+ B: int,
81
+ Mq: int,
82
+ Mkv: int,
83
+ Hq: int,
84
+ Hkv: int,
85
+ K: int,
86
+ bw: bool,
87
+ attn_bias_type,
88
+ ) -> None:
89
+ dtype = torch.float16
90
+ torch.manual_seed(10)
91
+ self.sub_label = (
92
+ f"B={B} Mq={Mq} Mkv={Mkv} Hq={Hq} Hkv={Hkv} K={K} TotalBytes="
93
+ f"{((B * Mkv * Hkv * K * 2) + (B * Mq * Hq * K) + (B * Mq * Hq * K)) * 2}"
94
+ )
95
+ self.label = "attn_decoding"
96
+ self.shapes = (B, Mq, Mkv, Hq, Hkv, K)
97
+
98
+ assert Hkv <= Hq
99
+ assert Hq % Hkv == 0
100
+ self.q = torch.randn(
101
+ [B, Mq, Hkv, Hq // Hkv, K], device="cuda", dtype=dtype, requires_grad=bw
102
+ )
103
+ self.k = torch.randn(
104
+ [B, Mkv, Hkv, 1, K], device="cuda", dtype=dtype, requires_grad=bw
105
+ ).expand(-1, -1, -1, Hq // Hkv, -1)
106
+ self.v = torch.randn(
107
+ [B, Mkv, Hkv, 1, K], device="cuda", dtype=dtype, requires_grad=bw
108
+ ).expand(-1, -1, -1, Hq // Hkv, -1)
109
+
110
+ if Hq == Hkv:
111
+ self.q = self.q[:, :, :, 0]
112
+ self.k = self.k[:, :, :, 0]
113
+ self.v = self.v[:, :, :, 0]
114
+ if Hkv == 1:
115
+ self.q = self.q[:, :, 0]
116
+ self.k = self.k[:, :, 0]
117
+ self.v = self.v[:, :, 0]
118
+
119
+ self.attn_bias = create_attn_bias(
120
+ attn_bias_type,
121
+ batch_size=B,
122
+ num_heads=Hq,
123
+ num_heads_groups=Hq // Hkv,
124
+ q_len=Mq,
125
+ kv_len=Mkv,
126
+ dtype=dtype,
127
+ device=device,
128
+ requires_grad=False,
129
+ fmt="BMHK",
130
+ op=self.OP,
131
+ )
132
+
133
+ if isinstance(
134
+ self.attn_bias,
135
+ xops.fmha.attn_bias.BlockDiagonalCausalWithOffsetPaddedKeysMask,
136
+ ):
137
+ self.q = self.q.view(1, -1, *self.q.shape[2:])
138
+ self.k = self.k.view(1, -1, *self.k.shape[2:])
139
+ self.v = self.v.view(1, -1, *self.v.shape[2:])
140
+
141
+ if hasattr(self.OP, "not_supported_reasons"):
142
+ inp = xops.fmha.Inputs(
143
+ query=self.q, key=self.k, value=self.v, attn_bias=self.attn_bias
144
+ )
145
+ not_supported_reasons = self.OP.not_supported_reasons(inp)
146
+ if not_supported_reasons:
147
+ raise NotSupportedInputError(not_supported_reasons)
148
+
149
+ def get_inputs(self):
150
+ inp = xops.fmha.Inputs(
151
+ query=self.q, key=self.k, value=self.v, attn_bias=self.attn_bias
152
+ )
153
+ return inp
154
+
155
+ def fw(self) -> None:
156
+ try:
157
+ xops.memory_efficient_attention_forward(
158
+ self.q, self.k, self.v, op=self.OP, attn_bias=self.attn_bias
159
+ )
160
+ except (RuntimeError, ValueError) as e:
161
+ print(f"Runtime error: {e}")
162
+
163
+
164
+ class AttentionDecodingCUTLASS(AttentionDecodingBase):
165
+ OP = xops.fmha.cutlass.FwOp
166
+
167
+
168
+ class AttentionDecodingCK(AttentionDecodingBase):
169
+ OP = xops.fmha.ck.FwOp
170
+
171
+ def __init__(
172
+ self,
173
+ B: int,
174
+ Mq: int,
175
+ Mkv: int,
176
+ Hq: int,
177
+ Hkv: int,
178
+ K: int,
179
+ bw: bool,
180
+ attn_bias_type,
181
+ ) -> None:
182
+ dtype = torch.float16
183
+ torch.manual_seed(10)
184
+ self.sub_label = (
185
+ f"B={B} Mq={Mq} Mkv={Mkv} Hq={Hq} Hkv={Hkv} K={K} TotalBytes="
186
+ f"{((B * Mkv * Hkv * K * 2) + (B * Mq * Hq * K) + (B * Mq * Hq * K)) * 2}"
187
+ )
188
+ self.label = "attn_decoding"
189
+ self.shapes = (B, Mq, Mkv, Hq, Hkv, K)
190
+
191
+ assert Hkv <= Hq
192
+ assert Hq % Hkv == 0
193
+ self.q = torch.randn(
194
+ [B, Mq, Hkv, Hq // Hkv, K], device="cuda", dtype=dtype, requires_grad=bw
195
+ )
196
+ self.k = torch.randn(
197
+ [B, Mkv, Hkv, 1, K], device="cuda", dtype=dtype, requires_grad=bw
198
+ ).expand(-1, -1, -1, Hq // Hkv, -1)
199
+ self.v = torch.randn(
200
+ [B, Mkv, Hkv, 1, K], device="cuda", dtype=dtype, requires_grad=bw
201
+ ).expand(-1, -1, -1, Hq // Hkv, -1)
202
+
203
+ if Hq == Hkv:
204
+ self.q = self.q[:, :, :, 0]
205
+ self.k = self.k[:, :, :, 0]
206
+ self.v = self.v[:, :, :, 0]
207
+
208
+ self.attn_bias = create_attn_bias(
209
+ attn_bias_type,
210
+ batch_size=B,
211
+ num_heads=Hq,
212
+ num_heads_groups=Hq // Hkv,
213
+ q_len=Mq,
214
+ kv_len=Mkv,
215
+ dtype=dtype,
216
+ device=device,
217
+ requires_grad=False,
218
+ fmt="BMHK",
219
+ op=self.OP,
220
+ )
221
+
222
+ if isinstance(
223
+ self.attn_bias,
224
+ xops.fmha.attn_bias.BlockDiagonalCausalWithOffsetPaddedKeysMask,
225
+ ):
226
+ self.q = self.q.view(1, -1, *self.q.shape[2:])
227
+ self.k = self.k.view(1, -1, *self.k.shape[2:])
228
+ self.v = self.v.view(1, -1, *self.v.shape[2:])
229
+
230
+ if hasattr(self.OP, "not_supported_reasons"):
231
+ inp = xops.fmha.Inputs(
232
+ query=self.q, key=self.k, value=self.v, attn_bias=self.attn_bias
233
+ )
234
+ not_supported_reasons = self.OP.not_supported_reasons(inp)
235
+ if not_supported_reasons:
236
+ raise NotSupportedInputError(not_supported_reasons)
237
+
238
+
239
+ class AttentionDecodingSplitKV(AttentionDecodingBase):
240
+ OP = xops.fmha.triton_splitk.FwOp
241
+
242
+
243
+ class AttentionDecodingCKSplitKV(AttentionDecodingBase):
244
+ OP = xops.fmha.ck_splitk.FwOp
245
+
246
+
247
+ class AttentionDecodingSplitInt4KV(AttentionDecodingBase):
248
+ OP = xops.fmha.triton_splitk.FwOp
249
+
250
+ def __init__(
251
+ self,
252
+ B: int,
253
+ Mq: int,
254
+ Mkv: int,
255
+ Hq: int,
256
+ Hkv: int,
257
+ K: int,
258
+ bw: bool,
259
+ attn_bias_type,
260
+ ) -> None:
261
+ # super(AttentionDecodingSplitInt4KV, self).__init__(B, Mq, Mkv, Hq, Hkv, K, bw, attn_bias_type)
262
+ dtype = torch.float16
263
+ torch.manual_seed(10)
264
+ self.sub_label = (
265
+ f"B={B} Mq={Mq} Mkv={Mkv} Hq={Hq} Hkv={Hkv} K={K} TotalBytes="
266
+ f"{((B * Mkv * Hkv * K * 2) + (B * Mq * Hq * K) + (B * Mq * Hq * K)) * 2}"
267
+ )
268
+ self.label = "attn_decoding"
269
+ self.shapes = (B, Mq, Mkv, Hq, Hkv, K)
270
+
271
+ assert Hkv <= Hq
272
+ assert Hq % Hkv == 0
273
+ self.q = torch.randn(
274
+ [B, Mq, Hkv, Hq // Hkv, K], device="cuda", dtype=dtype, requires_grad=bw
275
+ )
276
+ self.k = torch.randn(
277
+ [B, Mkv, Hkv, 1, K], device="cuda", dtype=dtype, requires_grad=bw
278
+ )
279
+ self.v = torch.randn(
280
+ [B, Mkv, Hkv, 1, K], device="cuda", dtype=dtype, requires_grad=bw
281
+ )
282
+
283
+ num_groups = 1
284
+ self.k = (
285
+ quantize_kv_int4(self.k, num_groups=num_groups)
286
+ .contiguous()
287
+ .view(torch.int32)
288
+ ).expand(-1, -1, -1, Hq // Hkv, -1)
289
+ self.v = (
290
+ quantize_kv_int4(self.v, num_groups=num_groups)
291
+ .contiguous()
292
+ .view(torch.int32)
293
+ ).expand(-1, -1, -1, Hq // Hkv, -1)
294
+
295
+ if Hq == Hkv:
296
+ self.q = self.q[:, :, :, 0]
297
+ self.k = self.k[:, :, :, 0]
298
+ self.v = self.v[:, :, :, 0]
299
+ if Hkv == 1:
300
+ self.q = self.q[:, :, 0]
301
+ self.k = self.k[:, :, 0]
302
+ self.v = self.v[:, :, 0]
303
+
304
+ self.attn_bias = create_attn_bias(
305
+ attn_bias_type,
306
+ batch_size=B,
307
+ num_heads=Hq,
308
+ num_heads_groups=Hq // Hkv,
309
+ q_len=Mq,
310
+ kv_len=Mkv,
311
+ dtype=dtype,
312
+ device=device,
313
+ requires_grad=False,
314
+ fmt="BMHK",
315
+ op=self.OP,
316
+ )
317
+
318
+ if isinstance(
319
+ self.attn_bias,
320
+ xops.fmha.attn_bias.BlockDiagonalCausalWithOffsetPaddedKeysMask,
321
+ ):
322
+ self.q = self.q.view(1, -1, *self.q.shape[2:])
323
+ self.k = self.k.view(1, -1, *self.k.shape[2:])
324
+ self.v = self.v.view(1, -1, *self.v.shape[2:])
325
+
326
+ if hasattr(self.OP, "not_supported_reasons"):
327
+ inp = xops.fmha.Inputs(
328
+ query=self.q, key=self.k, value=self.v, attn_bias=self.attn_bias
329
+ )
330
+ not_supported_reasons = self.OP.not_supported_reasons(inp)
331
+ if not_supported_reasons:
332
+ raise NotSupportedInputError(not_supported_reasons)
333
+
334
+
335
+ class AttentionDecodingPyTorchRepeat(AttentionDecodingBase):
336
+ def fw(self) -> None:
337
+ B, Mq, Mkv, Hq, Hkv, K = self.shapes
338
+ scale = 1 / K**0.5
339
+ q = self.q.reshape([B, Mq, -1, K]).permute(0, 2, 1, 3)
340
+ k = self.k.reshape([B, Mkv, -1, K]).permute(0, 2, 1, 3)
341
+ v = self.v.reshape([B, Mkv, -1, K]).permute(0, 2, 1, 3)
342
+ attn = (q @ k.transpose(-1, -2) * scale).softmax(-1)
343
+ return attn @ v
344
+
345
+
346
+ BENCHMARKS: Dict[str, Type[AttentionDecodingBase]] = {
347
+ "pytorch": AttentionDecodingPyTorchRepeat,
348
+ }
349
+
350
+ if torch.version.cuda:
351
+ BENCHMARKS["cutlass"] = AttentionDecodingCUTLASS
352
+
353
+ if torch.version.hip:
354
+ BENCHMARKS.update(
355
+ {
356
+ "ck": AttentionDecodingCK,
357
+ "ck_splitK": AttentionDecodingCKSplitKV,
358
+ }
359
+ )
360
+
361
+
362
+ if (sys.version_info.major, sys.version_info.minor) >= (3, 9):
363
+ BENCHMARKS["triton_splitK"] = AttentionDecodingSplitKV
364
+ BENCHMARKS["triton_int4KV"] = AttentionDecodingSplitInt4KV
365
+
366
+ try:
367
+ import flash_attn
368
+
369
+ class AttentionDecodingFlashAttention(AttentionDecodingBase):
370
+ def fw(self) -> None:
371
+ q, k, v = self.q, self.k, self.v
372
+ if q.ndim == 5:
373
+ B, Mq, H1, H2, K = q.shape
374
+ B, Mkv, H1, H2, K = k.shape
375
+ q = q.reshape([B, Mq, H1 * H2, K])
376
+ k = k[:, :, :, 0]
377
+ v = v[:, :, :, 0]
378
+ return flash_attn.flash_attn_func(q, k, v)
379
+
380
+ BENCHMARKS[f"flash-attention@{flash_attn.__version__}"] = (
381
+ AttentionDecodingFlashAttention
382
+ )
383
+ except ImportError:
384
+ pass
385
+
386
+
387
+ TEST_CASES = [
388
+ dict(
389
+ B=max(1, 2 ** (16 - i)),
390
+ Mq=1,
391
+ Mkv=2**i,
392
+ Hq=16,
393
+ Hkv=hkv,
394
+ K=128,
395
+ attn_bias_type=None,
396
+ )
397
+ for i in range(8, 18)
398
+ for hkv in range(1, 3)
399
+ ] + [
400
+ dict(B=i, Mq=1, Mkv=4097, Hq=8, Hkv=1, K=128, attn_bias_type=None)
401
+ for i in [2, 4, 8, 16, 32, 64, 128]
402
+ ]
403
+
404
+
405
+ def get_benchmark_names():
406
+ decoder_names = list(BENCHMARKS.keys())
407
+ decoder_names.remove("pytorch")
408
+ return decoder_names
409
+
410
+
411
+ # tests to verify correctness of each decoder implementation
412
+ @pytest.mark.parametrize(
413
+ "name, case",
414
+ [(name, case) for name in get_benchmark_names() for case in TEST_CASES],
415
+ )
416
+ def test_flash_attention_decoder(name, case):
417
+ baseline = AttentionDecodingPyTorchRepeat(
418
+ case["B"],
419
+ case["Mq"],
420
+ case["Mkv"],
421
+ case["Hq"],
422
+ case["Hkv"],
423
+ case["K"],
424
+ False,
425
+ case["attn_bias_type"],
426
+ )
427
+ if name == "ck-decoder" and case["Mkv"] >= 2**14:
428
+ pytest.skip("ck-decoder does not support Mkv >= 16K")
429
+
430
+ baseline_out = baseline.fw()
431
+ inputs = baseline.get_inputs()
432
+ decoder = BENCHMARKS[name]
433
+
434
+ assert name in ["ck_splitK", "ck", "triton_splitK", "triton_int4KV"]
435
+ decoder_output, ctx = decoder.OP.apply(inputs, False)
436
+
437
+ q, k, v = inputs.get_qkv_in_bmghk()
438
+ B, M, G, H, Kq = q.shape
439
+ mqa_swap_seqlen_head = False
440
+ if k.shape[3] > 1 and k.stride(3) == 0 and v.stride(3) == 0:
441
+ mqa_swap_seqlen_head = True
442
+ if mqa_swap_seqlen_head:
443
+ decoder_output = (
444
+ decoder_output.reshape(B, -1, M, Kq).transpose(1, 2).contiguous()
445
+ )
446
+ else:
447
+ decoder_output = decoder_output.reshape(B, H * G, -1, Kq).contiguous()
448
+
449
+ decoder_output = decoder_output.transpose(2, 1).contiguous()
450
+ torch.testing.assert_close(decoder_output, baseline_out, atol=1e-2, rtol=0)
451
+
452
+
453
+ def main() -> None:
454
+ """
455
+ run performance benchmark
456
+ """
457
+ benchmark_main_helper2(
458
+ "attn_decoding",
459
+ fw=True,
460
+ cases=CASES,
461
+ functions=BENCHMARKS,
462
+ min_run_time=min_run_time,
463
+ )
464
+
465
+
466
+ if __name__ == "__main__":
467
+ main() # pragma: no cover