bsbr
Block Sparse attention with Block Retrieval: ordinary attention inside fixed-size chunks, and a meta-attention that retrieves between them from one compressed state vector per chunk — so the between-chunk term grows with n/B instead of n. A pip-installable PyTorch implementation, benchmarked against six other architectures.
BSBR — Block Sparse attention with Block Retrieval — is an attention mechanism for
long sequences, and a PyTorch implementation of it you can pip install. It keeps
ordinary attention inside fixed-size chunks, and between chunks it retrieves from one
compressed state vector per chunk, so the between-chunk term grows with the number of
chunks rather than the number of tokens.
It went out as a thread on 29 March 2025, which is further down in full. The rest of this page is what the thread says, at more length, plus the benchmark numbers — including the ones that did not go the way they were supposed to.
Problem
Standard attention computes a score between every pair of tokens in the sequence:
is . That is quadratic in the sequence length in both compute and memory, and it is the reason long context is expensive rather than merely long. Doubling the context quadruples the attention matrix.
The field's answers mostly buy the complexity down by giving something up. Linear attention reassociates the matrix product to reach and loses expressiveness on the way. Sliding-window attention restricts each token to a local neighbourhood, , and simply cannot see past the window. Sparse patterns trade capacity for whichever sparsity structure you picked. The question BSBR asks is whether the local and the long-range parts have to be the same mechanism at all.
Solution
Break the sequence into chunks of block size . Then run two different things:
- Within a chunk, ordinary softmax attention. Local context is the thing standard attention is actually good at, and inside a chunk it costs — which is a constant once is fixed.
- Between chunks, retrieval. Compress each chunk into a state vector (a flattened for that chunk), then attend at the level of chunks with meta queries and meta keys. There are chunks, so this term grows with .
Both halves land in one expression:
The right-hand term is within-chunk attention under a within-chunk causal mask
. The left-hand term is the retrieval: and are the meta queries and
keys that operate at chunk granularity, is the causal mask over chunks,
holds the chunk states, and .repeat(B) broadcasts the retrieved chunk-level result
back down over the tokens of the chunk it belongs to. is elementwise.
The shape of the claim matters more than the constant: local attention stays exact, and the part that used to be quadratic becomes a retrieval over a summary whose length is the sequence divided by the block size.
How
The package is on PyPI in three flavours — the core, the research extras, and the converters:
pip install bsbr
pip install "bsbr[extras]" # evaluation tools, visualisation
pip install "bsbr[transformers]" # pretrained-model conversion
A model is an ordinary nn.Module:
import torch
from bsbr import BSBRModel
model = BSBRModel(
vocab_size=10000,
hidden_dim=512,
num_layers=4,
num_heads=8,
chunk_size=128,
ff_dim=2048,
dropout=0.1,
)
input_ids = torch.randint(0, 10000, (2, 256))
attention_mask = torch.ones(2, 256)
outputs = model(input_ids, attention_mask)
The block size is the dial
— chunk_size — is the hyperparameter the whole design turns on, and it trades three
things against each other at once. Larger chunks hold more local context and cost more
memory; smaller chunks are faster but put more of the sequence's relationships on the far
side of a retrieval; and where the boundary falls decides which relationships the model
can see exactly and which it sees through a summary. It is accepted by BSBRModel,
BSBRAttention and BSBRLayer alike.
Two more knobs
compression_factor sets how hard the chunk states are squeezed. Compressing further
costs less memory and less time and keeps less of the fine structure of the chunk — the
usual bargain, made explicit:
model = BSBRModel(
chunk_size=128,
compression_factor=4, # compress chunk states by 4x
...
)
chunk_overlap lets neighbouring chunks share tokens, which softens the attention
discontinuity at a block boundary — the artefact you would expect from cutting a sequence
into pieces and treating the cut as a wall:
model = BSBRModel(
chunk_size=128,
chunk_overlap=32, # 25% overlap between chunks
...
)
Beyond the attention itself the implementation reuses state across layers and specialises the attention masks for the autoregressive case. Streaming-first work — the case where a model is producing tokens for hours and the context never stops growing — is the direction it was pointed at, and is not done.
Converting a pretrained model
A new attention mechanism with no pretrained weights is a paper, not a tool. There are no
BSBR checkpoints; what there is instead is bsbr_transformers, which rebuilds a
Hugging Face model with BSBR attention in place of its own:
from transformers import GPT2LMHeadModel, AutoTokenizer
from bsbr_transformers.gpt2_converter import convert_to_bsbr
tokenizer = AutoTokenizer.from_pretrained(model_name)
original_model = GPT2LMHeadModel.from_pretrained(model_name).to(device)
bsbr_model = convert_to_bsbr(model_name, chunk_size=chunk_size).to(device)
GPT-2 is the converter that exists. Others were planned.
Tests
The comparison is seven architectures at matched hyperparameters — hidden dim 256, 4 heads, 2 layers where applicable — run at sequence lengths 64, 128, 256, 512 and 1024, on an Intel Core i9 CPU, against bsbr 0.1.2: BSBR, a standard transformer, linear attention, DeltaNet, a sliding-window transformer, a Hopfield network and a Gated Attention Unit. Empirical complexity is a power-law fit to the measured inference times rather than an asymptotic claim.
A separate evaluation converts a pretrained GPT-2 to BSBR and compares the converted model against the original on scaling, output similarity and next-token agreement.
The docs' training, task-specific and hardware-utilisation sections are marked as placeholder data, and nothing from them is quoted here.
Results
Inference time in seconds, CPU:
| Model | n=64 | n=128 | n=256 | n=512 | n=1024 | fit |
|---|---|---|---|---|---|---|
| BSBR | 0.462 | 0.560 | 0.753 | 1.570 | 3.092 | |
| Standard | 0.254 | 0.334 | 0.453 | 0.908 | 2.538 | |
| Hopfield | 0.255 | 0.365 | 0.478 | 0.937 | 2.568 | |
| SlidingWindow | 0.514 | 0.748 | 1.289 | 2.442 | 5.568 | |
| Linear | 1.570 | 2.742 | 4.896 | 8.879 | 17.322 | |
| GAU | 0.488 | 0.880 | 1.950 | 5.381 | 17.649 | |
| DeltaNet | 8.085 | 13.31 | 23.71 | 46.166 | 92.276 |
Against the other efficient-attention baselines this is a rout: BSBR is 5.6× faster than linear attention at n=1024, 5.7× faster than GAU, 30× faster than DeltaNet, and 1.8× faster than sliding-window.
Against the thing it is supposed to replace, it is not. The standard transformer is faster at every length measured, and BSBR carries 6.0M parameters against the standard's 3.6M (1.66×) and 22.83 MB of peak memory against 13.80 MB. The one number pointing the right way is the exponent — BSBR's 0.70 against standard's 0.81 — and n ≤ 1024 on a CPU is nowhere near long enough for an asymptotic advantage to pay off the constant. The benchmark page says so itself rather than rounding the fit up into a headline.
The conversion evaluation went worse, and is worth stating plainly. Scaling came out backwards: the original transformer at against the converted BSBR at , which the write-up flags as contrary to expectations. And the converted model does not behave like the model it came from — negative cosine similarity between their outputs and 0% agreement on next-token predictions. Transplanting attention into trained weights is not a weight-shape problem; those weights learned against a particular attention, and swapping it out is a change the weights have not seen.
So the honest summary is the one the docs give: BSBR conversion suits very long context where approximate outputs are acceptable, and the crossover against a standard transformer was not demonstrated at the lengths measured.
Lessons
A complexity class is a promise about the limit, not about your sequence length. beats eventually, and "eventually" is a real quantity with a real value, and on a CPU at n=1024 that value had not arrived. The block retrieval adds parameters (1.66×), memory (1.65×) and per-chunk bookkeeping up front, and the asymptotics have to earn all of it back before the first token of advantage. Benchmarking only against the other efficient-attention mechanisms would have looked like a win; benchmarking against the plain transformer is what made the picture true.
Converted weights are not ported weights. 0% next-token agreement is not a bug in the converter — it is the measurement telling you that a pretrained model's weights encode assumptions about the attention they were trained under. The converter is still the right tool to have built, because it is the only way to ask the question at all; the mistake would have been shipping it without the evaluation that says what it actually produces.
Publishing the negative result is the cheapest thing on this page and the most useful. The benchmark tables, the backwards scaling and the 0% agreement all live in the docs site, next to the claim they complicate. Anyone deciding whether to spend a week on BSBR can find out in five minutes that the answer today is "only if your sequences are very long and approximate is acceptable" — which is worth more than a page of favourable graphs would have been.
The thread
The announcement, in full.

