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.
Files changed (30) hide show
  1. flash_msa-0.1.0/LICENSE +21 -0
  2. flash_msa-0.1.0/MANIFEST.in +3 -0
  3. flash_msa-0.1.0/PKG-INFO +104 -0
  4. flash_msa-0.1.0/README.md +72 -0
  5. flash_msa-0.1.0/flash_msa/__init__.py +14 -0
  6. flash_msa-0.1.0/flash_msa/_flash_attn_compat.py +76 -0
  7. flash_msa-0.1.0/flash_msa/csrc/reverse_index_cuda.cu +211 -0
  8. flash_msa-0.1.0/flash_msa/flash_msa.py +214 -0
  9. flash_msa-0.1.0/flash_msa/msa_backward_cutedsl.py +1114 -0
  10. flash_msa-0.1.0/flash_msa/msa_forward_cutedsl.py +74 -0
  11. flash_msa-0.1.0/flash_msa/msa_select_cutedsl.py +885 -0
  12. flash_msa-0.1.0/flash_msa/reverse_index_cuda.py +169 -0
  13. flash_msa-0.1.0/flash_msa/selected_forward_cutedsl.py +612 -0
  14. flash_msa-0.1.0/flash_msa/tests/__init__.py +1 -0
  15. flash_msa-0.1.0/flash_msa/tests/testing_model.py +197 -0
  16. flash_msa-0.1.0/flash_msa/tests/testing_model_warmup.py +132 -0
  17. flash_msa-0.1.0/flash_msa/warmup/__init__.py +1 -0
  18. flash_msa-0.1.0/flash_msa/warmup/flash_msa_warmup.py +106 -0
  19. flash_msa-0.1.0/flash_msa/warmup/msa_backward_cutedsl_warmup.py +185 -0
  20. flash_msa-0.1.0/flash_msa/warmup/msa_forward_cutedsl_warmup.py +67 -0
  21. flash_msa-0.1.0/flash_msa.egg-info/PKG-INFO +104 -0
  22. flash_msa-0.1.0/flash_msa.egg-info/SOURCES.txt +28 -0
  23. flash_msa-0.1.0/flash_msa.egg-info/dependency_links.txt +1 -0
  24. flash_msa-0.1.0/flash_msa.egg-info/requires.txt +14 -0
  25. flash_msa-0.1.0/flash_msa.egg-info/top_level.txt +1 -0
  26. flash_msa-0.1.0/pyproject.toml +53 -0
  27. flash_msa-0.1.0/setup.cfg +4 -0
  28. flash_msa-0.1.0/setup.py +4 -0
  29. flash_msa-0.1.0/tests/test_eager_match.py +89 -0
  30. flash_msa-0.1.0/tests/test_warmup_eager_match.py +89 -0
@@ -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.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include flash_msa/csrc *.cu
@@ -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
+ }