datarekha

FlashAttention

Why attention is memory-bound, and how FlashAttention's tiling and online softmax compute the exact same result with far less GPU memory — making long context affordable.

10 min read Advanced NLP & Transformers Lesson 25 of 44

What you'll learn

  • Why standard attention is bottlenecked by memory bandwidth, not FLOPs
  • The GPU memory hierarchy — SRAM vs HBM — and why it decides speed
  • How tiling + the online softmax compute exact attention without storing N×N
  • Why FlashAttention is exact (not an approximation) and what FA-2/FA-3 added

Before you start

Self-attention is a handful of matrix multiplies — so why is it the slowest part of a transformer at long context? The surprise: on a modern GPU, attention is memory-bound, not compute-bound. The arithmetic is cheap; the bottleneck is shuffling a giant intermediate matrix to and from slow memory. FlashAttention fixes exactly that — without changing the math.

N×N scores = Q·Kᵀ
SRAM · ~20 MB · ~19 TB/s
Holds one Q,K,V tile + running (m, ℓ) softmax state.
HBM · tens of GB · ~3 TB/s
Reads Q,K,V once, writes the output once. The N×N matrix is never stored here.
Standard · score matrix in HBM
134 MB
O(N²) — per head
Flash · extra HBM for scores
~0
O(N) — streamed in SRAM

Same exact softmax attention — FlashAttention just never writes the N×N matrix to slow memory. At 32k tokens that one matrix is multiple GB per head; tiling makes it vanish, which is why long context became affordable.

The real bottleneck: HBM vs SRAM

A GPU has two very different memories:

  • HBM (high-bandwidth memory) — the tens of GB you think of as “GPU memory.” Large, but “only” ~2–3 TB/s.
  • SRAM — tiny on-chip memory next to the compute units, ~20 MB, but roughly 10× the bandwidth of HBM.

Compute has outpaced memory for years, so for an operation like attention the chip finishes the math and then waits on memory. The metric that matters is how many bytes you move through HBM, not how many FLOPs you do.

Standard attention is wasteful by this metric. For a sequence of length N, it:

  1. computes the scores S = QKᵀ — an N×N matrix — and writes it to HBM,
  2. reads N×N back to apply softmax, writes N×N again,
  3. reads N×N once more to multiply by V.

That N×N matrix is the problem. At N = 32k it’s multiple gigabytes per head, and it makes three round-trips through slow memory. The dot products are trivial; the data movement is everything.

Watch the tiling

Standard attention lights up the entire N×N matrix — it lives in HBM and is read back twice. FlashAttention streams one K/V block-column through SRAM at a time; only the current tile is ever resident, and the full matrix is never stored — so HBM cost stays O(N) instead of exploding as O(N²):

Standard attentionfull N×N written to HBMO(N²) memory & trafficFlashAttentiontileone tile streamed through SRAMO(N) memory

The online softmax — why it’s exact

The catch: softmax needs the whole row to normalise (you divide by the sum over all keys). If you only ever see one tile of keys at a time, how can you get the exact softmax? The online softmax trick: keep a running max m and running denominator , and rescale the accumulated output whenever a later tile reveals a larger value. When the last tile is done, the result is bit-for-bit the same as computing softmax over the full row at once.

That’s the key point most people miss: FlashAttention is not an approximation. Sparse and linear attention change the math to get speed; FlashAttention computes the identical softmax attention — it’s just IO-aware about how it touches memory. Prove it to yourself:

import numpy as np
rng = np.random.default_rng(0)

N, d = 8, 4
Q = rng.standard_normal((1, d))     # one query row, for clarity
K = rng.standard_normal((N, d))
V = rng.standard_normal((N, d))
scores = (Q @ K.T / np.sqrt(d))[0]  # N scores

# --- standard softmax over the full row at once ---
w = np.exp(scores - scores.max()); w /= w.sum()
out_full = w @ V

# --- online softmax: stream K/V in tiles of 2, keep running (m, l, acc) ---
m, l, acc = -np.inf, 0.0, np.zeros(d)
for t in range(0, N, 2):                 # one tile at a time
    s = scores[t:t+2]
    m_new = max(m, s.max())
    alpha = np.exp(m - m_new)            # rescale old state to the new max
    p = np.exp(s - m_new)
    l = l * alpha + p.sum()
    acc = acc * alpha + p @ V[t:t+2]
    m = m_new
out_online = acc / l

print("max abs difference:", np.abs(out_full - out_online).max())
# ~1e-16 — the tiled result is the exact same softmax attention.
max abs difference: 1.1102230246251565e-16

