flash-msa 0.1.0__tar.gz
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.
- flash_msa-0.1.0/LICENSE +21 -0
- flash_msa-0.1.0/MANIFEST.in +3 -0
- flash_msa-0.1.0/PKG-INFO +104 -0
- flash_msa-0.1.0/README.md +72 -0
- flash_msa-0.1.0/flash_msa/__init__.py +14 -0
- flash_msa-0.1.0/flash_msa/_flash_attn_compat.py +76 -0
- flash_msa-0.1.0/flash_msa/csrc/reverse_index_cuda.cu +211 -0
- flash_msa-0.1.0/flash_msa/flash_msa.py +214 -0
- flash_msa-0.1.0/flash_msa/msa_backward_cutedsl.py +1114 -0
- flash_msa-0.1.0/flash_msa/msa_forward_cutedsl.py +74 -0
- flash_msa-0.1.0/flash_msa/msa_select_cutedsl.py +885 -0
- flash_msa-0.1.0/flash_msa/reverse_index_cuda.py +169 -0
- flash_msa-0.1.0/flash_msa/selected_forward_cutedsl.py +612 -0
- flash_msa-0.1.0/flash_msa/tests/__init__.py +1 -0
- flash_msa-0.1.0/flash_msa/tests/testing_model.py +197 -0
- flash_msa-0.1.0/flash_msa/tests/testing_model_warmup.py +132 -0
- flash_msa-0.1.0/flash_msa/warmup/__init__.py +1 -0
- flash_msa-0.1.0/flash_msa/warmup/flash_msa_warmup.py +106 -0
- flash_msa-0.1.0/flash_msa/warmup/msa_backward_cutedsl_warmup.py +185 -0
- flash_msa-0.1.0/flash_msa/warmup/msa_forward_cutedsl_warmup.py +67 -0
- flash_msa-0.1.0/flash_msa.egg-info/PKG-INFO +104 -0
- flash_msa-0.1.0/flash_msa.egg-info/SOURCES.txt +28 -0
- flash_msa-0.1.0/flash_msa.egg-info/dependency_links.txt +1 -0
- flash_msa-0.1.0/flash_msa.egg-info/requires.txt +14 -0
- flash_msa-0.1.0/flash_msa.egg-info/top_level.txt +1 -0
- flash_msa-0.1.0/pyproject.toml +53 -0
- flash_msa-0.1.0/setup.cfg +4 -0
- flash_msa-0.1.0/setup.py +4 -0
- flash_msa-0.1.0/tests/test_eager_match.py +89 -0
- flash_msa-0.1.0/tests/test_warmup_eager_match.py +89 -0
flash_msa-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2026 flash-msa authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
flash_msa-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flash-msa
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CuTeDSL Flash-MSA training kernels for sparse attention.
|
|
5
|
+
Author: Flash-MSA contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: attention,cuda,cutedsl,pytorch,sparse-attention
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: cuda-python
|
|
20
|
+
Requires-Dist: nvidia-cutlass-dsl[cu13]
|
|
21
|
+
Requires-Dist: packaging
|
|
22
|
+
Requires-Dist: torch==2.12.0
|
|
23
|
+
Requires-Dist: ninja
|
|
24
|
+
Provides-Extra: fa4
|
|
25
|
+
Requires-Dist: flash-attn-4[cu13]; extra == "fa4"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: numpy; extra == "dev"
|
|
28
|
+
Requires-Dist: psutil; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest; extra == "dev"
|
|
30
|
+
Requires-Dist: wheel; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# Flash-MSA
|
|
34
|
+
Flash-MSA is written in CuTeDSL for Hopper and Blackwell GPUs (eg H100, B200) on CUDA 13.
|
|
35
|
+
|
|
36
|
+
These kernels implement training for the MiniMax Sparse Attention paper:
|
|
37
|
+
https://arxiv.org/abs/2606.13392
|
|
38
|
+
|
|
39
|
+
Briefly, MSA is a style of sparse attention fitted to GQA that uses a small proxy attention layer to select blocks of keys to provide to the main attention layer. This offers a massive speedup to inference by slashing the memory-bandwidth bottleneck of loading the full KV cache from HBM.
|
|
40
|
+
|
|
41
|
+
The proxy heads are trained via a KL-divergence loss between the main attention layer's attention scores over the sparsely selected blocks. The proxy heads are assigned groups of main attention heads to select keys for & average scores over for KL-teaching.
|
|
42
|
+
|
|
43
|
+
This library also includes MSA warmup kernels, which run the main attention densely and train the proxy attention on the full sequence.
|
|
44
|
+
|
|
45
|
+
More information is included in the [blog post](https://nanduruganesh.github.io/flash-msa).
|
|
46
|
+
|
|
47
|
+
# Installation
|
|
48
|
+
|
|
49
|
+
flash-msa depends on [flash-attn](https://github.com/dao-ailab/flash-attention). Try to configure your CUDA/Python/Torch versions to match one of the flash-attn wheels for a fast installation, but if you must build from source, set `MAX_JOBS=<max jobs>` to avoid `pip install flash-msa[attn]` bricking your CPU.
|
|
50
|
+
|
|
51
|
+
You will also need Python headers, e.g. `apt-get install python3.12-dev`, for whichever python version you are using.
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
pip install flash-msa
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
From source:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
python setup.py install
|
|
61
|
+
```
|
|
62
|
+
or
|
|
63
|
+
```
|
|
64
|
+
uv pip install -e . --no-build-isolation
|
|
65
|
+
```
|
|
66
|
+
# Usage
|
|
67
|
+
```
|
|
68
|
+
from flash_msa import flash_msa_func
|
|
69
|
+
attn_out, kl_loss = flash_msa_func(Q_proxy, K_proxy, Q, K, V, top_k, head_dim ** -0.5)
|
|
70
|
+
```
|
|
71
|
+
or
|
|
72
|
+
```
|
|
73
|
+
from flash_msa import flash_msa_warmup
|
|
74
|
+
attn_out, kl_loss = flash_msa_warmup_func(Q_proxy, K_proxy, Q, K, V, top_k, head_dim ** -0.5)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Note that kl_loss in the forward is just a torch.zeros placeholder, but after adding it to the main model loss, calling backward() will activate the on-the-fly gradient calcs equivalent to the actual proxy KL loss signal.
|
|
78
|
+
|
|
79
|
+
# Caveats
|
|
80
|
+
|
|
81
|
+
1. Flash-MSA only supports headdims 128, block size 128.
|
|
82
|
+
2. Flash-MSA does not currently return fully materialized KL div. loss term in the fwd/bwd (see [blog](https://nanduruganesh.github.io/flash-msa) for explanation).
|
|
83
|
+
3. No support for quantized training (fp8, nvfp4, mxfp4).
|
|
84
|
+
4. No support for attn temps / oai-style softmax bias.
|
|
85
|
+
5. Proxy Q is grouped by Main KV so Q_p <= KV heads for now.
|
|
86
|
+
|
|
87
|
+
These are not ridiculous to implement though so if there is demand or if someone makes a PR, I will update the repo to include these features.
|
|
88
|
+
|
|
89
|
+
# Testing
|
|
90
|
+
|
|
91
|
+
Test sparse MSA correctness against an eager implementation of MSA: `python tests/test_eager_match.py [args]`
|
|
92
|
+
|
|
93
|
+
Test warmup MSA correctness against an eager implementation of MSA: `python tests/test_warmup_eager_match.py [args]`
|
|
94
|
+
|
|
95
|
+
# Training
|
|
96
|
+
|
|
97
|
+
An MSA training example is implemented in this [Megatron-LM fork](https://github.com/nanduruganesh/Megatron-LM).
|
|
98
|
+
|
|
99
|
+
Notably, you must add the kl_loss returned by MSA kernels to the model's main CE loss before backward to train the proxy attention. The kl_loss is currently treated as a torch.zeros` placeholder and calculated on-the-fly in the backward, so logging the kl_loss will not reflect how proxy training is actually going. Some solutions to get some signal on proxy training are logging grad/update norms of proxy weights, or patching the forward kernel to calculate and accumulate KL div, but only doing this once every n steps to amortize how slow this would make the forward.
|
|
100
|
+
|
|
101
|
+
In general if you are going to train with this it is highly recommended to follow tips from [the paper](https://arxiv.org/abs/2606.13392), use MSA warmup before turning on MSA sparse training, and replicate any transformations to the main attention queries and keys (RoPE, QK norm, QK clip, etc) to the proxy queries and keys to improve proxy convergence.
|
|
102
|
+
|
|
103
|
+
# Inference
|
|
104
|
+
See MiniMax's [official repo](https://github.com/MiniMax-AI/MSA) for MSA inference kernels.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Flash-MSA
|
|
2
|
+
Flash-MSA is written in CuTeDSL for Hopper and Blackwell GPUs (eg H100, B200) on CUDA 13.
|
|
3
|
+
|
|
4
|
+
These kernels implement training for the MiniMax Sparse Attention paper:
|
|
5
|
+
https://arxiv.org/abs/2606.13392
|
|
6
|
+
|
|
7
|
+
Briefly, MSA is a style of sparse attention fitted to GQA that uses a small proxy attention layer to select blocks of keys to provide to the main attention layer. This offers a massive speedup to inference by slashing the memory-bandwidth bottleneck of loading the full KV cache from HBM.
|
|
8
|
+
|
|
9
|
+
The proxy heads are trained via a KL-divergence loss between the main attention layer's attention scores over the sparsely selected blocks. The proxy heads are assigned groups of main attention heads to select keys for & average scores over for KL-teaching.
|
|
10
|
+
|
|
11
|
+
This library also includes MSA warmup kernels, which run the main attention densely and train the proxy attention on the full sequence.
|
|
12
|
+
|
|
13
|
+
More information is included in the [blog post](https://nanduruganesh.github.io/flash-msa).
|
|
14
|
+
|
|
15
|
+
# Installation
|
|
16
|
+
|
|
17
|
+
flash-msa depends on [flash-attn](https://github.com/dao-ailab/flash-attention). Try to configure your CUDA/Python/Torch versions to match one of the flash-attn wheels for a fast installation, but if you must build from source, set `MAX_JOBS=<max jobs>` to avoid `pip install flash-msa[attn]` bricking your CPU.
|
|
18
|
+
|
|
19
|
+
You will also need Python headers, e.g. `apt-get install python3.12-dev`, for whichever python version you are using.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
pip install flash-msa
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
From source:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
python setup.py install
|
|
29
|
+
```
|
|
30
|
+
or
|
|
31
|
+
```
|
|
32
|
+
uv pip install -e . --no-build-isolation
|
|
33
|
+
```
|
|
34
|
+
# Usage
|
|
35
|
+
```
|
|
36
|
+
from flash_msa import flash_msa_func
|
|
37
|
+
attn_out, kl_loss = flash_msa_func(Q_proxy, K_proxy, Q, K, V, top_k, head_dim ** -0.5)
|
|
38
|
+
```
|
|
39
|
+
or
|
|
40
|
+
```
|
|
41
|
+
from flash_msa import flash_msa_warmup
|
|
42
|
+
attn_out, kl_loss = flash_msa_warmup_func(Q_proxy, K_proxy, Q, K, V, top_k, head_dim ** -0.5)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Note that kl_loss in the forward is just a torch.zeros placeholder, but after adding it to the main model loss, calling backward() will activate the on-the-fly gradient calcs equivalent to the actual proxy KL loss signal.
|
|
46
|
+
|
|
47
|
+
# Caveats
|
|
48
|
+
|
|
49
|
+
1. Flash-MSA only supports headdims 128, block size 128.
|
|
50
|
+
2. Flash-MSA does not currently return fully materialized KL div. loss term in the fwd/bwd (see [blog](https://nanduruganesh.github.io/flash-msa) for explanation).
|
|
51
|
+
3. No support for quantized training (fp8, nvfp4, mxfp4).
|
|
52
|
+
4. No support for attn temps / oai-style softmax bias.
|
|
53
|
+
5. Proxy Q is grouped by Main KV so Q_p <= KV heads for now.
|
|
54
|
+
|
|
55
|
+
These are not ridiculous to implement though so if there is demand or if someone makes a PR, I will update the repo to include these features.
|
|
56
|
+
|
|
57
|
+
# Testing
|
|
58
|
+
|
|
59
|
+
Test sparse MSA correctness against an eager implementation of MSA: `python tests/test_eager_match.py [args]`
|
|
60
|
+
|
|
61
|
+
Test warmup MSA correctness against an eager implementation of MSA: `python tests/test_warmup_eager_match.py [args]`
|
|
62
|
+
|
|
63
|
+
# Training
|
|
64
|
+
|
|
65
|
+
An MSA training example is implemented in this [Megatron-LM fork](https://github.com/nanduruganesh/Megatron-LM).
|
|
66
|
+
|
|
67
|
+
Notably, you must add the kl_loss returned by MSA kernels to the model's main CE loss before backward to train the proxy attention. The kl_loss is currently treated as a torch.zeros` placeholder and calculated on-the-fly in the backward, so logging the kl_loss will not reflect how proxy training is actually going. Some solutions to get some signal on proxy training are logging grad/update norms of proxy weights, or patching the forward kernel to calculate and accumulate KL div, but only doing this once every n steps to amortize how slow this would make the forward.
|
|
68
|
+
|
|
69
|
+
In general if you are going to train with this it is highly recommended to follow tips from [the paper](https://arxiv.org/abs/2606.13392), use MSA warmup before turning on MSA sparse training, and replicate any transformations to the main attention queries and keys (RoPE, QK norm, QK clip, etc) to the proxy queries and keys to improve proxy convergence.
|
|
70
|
+
|
|
71
|
+
# Inference
|
|
72
|
+
See MiniMax's [official repo](https://github.com/MiniMax-AI/MSA) for MSA inference kernels.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Public API for Flash-MSA."""
|
|
2
|
+
|
|
3
|
+
from flash_msa.flash_msa import sparse_attention as flash_msa_func
|
|
4
|
+
from flash_msa.warmup.flash_msa_warmup import sparse_attention_warmup
|
|
5
|
+
|
|
6
|
+
flash_msa_func_warmup = sparse_attention_warmup
|
|
7
|
+
flash_msa_warmup_func = sparse_attention_warmup
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"flash_msa_func",
|
|
11
|
+
"flash_msa_func_warmup",
|
|
12
|
+
"flash_msa_warmup_func",
|
|
13
|
+
"sparse_attention_warmup",
|
|
14
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
""" Patch to call the right varlen flash func depending on if FA2,3, or 4 was installed"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def flash_attn_varlen_forward(
|
|
9
|
+
*,
|
|
10
|
+
q: torch.Tensor,
|
|
11
|
+
k: torch.Tensor,
|
|
12
|
+
v: torch.Tensor,
|
|
13
|
+
cu_seqlens_q: torch.Tensor,
|
|
14
|
+
cu_seqlens_k: torch.Tensor,
|
|
15
|
+
max_seqlen_q: int,
|
|
16
|
+
max_seqlen_k: int,
|
|
17
|
+
softmax_scale: float,
|
|
18
|
+
causal: bool,
|
|
19
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
20
|
+
"""Return ``(out, lse)`` for either FlashAttention 2 or 3."""
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
from flash_attn.flash_attn_interface import _flash_attn_varlen_forward
|
|
24
|
+
|
|
25
|
+
out, lse, *_ = _flash_attn_varlen_forward(
|
|
26
|
+
q=q,
|
|
27
|
+
k=k,
|
|
28
|
+
v=v,
|
|
29
|
+
cu_seqlens_q=cu_seqlens_q,
|
|
30
|
+
cu_seqlens_k=cu_seqlens_k,
|
|
31
|
+
max_seqlen_q=max_seqlen_q,
|
|
32
|
+
max_seqlen_k=max_seqlen_k,
|
|
33
|
+
softmax_scale=softmax_scale,
|
|
34
|
+
causal=causal,
|
|
35
|
+
dropout_p=0.0,
|
|
36
|
+
)
|
|
37
|
+
return out, lse
|
|
38
|
+
except ModuleNotFoundError as exc:
|
|
39
|
+
if exc.name not in {"flash_attn", "flash_attn.flash_attn_interface"}:
|
|
40
|
+
raise
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
from flash_attn_interface import flash_attn_varlen_func
|
|
44
|
+
|
|
45
|
+
out, lse = flash_attn_varlen_func(
|
|
46
|
+
q,
|
|
47
|
+
k,
|
|
48
|
+
v,
|
|
49
|
+
cu_seqlens_q,
|
|
50
|
+
cu_seqlens_k,
|
|
51
|
+
max_seqlen_q,
|
|
52
|
+
max_seqlen_k,
|
|
53
|
+
softmax_scale=softmax_scale,
|
|
54
|
+
causal=causal,
|
|
55
|
+
return_attn_probs=True,
|
|
56
|
+
)
|
|
57
|
+
return out, lse
|
|
58
|
+
except ModuleNotFoundError as exc:
|
|
59
|
+
if exc.name != "flash_attn_interface":
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
from flash_attn.cute.interface import flash_attn_varlen_func
|
|
63
|
+
|
|
64
|
+
out, lse = flash_attn_varlen_func(
|
|
65
|
+
q,
|
|
66
|
+
k,
|
|
67
|
+
v,
|
|
68
|
+
cu_seqlens_q=cu_seqlens_q,
|
|
69
|
+
cu_seqlens_k=cu_seqlens_k,
|
|
70
|
+
max_seqlen_q=max_seqlen_q,
|
|
71
|
+
max_seqlen_k=max_seqlen_k,
|
|
72
|
+
softmax_scale=softmax_scale,
|
|
73
|
+
causal=causal,
|
|
74
|
+
return_lse=True,
|
|
75
|
+
)
|
|
76
|
+
return out, lse
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#include <torch/extension.h>
|
|
2
|
+
#include <ATen/cuda/CUDAContext.h>
|
|
3
|
+
#include <c10/cuda/CUDAException.h>
|
|
4
|
+
#include <cuda.h>
|
|
5
|
+
#include <cuda_runtime.h>
|
|
6
|
+
|
|
7
|
+
#include <algorithm>
|
|
8
|
+
#include <cstdint>
|
|
9
|
+
|
|
10
|
+
#define CHECK_CUDA(x) TORCH_CHECK((x).is_cuda(), #x " must be CUDA")
|
|
11
|
+
#define CHECK_CONTIGUOUS(x) TORCH_CHECK((x).is_contiguous(), #x " must be contiguous")
|
|
12
|
+
#define CHECK_INT32(x) TORCH_CHECK((x).scalar_type() == at::kInt, #x " must be int32")
|
|
13
|
+
#define CHECK_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x); CHECK_INT32(x)
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
|
|
17
|
+
constexpr int kThreads = 256;
|
|
18
|
+
|
|
19
|
+
__global__ void count_edges_kernel(
|
|
20
|
+
int const* __restrict__ block_indices,
|
|
21
|
+
int* __restrict__ counts,
|
|
22
|
+
int B,
|
|
23
|
+
int Hp,
|
|
24
|
+
int S,
|
|
25
|
+
int Kb,
|
|
26
|
+
int NB)
|
|
27
|
+
{
|
|
28
|
+
int64_t E = (int64_t)B * Hp * S * Kb;
|
|
29
|
+
int64_t stride = (int64_t)blockDim.x * gridDim.x;
|
|
30
|
+
for (int64_t e = (int64_t)blockIdx.x * blockDim.x + threadIdx.x; e < E; e += stride) {
|
|
31
|
+
int slot = (int)(e % Kb);
|
|
32
|
+
int64_t tmp = e / Kb;
|
|
33
|
+
int q = (int)(tmp % S);
|
|
34
|
+
tmp /= S;
|
|
35
|
+
int p = (int)(tmp % Hp);
|
|
36
|
+
int b = (int)(tmp / Hp);
|
|
37
|
+
|
|
38
|
+
int key_block = block_indices[((int64_t)(b * Hp + p) * S + q) * Kb + slot];
|
|
39
|
+
if ((unsigned)key_block >= (unsigned)NB) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
int bucket = (b * Hp + p) * NB + key_block;
|
|
43
|
+
atomicAdd(counts + bucket, 1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
__global__ void scan_fill_meta_kernel(
|
|
48
|
+
int const* __restrict__ counts,
|
|
49
|
+
int* __restrict__ bucket_offsets,
|
|
50
|
+
int* __restrict__ task_meta,
|
|
51
|
+
int B,
|
|
52
|
+
int Hp,
|
|
53
|
+
int NB,
|
|
54
|
+
int query_chunk,
|
|
55
|
+
int padded_tasks)
|
|
56
|
+
{
|
|
57
|
+
if (blockIdx.x != 0 || threadIdx.x != 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
int task = 0;
|
|
62
|
+
int buckets = B * Hp * NB;
|
|
63
|
+
for (int bucket = 0; bucket < buckets; ++bucket) {
|
|
64
|
+
bucket_offsets[bucket] = task;
|
|
65
|
+
int count = counts[bucket];
|
|
66
|
+
int chunks = (count + query_chunk - 1) / query_chunk;
|
|
67
|
+
int key_block = bucket % NB;
|
|
68
|
+
int tmp = bucket / NB;
|
|
69
|
+
int proxy_head = tmp % Hp;
|
|
70
|
+
int batch = tmp / Hp;
|
|
71
|
+
|
|
72
|
+
for (int c = 0; c < chunks && task < padded_tasks; ++c) {
|
|
73
|
+
int valid = count - c * query_chunk;
|
|
74
|
+
valid = valid > query_chunk ? query_chunk : valid;
|
|
75
|
+
int64_t row = (int64_t)task * 4;
|
|
76
|
+
task_meta[row + 0] = batch;
|
|
77
|
+
task_meta[row + 1] = proxy_head;
|
|
78
|
+
task_meta[row + 2] = key_block;
|
|
79
|
+
task_meta[row + 3] = valid;
|
|
80
|
+
++task;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
__global__ void scatter_edges_kernel(
|
|
86
|
+
int const* __restrict__ block_indices,
|
|
87
|
+
int const* __restrict__ bucket_offsets,
|
|
88
|
+
int* __restrict__ write_counts,
|
|
89
|
+
int* __restrict__ task_qids,
|
|
90
|
+
int B,
|
|
91
|
+
int Hp,
|
|
92
|
+
int S,
|
|
93
|
+
int Kb,
|
|
94
|
+
int NB,
|
|
95
|
+
int query_chunk,
|
|
96
|
+
int padded_tasks)
|
|
97
|
+
{
|
|
98
|
+
int64_t E = (int64_t)B * Hp * S * Kb;
|
|
99
|
+
int64_t stride = (int64_t)blockDim.x * gridDim.x;
|
|
100
|
+
for (int64_t e = (int64_t)blockIdx.x * blockDim.x + threadIdx.x; e < E; e += stride) {
|
|
101
|
+
int slot = (int)(e % Kb);
|
|
102
|
+
int64_t tmp = e / Kb;
|
|
103
|
+
int q = (int)(tmp % S);
|
|
104
|
+
tmp /= S;
|
|
105
|
+
int p = (int)(tmp % Hp);
|
|
106
|
+
int b = (int)(tmp / Hp);
|
|
107
|
+
|
|
108
|
+
int key_block = block_indices[((int64_t)(b * Hp + p) * S + q) * Kb + slot];
|
|
109
|
+
if ((unsigned)key_block >= (unsigned)NB) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
int bucket = (b * Hp + p) * NB + key_block;
|
|
114
|
+
int local = atomicAdd(write_counts + bucket, 1);
|
|
115
|
+
int task = bucket_offsets[bucket] + local / query_chunk;
|
|
116
|
+
if (task >= padded_tasks) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
int lane = local - (local / query_chunk) * query_chunk;
|
|
120
|
+
task_qids[(int64_t)task * query_chunk + lane] = q;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
} // namespace
|
|
125
|
+
|
|
126
|
+
void run_build_reverse_index(
|
|
127
|
+
torch::Tensor block_indices,
|
|
128
|
+
torch::Tensor counts,
|
|
129
|
+
torch::Tensor write_counts,
|
|
130
|
+
torch::Tensor bucket_offsets,
|
|
131
|
+
torch::Tensor task_meta,
|
|
132
|
+
torch::Tensor task_qids,
|
|
133
|
+
int64_t block_size,
|
|
134
|
+
int64_t query_chunk)
|
|
135
|
+
{
|
|
136
|
+
CHECK_INPUT(block_indices);
|
|
137
|
+
CHECK_INPUT(counts);
|
|
138
|
+
CHECK_INPUT(write_counts);
|
|
139
|
+
CHECK_INPUT(bucket_offsets);
|
|
140
|
+
CHECK_INPUT(task_meta);
|
|
141
|
+
CHECK_INPUT(task_qids);
|
|
142
|
+
|
|
143
|
+
TORCH_CHECK(block_indices.dim() == 4, "block_indices must have shape [B, Hp, S, Kb]");
|
|
144
|
+
TORCH_CHECK(task_meta.dim() == 2 && task_meta.size(1) == 4, "task_meta must have shape [T, 4]");
|
|
145
|
+
TORCH_CHECK(task_qids.dim() == 2 && task_qids.size(1) == query_chunk, "task_qids must have shape [T, query_chunk]");
|
|
146
|
+
TORCH_CHECK(block_size > 0 && query_chunk > 0, "block_size and query_chunk must be positive");
|
|
147
|
+
|
|
148
|
+
int B = (int)block_indices.size(0);
|
|
149
|
+
int Hp = (int)block_indices.size(1);
|
|
150
|
+
int S = (int)block_indices.size(2);
|
|
151
|
+
int Kb = (int)block_indices.size(3);
|
|
152
|
+
int NB = S / (int)block_size;
|
|
153
|
+
int buckets = B * Hp * NB;
|
|
154
|
+
int padded_tasks = (int)task_meta.size(0);
|
|
155
|
+
|
|
156
|
+
TORCH_CHECK(S % block_size == 0, "S must be divisible by block_size");
|
|
157
|
+
TORCH_CHECK(counts.numel() == buckets, "counts has wrong size");
|
|
158
|
+
TORCH_CHECK(write_counts.numel() == buckets, "write_counts has wrong size");
|
|
159
|
+
TORCH_CHECK(bucket_offsets.numel() == buckets, "bucket_offsets has wrong size");
|
|
160
|
+
TORCH_CHECK(task_qids.size(0) == padded_tasks, "task_meta/task_qids row mismatch");
|
|
161
|
+
|
|
162
|
+
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
|
|
163
|
+
|
|
164
|
+
C10_CUDA_CHECK(cudaMemsetAsync(counts.data_ptr<int>(), 0, counts.numel() * sizeof(int), stream));
|
|
165
|
+
C10_CUDA_CHECK(cudaMemsetAsync(write_counts.data_ptr<int>(), 0, write_counts.numel() * sizeof(int), stream));
|
|
166
|
+
C10_CUDA_CHECK(cudaMemsetAsync(task_meta.data_ptr<int>(), 0, task_meta.numel() * sizeof(int), stream));
|
|
167
|
+
C10_CUDA_CHECK(cudaMemsetAsync(task_qids.data_ptr<int>(), 0xff, task_qids.numel() * sizeof(int), stream));
|
|
168
|
+
|
|
169
|
+
int64_t edges = (int64_t)B * Hp * S * Kb;
|
|
170
|
+
int blocks = (int)std::min<int64_t>((edges + kThreads - 1) / kThreads, 65535);
|
|
171
|
+
blocks = std::max(blocks, 1);
|
|
172
|
+
|
|
173
|
+
count_edges_kernel<<<blocks, kThreads, 0, stream>>>(
|
|
174
|
+
block_indices.data_ptr<int>(),
|
|
175
|
+
counts.data_ptr<int>(),
|
|
176
|
+
B,
|
|
177
|
+
Hp,
|
|
178
|
+
S,
|
|
179
|
+
Kb,
|
|
180
|
+
NB);
|
|
181
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
182
|
+
|
|
183
|
+
scan_fill_meta_kernel<<<1, 1, 0, stream>>>(
|
|
184
|
+
counts.data_ptr<int>(),
|
|
185
|
+
bucket_offsets.data_ptr<int>(),
|
|
186
|
+
task_meta.data_ptr<int>(),
|
|
187
|
+
B,
|
|
188
|
+
Hp,
|
|
189
|
+
NB,
|
|
190
|
+
(int)query_chunk,
|
|
191
|
+
padded_tasks);
|
|
192
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
193
|
+
|
|
194
|
+
scatter_edges_kernel<<<blocks, kThreads, 0, stream>>>(
|
|
195
|
+
block_indices.data_ptr<int>(),
|
|
196
|
+
bucket_offsets.data_ptr<int>(),
|
|
197
|
+
write_counts.data_ptr<int>(),
|
|
198
|
+
task_qids.data_ptr<int>(),
|
|
199
|
+
B,
|
|
200
|
+
Hp,
|
|
201
|
+
S,
|
|
202
|
+
Kb,
|
|
203
|
+
NB,
|
|
204
|
+
(int)query_chunk,
|
|
205
|
+
padded_tasks);
|
|
206
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
210
|
+
m.def("run_build_reverse_index", &run_build_reverse_index, "Build MSA reverse index on CUDA");
|
|
211
|
+
}
|