Context. FlashAttention made exact attention fast by fusing the two matmuls and the softmax into one tiled kernel that never writes the score matrix to memory, but each such kernel supports a short list of variants, and a new mask (packed documents, a sliding window, a prefix) meant a handwritten kernel or a dense boolean mask whose memory traffic grows as . FlexAttention, from the PyTorch team, observes that almost every variant is a pointwise edit of the scores before the softmax, so the user writes the edit as a few lines of Python and the compiler inlines it into a handwritten fused template. A mask also tells the kernel which scores need not be computed. Summarized at the level of 128 by 128 tiles, it lets the kernel skip every fully masked tile, so attention costs what the visible tiles cost, whatever the sequence length. That is what let the speedrun train on 64K-token packed sequences in which each token sees only its own document.
score_mod(score, b, h, q_idx, kv_idx), which returns a new score, and mask_mod(b, h, q_idx, kv_idx), which says whether the score is kept. Softcapping is c * tanh(score / c), and causal, sliding-window and document masks are boolean expressions in the indices that compose by elementwise and/or. A mask is formally a score modification that adds ; the two are kept apart because a score_mod costs work on every score, while a mask_mod tells the compiler which scores need not be computed at all. The compiler inlines both into handwritten Triton templates that already hold the tiling and the online softmax, so speed tracks the handwritten kernels, and only pointwise variants are covered. The speedrun's mask in record #12 is three clauses (EOT is the end-of-text token that starts each document in the packed stream):docs = torch.cumsum(tokens == EOT, dim=0) # document id of every position
def mask_mod(b, h, q_idx, kv_idx):
causal = q_idx >= kv_idx
same_doc = docs[q_idx] == docs[kv_idx]
in_window = q_idx - kv_idx < window # 1024 in record #12
return causal & same_doc & in_window
block_mask = create_block_mask(mask_mod, None, None, T, T)
y = flex_attention(q, k, v, block_mask=block_mask)A tile-level summary of the mask lets the kernel skip fully masked tiles. create_block_mask evaluates mask_mod over the index grid and stores, for each row of query tiles, how many tiles are not fully masked and their column indices. The kernel loops over that list only, so
and the summary takes small integers. For the speedrun's shape the saving is large (my count): in one 65,536-token sequence causal attention touches 131,328 tiles, a causal 1024-token window touches 4,572 (3.5% of them), and causal attention inside independent 1024-token chunks touches 2,304, and document boundaries remove more. The same rule limits the gain: a mask whose visible scores are scattered over many tiles gains little, and a tile with one visible score costs a full tile. In the authors' benchmarks FlexAttention runs at 0.68 to 1.43 times FlashAttention-2 where that kernel supports the variant (the backward pass down to 0.86 times), and 5 to 8 times faster than PyTorch SDPA with a materialized mask, which must read a boolean mask; on packed sequences SDPA's throughput drops 25% from 2K to 8K tokens, while FlexAttention, needing only a document-id vector and the tile lists, keeps its throughput.
Record #12: document-masked packed sequences reduced the tokens needed by 37.5%. Record #11 trained on 1024-token chunks taken from the token stream at arbitrary points, so attention crossed document boundaries and a token near the start of a chunk had almost no context. Record #12 (7.2 to 5.03 minutes, 30% less time) keeps the same 524,288 tokens per step but gives each of the eight GPUs one 65,536-token sequence with the mask above. The step got slower, 145 to 162 ms, and the run needed 1875 steps instead of 3000, 37.5% fewer tokens to reach 3.28. Two things changed together, and the record does not separate them: no attention goes into unrelated documents, and every token sees up to 1024 tokens of its own document wherever it falls. The tile lists kept the cost low: about twice the attention work of 1024-token chunks, where dense causal attention would cost 57 times as much. The comparison also spans a PyTorch upgrade (2.5.1 to a 2.6 nightly), and validation moved to the same document-masked 64K format, which the rules allow but which gives validation tokens more context, so part of the gain may come from easier evaluation.
Record #13: growing the window over training saved 125 steps. Because mask_mod reads the window from a tensor, it can change without recompiling. Record #13 grows it linearly from 64 to 1792 tokens in steps of 64, which reduced the steps from 1875 to 1750 and the time from 5.03 to 4.66 minutes. The step time barely moved, so the gain is again loss per step, and the record does not separate a short window early from a longer one (1792) at the end. The same record also retuned the optimizer (Adam , the Muon learning rate, the cooldown and the momentum warmup) and validated with the final 1792-token window, so the schedule's own share is not isolated. The only source is a tweet; the closest paper is the sequence-length warmup of Li et al. (2021).
Consequences. FlexAttention makes the cost of a masked attention follow the tiles the mask touches, and it makes trying a new mask a few lines of Python. Its gain in the speedrun came from better loss per token: exact document masking gave each token clean, longer context, which reduced the tokens needed by more than a third. Later records gave a few layers long windows and the rest short ones (#20), and #29 replaced FlexAttention with FlashAttention-3 on variable-length packed documents capped at 2048 tokens (4.3 s with the cap), since by then the mask was causal, per-document and windowed, which that kernel's variable-length mode supports directly.
Terms
- score_mod
- pointwise edit of each attention score, given its batch, head, query and key indices
- mask_mod
- boolean keep/drop for each score; also tells the kernel which tiles can be skipped
- BlockMask
- for each row of 128-token query tiles, the count and indices of tiles not fully masked
- work of a masked attention
- proportional to the number of tiles with at least one visible score
- document mask
- docs[q_idx] == docs[kv_idx], with docs the cumulative count of document-start tokens
- tile count, 64K tokens
- dense causal 131,328; causal window 1024: 4,572; causal 1024-token chunks: 2,304
- record #12
- 64K-token packed sequences, document + causal + 1024 window; 3000 to 1875 steps, 7.2 to 5.03 min
- record #13
- window grown linearly from 64 to 1792 tokens, with optimizer retuning; 1875 to 1750 steps, 5.03 to 4.66 min
Quiz
- Why does SDPA with a materialized document mask lose throughput as packed sequence length grows, while FlexAttention does not?The materialized mask is and must be loaded. FlexAttention computes the mask from a length- document-id vector inside the kernel and skips masked tiles using an index list.
- Suppose each query attends to 64 keys chosen at random from a 64K-token sequence. How much would the BlockMask help?Work follows tiles with at least one visible score. Scattering 64 keys per query over 512 key tiles, across 128 queries per tile row, leaves almost no tile empty, so nearly all tiles are still computed.
- Why does a causal 1024-token window on a 64K packed sequence cost about twice the attention work of causal attention inside independent 1024-token chunks?Inside a chunk a token at position sees only keys, so the average is 512; in the window every token sees up to 1024. The tile counts are 4,572 against 2,304.
- Record #12 also switched validation to 64K document-masked sequences. Why does that matter when reading its 37.5% reduction in steps?Under fixed 1024-token chunks many validation tokens had little context; under document masking with a 1024 window each sees up to 1024 tokens of its own document. The record does not separate this from faster learning.
Sources
- Juechu Dong, Boyuan Feng, Driss Guessous, Yanbo Liang, and Horace He. Flex Attention: A Programming Model for Generating Optimized Attention Kernels. MLSys, 2025.
- Conglong Li, Minjia Zhang, and Yuxiong He. The Stability-Efficiency Dilemma: Investigating Sequence Length Warmup for Training GPT Models. NeurIPS, 2022.
- Keller Jordan et al. modded-nanogpt. GitHub repository, 2024.