Don’t store it — recompute it

The forward pass never keeps the N×N matrix, so the backward pass can’t read it back either. FlashAttention simply recomputes the needed tiles during the backward pass. That sounds wasteful, but recomputing in SRAM is far cheaper than the HBM round-trips it avoids — a classic compute-for-memory trade. The upshot: training memory drops from O(N²) to O(N), which is what let context windows jump from 2k to 128k+.

In one breath

  • Attention is memory-bound, not compute-bound: the cost is moving the N×N score matrix through slow HBM, not the FLOPs.
  • FlashAttention never writes that matrix to HBM — it tiles the computation through fast on-chip SRAM and carries a running softmax (max m, denominator ℓ) from tile to tile.
  • The online softmax rescales the accumulated output when a later tile reveals a bigger score, so the result is bit-for-bit exact — not an approximation.
  • The backward pass recomputes tiles instead of storing them, dropping training memory from O(N²) to O(N) — what made 128k-token context practical.
  • You get it for free via F.scaled_dot_product_attention and every serving stack; FA-2 improved warp partitioning, FA-3 added FP8 and Hopper async.

Check yourself

Quick check

0/3
Q1Why is standard attention called 'memory-bound' on a modern GPU?
Q2How can FlashAttention compute an exact softmax while only ever seeing one tile of keys at a time?
Q3Why does FlashAttention recompute attention tiles during the backward pass?

What to remember

  • Attention is memory-bound: the cost is moving the N×N score matrix through HBM, not the FLOPs.
  • FlashAttention keeps that matrix out of HBM entirely — tiling through fast SRAM plus an online softmax that rescales running state.
  • It’s exact, not approximate; the backward pass recomputes tiles instead of storing them, cutting memory from O(N²) to O(N).
  • You get it for free via scaled_dot_product_attention and every modern serving stack — it’s why 128k-token context is practical.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

FAQCommon questions

Questions about this lesson

Is FlashAttention an approximation of attention?

No — and this is the most common misconception. FlashAttention computes exactly the same softmax attention as the standard implementation, down to floating-point rounding. It only changes how memory is accessed: it tiles the computation so the N×N score matrix never touches slow HBM, and it uses an online softmax that rescales running statistics so a tiled pass gives the identical result. Sparse and linear attention change the math for speed; FlashAttention does not.

Why is attention memory-bound rather than compute-bound?

On modern GPUs, compute throughput has far outgrown memory bandwidth. Attention's matrix multiplies are cheap in FLOPs, but the standard implementation writes the N×N score matrix to HBM and reads it back twice (for softmax and for ×V). The chip finishes the arithmetic and then waits on memory, so wall-clock time is dominated by bytes moved through HBM, not by the multiply-accumulates.

What is the online softmax in FlashAttention?

Softmax normally needs the whole row to normalise, but FlashAttention only sees one tile of keys at a time. The online softmax keeps a running maximum m and running denominator ℓ; when a later tile contains a larger score, it rescales the already-accumulated output by exp(m_old − m_new) before adding the new tile. After the final tile the accumulated result equals a full-row softmax exactly, which is what makes the tiled computation correct.

Practice this in an interview

All questions
What does self-attention actually compute, and why is it useful?

Self-attention lets every position in a sequence directly query every other position, producing a weighted blend of value vectors where the weights reflect learned pairwise relevance. This gives the model a constant-depth path between any two tokens regardless of how far apart they are, which is what enables transformers to capture long-range dependencies that RNNs miss.

Why is standard self-attention O(n^2) in sequence length, and how is it addressed?

Self-attention computes a dot product between every query and every key, producing an n-by-n score matrix that must be stored and normalised. Both compute and memory scale quadratically with sequence length, which becomes prohibitive at context lengths beyond a few thousand tokens. Approximate attention methods trade exact attention for sub-quadratic complexity.

How do state-space models like Mamba differ from attention, and when would you use one?

A state-space model carries a fixed-size hidden state forward through the sequence like a selective recurrence, giving O(N) time and constant per-step memory with no KV cache that grows with context. Attention instead compares every token to every other, which is O(N^2) but allows exact lookup of any past token. Mamba's gates are input-dependent, recovering much of attention's content-awareness; the trade-off is that a fixed state can't recall arbitrary far-back tokens as precisely. In practice, hybrids that interleave Mamba layers with a few attention layers give near-linear cost with near-attention quality.

Related lessons

Explore further

Skip to content