One-sentence summary: FlashAttention is not approximate attention, and it does not remove the score interactions. Tiling and online Softmax avoid repeatedly writing a complete intermediate matrix to device memory.
21.1 The Bottleneck Is Not Only “Too Much Arithmetic”
For one attention head, let :
The leading arithmetic remains . A naive implementation also materializes an score matrix and may materialize another equally large probability matrix after Softmax.
At , one FP16 score matrix for one head and one sample occupies:
If a naive path retains that matrix for batch size 8 and 32 heads, the total is 8 GiB—before masks, probabilities, Dropout, and other backward state. This is an account of a naively materialized path, not a claim that every current PyTorch attention call really allocates 8 GiB.
The other half of the problem is movement:
read Q/K from HBM → compute S=QKᵀ → write S to HBM
read S from HBM → mask/Softmax → write P to HBM
read P/V from HBM → compute O=PV → write O to HBM
Tensor Cores can multiply matrices quickly, but idle compute units still wait for data. FlashAttention attacks this I/O path.
21.2 Tiling: Keep the Large Sheet off the Floor
Keep the original desk analogy: HBM is a large bookshelf, while registers, shared memory, and L1 are the small work surface. Only one set of pages fits on the desk, so finish the current calculation—or reduce it to a compact running state—before fetching the next set.
FlashAttention brings one tile and one pair on-chip:
Sᵢⱼ = QᵢKⱼᵀ / √d + mask
↓
online-Softmax state
↓
accumulate output Oᵢ
Several hardware numbers are easily conflated. On A100, the whole GPU has 40 MB of L2; each SM has a combined L1/texture/shared-memory data cache of at most 192 KB, with a configurable shared-memory carveout. Those resources are not one freely shared “20 MB SRAM pool.” The 40 GB A100 has about 1550 GB/s HBM2 bandwidth and the 80 GB variant about 2039 GB/s. Different parts and generations do not support one timeless “SRAM is always 20 times faster” table.
The idealized tile sizes in Algorithm 1 of the original paper are:
Here is the per-head dimension, and is the number of scalar elements that fit in the paper's abstract on-chip memory—not a raw byte count to plug in without the dtype. Real kernels also balance dtype, register pressure, shared-memory carveout, alignment, warp partitioning, and GPU generation. The formula does not uniquely decree that every real tile must be 64.
21.3 Online Softmax: See One Tile, Preserve the Whole Row
The Softmax denominator spans a full row. Applying an independent Softmax to every tile and concatenating the results would be wrong. For one query row, maintain across processed key blocks:
- : the running maximum score;
- : the running exponential sum at that maximum's scale;
- : the unnormalized, Value-weighted accumulator.
When a new score block and its arrive:
Return after the final block. If a later tile contains a larger score, rescales everything already accumulated to the new numerical scale.
This unmasked PyTorch version is for checking the recurrence, not for speed:
import math
import torch
def online_attention(q, k, v, block_size):
# q: [Nq, d], k: [Nk, d], v: [Nk, dv]
scale = 1.0 / math.sqrt(q.size(-1))
rows = q.size(0)
m = torch.full((rows,), -torch.inf, device=q.device, dtype=q.dtype)
denominator = torch.zeros_like(m)
accumulator = torch.zeros(
rows, v.size(-1), device=q.device, dtype=q.dtype
)
for start in range(0, k.size(0), block_size):
k_block = k[start:start + block_size]
v_block = v[start:start + block_size]
scores = q @ k_block.transpose(-2, -1) * scale
block_max = scores.max(dim=-1).values
new_m = torch.maximum(m, block_max)
correction = torch.exp(m - new_m)
probabilities = torch.exp(scores - new_m[:, None])
denominator = (
correction * denominator + probabilities.sum(dim=-1)
)
accumulator = (
correction[:, None] * accumulator + probabilities @ v_block
)
m = new_m
return accumulator / denominator[:, None]
For the same , it should match softmax(Q @ K.T / sqrt(d)) @ V within floating-point tolerance. “Exact attention” means the algorithm does not introduce sparse or low-rank approximation; reordered finite-precision operations are not promised to be bit-for-bit identical.
21.4 Keep Arithmetic, Extra Memory, and HBM I/O Separate
| Question | Naively materialized attention | FlashAttention |
|---|---|---|
| Leading arithmetic | ||
| Extra intermediate memory, fixed d | ||
| HBM accesses in the original abstract machine |
The third row has conditions: counts on-chip elements, and the paper analyzes . It cannot be casually simplified to . Nor does the asymptotic drop from quadratic to linear extra memory mean real peak memory is exactly N times smaller; Q/K/V, outputs, weights, optimizer state, allocator behavior, and the rest of the model remain.
In backward, FlashAttention recomputes local score tiles from saved statistics instead of retaining the full probability matrix. This adds some FLOPs, but it does not imply that backward must be slower overall. Saving HBM traffic can still make the kernel and end-to-end training faster.
21.5 FA1 to FA4: One Core Idea, Hardware-Specific Kernels
- FA1 (2022) established I/O-aware tiling plus online Softmax without materializing the full matrix.
- FA2 (2023) reduced non-matmul FLOPs and improved thread-block and warp work partitioning. On the paper's A100 workloads, it reported roughly 2× FA1, 50–73% of theoretical peak, and up to 225 TFLOPs/s/GPU at 72% model FLOPs utilization for GPT-style training. Those are bounded paper measurements, not a guaranteed speedup for any model.
- FA3 (2024; still labelled a Hopper beta in the official repository) uses H100/H800 paths such as TMA, WGMMA, and FP8.
- FA4 (2026) targets the asymmetric scaling of Blackwell B200/GB200 and uses CuTe DSL with pipeline co-design. As of 2026-07 the official repository provides
flash-attn-4pre-releases; head-dimension, dtype, and backward coverage must still be checked against the current release rather than inferred from FA2.
A larger version number is not universally faster on every GPU. FA3 targets Hopper and FA4 targets Blackwell; Ampere and Ada may still use FA2.
21.6 In Practice: Let the Framework Dispatch First
PyTorch's scaled_dot_product_attention selects among available backends according to device, dtype, shape, masks, and other constraints. With the Chapter 18 layout, arrange Q/K/V as [batch, heads, time, head_dim]:
import torch.nn.functional as F
def attention(q, k, v, dropout_p, training):
return F.scaled_dot_product_attention(
q,
k,
v,
attn_mask=None,
dropout_p=dropout_p if training else 0.0,
is_causal=True,
)
scaled_dot_product_attention always applies Dropout according to the passed dropout_p; it does not inspect an enclosing module's training flag. Pass 0.0 explicitly in evaluation.
To prove that one input is eligible for the Flash backend, restrict the backend in diagnostic code:
from torch.nn.attention import SDPBackend, sdpa_kernel
with sdpa_kernel(SDPBackend.FLASH_ATTENTION):
output = F.scaled_dot_product_attention(
q, k, v, dropout_p=0.0, is_causal=True
)
PyTorch reports why a fused kernel cannot run. Production code should not necessarily disable every fallback merely to claim the word “Flash.”
The official Dao-AILab/flash-attention package is another direct route. Its hardware support is no longer accurately described as “new NVIDIA only”: as of 2026-07 the FA2 CUDA path covers Ampere/Ada/Hopper, while the official repository also provides CK and Triton AMD backends for ROCm. Check the current PyTorch, CUDA/ROCm, GPU, dtype, and head-dimension matrix before installing.
21.7 How to Benchmark Honestly
At minimum, freeze and report:
- GPU model and power mode; PyTorch, CUDA/ROCm, and kernel versions;
- dtype, batch, heads, query length, key length, and head dimension;
- causal/noncausal mode, mask, Dropout, and forward versus forward+backward;
- warmup, timing method, median/percentiles, and whether this is a kernel microbenchmark or whole-model wall time;
- peak memory plus output and gradient error.
For short sequences or small batches, launch and layout overhead can erase the gain. An attention kernel running 2× faster does not imply that the whole model—or per-token latency—runs 2× faster.
FlashAttention and KV caching also solve different problems: FlashAttention reduces I/O within one attention computation; a KV cache avoids recomputing old Keys and Values across autoregressive decoding steps.
21.8 Chapter Summary
- FlashAttention preserves the mathematical attention result; its core contribution is an I/O-aware implementation.
- It still performs leading arithmetic, but does not materialize the full intermediate matrix.
- Online Softmax rescales both the denominator and Value accumulator, which makes tile results composable.
- is head dimension and is abstract on-chip capacity; model width and raw byte counts cannot be substituted blindly.
- FA1–FA4 kernels and hardware support keep changing, so every speed number needs a workload and version boundary.
- PyTorch SDPA can dispatch automatically, but callers must still pass
dropout_p=0.0for evaluation.
Chapter Checklist
- Separate compute complexity, extra-memory complexity, and HBM I/O
- Derive the online-Softmax tile merge with
- Explain why “exact” is not a bit-for-bit promise
- Call PyTorch SDPA correctly and verify the backend actually selected
- Design a benchmark that does not present kernel speed as whole-model speed
Primary Sources
- FlashAttention (NeurIPS 2022)
- FlashAttention-2 (ICLR 2024)
- FlashAttention-3 (2024)
- FlashAttention-4 (2026)
- Dao-AILab/flash-attention
- FlashAttention releases
- PyTorch scaled_dot_product_attention
- NVIDIA Ampere Tuning Guide
See You in the Next Chapter
FlashAttention makes the current attention computation cheaper, but Chapter 20's autoregressive loop still recomputes old Keys and Values at every step. Chapter 22 removes that cross-step redundancy with a KV Cache.