Block Sparse Attention WIth Block Retrieval!
docs: jacobfv.github.io/bsbr repo: github.com/JacobFV/bsbr


Why block sparse? The standard transformer attention mechanism computes attention scores between all pairs of tokens in a sequence. which leads to O(n²) complexity in both computation and memory


BSBR addresses this scalability issue by breaking the sequence into chucks of block size B and then combining standard attention within fixed-size chunks and a meta-attention mechanism to efficiently retrieve information between chunks. It works by compressing each chunk’s…


You can take advantage of these insights using the bsbr python library. Just pip install bsbr!


The chunk size (B) is a crucial hyperparameter that affects: - Memory Usage: Larger chunks use more memory but provide better local context - Computation Time: Smaller chunks are faster but may miss important long-range dependencies - Model Expressivity: Chunk size affects how…


You can control the compression factor which determines how much information is preserved in chunk states. Higher compression factors reduce memory usage and speed up computation but may lose fine-grained information:


BSBR also supports overlap between chunks which may help mitigate attention discontinuities at the block boundaries:


And besides sparse attention, the implementation also supports state reuse across layers, optimized attention masks for autoregression, and I plan on implementing more streaming-first optimizations when I get time. Imagine building agentic software that weaves threads of tokens…

Finally, how useful is a new architecture without pretrained models? Well sorry I don’t have any yet lol but the bsbr_transformers provides tools to convert pretrained huggingface transformers into bsbr ones. Just make sure to pip install bsbr[transformers] and then from…


Finishing off with an end to end example. I hope you find bsbr useful! Please also give OG’s poast a read:

The work it starts from is Shengding Hu's streaming models for efficient long-context reasoning, which is the post the thread ends by pointing at.