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.
- xformers/__init__.py +73 -0
- xformers/_cpp_lib.py +138 -0
- xformers/_deprecation_warning.py +12 -0
- xformers/attn_bias_utils.py +12 -0
- xformers/benchmarks/__init__.py +4 -0
- xformers/benchmarks/benchmark_attn_decoding.py +467 -0
- xformers/benchmarks/benchmark_indexing.py +191 -0
- xformers/benchmarks/benchmark_mem_eff_attention.py +373 -0
- xformers/benchmarks/benchmark_merge_attentions.py +92 -0
- xformers/benchmarks/benchmark_sequence_parallel_fused.py +473 -0
- xformers/benchmarks/benchmark_tiled_matmul.py +122 -0
- xformers/benchmarks/utils.py +767 -0
- xformers/checkpoint.py +592 -0
- xformers/flash_attn_3/__init__.py +4 -0
- xformers/fwbw_overlap.py +536 -0
- xformers/info.py +75 -0
- xformers/ops/__init__.py +138 -0
- xformers/ops/_triton/__init__.py +19 -0
- xformers/ops/_triton/k_index_select_cat.py +184 -0
- xformers/ops/_triton/k_scaled_index_add.py +365 -0
- xformers/ops/_triton/matmul_perf_model.py +250 -0
- xformers/ops/_triton/rmsnorm_kernels.py +159 -0
- xformers/ops/_triton/rope_padded_kernels.py +224 -0
- xformers/ops/_triton/tiled_matmul_kernels.py +436 -0
- xformers/ops/common.py +64 -0
- xformers/ops/differentiable_collectives.py +187 -0
- xformers/ops/fmha/__init__.py +105 -0
- xformers/ops/fmha/_triton/__init__.py +4 -0
- xformers/ops/fmha/_triton/splitk_kernels.py +13 -0
- xformers/ops/fmha/attn_bias.py +49 -0
- xformers/ops/fmha/ck.py +6 -0
- xformers/ops/fmha/ck_splitk.py +16 -0
- xformers/ops/fmha/common.py +20 -0
- xformers/ops/fmha/cutlass.py +6 -0
- xformers/ops/fmha/cutlass_blackwell.py +10 -0
- xformers/ops/fmha/dispatch.py +10 -0
- xformers/ops/fmha/flash.py +24 -0
- xformers/ops/fmha/flash3.py +36 -0
- xformers/ops/fmha/merge_training.py +10 -0
- xformers/ops/fmha/torch_attention_compat.py +9 -0
- xformers/ops/fmha/triton_splitk.py +65 -0
- xformers/ops/indexing.py +237 -0
- xformers/ops/modpar_layers.py +165 -0
- xformers/ops/rmsnorm.py +113 -0
- xformers/ops/rope_padded.py +300 -0
- xformers/ops/seqpar.py +365 -0
- xformers/ops/sequence_parallel_fused_ops.py +847 -0
- xformers/ops/swiglu_op.py +423 -0
- xformers/ops/tiled_matmul.py +318 -0
- xformers/ops/tree_attention.py +14 -0
- xformers/ops/unbind.py +129 -0
- xformers/profiler/__init__.py +15 -0
- xformers/profiler/api.py +92 -0
- xformers/profiler/device_limits.py +113 -0
- xformers/profiler/find_slowest.py +169 -0
- xformers/profiler/profile_analyzer.py +235 -0
- xformers/profiler/profiler.py +375 -0
- xformers/profiler/profiler_dcgm.py +45 -0
- xformers/profiler/profiler_dcgm_impl.py +216 -0
- xformers/sparse/__init__.py +6 -0
- xformers/sparse/blocksparse_tensor.py +278 -0
- xformers/sparse/utils.py +123 -0
- xformers/test.py +4 -0
- xformers/triton/__init__.py +4 -0
- xformers/triton/importing.py +31 -0
- xformers/utils.py +145 -0
- xformers/version.py +2 -0
- xformers-0.0.35.dev1146.dist-info/METADATA +32 -0
- xformers-0.0.35.dev1146.dist-info/RECORD +72 -0
- xformers-0.0.35.dev1146.dist-info/WHEEL +5 -0
- xformers-0.0.35.dev1146.dist-info/licenses/LICENSE +35 -0
- 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,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
|