handwoven8588 commited on
Commit
7e68637
·
verified ·
1 Parent(s): 540e0bb

Card: every comparison against nomic-ai/CodeRankEmbed; plain wording; citation fixed

Browse files
Files changed (1) hide show
  1. README.md +96 -83
README.md CHANGED
@@ -16,8 +16,8 @@ language:
16
  # CodeRankEmbed-flash-attn
17
 
18
  A **bf16 quantization of [`nomic-ai/CodeRankEmbed`](https://huggingface.co/nomic-ai/CodeRankEmbed)**
19
- with a **three-tier attention dispatch built into a custom `modeling_hf_nomic_bert.py` shipped in
20
- this repo.** It is not a finetune — the weights are the original CodeRankEmbed weights cast to bf16
21
  (no further training). Two of the three tiers replace the original eager `O(seq²)` attention with an
22
  `O(N)` unpadded path; the third keeps the original eager algorithm as the correctness reference and
23
  universal fallback.
@@ -32,7 +32,7 @@ comfortably — with **parity embeddings** (no quality change):
32
 
33
  - `torch_varlen` — `torch.nn.attention.varlen.varlen_attn`, shipped in torch itself (no extra
34
  package), available from torch **2.10.0** onward.
35
- - `flash_attn` — the original `flash_attn` varlen-packed kernel. Kept as a fallback for older torch
36
  builds that don't yet have `torch.nn.attention.varlen` but do have `flash_attn` installed. This
37
  package is **optional**.
38
 
@@ -71,7 +71,7 @@ file ships all three paths itself, so no runtime patching or post-load hooks are
71
  batch flattened (`[1, ΣL]` token ids with sequence boundaries, no padding at all), and the model
72
  returns one `[1, ΣL, 768]` output that sentence-transformers pools per sequence. The opt-in needs
73
  the `flash_attn` package installed, because transformers checks for it at load and raises
74
- otherwise, even though the attention itself still runs on whichever tier the dispatch picks. On
75
  the `eager` tier the flattened batch is re-padded and runs the padded path.
76
  - **Loads bf16 by default.** `flash_attn` and `torch_varlen` both require half precision and the
77
  model runs bf16 in any real serving setup, so the weights are stored bf16 and `config.json`
@@ -122,106 +122,115 @@ model[0].auto_model.encoder = torch.compile(model[0].auto_model.encoder, dynamic
122
 
123
  ## Parity & performance
124
 
125
- The weights are the original CodeRankEmbed weights (bf16-cast), so embeddings match the fp32
126
- original to within bf16 precision. Every number below is measured on two cards, an RTX 3090 Ti
127
- (sm_86) and an RTX 5090 Laptop GPU (sm_120), against the **pre-dispatch revision of this repo
128
- (`e361c6f`) on the same GPU**, which runs the original `flash_attn` path on padded batches.
 
 
129
 
130
  **Corpus.** The first 16,384 `document` fields (real Python functions) of
131
  [`lightonai/cornstack`](https://huggingface.co/datasets/lightonai/cornstack)'s Python split, in
132
  shard order (`train-00000-of-00682.parquet`, then `train-00001-…`): 2,684,878 tokens, 7 to 4,746
133
  per function, 164 mean, 92 median. Each row is one `encode()` call over all of them at the stated
134
  `batch_size=`, after one untimed warm-up of the same call. Cosine similarity is per function, on
135
- fp32-renormalized output, against one batch-32 encode by the pre-dispatch revision. Peak memory is
136
- the encode's peak CUDA allocation above the loaded weights (~266 MiB), the same on both cards to
137
  within 32 MiB. `torch 2.12.1+cu130`, `transformers 5.11.0`, `sentence-transformers 6.1.0`,
138
  `flash-attn 2.8.3` (`2.8.3.post1` on the 3090 Ti).
139
 
140
- ### Agreement with the pre-dispatch revision
141
 
142
  | tier | load | 3090 Ti min / mean | 5090 Laptop min / mean |
143
  | --- | --- | --- | --- |
144
- | `eager` | default | 0.99940 / 0.99992 | 0.99972 / 0.99992 |
145
- | `eager` | flattened | 0.99940 / 0.99992 | 0.99816 / 0.99992 |
146
- | `torch_varlen` | default | 0.99940 / 0.99995 | 0.99975 / 0.99995 |
147
- | `torch_varlen` | flattened | 0.99940 / 0.99995 | 0.99975 / 0.99995 |
148
- | `flash_attn` | default | 0.99978 / 0.99998 | 0.99981 / 0.99999 |
149
- | `flash_attn` | flattened | 0.99978 / 0.99998 | 0.99981 / 0.99999 |
150
- | `torch_varlen`, compiled | default | 0.99816 / 0.99994 | 0.99866 / 0.99994 |
151
- | `torch_varlen`, compiled | flattened | 0.99816 / 0.99994 | 0.99925 / 0.99994 |
152
- | `flash_attn`, compiled | default | 0.99818 / 0.99994 | 0.99876 / 0.99994 |
153
- | `flash_attn`, compiled | flattened | 0.99818 / 0.99994 | 0.99876 / 0.99994 |
154
-
155
- `eager` at batch 4, the others at batch 256. `torch_varlen` is a different, torch-native kernel and
156
- `eager` a different algorithm; `flash_attn` is the same kernel, and where it sits below 1 the rest
157
- of the network is running on packed tokens, so its matrix shapes and bf16 rounding differ.
158
- Compiling reassociates bf16 arithmetic, which leaves the mean unchanged and moves a few functions
159
- further. `auto`, the default with no override, kept `torch_varlen` on both cards. The flattened
160
- `eager` minimum on the 5090 Laptop is one 2,166-token function padded next to a 2,195-token one:
161
- when it flattens, sentence-transformers interleaves long and short texts, which changes who shares
162
- a padded `eager` batch (the same function alone scores 0.99994).
163
 
164
  ### Speed and memory
165
 
166
- | load | batch size | peak memory, eager | peak memory, compiled | 3090 Ti eager | 5090 Laptop eager | 3090 Ti compiled | 5090 Laptop compiled |
167
  | --- | --- | --- | --- | --- | --- | --- | --- |
168
- | pre-dispatch revision | 32 | 4.4 GiB | – | 23.8 s | 21.4 s | – | – |
169
- | default | 32 | 2.5 GiB | 1.6 GiB | 16.4 s | 14.3 s | 14.0 s | 12.2 s |
170
- | default | 256 | 10.9 GiB | 7.1 GiB | 15.1 s | 15.1 s | 12.7 s | 12.3 s |
171
- | default | 1,024 | out of memory | 16.3 GiB | out of memory | out of memory | 13.8 s | 13.4 s |
172
  | default | 2,048 | out of memory | out of memory | out of memory | out of memory | out of memory | out of memory |
173
- | flattened | 32 | 1.4 GiB | 0.9 GiB | 15.6 s | 13.7 s | 13.2 s | 11.7 s |
174
- | flattened | 256 | 6.9 GiB | 4.5 GiB | 13.7 s | 14.4 s | 11.4 s | 11.7 s |
175
- | flattened | 1,024 | 17.0 GiB | 11.1 GiB | 13.4 s | 14.7 s | 10.9 s | 11.8 s |
176
- | flattened | 2,048 | out of memory | 16.6 GiB | out of memory | out of memory | 10.7 s | 11.4 s |
177
-
178
- `torch_varlen`; `flash_attn` runs within 1% of it. Compiled means
 
 
 
179
  `model[0].auto_model.encoder = torch.compile(model[0].auto_model.encoder, dynamic=True)`.
180
 
181
- - **Memory follows the real tokens in the heaviest batch, not the number of functions.** Both
182
- loads run the encoder padding-free, and every out-of-memory cell failed allocating one 3,072-wide
183
- MLP activation over that batch's tokens. sentence-transformers length-sorts the corpus, so the
184
- default load's first batch of 1,024 holds the longest functions, 869,948 tokens. Flattened, it
185
- interleaves longest with shortest, so its heaviest batch of 1,024 holds 593,716 tokens.
186
- - **Flattening** cuts peak memory by 36–42% against the default load and wall time by up to 9%.
187
- - **Compiling** is 1.17–1.24× faster at the same batch size and cuts peak memory by about a third,
188
- because inductor fuses the element-wise work around each attention call. It compiles one graph,
189
- with no graph breaks, once per process (the first encode takes 4–10 s longer), and does not
190
- recompile as batch lengths change; the one further compile comes once a batch passes about
191
- 699,000 tokens, where inductor switches to 64-bit indexing.
192
- - **Together**, flattened and compiled at batch 2,048 encode the corpus in 10.7 s (3090 Ti) and
193
- 11.4 s (5090 Laptop), 2.2× and 1.9× the pre-dispatch revision. `eager` at batch 4 takes 34–52 s.
 
194
 
195
  ### Real documents
196
 
197
  768 Python files sampled with a fixed seed from one shard of
198
  [`HuggingFaceTB/stack-edu`](https://huggingface.co/datasets/HuggingFaceTB/stack-edu) (730,431
199
- tokens after truncation to the model's 8192 `max_seq_length`), on `torch_varlen`, uncompiled.
200
- **Sorted** is one `encode()` call over all 768, so sentence-transformers length-sorts them;
201
- **file** is one call per batch of consecutive files, so each batch mixes short and long files.
202
- Peak memory here includes the weights; **largest** is the largest batch that fit on either card.
 
203
 
204
- | load | order | batch size | peak memory | 3090 Ti | 5090 Laptop |
205
  | --- | --- | --- | --- | --- | --- |
206
- | default | sorted | 4 | 1.3 GiB | 5.61 s | 4.75 s |
207
- | default | sorted | 32 | 6.1 GiB | 4.79 s | 4.72 s |
208
- | default | sorted | 64 | 9.1 GiB | 4.80 s | 4.84 s |
209
- | default | sorted | 353 (largest) | 18.2 GiB | 5.77 s | 5.98 s |
210
- | default | file | 4 | 0.6 GiB | 6.49 s | 5.64 s |
211
- | default | file | 32 | 1.6 GiB | 6.39 s | 5.70 s |
212
- | default | file | 64 | 2.8 GiB | 6.44 s | 5.84 s |
213
- | flattened | sorted | 4 | 0.8 GiB | 5.51 s | 4.56 s |
214
- | flattened | sorted | 32 | 3.8 GiB | 4.54 s | 4.56 s |
215
- | flattened | sorted | 64 | 6.1 GiB | 4.47 s | 4.63 s |
216
- | flattened | sorted | 628 (largest) | 19.2 GiB | 4.60 s | 4.75 s |
217
- | flattened | file | 4 | 0.6 GiB | 5.83 s | 4.75 s |
218
- | flattened | file | 32 | 1.6 GiB | 4.73 s | 4.70 s |
219
- | flattened | file | 64 | 2.7 GiB | 4.53 s | 4.66 s |
220
-
221
- Flattening also makes a file's embedding independent of the other files in its batch: between
222
- the file-order and sorted runs, the minimum cosine over the 768 files is 0.9999998 under the
223
- flattened load on both cards and both varlen tiers, against as low as 0.99987 under the default
224
- load.
 
 
 
 
 
225
 
226
  ## What changed vs the source repo
227
 
@@ -233,10 +242,10 @@ load.
233
  fp32 and `load_state_dict`-ed the checkpoint into fp32 params, **ignoring `torch_dtype`**. The
234
  copy here adds the standard transformers dtype resolution (explicit arg → `config.torch_dtype` →
235
  checkpoint dtype) so the model loads in its declared dtype.
236
- 3. **Three-tier attention dispatch**: `NomicBertAttention.forward` now selects one of three
237
  attention implementations at call time — `torch_varlen` (torch's own
238
  `torch.nn.attention.varlen.varlen_attn`, no third-party kernel, needs torch ≥ 2.10.0),
239
- `flash_attn` (the original flash-attn varlen-packed kernel, kept as a fallback for older torch
240
  builds that have it installed), and `eager` (the original padded attention algorithm, numerically
241
  unchanged, and the default off CUDA sm_80+; it now converts a raw 2-D `[B, S]` mask to the
242
  additive form before adding it, for the case where a `device_map` split hands it a mask a varlen
@@ -269,8 +278,12 @@ derives from Tri Dao's BERT implementation, and `CodeRankEmbed` was trained by t
269
 
270
  ```bibtex
271
  @misc{suresh2025cornstackhighqualitycontrastivedata,
272
- title = {CoRNStack: High-Quality Contrastive Data for Text and Code Retrieval},
273
- author = {Suresh, K N Q and Wang, Xiang and Khan, Saqib and others},
274
- year = {2025},
 
 
 
 
275
  }
276
  ```
 
16
  # CodeRankEmbed-flash-attn
17
 
18
  A **bf16 quantization of [`nomic-ai/CodeRankEmbed`](https://huggingface.co/nomic-ai/CodeRankEmbed)**
19
+ with a custom `modeling_hf_nomic_bert.py`, shipped in this repo, that **picks one of three
20
+ attention implementations** for the hardware it runs on. It is not a finetune — the weights are the original CodeRankEmbed weights cast to bf16
21
  (no further training). Two of the three tiers replace the original eager `O(seq²)` attention with an
22
  `O(N)` unpadded path; the third keeps the original eager algorithm as the correctness reference and
23
  universal fallback.
 
32
 
33
  - `torch_varlen` — `torch.nn.attention.varlen.varlen_attn`, shipped in torch itself (no extra
34
  package), available from torch **2.10.0** onward.
35
+ - `flash_attn` — the `flash_attn` package's varlen kernel. Kept as a fallback for older torch
36
  builds that don't yet have `torch.nn.attention.varlen` but do have `flash_attn` installed. This
37
  package is **optional**.
38
 
 
71
  batch flattened (`[1, ΣL]` token ids with sequence boundaries, no padding at all), and the model
72
  returns one `[1, ΣL, 768]` output that sentence-transformers pools per sequence. The opt-in needs
73
  the `flash_attn` package installed, because transformers checks for it at load and raises
74
+ otherwise, even though the attention itself still runs on whichever tier the model picks. On
75
  the `eager` tier the flattened batch is re-padded and runs the padded path.
76
  - **Loads bf16 by default.** `flash_attn` and `torch_varlen` both require half precision and the
77
  model runs bf16 in any real serving setup, so the weights are stored bf16 and `config.json`
 
122
 
123
  ## Parity & performance
124
 
125
+ Every number below compares this repo with the original model,
126
+ [`nomic-ai/CodeRankEmbed`](https://huggingface.co/nomic-ai/CodeRankEmbed) (revision `3c4b608`),
127
+ run as published: fp32 weights, its own modeling file, padded attention. Both run on the same GPU,
128
+ on two cards: an RTX 3090 Ti (sm_86) and an RTX 5090 Laptop GPU (sm_120). The original's modeling
129
+ file calls `get_extended_attention_mask`, which transformers has deprecated and no longer ships
130
+ by 5.17, so it was measured on transformers 5.11; this repo loads on 5.17 too.
131
 
132
  **Corpus.** The first 16,384 `document` fields (real Python functions) of
133
  [`lightonai/cornstack`](https://huggingface.co/datasets/lightonai/cornstack)'s Python split, in
134
  shard order (`train-00000-of-00682.parquet`, then `train-00001-…`): 2,684,878 tokens, 7 to 4,746
135
  per function, 164 mean, 92 median. Each row is one `encode()` call over all of them at the stated
136
  `batch_size=`, after one untimed warm-up of the same call. Cosine similarity is per function, on
137
+ fp32-renormalized output, against the original model's embedding of the same function. Peak
138
+ memory is the encode's peak CUDA allocation above the loaded weights, the same on both cards to
139
  within 32 MiB. `torch 2.12.1+cu130`, `transformers 5.11.0`, `sentence-transformers 6.1.0`,
140
  `flash-attn 2.8.3` (`2.8.3.post1` on the 3090 Ti).
141
 
142
+ ### Agreement with the original model
143
 
144
  | tier | load | 3090 Ti min / mean | 5090 Laptop min / mean |
145
  | --- | --- | --- | --- |
146
+ | `eager` | default | 0.99902 / 0.99991 | 0.99904 / 0.99991 |
147
+ | `eager` | flattened | 0.99916 / 0.99991 | 0.99904 / 0.99991 |
148
+ | `torch_varlen` | default | 0.99896 / 0.99993 | 0.99894 / 0.99993 |
149
+ | `torch_varlen` | flattened | 0.99896 / 0.99993 | 0.99894 / 0.99993 |
150
+ | `flash_attn` | default | 0.99841 / 0.99993 | 0.99914 / 0.99993 |
151
+ | `flash_attn` | flattened | 0.99841 / 0.99993 | 0.99914 / 0.99993 |
152
+ | `torch_varlen`, compiled | default | 0.99960 / 0.99995 | 0.99936 / 0.99995 |
153
+ | `torch_varlen`, compiled | flattened | 0.99960 / 0.99995 | 0.99936 / 0.99995 |
154
+ | `flash_attn`, compiled | default | 0.99946 / 0.99995 | 0.99952 / 0.99995 |
155
+ | `flash_attn`, compiled | flattened | 0.99946 / 0.99995 | 0.99952 / 0.99995 |
156
+
157
+ `eager` at batch 4, the others at batch 256. The mean cosine is 0.99991 or higher everywhere; the
158
+ minimum is the one function, of 16,384, that moves most under bf16 weights and arithmetic.
159
+ Compiled rows sit closer to the original than uncompiled ones: inductor computes each fused run
160
+ of element-wise work in fp32 and rounds to bf16 once, instead of after every operation. `auto`,
161
+ the default with no override, picked `torch_varlen` on both cards.
 
 
 
162
 
163
  ### Speed and memory
164
 
165
+ | model / load | batch size | peak memory | peak memory, compiled | 3090 Ti | 5090 Laptop | 3090 Ti, compiled | 5090 Laptop, compiled |
166
  | --- | --- | --- | --- | --- | --- | --- | --- |
167
+ | `nomic-ai/CodeRankEmbed` (fp32) | 4 | 8.4 GiB | – | 68.9 s | 77.8 s | – | – |
168
+ | default | 32 | 2.5 GiB | 1.6 GiB | 16.3 s | 14.6 s | 14.0 s | 12.4 s |
169
+ | default | 256 | 10.9 GiB | 7.1 GiB | 15.1 s | 15.4 s | 12.8 s | 12.6 s |
170
+ | default | 1,024 | out of memory | 16.3 GiB | out of memory | out of memory | 13.8 s | 13.8 s |
171
  | default | 2,048 | out of memory | out of memory | out of memory | out of memory | out of memory | out of memory |
172
+ | flattened | 32 | 1.4 GiB | 0.9 GiB | 15.5 s | 13.8 s | 13.2 s | 12.5 s |
173
+ | flattened | 256 | 6.9 GiB | 4.5 GiB | 13.6 s | 14.5 s | 11.4 s | 12.2 s |
174
+ | flattened | 1,024 | 17.0 GiB | 11.1 GiB | 13.4 s | 14.9 s | 10.9 s | 12.4 s |
175
+ | flattened | 2,048 | out of memory | 16.6 GiB | out of memory | out of memory | 10.8 s | 12.8 s |
176
+
177
+ This repo's rows are `torch_varlen`; `flash_attn` runs within 1% of it on the 3090 Ti and within
178
+ 6% on the 5090 Laptop. The original runs at batch 4: its padded fp32 attention needs
179
+ `batch × heads × seq²` memory, 8.4 GiB at batch 4 for this corpus's longest function. Compiled
180
+ means
181
  `model[0].auto_model.encoder = torch.compile(model[0].auto_model.encoder, dynamic=True)`.
182
 
183
+ - **Against the original**, the same 16,384 functions encode 5.1–5.6× faster uncompiled and
184
+ 6.4× faster compiled (10.8 s on the 3090 Ti, 12.2 s on the 5090 Laptop, against 68.9 s and
185
+ 77.8 s), in a third of the memory at batch 32.
186
+ - **Memory follows the real tokens in the heaviest batch, not the number of functions.** This
187
+ repo runs the encoder on real tokens only, and every out-of-memory cell failed allocating one
188
+ 3,072-wide MLP activation over that batch's tokens. sentence-transformers length-sorts the
189
+ corpus, so the default load's first batch of 1,024 holds the longest functions, 869,948 tokens;
190
+ flattened, it interleaves longest with shortest, so its heaviest batch of 1,024 holds 593,716.
191
+ - **Flattening** cuts peak memory by 36–42% against the default load and wall time by 5–10%.
192
+ - **Compiling** is 1.10–1.23× faster at the same batch size and cuts peak memory by about a
193
+ third. It compiles one graph, with no graph breaks, once per process (the first encode takes
194
+ about 4–5 s longer), and does not recompile as batch lengths change; the only further compiles
195
+ come once a batch passes about 699,000 and 932,000 tokens, where inductor switches to 64-bit
196
+ indexing.
197
 
198
  ### Real documents
199
 
200
  768 Python files sampled with a fixed seed from one shard of
201
  [`HuggingFaceTB/stack-edu`](https://huggingface.co/datasets/HuggingFaceTB/stack-edu) (730,431
202
+ tokens after truncation to 8,192 tokens), this repo on `torch_varlen`, uncompiled. **Sorted** is
203
+ one `encode()` call over all 768, so sentence-transformers length-sorts them; **file** is one call
204
+ per batch of consecutive files, so each batch mixes short and long files. The original runs at
205
+ batch 1 and 2, the most its padded fp32 attention fits at 8,192 tokens. Peak memory here includes
206
+ the weights; **largest** is the largest batch that fit on either card.
207
 
208
+ | model / load | order | batch size | peak memory | 3090 Ti | 5090 Laptop |
209
  | --- | --- | --- | --- | --- | --- |
210
+ | `nomic-ai/CodeRankEmbed` (fp32) | sorted | 1 | 6.8 GiB | 26.08 s | 33.05 s |
211
+ | `nomic-ai/CodeRankEmbed` (fp32) | sorted | 2 | 12.9 GiB | 27.13 s | 37.84 s |
212
+ | `nomic-ai/CodeRankEmbed` (fp32) | file | 1 | 6.7 GiB | 26.79 s | 34.54 s |
213
+ | `nomic-ai/CodeRankEmbed` (fp32) | file | 2 | 12.9 GiB | 43.24 s | 58.98 s |
214
+ | default | sorted | 4 | 1.3 GiB | 5.54 s | 5.46 s |
215
+ | default | sorted | 32 | 6.1 GiB | 4.78 s | 5.17 s |
216
+ | default | sorted | 64 | 9.1 GiB | 4.79 s | 5.25 s |
217
+ | default | sorted | 353 (largest) | 18.2 GiB | 5.73 s | 5.83 s |
218
+ | default | file | 4 | 0.6 GiB | 6.40 s | 5.80 s |
219
+ | default | file | 32 | 1.6 GiB | 6.34 s | 6.03 s |
220
+ | default | file | 64 | 2.8 GiB | 6.41 s | 6.16 s |
221
+ | flattened | sorted | 4 | 0.8 GiB | 5.47 s | 5.10 s |
222
+ | flattened | sorted | 32 | 3.8 GiB | 4.54 s | 4.97 s |
223
+ | flattened | sorted | 64 | 6.1 GiB | 4.48 s | 5.01 s |
224
+ | flattened | sorted | 628 (largest) | 19.2 GiB | 4.59 s | 5.25 s |
225
+ | flattened | file | 4 | 0.6 GiB | 5.77 s | 5.37 s |
226
+ | flattened | file | 32 | 1.6 GiB | 4.72 s | 4.97 s |
227
+ | flattened | file | 64 | 2.7 GiB | 4.51 s | 5.04 s |
228
+
229
+ Against the original, the 768 files agree at a mean cosine of 0.99991 and a minimum of 0.99881 to
230
+ 0.99941, depending on card and tier. This repo is 5.8× (3090 Ti) and 6.7× (5090 Laptop) faster
231
+ than the original's best time. Flattening also makes a file's embedding independent of the other
232
+ files in its batch: between the file-order and sorted runs, the minimum cosine over the 768 files
233
+ is 1.0000 under the flattened load, against 0.99987 under the default load.
234
 
235
  ## What changed vs the source repo
236
 
 
242
  fp32 and `load_state_dict`-ed the checkpoint into fp32 params, **ignoring `torch_dtype`**. The
243
  copy here adds the standard transformers dtype resolution (explicit arg → `config.torch_dtype` →
244
  checkpoint dtype) so the model loads in its declared dtype.
245
+ 3. **Three attention tiers**: `NomicBertAttention.forward` now selects one of three
246
  attention implementations at call time — `torch_varlen` (torch's own
247
  `torch.nn.attention.varlen.varlen_attn`, no third-party kernel, needs torch ≥ 2.10.0),
248
+ `flash_attn` (the `flash_attn` package's varlen kernel, kept as a fallback for older torch
249
  builds that have it installed), and `eager` (the original padded attention algorithm, numerically
250
  unchanged, and the default off CUDA sm_80+; it now converts a raw 2-D `[B, S]` mask to the
251
  additive form before adding it, for the case where a `device_map` split hands it a mask a varlen
 
278
 
279
  ```bibtex
280
  @misc{suresh2025cornstackhighqualitycontrastivedata,
281
+ title={CoRNStack: High-Quality Contrastive Data for Better Code Retrieval and Reranking},
282
+ author={Tarun Suresh and Revanth Gangi Reddy and Yifei Xu and Zach Nussbaum and Andriy Mulyar and Brandon Duderstadt and Heng Ji},
283
+ year={2025},
284
+ eprint={2412.01007},
285
+ archivePrefix={arXiv},
286
+ primaryClass={cs.CL},
287
+ url={https://arxiv.org/abs/2412.01007},
288
  }
289
  ```