Skip to content

FlashAttention

Exact attention without the n×nn\times n matrix

Chapter 12 of 21 · III. Fast attention and inference · about 30 minutes. Builds on: 6. Attention, 10. Generating text: the KV cache and inference cost.

Attention is bottlenecked by memory, not math

Section titled “Attention is bottlenecked by memory, not math”

A GPU has a small amount of very fast on-chip memory (SRAM) and a large amount of slower memory (HBM, the “GPU memory” in the spec sheet). Moving data between them costs far more time than the arithmetic done on it. The textbook implementation of attention does this:

  1. Compute S=QK⊤/dS=QK^{\top}/\sqrt{d} (n×nn\times n) and write it to HBM.
  2. Read SS, compute softmax PP (n×nn\times n), write PP to HBM.
  3. Read PP and VV, compute O=PVO=PV, write OO.

For n=32,000n = 32,000 tokens, SS has a billion entries per head. Writing and reading these matrices dominates the runtime, and storing them dominates memory. FlashAttention computes exactly the same result but never writes SS or PP to HBM.

Split QQ into row blocks and KK, VV into column blocks small enough to fit in SRAM. For each QQ block, loop over the K/VK / V blocks, computing partial scores on chip and accumulating into the output. The obstacle: softmax normalizes over a whole row, but the loop only sees one block of the row at a time. The way around it is the online softmax .

For one query row, scores s1,…,sns_1,\dots,s_n and values v1,…,vnv_1,\dots,v_n, the exact output is ∑jesjvj/∑jesj\sum_j e^{s_j}v_j\big/\sum_j e^{s_j}. For numerical safety one subtracts the row maximum mm. Process the row in blocks and maintain three running quantities for the blocks seen so far: the max mm, the denominator l=∑jesj−ml=\sum_j e^{s_j-m}, and the unnormalized output o=∑jesj−mvjo=\sum_j e^{s_j-m}v_j. When a new block arrives with maximum mbm_b, set m′=max⁡(m,mb)m'=\max(m,m_b). The old terms were computed against the old max, so rescale them by em−m′e^{m-m'}:

m′=max⁡(m,  mb)l′=em−m′ l+∑j∈blockesj−m′o′=em−m′ o+∑j∈blockesj−m′ vjoutput=o / l\begin{aligned}m'&=\max(m,\;m_b)\\l'&=e^{m-m'}\,l+\sum_{j\in\text{block}}e^{s_j-m'}\\o'&=e^{m-m'}\,o+\sum_{j\in\text{block}}e^{s_j-m'}\,v_j\\\text{output}&=o\,/\,l\end{aligned}

Each update leaves (ll, oo) equal to what you would have computed with the new max over everything seen, so after the last block the result is exactly softmax-weighted attention. See it:

  • Memory: O(n)O(n) instead of O(n2)O(n^{2}), because the score matrix is never stored. That is what made 100K-token contexts practical to train.
  • Speed: far fewer HBM reads and writes. The arithmetic count is unchanged (still O(n2⋅d)O(n^{2}\cdot d)), but time falls because memory traffic dominated.
  • Backward pass: instead of saving the n×nn\times n probabilities, store only each row’s log-sum-exp (a vector of nn numbers) and recompute the scores block by block during backpropagation. This trades extra arithmetic, which is cheap on GPUs, for memory traffic, which is not.
  • Exactness: it is not an approximation. Outputs match standard attention up to floating-point rounding.

FlashAttention-2 reorganized the loops and work partitioning across thread blocks and warps and reduced non-matrix-multiply work, giving roughly a further 2× speed-up on the same hardware.

Why you should know this even if you never write a kernel

Section titled “Why you should know this even if you never write a kernel”

Attention kernels are the reason sequence length is a software choice rather than a hard wall, they appear in every training and inference framework, and their numerical behavior (the order in which sums are accumulated, low-precision accumulation) is one source of tiny output differences between hardware and software versions.

Why can FlashAttention be faster even though it does the same number of FLOPs?

Runtime is dominated by reading and writing the n×nn\times n matrices to slow memory; keeping blocks in on-chip memory removes most of that traffic.

In the online update, what happens to m, l and o when a new block does not contain a larger score than the current max?

m′=mm' = m, so the rescale factor em−m′=1e^{m-m'}=1; the new block’s terms are simply added to ll and oo.

Why store a log-sum-exp per row rather than the probability matrix for the backward pass?

With log-sum-exp the probabilities can be recomputed from the scores, which are themselves recomputed from QQ and KK blocks; storing only nn numbers per head avoids n2n^{2} memory.

  • The online softmax normalizer; short and readable.
  • FlashAttention.
  • FlashAttention-2.