Learning FlashAttention the Hard Way — Part 1

When looking for FlashAttention tutorials online, I was frustrated that existing ones are way too simple! Where's the fun in that? Would you enjoy breezing through a Dark Souls boss on the first attempt? I am here to fix this critical gap and present the Dark Souls of FlashAttention tutorials. Enjoy countless detours into abstract algebra, precision analysis, and other esoteric math exercises.
We've established that you love pain, but is there a point to all these derivations? If you need an extra nudge: the goal is to show that FlashAttention is not a one-of-a-kind deep optimization, but a single example from a much larger class. That matters most if you're interested in how ML compilers work, which must optimize countless models without special-casing.
This is Part 1/2. The theoretical foundation: the algebra that turns attention into an associative reduction, and why that single fact is the only thing its GPU implementation needs. Part 2 (forthcoming) takes this down to the metal and focuses on generating efficient kernels.
Associativity is All You Need
Start with the most boring operation in machine learning: summing a vector.
It is trivially parallelizable, thanks to the associativity of +:
Because the bracketing doesn't matter, you can chop the elements into contiguous chunks, sum each chunk on its own (a thread, a warp, a whole thread block), and then add up the partials:
- Blocking cuts the axis into tiles and sums the tile totals.
- Cooperative reduction gives each thread a strided slice and combines the per-thread partials with a warp shuffle or a shared-memory tree.
- Split-K lets separate thread blocks own chunks, write their partial sums to scratch, and fold them in a small second kernel.
A GPU needs one more thing: a way to deal with the ragged edge. is almost never a clean multiple of the tile width, and at runtime you might not even know it until the kernel launches. A neutral element lets you mask the overhang: pad the tail with , and one kernel shape stays correct at every length.
Two requirements, then: the operation has to be associative (so chunks split and recombine), and it has to have a neutral element (so tails and masks come for free). An operator with both is a monoid. + with is one; so are max with , min with , boolean and/or, and matrix-multiply. A compiler that can lower one monoid reduction can therefore lower all of them: only the combine operator changes, never the schedule.
Formally, a monoid is a set together with a binary operation and a distinguished element satisfying three axioms:
Softmax Looks Like It Breaks the Rules
Here is the operation we actually want, the softmax of a length- score vector :
On paper this is a clean reduction: compute the denominator (a +-monoid over the values ), then divide. Floating point spoils the fun. Scores in a transformer routinely climb into the tens, and easily overflows float32. So every real implementation reaches for the safe softmax, which subtracts the maximum before exponentiating:
Subtracting is free (multiply top and bottom by ), but it rescues the range: every exponent , so and nothing overflows. The denominator is now .
The catch: depends on the whole vector, and depends on . Done naively, that's three passes over . Each pass introduces launch overhead and pays the price of moving elements to and from DRAM/HBM:
- Max-reduce to find .
- Sum to find .
- Divide each by .
Online Softmax Reparametrization
The escape is online softmax (Milakov and Gimelshein, 2018). Carry a small state , where is the running maximum and is the running denominator expressed relative to that maximum:
A single score enters this carrier as , since . Now, how do we merge two states and that summarize two disjoint chunks? The merged maximum is obvious; the merged denominator has to rescale each side so both are expressed relative to the new common maximum:
That's the associative online-softmax combine , with identity . Same reduction machinery as the boring vector sum; the only thing that changed is the combine.
Why the Online-Softmax Combine Associates
The check is short, and it's the same one we'll reuse for attention. Take three states
and merge left-first, writing :
Now merge that with , writing :
The intermediate maximum cancelled clean: . What's left is fully symmetric in , , , and bracketing the other way, , lands on the very same expression. So is associative, and since max and + are commutative too, under is a commutative monoid.
The Same Move: Welford's Variance
If the online-softmax carrier still feels like a one-off hack, here's a completely different problem with the same shape: computing the variance of a stream. The naive formula is a single pass (accumulate the sum of squares in one loop, then subtract), but it bleeds precision catastrophically. It subtracts two large, nearly equal quantities and throws away most of the significant digits whenever the variance is small next to the mean. The stable two-pass formula needs the mean in hand before it can accumulate squared deviations.
Welford's algorithm (1962), in its parallel form due to Chan, Golub, and LeVeque, enriches the carrier to a triple : the count, the running mean, and the running sum of squared deviations:
A single sample injects as , and the streaming update that folds one more sample into the running triple is the classic stable recurrence, which accumulates the deviation around the mean as it moves:
Two partials over disjoint chunks and merge symmetrically. This is the associative combine , with identity :
Once the reduction finishes, the variance is just a division on the final triple. Divide by for the population variance, or by for the sample variance (the is Bessel's correction, which removes the bias you get from measuring spread around the estimated mean instead of the true one):
Why Welford's Combine Associates
This section is a side quest. Welford Variance is just another example of a secretly associative operation provided to help build the intuition for recognizing such operations. We're not going to use Welford's Variance in the rest of the article.
Take three disjoint index sets , , . Each carries its own partial triple. Partial for :
Partial for , are defined similarly. The count is a plain addition and the mean is a weighted average, i.e. distributive, so both compose regardless of bracketing. Only needs work.
First, let's prove a quick fact that weighted deviations sum to zero:
Now, let's take a look at a weighted sum of squared deviations around an arbitrary reference :
Specialize to a single group ( or ). Use unit weights (, so the total weight is the count ). The value-mean will become . Set . The leftover sum is by definition:
Now is the deviation of around . Split it into two sums, one over , another over , and apply to each:
This is the Welford merge in deviation form — the same quantity as the form from earlier, with the spread now taken around the merged mean.
Now do the same one level up: split into a sum over and a sum over , and apply to each around the final mean — it holds for any reference, being at unit weights. Then substitute from :
Note that the expression in the brackets is just read right to left, with weights , values (their weighted mean is ), and reference .
Thus, finally we get:
symmetric in . The other bracketing is derived similarly and yields the same result. So is associative (and commutative), with identity — a commutative monoid.
FlashAttention: Not So Special
The same special move defeats attention. Let's start from the form everyone has seen. Stack the queries, keys, and values into matrices ( tokens, head dimension ); attention is three operations:
where are the score and probability matrices, the softmax runs row-wise, and is the output. The troublemaker is : an matrix, and the whole point of FlashAttention is to never write it to memory.
What makes this tractable: every output row is independent. Row of depends only on row of , plus all of and . So fix one query row and watch its output row get built up from each key row and value row :
This is exactly the matrix attention above, read one row at a time: is entry of , the softmax weights are row of , and is row of . The per-row view is what lets a scalar carrier suffice. From one row's vantage, the matmul is a dot product against each key, and the matmul is a weighted sum of value rows, so the entire row reduces over the key/value pairs. That reduction is the object we will fold.
The output is a softmax-weighted average of the value vectors. The denominator and the maximum are exactly the online-softmax statistics from before; the only new ingredient is the weighted sum of values in the numerator, so we carry one additional running accumulator.
The State
Over a block of key/value rows (one KV tile of and , equivalently a block of columns of this row of ), FlashAttention carries a triple of running statistics, the state :
Here is the unnormalized row of the output . A single key/value pair enters as the state , and the attention row is read off only at the very end as : a single division, deferred to the last moment.
The Merge, and the Twist
The combine takes two blocks' states and and stitches them into one. To stay numerically safe it keeps whichever max is larger and shrinks the other side toward it:
Both branches are the one symmetric rule , , . The case split just makes the exponent (always , so ) explicit, the way a kernel writes it to avoid overflow.
This is what makes the carrier a twisted monoid rather than a plain one. In a direct product the coordinates never interact; you add componentwise, . Here the first coordinate reaches into the others: before and may add, they are rescaled by the shift , and that shift is dictated entirely by the maxes. One coordinate acting on the addition of the rest is the "twist."
Why Attention's Combine Still Associates
The new ingredient over plain softmax is , and it couples to : how much the running output is rescaled depends on the running max, which is itself being reduced. That coupling is worth proving harmless, not assuming so.
By hand. Track the channel through both bracketings of three states (the channel is the softmax proof verbatim, and is plain max). With and :
The intermediate max cancels out (), leaving an expression symmetric in , and the other bracketing reaches the identical line. So is associative, with identity : a commutative monoid.
Why it had to cancel? That cancellation is not a lucky accident of the exponential; it is what happens whenever the carrier is secretly a plain sum. Undo the relative-to-max stabilization and write each running quantity in absolute form, with weights :
In these coordinates the merge is componentwise addition (, , ). Three independent monoids side by side, associative because + and max are. The pair is exactly what's needed so that the weighted average
falls out of a single associative reduction. FlashAttention's associativity is therefore not a fact about attention at all; it is the associativity of + in this direct-product monoid, read through the numerically-stable change of coordinates the Twisted Monoid subsection makes precise.
Where did this framing come from? There's no single origin to point at. The recurrence showed up first as an algorithm, not an algebra, buried in an online-softmax paper (Milakov & Gimelshein, 2018), then carried into attention by Rabe & Staats (2021) and FlashAttention (Dao et al., 2022), each one arguing that it tiles without ever uttering the word monoid. That the block-merge is commutative and associative got noticed later, by systems people who needed exactly that to split a long context across blocks and devices: split-KV / Flash-Decoding and FlashInfer's cascade inference (2024) say it out loud. The explicit monoid-with-a-proof, run as a parallel prefix scan, is recent: ELSA (Hsu et al., 2026) leans on this very framework to show that FlashAttention associates.
Algebraic Generalization
We've looked at three examples (stable softmax, Welford's variance, and FlashAttention) and claimed they all belong to the same class of "secretly associative" operations. This section makes that precise: it introduces the twisted monoid, a transformation of a monoid that retains its properties; analyzes numerical stability; and gives a way to test whether an operation is secretly associative at all. Welcome to Anor Londo.
Twisted Monoid
That the attention carrier stays a monoid under the max-rescale is not special to attention. It is a standard piece of universal algebra called transport of structure (for a careful modern account see Holm, A Note on Transport of Algebraic Structures, Theory and Applications of Categories, 2015):
Transport of structure. Let be a monoid and any bijection. Then with
is again a monoid, and is an isomorphism: associativity and the identity law pass straight through the bijection.
Proof. Because is a bijection, for every , and writing any element of as is unambiguous. Applying to the definition gives the one identity the proof runs on:
So each axiom for is an equation that turns into the same equation for , which already holds in . Put , , :
- Closure. , so , and sends it back into ; hence .
- Associativity. Since , we get , and likewise . These are equal because in .
- Identity. With , , and symmetrically .
Finally, holds directly from the definition, so is a homomorphism; being a bijection, it is an isomorphism.
Looking at the attention example:
Before the three factors never touch: max, +, + run side by side. After they look coupled, because rescaling by ties and to ; that apparent coupling is the entire twist, and the associativity we checked by hand is nothing but the associativity of the direct product seen through .
Which may we use? Any bijection: injective so no two inputs collide, surjective so every target is hit, hence invertible. Affine maps ( with ) and qualify; squaring and sine do not (both fail injectivity). Since machine learning mostly rearranges affine maps and exponentials (both bijective), no matter how the reductions are twisted, they stay associative.
Welford, the Easy Way
Earlier we ground out Welford's associativity by hand. With transport of structure, that whole argument shrinks to one observation: is a twist of three plain running sums.
The base is three independent sum-monoids: merging two chunks is componentwise addition , associative because + is, with identity . The map is a bijection on the reachable states (invert it by , ); at the identity — , where is undefined — adjoin formally to both sides with mapping identity to identity, the same footnote as attention's fiber. Using the transport of structure:
so it inherits the monoid laws of .
Where attention's twist was a rescale by , Welford's is the nonlinear , .
Numerical Analysis of the FlashAttention Kernel
Time to throw a handful of dirt into our bucket of honey. Associativity is preserved under a wide range of transformations, but it says nothing about the detail that actually bites on a GPU: numerical stability, which has to be checked separately.
Recall the FlashAttention carrier state:
We need to check two things: no overflow, and no catastrophic cancellation (severe loss of precision from rounding).
Range (overflow analysis). Every exponent satisfies , so and no term can overflow. The argmax term is exactly , which pins . Since , the final divide cannot overflow.
Accuracy (perturbation analysis). Floating-point addition rounds, so the computed denominator is not but the exact sum of slightly perturbed weights, each nudged by a tiny relative error :
where is the unit roundoff: the largest relative error of a single rounding, e.g., for bf16 (Higham, 2002). (We track the additions only; each weight's own rounding contributes one more per term)
Let's derive the upper bound for the error, keeping in mind that every weight :
Dividing by gives the relative error outright:
The derivation for and the final term is analogous.
That worst case assumes every rounding conspires in one direction; in practice they scatter, and the error behaves like (a heuristic, not a guaranteed bound). Tiling changes nothing: each merge rescales the value by before adding, never amplifying, so streaming over KV tiles is at least as accurate as one batch sum — the bound scales with how many additions each term participates in, and merging in tiles only reduces that count.
Quantization is a special case worth mentioning. Rounding is many-to-one, hence not invertible, so transport of structure does not apply: we cannot use the theorem to claim quantized layers stay associative. The de-quantization, on the other hand, is an affine map and thus a bijection, so the same methodology applies, and the online form keeps a usable numerical guarantee. I leave the proof as an exercise to the most determined soul.
When Is a Loop Secretly Associative?
Transport of structure tells us whether an associative operation stays associative under a given transformation. In practice we usually want the opposite. You're handed a loop that looks strictly sequential, the way safe softmax did, and you need to work out whether it can be made associative at all.
Model the loop as a function on a list of inputs (written as Python-style literals, ). We want to be a list homomorphism: a function that preserves structure, associativity in our case. Formally, there is an operation such that:
If such a exists, folds under any bracketing. Whether it exists is decided by the Third Homomorphism Theorem (Gibbons, 1996, building on Bird's list-homomorphism framework), which involves three different operators, so keep them apart:
- , the combine, : the associative operator we are after;
- , the forward step, : fold one element onto the end of the accumulator (a
foldl); - , the backward step, : fold one onto the front (a
foldr).
The two steps absorb a single element, not merge two states, and need not be associative. The theorem guarantees the associative exists the moment both steps exist (you still have to construct it):
Third Homomorphism Theorem. If is computable both as a left fold, , and as a right fold, , then the combine (the associative operator in ) is guaranteed to exist.
Online Softmax
First we need a carrier (the state) so we can update incrementally. No algorithm hands it to you; you guess. The obvious candidate is the max and the denominator themselves. That is the whole idea of an online algorithm: keep a running state for the sequence so far and extend it one element at a time.
In the context of the theorem, the function mapping input sequence to the state is , specifically:
Given the state, how does it change when one more element arrives? Let's derive the update:
That gives us the online update. Now let's check the 3HT conditions. We just did the forward update; restated in operator notation:
The definition of ignores order, so backward update is essentially the same:
Both folds exist and agree, so 3HT guarantees an associative exists. Let's construct it.
Split the sequence anywhere into two parts and , with states and . By the homomorphism property their combine is of the whole sequence, which we recompute with the same rescale as the single-element update, now applied to both parts at the common max :
So the associative combine is
This is the single-element step generalized, since with .
That is the online-softmax operator, recovered by walking backwards from the loop definition. We never had to guess the combine; we read it off the folds.
Alternative Twist: Base 2
The transport of structure lets us pick any bijection, so let's do it to compute a twist that is using update instead of , which is faster on the hardware.
The only place base is baked in is the weight . Rewrite it in base 2:
The rescale is affine with nonzero slope — a bijection, so the reduction stays associative.
Now every exponential in the carrier is base 2. The state over the rescaled scores:
A single score enters as . The forward step folds one more rescaled score using only exp2f:
and the merge is the online-softmax combine with in place of :
The softmax weight reads off as . The max-subtraction still pins the argmax term to , so the stability analysis is untouched.
In attention the constant folds into the softmax scale you already apply,
so the base change is free. This is exactly the qk_scale you see in FlashAttention-2 and the Triton kernels. You pay the multiply once, globally, instead of once per exponential in the reduction.
Wrap-Up
FlashAttention is usually taught as a feat of GPU engineering. The engineering is real, but zooming out lets you see a higher-level algebraic structure that explains why it works and lets you discover other secretly associative reductions. Key points:
- A reduction parallelizes exactly when its operator is associative. Blocking, cooperative combine, split-K, prefix sum, and identity-masked tails are all re-bracketings, so they are legal for any monoid.
- Safe softmax, Welford's variance, and FlashAttention are all associative. The transport-of-structure principle gives an easy way to check whether an operation performed in a loop is associative — and thus efficiently parallelizable.
- Bird's 3HT can be used to check whether a loop we know nothing about is secretly associative.
- Associativity is free; numerical stability is not. A bijection keeps the reduction associative but says nothing about overflow or cancellation. That you check separately.
Don't you dare go hollow
References
Online softmax and attention
- Maxim Milakov, Natalia Gimelshein. Online Normalizer Calculation for Softmax. arXiv:1805.02867, 2018. The one-pass safe-softmax recurrence; the carrier and its rescale-and-merge combine.
- Markus Rabe, Charles Staats. Self-Attention Does Not Need Memory. arXiv:2112.05682, 2021. Streaming attention with constant memory by carrying the running softmax statistics — the conceptual precursor to FlashAttention.
- Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, Christopher Ré. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. NeurIPS 2022. The IO-aware tiled kernel built on the online-softmax recurrence. (The kernel-engineering successors, FlashAttention-2 and -3, belong with Part 2.)
- Chih-Chung Hsu, Xin-Di Ma, Wo-Ting Liao, Chia-Ming Lee. ELSA: Exact Linear-Scan Attention for Fast and Memory-Light Vision Transformers. arXiv:2604.23798, 2026. Casts the online-softmax attention combine as an associative monoid — the same state , identity , and rescale-and-add merge used here, with the associativity proof — and exploits it to run exact attention as an parallel prefix scan.
Stable streaming statistics
- B. P. Welford. Note on a Method for Calculating Corrected Sums of Squares and Products. Technometrics 4(3), 1962. The original running-variance recurrence.
- Tony F. Chan, Gene H. Golub, Randall J. LeVeque. Algorithms for Computing the Sample Variance: Analysis and Recommendations. The American Statistician 37(3), 1983. The parallel (pairwise-mergeable) form of Welford used in the carrier above.
Reductions, monoids, and parallel algebra
- Guy E. Blelloch. Prefix Sums and Their Applications. CMU-CS-90-190, 1990. The canonical treatment of associative operators as the substrate for parallel scan and reduce.
- Richard S. Bird. An Introduction to the Theory of Lists. Oxford PRG-56, 1987. The first two homomorphism theorems and the algebraic view of foldable computations — the "find the monoid" methodology.
- Henrik Holm. A Note on Transport of Algebraic Structures. Theory and Applications of Categories, 2015. The modern, careful account of transport of structure: any bijection carries a monoid onto its image.
Numerical stability
- Nicholas J. Higham. Accuracy and Stability of Numerical Algorithms. SIAM, 2nd ed., 2002. The reference for floating-point error analysis, including the unit-roundoff bound used in the FlashAttention accuracy analysis above.
Homomorphism-based parallelization and synthesis
- Jeremy Gibbons. The Third Homomorphism Theorem. Journal of Functional Programming 6(4), 1996. "Leftwards and rightwards ⟹ a list homomorphism": the existence theorem that turns direction-agnosticism into associativity.
- Calvin Smith, Aws Albarghouthi. MapReduce Program Synthesis. PLDI 2016. Synthesizes the map and reduce (the ) from a sequential reference by solving the homomorphism constraint with an SMT backend.
- Tianle Liu et al. From Batch to Stream: Automatic Generation of Online Algorithms. arXiv:2404.04743, 2024. Synthesizing online (streaming) algorithms from batch specifications — the batch-softmax-to-online-softmax move as a general transformation.


