A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 7, 11:30 UTC
[Attention] FA4 integration (#32974)
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com> Signed-off-by: Matthew Bonanni <mbonanni@redhat.com> Signed-off-by: Lucas Wilkinson <LucasWilkinson@users.noreply.github.com> Co-authored-by: Matthew Bonanni <mbonanni@redhat.com> Co-authored-by: Tyler Michael Smith <tyler@neuralmagic.com>
Lucas Wilkinson committed
Mar 1, 2026 at 18:44 UTC
8b5014d3dd343736ccf3e26cd44a0bb7700d205c
15 files changed
+817
-54
.buildkite/test_areas/misc.yaml
+1
@@ -9,6 +9,7 @@ steps:
9
- tests/v1
10
commands:
11
- uv pip install --system -r /vllm-workspace/requirements/kv_connectors.txt
12
+ - export VLLM_WORKER_MULTIPROC_METHOD=spawn
13
# split the test to avoid interference
14
- pytest -v -s -m 'not cpu_test' v1/core
15
- pytest -v -s v1/executor
.gitignore
+2
@@ -3,6 +3,8 @@
3
4
# vllm-flash-attn built from source
5
vllm/vllm_flash_attn/*
6
+!vllm/vllm_flash_attn/__init__.py
7
+!vllm/vllm_flash_attn/flash_attn_interface.py
8
9
# OpenAI triton kernels copied from source
10
vllm/third_party/triton_kernels/*
cmake/external_projects/vllm_flash_attn.cmake
+55
-30
@@ -17,7 +17,8 @@ endif()
17
# They should be identical but if they aren't, this is a massive footgun.
18
#
19
# The vllm-flash-attn install rules are nested under vllm to make sure the library gets installed in the correct place.
20
-# To only install vllm-flash-attn, use --component _vllm_fa2_C (for FA2) or --component _vllm_fa3_C (for FA3).
20
+# To only install vllm-flash-attn, use --component _vllm_fa2_C (for FA2), --component _vllm_fa3_C (for FA3),
21
+# or --component _vllm_fa4_cutedsl_C (for FA4 CuteDSL Python files).
22
# If no component is specified, vllm-flash-attn is still installed.
23
24
# If VLLM_FLASH_ATTN_SRC_DIR is set, vllm-flash-attn is installed from that directory instead of downloading.
@@ -38,7 +39,7 @@ else()
39
FetchContent_Declare(
40
vllm-flash-attn
41
GIT_REPOSITORY https://github.com/vllm-project/flash-attention.git
41
- GIT_TAG 5824e6e2008271063c3229ab3e7032bd74abbbc6
42
+ GIT_TAG 140c00c0241bb60cc6e44e7c1be9998d4b20d8d2
43
GIT_PROGRESS TRUE
44
# Don't share the vllm-flash-attn build between build types
45
BINARY_DIR ${CMAKE_BINARY_DIR}/vllm-flash-attn
@@ -46,38 +47,62 @@ else()
47
endif()
48
49
49
-# Ensure the vllm/vllm_flash_attn directory exists before installation
50
-install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/vllm/vllm_flash_attn\")" ALL_COMPONENTS)
51
-
52
-# Make sure vllm-flash-attn install rules are nested under vllm/
53
-# This is here to support installing all components under the same prefix with cmake --install.
54
-# setup.py installs every component separately but uses the same prefix for all.
55
-# ALL_COMPONENTS is used to avoid duplication for FA2 and FA3,
56
-# and these statements don't hurt when installing neither component.
57
-install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY FALSE)" ALL_COMPONENTS)
58
-install(CODE "set(OLD_CMAKE_INSTALL_PREFIX \"\${CMAKE_INSTALL_PREFIX}\")" ALL_COMPONENTS)
59
-install(CODE "set(CMAKE_INSTALL_PREFIX \"\${CMAKE_INSTALL_PREFIX}/vllm/\")" ALL_COMPONENTS)
50
+# Install rules for FA components need the install prefix nested under vllm/
51
+# These run at install time, before the FA library's own install rules
52
+foreach(_FA_COMPONENT _vllm_fa2_C _vllm_fa3_C)
53
+ install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY FALSE)" COMPONENT ${_FA_COMPONENT})
54
+ install(CODE "set(OLD_CMAKE_INSTALL_PREFIX \"\${CMAKE_INSTALL_PREFIX}\")" COMPONENT ${_FA_COMPONENT})
55
+ install(CODE "set(CMAKE_INSTALL_PREFIX \"\${CMAKE_INSTALL_PREFIX}/vllm/\")" COMPONENT ${_FA_COMPONENT})
56
+endforeach()
57
58
# Fetch the vllm-flash-attn library
59
FetchContent_MakeAvailable(vllm-flash-attn)
60
message(STATUS "vllm-flash-attn is available at ${vllm-flash-attn_SOURCE_DIR}")
61
65
-# Restore the install prefix
66
-install(CODE "set(CMAKE_INSTALL_PREFIX \"\${OLD_CMAKE_INSTALL_PREFIX}\")" ALL_COMPONENTS)
67
-install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY TRUE)" ALL_COMPONENTS)
62
+# Restore the install prefix after FA's install rules
63
+foreach(_FA_COMPONENT _vllm_fa2_C _vllm_fa3_C)
64
+ install(CODE "set(CMAKE_INSTALL_PREFIX \"\${OLD_CMAKE_INSTALL_PREFIX}\")" COMPONENT ${_FA_COMPONENT})
65
+ install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY TRUE)" COMPONENT ${_FA_COMPONENT})
66
+endforeach()
67
+
68
+# Install shared Python files for both FA2 and FA3 components
69
+foreach(_FA_COMPONENT _vllm_fa2_C _vllm_fa3_C)
70
+ # Ensure the vllm/vllm_flash_attn directory exists before installation
71
+ install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/vllm/vllm_flash_attn\")"
72
+ COMPONENT ${_FA_COMPONENT})
73
+
74
+ # Copy vllm_flash_attn python files (except __init__.py and flash_attn_interface.py
75
+ # which are source-controlled in vllm)
76
+ install(
77
+ DIRECTORY ${vllm-flash-attn_SOURCE_DIR}/vllm_flash_attn/
78
+ DESTINATION vllm/vllm_flash_attn
79
+ COMPONENT ${_FA_COMPONENT}
80
+ FILES_MATCHING PATTERN "*.py"
81
+ PATTERN "__init__.py" EXCLUDE
82
+ PATTERN "flash_attn_interface.py" EXCLUDE
83
+ )
84
+
85
+endforeach()
86
69
-# Copy over the vllm-flash-attn python files (duplicated for fa2 and fa3, in
70
-# case only one is built, in the case both are built redundant work is done)
71
-install(
72
- DIRECTORY ${vllm-flash-attn_SOURCE_DIR}/vllm_flash_attn/
73
- DESTINATION vllm/vllm_flash_attn
74
- COMPONENT _vllm_fa2_C
75
- FILES_MATCHING PATTERN "*.py"
76
-)
87
+#
88
+# FA4 CuteDSL component
89
+# This is a Python-only component that copies the flash_attn/cute directory
90
+# and transforms imports to match our package structure.
91
+#
92
+add_custom_target(_vllm_fa4_cutedsl_C)
93
78
-install(
79
- DIRECTORY ${vllm-flash-attn_SOURCE_DIR}/vllm_flash_attn/
80
- DESTINATION vllm/vllm_flash_attn
81
- COMPONENT _vllm_fa3_C
82
- FILES_MATCHING PATTERN "*.py"
83
-)
94
+# Copy flash_attn/cute directory (needed for FA4) and transform imports
95
+# The cute directory uses flash_attn.cute imports internally, which we replace
96
+# with vllm.vllm_flash_attn.cute to match our package structure.
97
+install(CODE "
98
+ file(GLOB_RECURSE CUTE_PY_FILES \"${vllm-flash-attn_SOURCE_DIR}/flash_attn/cute/*.py\")
99
+ foreach(SRC_FILE \${CUTE_PY_FILES})
100
+ file(RELATIVE_PATH REL_PATH \"${vllm-flash-attn_SOURCE_DIR}/flash_attn/cute\" \${SRC_FILE})
101
+ set(DST_FILE \"\${CMAKE_INSTALL_PREFIX}/vllm/vllm_flash_attn/cute/\${REL_PATH}\")
102
+ get_filename_component(DST_DIR \${DST_FILE} DIRECTORY)
103
+ file(MAKE_DIRECTORY \${DST_DIR})
104
+ file(READ \${SRC_FILE} FILE_CONTENTS)
105
+ string(REPLACE \"flash_attn.cute\" \"vllm.vllm_flash_attn.cute\" FILE_CONTENTS \"\${FILE_CONTENTS}\")
106
+ file(WRITE \${DST_FILE} \"\${FILE_CONTENTS}\")
107
+ endforeach()
108
+" COMPONENT _vllm_fa4_cutedsl_C)
docs/design/attention_backends.md
+2
-1
@@ -168,6 +168,7 @@ Priority is **1 = highest** (tried first).
168
| `FLASHINFER` | TRTLLM† | fp16, bf16 | `auto`, `bfloat16`, `fp8`, `fp8_e4m3`, `fp8_e5m2` | 16, 32, 64 | 64, 128, 256 | ✅ | ❌ | ✅ | Decoder | 10.x |
169
| `FLASH_ATTN` | FA2* | fp16, bf16 | `auto`, `bfloat16` | %16 | Any | ❌ | ❌ | ✅ | All | ≥8.0 |
170
| `FLASH_ATTN` | FA3* | fp16, bf16 | `auto`, `bfloat16`, `fp8`, `fp8_e4m3`, `fp8_e5m2` | %16 | Any | ✅ | ❌ | ✅ | All | 9.x |
171
+| `FLASH_ATTN` | FA4* | fp16, bf16 | `auto`, `bfloat16` | %16 | Any | ❌ | ❌ | ✅ | All | ≥10.0 |
172
| `FLASH_ATTN_DIFFKV` | | fp16, bf16 | `auto` | Any | Any | ❌ | ❌ | ✅ | Decoder | Any |
173
| `FLEX_ATTENTION` | | fp16, bf16, fp32 | `auto`, `bfloat16` | Any | Any | ❌ | ✅ | ❌ | Decoder, Encoder Only | Any |
174
| `ROCM_AITER_FA` | | fp16, bf16 | `auto` | 16, 32 | 64, 128, 256 | ❌ | ❌ | ❌ | Decoder | N/A |
@@ -178,7 +179,7 @@ Priority is **1 = highest** (tried first).
179
180
> **†** FlashInfer uses TRTLLM attention on Blackwell (SM100), which supports sinks. Disable via `--attention-config.use_trtllm_attention=0`.
181
>
181
-> **\*** Specify the FlashAttention version via `--attention-config.flash_attn_version=2` or `3`. Default is FA3 on SM90, FA2 otherwise.
182
+> **\*** Specify the FlashAttention version via `--attention-config.flash_attn_version=2`, `3`, or `4`. Default is FA4 on SM100+ (Blackwell), FA3 on SM90 (Hopper), FA2 otherwise.
183
184
## MLA (Multi-head Latent Attention) Backends
185
requirements/cuda.txt
+4
@@ -11,3 +11,7 @@ torchaudio==2.10.0
11
torchvision==0.25.0 # Required for phi3v processor. See https://github.com/pytorch/vision?tab=readme-ov-file#installation for corresponding version
12
# FlashInfer should be updated together with the Dockerfile
13
flashinfer-python==0.6.4
14
+
15
+# QuACK and Cutlass DSL for FA4 (cute-DSL implementation)
16
+nvidia-cutlass-dsl>=4.4.0.dev1
17
+quack-kernels>=0.2.7
setup.py
+5
@@ -976,6 +976,11 @@ if _is_cuda():
976
):
977
# FA3 requires CUDA 12.3 or later
978
ext_modules.append(CMakeExtension(name="vllm.vllm_flash_attn._vllm_fa3_C"))
979
+ # FA4 CuteDSL - Python-only component for FA4's cute DSL support
980
+ # Optional since this doesn't produce a .so file, just copies Python files
981
+ ext_modules.append(
982
+ CMakeExtension(name="vllm.vllm_flash_attn._vllm_fa4_cutedsl_C", optional=True)
983
+ )
984
if envs.VLLM_USE_PRECOMPILED or (
985
CUDA_HOME and get_nvcc_cuda_version() >= Version("12.9")
986
):
tools/pre_commit/generate_attention_backend_docs.py
+98
-10
@@ -563,14 +563,53 @@ def analyze_backend(backend_name: str, class_path: str) -> dict[str, Any] | None
563
564
565
# ---------------------------------------------------------------------------
566
-# Special backend variant parsers (FA2/FA3, FlashInfer TRTLLM, MLA prefill)
566
+# Special backend variant parsers (FA2/FA3/FA4, FlashInfer TRTLLM, MLA prefill)
567
# ---------------------------------------------------------------------------
568
569
570
+def _parse_fa4_supported_caps() -> str | None:
571
+ """Parse flash_attn_interface.py for FA4 supported compute capabilities.
572
+
573
+ Looks for `cc not in [9, 10, 11]` pattern in _is_fa4_supported().
574
+ """
575
+ fa_interface_file = (
576
+ REPO_ROOT / "vllm" / "vllm_flash_attn" / "flash_attn_interface.py"
577
+ )
578
+ if not fa_interface_file.exists():
579
+ return None
580
+
581
+ try:
582
+ tree = ast.parse(fa_interface_file.read_text())
583
+ except Exception:
584
+ return None
585
+
586
+ for node in ast.walk(tree):
587
+ if not isinstance(node, ast.FunctionDef) or node.name != "_is_fa4_supported":
588
+ continue
589
+ for n in ast.walk(node):
590
+ if not (
591
+ isinstance(n, ast.Compare)
592
+ and len(n.ops) == 1
593
+ and isinstance(n.ops[0], ast.NotIn)
594
+ and isinstance(n.comparators[0], ast.List)
595
+ ):
596
+ continue
597
+ caps: list[int] = [
598
+ e.value
599
+ for e in n.comparators[0].elts
600
+ if isinstance(e, ast.Constant) and isinstance(e.value, int)
601
+ ]
602
+ if caps:
603
+ caps.sort()
604
+ return f"{caps[0]}.x-{caps[-1]}.x"
605
+
606
+ return None
607
+
608
+
609
def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
571
- """Parse fa_utils.py to detect FA2 vs FA3 feature differences.
610
+ """Parse fa_utils.py to detect FA2 vs FA3 vs FA4 feature differences.
611
573
- Returns a dict with 'fa2' and 'fa3' keys containing their respective
612
+ Returns a dict with 'fa2', 'fa3', and 'fa4' keys containing their respective
613
feature overrides for compute capability, KV cache dtypes, and sink support.
614
"""
615
if not FA_UTILS_FILE.exists():
@@ -585,6 +624,7 @@ def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
624
fa3_supports_fp8 = False
625
fa3_supports_sinks = False
626
fa3_compute_cap: str | None = None
627
+ fa4_compute_cap: str | None = None
628
629
for node in ast.walk(tree):
630
if not isinstance(node, ast.FunctionDef):
@@ -614,14 +654,12 @@ def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
654
fa3_supports_sinks = True
655
break
656
617
- # Check get_flash_attn_version for FA3 compute capability
618
- # Look for the ternary: 3 if (device_capability.major == 9 ...) else 2
657
+ # Check get_flash_attn_version for FA3/FA4 compute capability
658
if node.name == "get_flash_attn_version":
659
for n in ast.walk(node):
621
- # Look for IfExp (ternary) with `device_capability.major == 9`
660
+ # Handle IfExp (ternary) with `device_capability.major == 9`
661
if isinstance(n, ast.IfExp):
662
test = n.test
624
- # Check if test is a BoolOp (and) containing the major check
663
if isinstance(test, ast.BoolOp):
664
for val in test.values:
665
if (
@@ -634,6 +672,38 @@ def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
672
fa3_compute_cap = f"{val.comparators[0].value}.x"
673
break
674
675
+ # Handle If statements for FA3/FA4 detection
676
+ # e.g. `if device_capability.major == 9` -> FA3
677
+ # `elif device_capability.major >= 10` -> FA4
678
+ if isinstance(n, ast.If):
679
+ test = n.test
680
+ comparisons = (
681
+ [v for v in test.values if isinstance(v, ast.Compare)]
682
+ if isinstance(test, ast.BoolOp)
683
+ else [test]
684
+ if isinstance(test, ast.Compare)
685
+ else []
686
+ )
687
+ for comp in comparisons:
688
+ if not (
689
+ isinstance(comp.left, ast.Attribute)
690
+ and comp.left.attr == "major"
691
+ and comp.comparators
692
+ and isinstance(comp.comparators[0], ast.Constant)
693
+ and isinstance(comp.comparators[0].value, int)
694
+ ):
695
+ continue
696
+ op = comp.ops[0]
697
+ val = comp.comparators[0].value
698
+ if isinstance(op, ast.Eq) and fa3_compute_cap is None:
699
+ fa3_compute_cap = f"{val}.x"
700
+ elif isinstance(op, ast.GtE) and fa4_compute_cap is None:
701
+ fa4_compute_cap = f"≥{val}.0"
702
+
703
+ # Fallback: try to parse FA4 compute caps from flash_attn_interface.py
704
+ if fa4_compute_cap is None:
705
+ fa4_compute_cap = _parse_fa4_supported_caps()
706
+
707
return {
708
"fa2": {
709
"supports_fp8": False,
@@ -644,6 +714,11 @@ def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
714
"supports_fp8": fa3_supports_fp8,
715
"supports_sink": fa3_supports_sinks,
716
},
717
+ "fa4": {
718
+ "compute_capability": fa4_compute_cap,
719
+ "supports_fp8": False,
720
+ "supports_sink": False,
721
+ },
722
}
723
724
@@ -760,7 +835,7 @@ def parse_mla_prefill_backends() -> list[dict[str, Any]]:
835
836
837
# ---------------------------------------------------------------------------
763
-# Backend variant expansion (FA2/FA3, FlashInfer native/TRTLLM)
838
+# Backend variant expansion (FA2/FA3/FA4, FlashInfer native/TRTLLM)
839
# ---------------------------------------------------------------------------
840
841
@@ -768,7 +843,7 @@ def _expand_flash_attn_variants(
843
all_backends: list[dict[str, Any]],
844
fa_features: dict[str, dict[str, Any]],
845
) -> list[dict[str, Any]]:
771
- """Expand FLASH_ATTN into FA2 and FA3 variants with different capabilities."""
846
+ """Expand FLASH_ATTN into FA2, FA3, and FA4 variants."""
847
expanded = []
848
for backend in all_backends:
849
if backend["name"] != "FLASH_ATTN":
@@ -801,6 +876,18 @@ def _expand_flash_attn_variants(
876
877
expanded.append(fa2)
878
expanded.append(fa3)
879
+
880
+ # Create FA4 entry if FA4 features are available
881
+ if "fa4" in fa_features:
882
+ fa4 = backend.copy()
883
+ fa4["version"] = "FA4*"
884
+ fa4["_sort_key"] = "FLASH_ATTN"
885
+ fa4["_sort_order"] = 2
886
+ if fa_features["fa4"].get("compute_capability"):
887
+ fa4["compute_capability"] = fa_features["fa4"]["compute_capability"]
888
+ fa4["supports_sink"] = fa_features["fa4"]["supports_sink"]
889
+ expanded.append(fa4)
890
+
891
return expanded
892
893
@@ -1360,7 +1447,8 @@ def generate_docs() -> str:
1447
if fa_features:
1448
footnotes.append(
1449
"> **\\*** Specify the FlashAttention version via "
1363
- "`--attention-config.flash_attn_version=2` or `3`. Default is FA3 on SM90, "
1450
+ "`--attention-config.flash_attn_version=2`, `3`, or `4`. "
1451
+ "Default is FA4 on SM100+ (Blackwell), FA3 on SM90 (Hopper), "
1452
"FA2 otherwise."
1453
)
1454
if footnotes:
vllm/config/attention.py
+2
-2
@@ -16,8 +16,8 @@ class AttentionConfig:
16
backend: AttentionBackendEnum | None = None
17
"""Attention backend to use. If None, will be selected automatically."""
18
19
- flash_attn_version: Literal[2, 3] | None = None
20
- """Force vllm to use a specific flash-attention version (2 or 3).
19
+ flash_attn_version: Literal[2, 3, 4] | None = None
20
+ """Force vllm to use a specific flash-attention version (2, 3, or 4).
21
Only valid when using the flash-attention backend."""
22
23
use_prefill_decode_attention: bool = False
vllm/model_executor/layers/attention/mla_attention.py
+3
-1
@@ -2014,7 +2014,9 @@ class MLACommonImpl(MLAAttentionImpl[M], Generic[M]):
2014
# RoCM and the latter has an additional parameter to control
2015
# FA2 vs FA3
2016
self.flash_attn_varlen_func = flash_attn_varlen_func
2017
- self.vllm_flash_attn_version = get_flash_attn_version()
2017
+ self.vllm_flash_attn_version = get_flash_attn_version(
2018
+ head_size=self.qk_head_dim
2019
+ )
2020
if self.vllm_flash_attn_version is not None:
2021
self.flash_attn_varlen_func = functools.partial(
2022
flash_attn_varlen_func, fa_version=self.vllm_flash_attn_version
vllm/model_executor/layers/attention/mm_encoder_attention.py
+3
-1
@@ -204,7 +204,9 @@ class MMEncoderAttention(CustomOp):
204
}
205
206
self._fa_version = (
207
- get_flash_attn_version() if self.is_flash_attn_backend else None
207
+ get_flash_attn_version(head_size=head_size)
208
+ if self.is_flash_attn_backend
209
+ else None
210
)
211
212
if self.attn_backend == AttentionBackendEnum.FLASHINFER:
vllm/v1/attention/backends/fa_utils.py
+41
-7
@@ -52,7 +52,9 @@ elif current_platform.is_rocm():
52
reshape_and_cache_flash = ops.reshape_and_cache_flash
53
54
55
-def get_flash_attn_version(requires_alibi: bool = False) -> int | None:
55
+def get_flash_attn_version(
56
+ requires_alibi: bool = False, head_size: int | None = None
57
+) -> int | None:
58
# import here to avoid circular dependencies
59
from vllm.platforms import current_platform
60
@@ -72,9 +74,15 @@ def get_flash_attn_version(requires_alibi: bool = False) -> int | None:
74
assert device_capability is not None
75
76
# 1. default version depending on platform
75
- fa_version = (
76
- 3 if (device_capability.major == 9 and is_fa_version_supported(3)) else 2
77
- )
77
+ if device_capability.major == 9 and is_fa_version_supported(3):
78
+ # Hopper (SM90): prefer FA3
79
+ fa_version = 3
80
+ elif device_capability.major == 10 and is_fa_version_supported(4):
81
+ # Blackwell (SM100+, restrict to SM100 for now): prefer FA4
82
+ fa_version = 4
83
+ else:
84
+ # Fallback to FA2
85
+ fa_version = 2
86
87
# 2. override if passed by environment or config
88
from vllm.config import get_current_vllm_config_or_none
@@ -87,12 +95,12 @@ def get_flash_attn_version(requires_alibi: bool = False) -> int | None:
95
fa_version = vllm_config.attention_config.flash_attn_version
96
97
# 3. fallback for unsupported combinations
90
- if device_capability.major == 10 and fa_version == 3:
98
+ if device_capability.major >= 10 and fa_version == 3:
99
logger.warning_once(
100
"Cannot use FA version 3 on Blackwell platform, "
93
- "defaulting to FA version 2."
101
+ "defaulting to FA version 4 if supported, otherwise FA2."
102
)
95
- fa_version = 2
103
+ fa_version = 4 if is_fa_version_supported(4) else 2
104
105
if requires_alibi and fa_version == 3:
106
logger.warning_once(
@@ -100,6 +108,28 @@ def get_flash_attn_version(requires_alibi: bool = False) -> int | None:
108
)
109
fa_version = 2
110
111
+ if requires_alibi and fa_version == 4:
112
+ logger.warning_once(
113
+ "Cannot use FA version 4 with ALiBi, defaulting to FA version 2."
114
+ )
115
+ fa_version = 2
116
+
117
+ # FA4 on SM100 (Blackwell) has TMEM capacity limits that restrict
118
+ # supported head dimensions.
119
+ # See: https://github.com/Dao-AILab/flash-attention/issues/1959
120
+ if (
121
+ fa_version == 4
122
+ and device_capability.major >= 10
123
+ and head_size is not None
124
+ and head_size > 128
125
+ ):
126
+ logger.warning_once(
127
+ "FA4 on Blackwell does not support head_size=%d due to TMEM "
128
+ "capacity limits, defaulting to FA version 2.",
129
+ head_size,
130
+ )
131
+ fa_version = 2
132
+
133
if not is_fa_version_supported(fa_version):
134
logger.error(
135
"Cannot use FA version %d is not supported due to %s",
@@ -139,6 +169,10 @@ def flash_attn_supports_mla():
169
return is_fa_version_supported(
170
3
171
) and current_platform.is_device_capability_family(90)
172
+
173
+ # NOTE(Lucas): FA4 CuteDSL does NOT currently support MLA's non-standard
174
+ # head dimensions (576 for qk, 512 for v) due to TMEM capacity limits.
175
+
176
except (ImportError, AssertionError):
177
pass
178
return False
vllm/v1/attention/backends/flash_attn.py
+9
-1
@@ -580,7 +580,15 @@ class FlashAttentionImpl(AttentionImpl):
580
self.num_queries_per_kv = self.num_heads // self.num_kv_heads
581
582
self.attn_type = attn_type
583
- self.vllm_flash_attn_version = get_flash_attn_version()
583
+ self.vllm_flash_attn_version = get_flash_attn_version(
584
+ requires_alibi=alibi_slopes is not None,
585
+ head_size=head_size,
586
+ )
587
+ logger.info_once(
588
+ "Using FlashAttention version %s",
589
+ self.vllm_flash_attn_version,
590
+ scope="local",
591
+ )
592
# Cache the batch invariant result for use in forward passes
593
self.batch_invariant_enabled = vllm_is_batch_invariant()
594
vllm/v1/cudagraph_dispatcher.py
+1
-1
@@ -137,7 +137,7 @@ class CudagraphDispatcher:
137
num_tokens_padded = self._bs_to_padded_graph_size[num_tokens]
138
139
if uniform_decode and self.cudagraph_mode.has_mode(CUDAGraphMode.FULL):
140
- num_reqs = num_tokens_padded // uniform_decode_query_len
140
+ num_reqs = min(num_tokens_padded // uniform_decode_query_len, max_num_seqs)
141
assert num_tokens_padded % uniform_decode_query_len == 0
142
else:
143
uniform_decode = False
vllm/vllm_flash_attn/__init__.py
new
+24
@@ -0,0 +1,24 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+
4
+from vllm.vllm_flash_attn.flash_attn_interface import (
5
+ FA2_AVAILABLE,
6
+ FA3_AVAILABLE,
7
+ fa_version_unsupported_reason,
8
+ flash_attn_varlen_func,
9
+ get_scheduler_metadata,
10
+ is_fa_version_supported,
11
+)
12
+
13
+if not (FA2_AVAILABLE or FA3_AVAILABLE):
14
+ raise ImportError(
15
+ "vllm.vllm_flash_attn requires the CUDA flash attention extensions "
16
+ "(_vllm_fa2_C or _vllm_fa3_C). On ROCm, use upstream flash_attn."
17
+ )
18
+
19
+__all__ = [
20
+ "fa_version_unsupported_reason",
21
+ "flash_attn_varlen_func",
22
+ "get_scheduler_metadata",
23
+ "is_fa_version_supported",
24
+]
vllm/vllm_flash_attn/flash_attn_interface.py
new
+567
@@ -0,0 +1,567 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+# Copyright (c) 2023, Tri Dao.
4
+# ruff: noqa: E501
5
+
6
+
7
+import torch
8
+
9
+# isort: off
10
+# We need to import the CUDA kernels after importing torch
11
+# Use relative import to support build-from-source installation in vLLM
12
+
13
+try:
14
+ from . import _vllm_fa2_C # type: ignore[attr-defined] # noqa: F401
15
+
16
+ FA2_UNAVAILABLE_REASON = None
17
+ FA2_AVAILABLE = True
18
+except ImportError as e:
19
+ FA2_UNAVAILABLE_REASON = str(e)
20
+ FA2_AVAILABLE = False
21
+
22
+try:
23
+ from . import _vllm_fa3_C # type: ignore[attr-defined] # noqa: F401
24
+
25
+ FA3_UNAVAILABLE_REASON = None
26
+ FA3_AVAILABLE = True
27
+except ImportError as e:
28
+ FA3_UNAVAILABLE_REASON = str(e)
29
+ FA3_AVAILABLE = False
30
+
31
+
32
+try:
33
+ import os
34
+
35
+ _cute_interface_path = os.path.join(
36
+ os.path.dirname(__file__), "cute", "interface.py"
37
+ )
38
+ if not os.path.exists(_cute_interface_path):
39
+ raise ImportError("vllm.vllm_flash_attn.cute.interface not found")
40
+
41
+ FA4_UNAVAILABLE_REASON = None
42
+ FA4_AVAILABLE = True
43
+except (ImportError, ModuleNotFoundError) as e:
44
+ FA4_UNAVAILABLE_REASON = str(e)
45
+ FA4_AVAILABLE = False
46
+
47
+# isort: on
48
+
49
+DEFAULT_FA_VERSION = 2
50
+
51
+
52
+def _is_fa2_supported() -> tuple[bool, str | None]:
53
+ if not FA2_AVAILABLE:
54
+ return False, f"FA2 is unavailable due to: {FA2_UNAVAILABLE_REASON}"
55
+ from vllm.platforms import current_platform
56
+
57
+ if not current_platform.has_device_capability(80):
58
+ return False, "FA2 is only supported on devices with compute capability >= 8"
59
+ return True, None
60
+
61
+
62
+def _is_fa3_supported() -> tuple[bool, str | None]:
63
+ if not FA3_AVAILABLE:
64
+ return False, f"FA3 is unavailable due to: {FA3_UNAVAILABLE_REASON}"
65
+ from vllm.platforms import current_platform
66
+
67
+ if not current_platform.is_device_capability_family(90):
68
+ return False, "FA3 is only supported on devices with compute capability 9.x"
69
+ return True, None
70
+
71
+
72
+def _is_fa4_supported() -> tuple[bool, str | None]:
73
+ if not FA4_AVAILABLE:
74
+ return False, f"FA4 is unavailable due to: {FA4_UNAVAILABLE_REASON}"
75
+ from vllm.platforms import current_platform
76
+
77
+ if not (
78
+ current_platform.is_device_capability_family(90)
79
+ or current_platform.is_device_capability_family(100)
80
+ or current_platform.is_device_capability_family(110)
81
+ ):
82
+ return (
83
+ False,
84
+ "FA4 is only supported on devices with compute capability 9.x, 10.x, or 11.x",
85
+ )
86
+ return True, None
87
+
88
+
89
+def is_fa_version_supported(fa_version: int) -> bool:
90
+ if fa_version == 2:
91
+ return _is_fa2_supported()[0]
92
+ elif fa_version == 3:
93
+ return _is_fa3_supported()[0]
94
+ elif fa_version == 4:
95
+ return _is_fa4_supported()[0]
96
+ else:
97
+ raise ValueError(f"Unsupported FA version: {fa_version}")
98
+
99
+
100
+def fa_version_unsupported_reason(fa_version: int) -> str | None:
101
+ if fa_version == 2:
102
+ return _is_fa2_supported()[1]
103
+ elif fa_version == 3:
104
+ return _is_fa3_supported()[1]
105
+ elif fa_version == 4:
106
+ return _is_fa4_supported()[1]
107
+ else:
108
+ raise ValueError(f"Unsupported FA version: {fa_version}")
109
+
110
+
111
+#
112
+# For vLLM we only care about `flash_attn_varlen_func` and
113
+# `flash_attn_with_kvcache` so we only maintain wrappers for these two.
114
+#
115
+
116
+
117
+def maybe_contiguous(x):
118
+ return x.contiguous() if x is not None and x.stride(-1) != 1 else x
119
+
120
+
121
+# NOTE only used in FA3
122
+def get_scheduler_metadata(
123
+ batch_size,
124
+ max_seqlen_q,
125
+ max_seqlen_k,
126
+ num_heads_q,
127
+ num_heads_kv,
128
+ headdim,
129
+ cache_seqlens: torch.Tensor,
130
+ qkv_dtype=torch.bfloat16,
131
+ headdim_v=None,
132
+ cu_seqlens_q: torch.Tensor | None = None,
133
+ cu_seqlens_k_new: torch.Tensor | None = None,
134
+ cache_leftpad: torch.Tensor | None = None,
135
+ page_size: int | None = None,
136
+ max_seqlen_k_new=0,
137
+ causal=False,
138
+ window_size=(-1, -1), # -1 means infinite context window
139
+ has_softcap=False,
140
+ num_splits=0, # Can be tuned for speed
141
+ pack_gqa=None, # Can be tuned for speed
142
+ sm_margin=0, # Can be tuned if some SMs are used for communication
143
+):
144
+ cache_seqlens = maybe_contiguous(cache_seqlens)
145
+ if headdim_v is None:
146
+ headdim_v = headdim
147
+ scheduler_metadata = torch.ops._vllm_fa3_C.get_scheduler_metadata(
148
+ batch_size,
149
+ max_seqlen_q,
150
+ max_seqlen_k,
151
+ num_heads_q,
152
+ num_heads_kv,
153
+ headdim,
154
+ headdim_v,
155
+ qkv_dtype,
156
+ cache_seqlens,
157
+ cu_seqlens_q,
158
+ None, # cu_seqlens_k
159
+ cu_seqlens_k_new,
160
+ None, # seqused_q
161
+ cache_leftpad,
162
+ page_size,
163
+ max_seqlen_k_new,
164
+ causal,
165
+ window_size[0],
166
+ window_size[1],
167
+ has_softcap,
168
+ num_splits,
169
+ pack_gqa,
170
+ sm_margin,
171
+ )
172
+
173
+ return scheduler_metadata
174
+
175
+
176
+def flash_attn_varlen_func(
177
+ q,
178
+ k,
179
+ v,
180
+ max_seqlen_q,
181
+ cu_seqlens_q,
182
+ max_seqlen_k,
183
+ cu_seqlens_k=None, # only used for non-paged prefill
184
+ seqused_k=None,
185
+ q_v=None,
186
+ dropout_p=0.0,
187
+ softmax_scale=None,
188
+ causal=False,
189
+ window_size: list[int] | None = None,
190
+ softcap=0.0, # 0.0 means deactivated
191
+ alibi_slopes=None,
192
+ deterministic=False,
193
+ return_attn_probs=False,
194
+ block_table=None,
195
+ return_softmax_lse=False,
196
+ out=None,
197
+ # FA3 Only
198
+ scheduler_metadata=None,
199
+ q_descale=None,
200
+ k_descale=None,
201
+ v_descale=None,
202
+ num_splits: int = 0,
203
+ # Version selector
204
+ fa_version: int = DEFAULT_FA_VERSION,
205
+ s_aux=None,
206
+ cp_world_size=1,
207
+ cp_rank=0,
208
+ cp_tot_seqused_k=None,
209
+):
210
+ """dropout_p should be set to 0.0 during evaluation
211
+ Supports multi-query and grouped-query attention (MQA/GQA) by passing in K, V with fewer heads
212
+ than Q. Note that the number of heads in Q must be divisible by the number of heads in KV.
213
+ For example, if Q has 6 heads and K, V have 2 heads, head 0, 1, 2 of Q will attention to head
214
+ 0 of K, V, and head 3, 4, 5 of Q will attention to head 1 of K, V.
215
+
216
+ If causal=True, the causal mask is aligned to the bottom right corner of the attention matrix.
217
+ For example, if seqlen_q = 2 and seqlen_k = 5, the causal mask (1 = keep, 0 = masked out) is:
218
+ 1 1 1 1 0
219
+ 1 1 1 1 1
220
+ If seqlen_q = 5 and seqlen_k = 2, the causal mask is:
221
+ 0 0
222
+ 0 0
223
+ 0 0
224
+ 1 0
225
+ 1 1
226
+ If the row of the mask is all zero, the output will be zero.
227
+
228
+ If window_size != (-1, -1), implements sliding window local attention. Query at position i
229
+ will only attend to keys between
230
+ [i + seqlen_k - seqlen_q - window_size[0], i + seqlen_k - seqlen_q + window_size[1]] inclusive.
231
+
232
+ Arguments:
233
+ q: (total_q, nheads, headdim), where total_q = total number of query tokens in the batch.
234
+ k: (total_k, nheads_k, headdim), where total_k = total number of key tokens in the batch.
235
+ v: (total_k, nheads_k, headdim), where total_k = total number of key tokens in the batch.
236
+ cu_seqlens_q: (batch_size + 1,), dtype torch.int32. The cumulative sequence lengths
237
+ of the sequences in the batch, used to index into q.
238
+ cu_seqlens_k: (batch_size + 1,), dtype torch.int32. The cumulative sequence lengths
239
+ of the sequences in the batch, used to index into kv.
240
+ max_seqlen_q: int. Maximum query sequence length in the batch.
241
+ max_seqlen_k: int. Maximum key sequence length in the batch.
242
+ dropout_p: float. Dropout probability.
243
+ softmax_scale: float. The scaling of QK^T before applying softmax.
244
+ Default to 1 / sqrt(headdim).
245
+ causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling).
246
+ window_size: (left, right). If not (-1, -1), implements sliding window local attention.
247
+ softcap: float. Anything > 0 activates softcapping attention.
248
+ alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of
249
+ (-alibi_slope * |i + seqlen_k - seqlen_q - j|)
250
+ is added to the attention score of query i and key j.
251
+ deterministic: bool. Whether to use the deterministic implementation of the backward pass,
252
+ which is slightly slower and uses more memory. The forward pass is always deterministic.
253
+ return_attn_probs: bool. Whether to return the attention probabilities. This option is for
254
+ testing only. The returned probabilities are not guaranteed to be correct
255
+ (they might not have the right scaling).
256
+ Return:
257
+ out: (total, nheads, headdim).
258
+ softmax_lse [optional, if return_softmax_lse=True]: (nheads, total_q_seqlen). The
259
+ logsumexp of each row of the matrix QK^T * scaling (e.g., log of the softmax
260
+ normalization factor).
261
+ """
262
+ assert cu_seqlens_k is not None or seqused_k is not None, (
263
+ "cu_seqlens_k or seqused_k must be provided"
264
+ )
265
+ assert cu_seqlens_k is None or seqused_k is None, (
266
+ "cu_seqlens_k and seqused_k cannot be provided at the same time"
267
+ )
268
+ assert block_table is None or seqused_k is not None, (
269
+ "seqused_k must be provided if block_table is provided"
270
+ )
271
+
272
+ if softmax_scale is None:
273
+ softmax_scale = q.shape[-1] ** (-0.5)
274
+ # custom op does not support non-tuple input
275
+ real_window_size: tuple[int, int]
276
+ if window_size is None:
277
+ real_window_size = (-1, -1)
278
+ else:
279
+ assert len(window_size) == 2
280
+ real_window_size = (window_size[0], window_size[1])
281
+ q, k, v = [maybe_contiguous(x) for x in (q, k, v)]
282
+
283
+ dummy_cu_seqlens_k = torch.empty_like(cu_seqlens_q)
284
+
285
+ if fa_version == 2:
286
+ if (
287
+ scheduler_metadata is not None
288
+ and q_descale is not None
289
+ and k_descale is not None
290
+ and v_descale is not None
291
+ ):
292
+ raise NotImplementedError(
293
+ "FA2 does not support scheduler_metadata, q_descale, "
294
+ "k_descale, v_descale"
295
+ )
296
+ if s_aux is not None:
297
+ raise NotImplementedError("FA2 does not support s_aux")
298
+ if num_splits > 1:
299
+ raise NotImplementedError("FA2 does not support num_splits > 1")
300
+ out, softmax_lse = torch.ops._vllm_fa2_C.varlen_fwd(
301
+ q,
302
+ k,
303
+ v,
304
+ out,
305
+ cu_seqlens_q,
306
+ # cu_seqlens_k not used since we use seqused_k, but flash_api.cpp
307
+ # still wants it so we pass all zeros
308
+ dummy_cu_seqlens_k if cu_seqlens_k is None else cu_seqlens_k,
309
+ seqused_k,
310
+ None,
311
+ block_table,
312
+ alibi_slopes,
313
+ max_seqlen_q,
314
+ max_seqlen_k,
315
+ dropout_p,
316
+ softmax_scale,
317
+ False,
318
+ causal,
319
+ real_window_size[0],
320
+ real_window_size[1],
321
+ softcap,
322
+ return_softmax_lse and dropout_p > 0,
323
+ num_splits,
324
+ None,
325
+ )
326
+ elif fa_version == 3:
327
+ assert alibi_slopes is None, "Alibi is not supported in FA3"
328
+ out, softmax_lse, _, _ = torch.ops._vllm_fa3_C.fwd(
329
+ q,
330
+ k,
331
+ v,
332
+ None,
333
+ None, # k_new, v_new
334
+ q_v,
335
+ out,
336
+ cu_seqlens_q,
337
+ cu_seqlens_k, # cu_seqlens_k
338
+ None, # cu_seqlens_k_new
339
+ None,
340
+ seqused_k, # seqused_q, seqused_k
341
+ max_seqlen_q,
342
+ max_seqlen_k,
343
+ block_table,
344
+ None, # kv_batch_idx
345
+ None, # leftpad_k
346
+ None,
347
+ None,
348
+ None, # rotary_cos, rotary_sin, seqlens_rotary
349
+ q_descale,
350
+ k_descale,
351
+ v_descale,
352
+ softmax_scale,
353
+ causal,
354
+ real_window_size[0],
355
+ real_window_size[1],
356
+ softcap,
357
+ True, # rotary_interleaved
358
+ scheduler_metadata,
359
+ num_splits,
360
+ None, # pack_gqa
361
+ 0, # sm_margin
362
+ s_aux, # s_aux
363
+ cp_world_size,
364
+ cp_rank,
365
+ cp_tot_seqused_k,
366
+ )
367
+ elif fa_version == 4:
368
+ assert alibi_slopes is None, "Alibi is not supported in FA4"
369
+ # FA4 on SM90 doesn't support paged KV; SM100+ does
370
+ from vllm.platforms import current_platform
371
+
372
+ if block_table is not None and current_platform.is_device_capability_family(90):
373
+ raise NotImplementedError(
374
+ "FA4 with paged KV is not supported on SM90 (Hopper). "
375
+ "Use FA3 or upgrade to Blackwell (SM100+)."
376
+ )
377
+ from vllm.vllm_flash_attn.cute.interface import _flash_attn_fwd
378
+
379
+ out, softmax_lse = _flash_attn_fwd(
380
+ q,
381
+ k,
382
+ v,
383
+ cu_seqlens_q=cu_seqlens_q,
384
+ cu_seqlens_k=cu_seqlens_k,
385
+ seqused_k=seqused_k,
386
+ max_seqlen_q=max_seqlen_q,
387
+ max_seqlen_k=max_seqlen_k,
388
+ page_table=block_table,
389
+ softmax_scale=softmax_scale,
390
+ causal=causal,
391
+ softcap=softcap,
392
+ window_size_left=real_window_size[0] if real_window_size[0] >= 0 else None,
393
+ window_size_right=real_window_size[1] if real_window_size[1] >= 0 else None,
394
+ num_splits=num_splits,
395
+ return_lse=return_softmax_lse,
396
+ out=out,
397
+ )
398
+ else:
399
+ raise ValueError(f"Unsupported FA version: {fa_version}")
400
+ return (out, softmax_lse) if return_softmax_lse else out
401
+
402
+
403
+def sparse_attn_func(
404
+ q,
405
+ k,
406
+ v,
407
+ block_count,
408
+ block_offset,
409
+ column_count,
410
+ column_index,
411
+ dropout_p=0.0,
412
+ softmax_scale=None,
413
+ causal=False,
414
+ softcap=0.0, # 0.0 means deactivated
415
+ alibi_slopes=None,
416
+ deterministic=False,
417
+ return_attn_probs=False,
418
+ *,
419
+ return_softmax_lse=False,
420
+ out=None,
421
+):
422
+ """Compute attention with vertical and slash sparsity patterns.
423
+ Most Arguments are the same with the flash_attn_func interface, except for 4 extra args:
424
+ block_count and block_offset for slash sparsity patterns, and
425
+ column_count and column_index for vertical sparsity patterns.
426
+ For more details please refer to Appendix C.4.2 of paper https://arxiv.org/abs/2407.02490.
427
+
428
+ Arguments:
429
+ q: (batch_size, seqlen, nheads, headdim)
430
+ k: (batch_size, seqlen, nheads_k, headdim)
431
+ v: (batch_size, seqlen, nheads_k, headdim)
432
+ block_count: (batch_size, nheads, cdiv(seqlen, BLOCK_M))
433
+ block_offset: (batch_size, nheads, cdiv(seqlen, BLOCK_M), NNZ_S)
434
+ column_count: (batch_size, nheads, cdiv(seqlen, BLOCK_M))
435
+ column_index: (batch_size, nheads, cdiv(seqlen, BLOCK_M), NNZ_V)
436
+ dropout_p: float. Dropout probability.
437
+ softmax_scale: float. The scaling of QK^T before applying softmax.
438
+ Default to 1 / sqrt(headdim).
439
+ causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling).
440
+ alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of
441
+ (-alibi_slope * |i + seqlen_k - seqlen_q - j|)
442
+ is added to the attention score of query i and key j.
443
+ deterministic: bool. Whether to use the deterministic implementation of the backward pass,
444
+ which is slightly slower and uses more memory. The forward pass is always deterministic.
445
+ return_attn_probs: bool. Whether to return the attention probabilities. This option is for
446
+ testing only. The returned probabilities are not guaranteed to be correct
447
+ (they might not have the right scaling).
448
+ Return:
449
+ out: (batch_size, seqlen, nheads, headdim).
450
+ softmax_lse [optional, if return_softmax_lse=True]: (batch_size, nheads, seqlen). The
451
+ logsumexp of each row of the matrix QK^T * scaling (e.g., log of the softmax
452
+ normalization factor).
453
+ """
454
+ if softmax_scale is None:
455
+ softmax_scale = q.shape[-1] ** (-0.5)
456
+
457
+ q, k, v = [maybe_contiguous(x) for x in (q, k, v)]
458
+ out, softmax_lse = torch.ops._vllm_fa2_C.fwd_sparse(
459
+ q,
460
+ k,
461
+ v,
462
+ block_count,
463
+ block_offset,
464
+ column_count,
465
+ column_index,
466
+ out,
467
+ alibi_slopes,
468
+ dropout_p,
469
+ softmax_scale,
470
+ causal,
471
+ softcap,
472
+ return_attn_probs and dropout_p > 0,
473
+ None,
474
+ )
475
+ return (out, softmax_lse) if return_softmax_lse else out
476
+
477
+
478
+def sparse_attn_varlen_func(
479
+ q,
480
+ k,
481
+ v,
482
+ block_count,
483
+ block_offset,
484
+ column_count,
485
+ column_index,
486
+ cu_seqlens_q,
487
+ cu_seqlens_k,
488
+ max_seqlen_q,
489
+ max_seqlen_k,
490
+ dropout_p=0.0,
491
+ softmax_scale=None,
492
+ causal=False,
493
+ softcap=0.0, # 0.0 means deactivated
494
+ alibi_slopes=None,
495
+ deterministic=False,
496
+ return_attn_probs=False,
497
+ *,
498
+ return_softmax_lse=False,
499
+ out=None,
500
+):
501
+ """Compute attention with vertical and slash sparsity patterns.
502
+ Most Arguments are the same with the flash_attn_varlen_func interface, except for 4 extra args:
503
+ block_count and block_offset for slash sparsity patterns, and
504
+ column_count and column_index for vertical sparsity patterns.
505
+ For more details please refer to Appendix C.4.2 of paper https://arxiv.org/abs/2407.02490.
506
+
507
+ Arguments:
508
+ q: (total_q, nheads, headdim), where total_q = total number of query tokens in the batch.
509
+ k: (total_k, nheads_k, headdim), where total_k = total number of key tokens in the batch.
510
+ v: (total_k, nheads_k, headdim), where total_k = total number of key tokens in the batch.
511
+ block_count: (batch_size, nheads, cdiv(seqlen, BLOCK_M))
512
+ block_offset: (batch_size, nheads, cdiv(seqlen, BLOCK_M), NNZ_S)
513
+ column_count: (batch_size, nheads, cdiv(seqlen, BLOCK_M))
514
+ column_index: (batch_size, nheads, cdiv(seqlen, BLOCK_M), NNZ_V)
515
+ cu_seqlens_q: (batch_size + 1,), dtype torch.int32. The cumulative sequence lengths
516
+ of the sequences in the batch, used to index into q.
517
+ cu_seqlens_k: (batch_size + 1,), dtype torch.int32. The cumulative sequence lengths
518
+ of the sequences in the batch, used to index into kv.
519
+ max_seqlen_q: int. Maximum query sequence length in the batch.
520
+ max_seqlen_k: int. Maximum key sequence length in the batch.
521
+ dropout_p: float. Dropout probability.
522
+ softmax_scale: float. The scaling of QK^T before applying softmax.
523
+ Default to 1 / sqrt(headdim).
524
+ causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling).
525
+ softcap: float. Anything > 0 activates softcapping attention.
526
+ alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of
527
+ (-alibi_slope * |i + seqlen_k - seqlen_q - j|)
528
+ is added to the attention score of query i and key j.
529
+ deterministic: bool. Whether to use the deterministic implementation of the backward pass,
530
+ which is slightly slower and uses more memory. The forward pass is always deterministic.
531
+ return_attn_probs: bool. Whether to return the attention probabilities. This option is for
532
+ testing only. The returned probabilities are not guaranteed to be correct
533
+ (they might not have the right scaling).
534
+ Return:
535
+ out: (total, nheads, headdim).
536
+ softmax_lse [optional, if return_softmax_lse=True]: (nheads, total_q_seqlen). The
537
+ logsumexp of each row of the matrix QK^T * scaling (e.g., log of the softmax
538
+ normalization factor).
539
+ """
540
+ if softmax_scale is None:
541
+ softmax_scale = q.shape[-1] ** (-0.5)
542
+
543
+ q, k, v = [maybe_contiguous(x) for x in (q, k, v)]
544
+ out, softmax_lse = torch.ops._vllm_fa2_C.varlen_fwd_sparse(
545
+ q,
546
+ k,
547
+ v,
548
+ block_count,
549
+ block_offset,
550
+ column_count,
551
+ column_index,
552
+ out,
553
+ cu_seqlens_q,
554
+ cu_seqlens_k,
555
+ None,
556
+ alibi_slopes,
557
+ max_seqlen_q,
558
+ max_seqlen_k,
559
+ dropout_p,
560
+ softmax_scale,
561
+ False,
562
+ causal,
563
+ softcap,
564
+ return_attn_probs and dropout_p > 0,
565
+ None,
566
+ )
567
+ return (out, softmax_lse) if return_softmax_lse else out