Abstract

Attention is commonly expressed as two matrix multiplications separated by a softmax. On modern GPUs, however, this operator is often constrained not by arithmetic throughput but by transfers between high-bandwidth memory (HBM) and on-chip SRAM. A conventional implementation materializes two N×NN \times N intermediate matrices: the attention scores and their normalized probabilities. FlashAttention avoids materializing either matrix in HBM. It computes score tiles on chip, consumes them immediately through an online softmax, and discards them. This article explains that central design independently of any particular FlashAttention version or GPU generation.

1. Introduction

Transformer attention computes

O=softmax⁡(QK⊤)V, O = \operatorname{softmax}(QK^\top)V,

where QQ, KK, and V∈RN×dV \in \mathbb{R}^{N \times d}, NN is the sequence length, and dd is the head dimension. This formulation suggests a straightforward implementation: compute the score matrix S=QK⊤S=QK^\top, apply row-wise softmax to obtain PP, and compute O=PVO=PV.

The straightforward implementation is inefficient because both SS and PP contain N2N^2 elements. Each intermediate is written to HBM and read by a subsequent kernel. GPU tensor cores can compute the matrix products rapidly, but the intermediate traffic repeatedly crosses a much slower memory interface.

FlashAttention is based on one systems observation:

The score matrix is required by the computation, but it does not need to exist in HBM.

The kernel computes one score tile in on-chip SRAM, immediately incorporates it into a running softmax and output, and then discards it. This reordering computes the same attention function with substantially less memory traffic.

The main contributions of the design are:

  1. An IO-aware tiling of the complete attention operator, rather than either matrix multiplication in isolation.
  2. An online-softmax recurrence that enables exact block-wise normalization.
  3. A fused GPU kernel that retains score and probability tiles on chip.
  4. A backward pass that recomputes inexpensive intermediates instead of storing quadratic state.

2. Background

2.1 GPU memory hierarchy

GPUs expose a large HBM capacity and a much smaller on-chip storage hierarchy composed of registers and shared memory. HBM holds model state and activations but has much lower bandwidth relative to the arithmetic rate of tensor cores. Shared memory is fast enough to feed matrix-multiply units, but only a small tile of the working set fits at one time.

This asymmetry makes data movement a first-class algorithmic cost. An implementation with the minimum number of floating-point operations can still underperform if it repeatedly materializes large intermediates in HBM.

2.2 Conventional attention

A conventional implementation launches separate kernels for three stages:

S=QK⊤,P=softmax⁡(S),O=PV. S = QK^\top, \qquad P = \operatorname{softmax}(S), \qquad O = PV.
Three separately launched kernels with the score and probability matrices written to and reread from HBM between them.
Figure 1. Conventional attention as three kernel launches. Amber marks a kernel; gray marks tensors whose size is linear in the sequence length; red marks the two quadratic intermediates that are written to HBM and read back, which is the traffic FlashAttention removes.

The inputs and output require Θ(Nd)\Theta(Nd) storage, whereas each intermediate requires Θ(N2)\Theta(N^2) storage. For long sequences, the quadratic traffic dominates.

For example, with N=16,384N=16{,}384 and d=128d=128, each of QQ, KK, and VV contains approximately 2.1 million elements. The score matrix contains approximately 268 million elements. The principal problem is not that these scores must be computed; it is that a conventional schedule writes and rereads them.

The consequence is visible without any timing at all. Running the same attention over the same tensors, once with the two intermediates materialized and once through a fused kernel, and recording peak allocated memory:

Sequence lengthConventionalFusedRatio
51256 MB46 MB1.2×
1,024119 MB60 MB2.0×
2,048336 MB88 MB3.8×
4,0961,160 MB144 MB8.1×
8,1924,368 MB256 MB17.1×
Log-log plot of peak allocated memory against sequence length. The conventional curve has slope two, while every fused backend collapses onto a single line of slope one.
Figure 2. Peak allocated memory against sequence length. The conventional implementation's slope is two — doubling the sequence quadruples the footprint — while every fused backend sits on a slope of one. The vertical gap is what the next sections remove, and it is the one claim in this article that needs no caveat about which kernel ran.

Two things about this measurement are worth noting. First, it is a memory result, not a speed result: it holds regardless of how well the fused kernel is tuned, because the intermediates simply do not exist. Second, the ratio is not a constant — it is a slope difference. Measuring only at short sequence lengths would show 1.2× and support the wrong conclusion.

2.3 Numerically stable softmax

For one score row xx, stable softmax is

softmax⁡(x)i=exi−m∑jexj−m,m=max⁡jxj. \operatorname{softmax}(x)_i = \frac{e^{x_i-m}}{\sum_j e^{x_j-m}}, \qquad m=\max_j x_j.

Subtracting the row maximum prevents exponential overflow. The apparent obstacle to tiling is that both the maximum and normalization sum depend on the complete row.

3. Motivation

Tiling either matrix multiplication alone does not solve the problem. A tuned GEMM can efficiently produce SS, but if SS is subsequently written to HBM, consumed by a softmax kernel, and written again as PP, the implementation retains the dominant traffic.

The optimization boundary must therefore include the full operator:

A query tile and streamed key-value blocks feeding an on-chip loop of score tile, online-softmax update, and running output, with the output written to HBM once.
Figure 3. The fused alternative. The green region stays in registers and shared memory: for every key block the kernel forms a score tile, folds it into the running softmax statistics, and updates the accumulator. Only the final output returns to HBM.

This produces two requirements:

  • The score and probability tiles must remain on chip and never become global intermediates.
  • Softmax statistics computed from separate key blocks must be merged exactly.

The second requirement is the key algorithmic problem solved by FlashAttention.

4. Design

4.1 Tiled execution

Partition the query matrix into row blocks QiQ_i and the key and value matrices into blocks KjK_j and VjV_j. For each query block, the kernel scans all key/value blocks:

  1. Load QiQ_i, KjK_j, and VjV_j into on-chip storage.
  2. Compute the score tile Sij=QiKj⊤S_{ij}=Q_iK_j^\top.
  3. Merge SijS_{ij} into the running softmax statistics for QiQ_i.
  4. Accumulate the weighted values into the running output OiO_i.
  5. Discard SijS_{ij} and proceed to the next key/value block.

Only the final OiO_i is written to HBM.

4.2 Online softmax

For one query row, maintain three quantities after processing some key blocks:

  • mm: the largest score observed so far;
  • ℓ\ell: the sum of exponentials relative to mm;
  • zz: the unnormalized weighted-value sum relative to mm.

For a new score block ss with corresponding value block VjV_j, compute

m~=max⁡(s),m′=max⁡(m,m~). \tilde m = \max(s), \qquad m' = \max(m, \tilde m).

The previous state was expressed relative to mm, whereas the merged state must be expressed relative to m′m'. Rescale the old contribution and add the new block:

ℓ′=em−m′ℓ+∑kesk−m′, \ell' = e^{m-m'}\ell + \sum_k e^{s_k-m'},
z′=em−m′z+∑kesk−m′Vj,k. z' = e^{m-m'}z + \sum_k e^{s_k-m'}V_{j,k}.

After all blocks have been processed,

O=zℓ. O = \frac{z}{\ell}.

This recurrence is exact in real arithmetic. Every old exponential is converted from the old reference maximum to the new one because

ex−mem−m′=ex−m′. e^{x-m}e^{m-m'} = e^{x-m'}.

4.3 Worked example

Consider one score row split into two blocks:

s=[1,2∣0,3],V=[10,20∣30,40]. s=[1,2 \mid 0,3], \qquad V=[10,20 \mid 30,40].

For the first block, m=2m=2. Its unnormalized weights are [e−1,1][e^{-1},1]. The second block introduces a new maximum m′=3m'=3. The old weights are multiplied by e2−3=e−1e^{2-3}=e^{-1}, becoming [e−2,e−1][e^{-2},e^{-1}]. The new block contributes [e−3,1][e^{-3},1].

The final normalization is therefore

O=10e−2+20e−1+30e−3+40e−2+e−1+e−3+1, O = \frac{10e^{-2}+20e^{-1}+30e^{-3}+40} {e^{-2}+e^{-1}+e^{-3}+1},

which is exactly the result obtained by applying ordinary softmax to all four scores simultaneously. The algorithm has changed the evaluation order, not the function.

4.4 IO complexity

Let MM denote the available on-chip storage. FlashAttention chooses tile dimensions so that the active Q, K, V, score, and output tiles fit within MM. The original analysis derives an HBM-access complexity of

Θ(N2d2M), \Theta\left(\frac{N^2d^2}{M}\right),

compared with the Θ(N2)\Theta(N^2) intermediate traffic of conventional attention. The original paper further shows that this bound is asymptotically optimal for a range of practical SRAM capacities. The practical consequence is more important than the notation: increasing useful on-chip tile capacity directly reduces repeated HBM transfers.

4.5 Backward pass

Saving the full probability matrix for backpropagation would reintroduce quadratic memory consumption. FlashAttention instead stores the output and one log-sum-exp statistic per row. During the backward pass, it reloads Q, K, and V tiles and recomputes the local score and probability tiles on chip.

This is a deliberate trade: perform additional arithmetic to avoid substantially more HBM traffic. On modern GPUs, recomputation is often cheaper than storing and retrieving the quadratic intermediates.

Measured at N=8,192N=8{,}192 with B=1B=1, H=8H=8, d=128d=128 in BF16, the trade resolves clearly in favor of recomputation. Peak allocated memory for forward-plus-backward, against a conventional implementation:

BackendForwardForward + backwardBackward/forward ratio
Conventional2,240 MB4,304 MB1.92×
Fused (best)128 MB225 MB1.76×

Recomputation roughly doubles the backward cost in time relative to forward — a ratio of 3.5 rather than 1.8 — while keeping the footprint flat. Against the conventional path the fused backward is 19× smaller in memory. The extra arithmetic is real and cheap; the storage it avoids is not.

Log-log plot of peak allocated memory against sequence length. Solid lines (forward plus backward) sit above dashed lines (forward only) by a small margin for fused backends, while the conventional curve rises two orders of magnitude higher.
Figure 4. Peak memory for forward alone (dashed) and forward plus backward (solid). The gap between the two lines is what the backward pass had to retain. For fused kernels it is a few tens of megabytes at every sequence length; for the conventional path it is gigabytes, and grows without bound.
Log-log plot of GPU device time against sequence length, with forward-only and forward-plus-backward curves for each backend. Fused backends cluster at the bottom while the conventional curve rises steeply.
Figure 5. GPU device time, forward against forward-plus-backward. The backward pass costs roughly three times the forward for a fused kernel and much less than that for the conventional one in relative terms — because the conventional forward is already dominated by writing intermediates that the fused forward never writes.

One detail from that measurement matters for anyone reproducing it: on a small batch, the host-side launch path can cost more than the backward pass itself. At N=8,192N=8{,}192 the fastest kernel spent 0.365 ms on the GPU but 0.686 ms enqueueing the work, so wall-clock timing reported 0.950 ms — more than twice its device time. A backward-pass benchmark that reports only wall clock is measuring the launcher.

Grouped bar chart comparing GPU device time against host launch time per backend. For the fastest fused kernel the host bar is nearly twice the GPU bar, while for the flash-based backends the GPU dominates.
Figure 6. The split that a wall-clock number hides. For the vendor kernel the host spends nearly twice as long enqueueing the work as the GPU spends executing it, so its measured latency is a launcher measurement. For the flash-based backends the GPU dominates and wall clock is honest. Reporting one number without the other is how a kernel speedup becomes a step slowdown.

5. Concrete GPU Implementation

5.1 Kernel structure

A simplified forward kernel has the following structure. This is the version-1/version-2 structure: one thread block owns a query tile, and the online-softmax state is scalar per row. Later versions change the scheduling around this loop — which warp does what, when copies are issued, where accumulators live — but not the loop itself. Those changes are the subject of FlashAttention in Practice.

parallel for each query tile Q_i:
    load Q_i
    m = -infinity
    l = 0
    z = 0

    for each key/value tile (K_j, V_j):
        load K_j and V_j into shared memory
        S_ij = Q_i @ transpose(K_j)

        new_m = max(m, rowmax(S_ij))
        old_scale = exp(m - new_m)
        P_ij = exp(S_ij - new_m)

        l = old_scale * l + rowsum(P_ij)
        z = old_scale * z + P_ij @ V_j
        m = new_m

    O_i = z / l
    store O_i

One thread block typically owns one or more query tiles. Warps cooperate on tensor-core matrix multiplications, row-wise maximum and sum reductions, and movement between HBM, shared memory, and registers. Causal attention skips score tiles that lie entirely above the diagonal and masks only tiles intersecting the diagonal.

5.2 Fusion boundary

The implementation must fuse score computation, masking, softmax, dropout when required, and value accumulation into one kernel. Writing S_ij or P_ij to global memory between these stages defeats the design. The fusion boundary, rather than a novel matrix-multiplication primitive, is what removes the quadratic intermediate traffic.

5.3 Resource tradeoffs

Larger tiles improve data reuse but consume more shared memory and registers, and excessive per-block resource use reduces occupancy. How a given implementation resolves that tradeoff depends on the target architecture, which is the subject of the next article.

One consequence of that tradeoff is measurable, and it is the most commonly over-generalised number in this literature: how much fusion helps depends on the head dimension. At a fixed sequence length of 8,192 and a fixed FLOP count per query, varying dhd_h alone changes the speedup by an order of magnitude:

Backenddh=32d_h{=}32dh=64d_h{=}64dh=128d_h{=}128dh=256d_h{=}256
SDPA efficient1.031 ms1.142 ms1.828 ms3.483 ms
SDPA flash0.3250.4870.8751.544 ms
flash-attn0.3120.5160.9621.778 ms
Vendor fused0.1320.1320.165unsupported

The pattern is not what one would guess. Fused kernels get slower as heads get deeper — SDPA flash degrades 4.7× from dh=32d_h=32 to dh=256d_h=256 — because a deeper head spends more of the tile budget on depth instead of on length, which reduces reuse. The vendor kernel is the exception: nearly flat, within 25% across the whole range.

Log-log plot of speedup against head dimension for four backends, all rising as head dimension shrinks, with the vendor kernel far above the others.
Figure 7. Speedup against the conventional path as a function of head dimension. Every backend improves as heads get narrower, and the spread between backends widens with depth. Quoting a single speedup figure for “fused attention” therefore says more about the chosen head dimension than about the kernel.

This is worth carrying into any comparison. A model with dh=128d_h=128 and a model with dh=32d_h=32 are not the same benchmark, and a fused kernel that wins comfortably on one may barely win on the other.

6. Evidence From the Original Implementation

The NeurIPS 2022 paper reports up to 7.6x kernel-level speedup over a PyTorch attention implementation. At the model level, it reports 15% end-to-end wall-clock improvement for BERT-large, approximately 3x speedup for GPT-2 at sequence length 1K, and approximately 2.4x speedup on Long Range Arena workloads [1]. These measurements are workload-specific, but they establish the practical consequence of the design: avoiding quadratic HBM intermediates improves both memory capacity and execution time.

6.1 Reproducing the shape of the result on current hardware

The absolute numbers above belong to the hardware of 2022. What transfers is the shape: speedup that grows with sequence length, and a memory footprint that stops being quadratic. Both reproduce on a current GPU, and reporting the shape is more useful than quoting an absolute figure measured on hardware nobody has.

Measured on a B300 (SM 10.3), B=2B=2, H=8H=8, dh=128d_h=128, BF16, causal:

Sequence lengthConventionalFusedSpeedup
5120.088 ms0.029 ms3.0×
1,0240.110 ms0.032 ms3.5×
2,0480.630 ms0.032 ms19.6×
4,0962.061 ms0.051 ms40.1×
8,1929.640 ms0.164 ms58.7×

Two features of this table are the same features as the paper’s: the fused path is nearly flat in latency while the conventional path is quadratic, so the speedup is a function of sequence length rather than a constant. And below roughly 2,048 tokens every fused backend lands on a launch-overhead floor of about 0.03 ms, which is why the short-sequence ratios say more about kernel launch than about attention.

Log-log plot of speedup against sequence length for every backend, all curves rising steeply with length and separating from each other at long sequences.
Figure 8. Speedup against the conventional path as a function of sequence length, one line per backend. The growth is the point: a fused kernel is not a fixed multiplier, it is a multiplier that scales with the problem. Reporting a single speedup without the length it was measured at makes the number uninterpretable.
Log-log plot of effective TFLOP per second against sequence length, with fused kernels climbing toward hundreds of teraflops while the conventional and math paths stay low.
Figure 9. The same data as effective throughput. At short lengths every backend is far from peak because the work is too small to occupy the machine; as length grows the fused kernels climb toward the hardware's capability while the conventional path plateaus. Utilisation, not latency, is what shows the machine being used.

A note on what “fused” means here. On this GPU generation, the fastest fused kernel is the vendor library’s, not the flash-attn package: the latter ships kernels compiled for SM80 and runs them through forward compatibility, which works but is roughly 6× slower at these shapes. Any claim about “the FlashAttention kernel” on a new architecture has to start by establishing which kernel actually executed.

7. Discussion

FlashAttention demonstrates three broader systems principles.

First, operators should be optimized across their full dataflow. Optimizing two GEMMs independently misses the traffic created by the nonlinear operation between them.

Second, algebra can expose a better execution schedule. Online softmax transforms a global normalization into a mergeable state, enabling the surrounding system optimization.

Third, recomputation can be preferable to storage. The backward pass performs more arithmetic but moves less data, a profitable exchange on machines whose compute throughput grows faster than memory bandwidth.

FlashAttention does not reduce the asymptotic arithmetic complexity of dense attention: it still performs Θ(N2d)\Theta(N^2d) work. Its contribution is to make that arithmetic execute closer to the capabilities of the hardware by reducing avoidable IO.

7.1 A kernel speedup is not a step speedup

Every number in this article so far measures attention in isolation. In a training step, attention is one term among several, and the step can only improve by its share. Measured on a small model (8 layers, D=512D=512, Dff=2048D_{ff}=2048, batch 8, BF16):

Sequence lengthAttention share of stepAttention speedupStep speedup
2563.5%0.91×1.12×
1,02410.7%4.24×1.58×
4,09620.9%10.42×10.00×

The first row is the trap: at a short sequence length attention is 3.5% of the step, so a “faster” attention kernel is invisible, and the measured attention speedup is below one because the comparison sits on the launch floor. Nothing was made worse; the measurement simply has no signal.

The middle row is ordinary Amdahl behaviour: a 4.2× improvement to a 10.7% component yields about 1.6× for the step.

The last row is the interesting one. The step improves by 10× — 154,000 to 1,544,000 tokens per second — which is more than attention’s measured share would predict. The reason is that the conventional path’s quadratic intermediates also inflate allocator pressure and the backward pass, so its cost spills outside the attention kernels the profiler attributes. Kernel-level attribution therefore tends to understate the gain from removing those intermediates, just as step-level timing understates the kernel improvement when the step is not attention-bound. Both numbers are worth reporting, and neither alone is the answer.

Log-scale plot: solid lines show whole-step speedup and dashed lines show attention-only speedup, converging as sequence length grows and diverging sharply at short lengths.
Figure 10. Attention-only speedup (dashed) against whole-training-step speedup (solid). At short sequence lengths the two diverge by an order of magnitude and the step barely moves; as attention becomes a larger share, they converge. Quoting the dashed number as though it were the solid one is the most common overclaim in this literature.
Plot of attention's share of the training step against sequence length, rising from a few percent at short lengths to over half at long ones.
Figure 11. Why the two curves above converge. Attention's share of a training step grows with sequence length, so the ceiling on what any attention kernel can contribute rises with it. The kernel is the same at every point on this axis; only its relevance changes.
Bar chart of loss after twenty steps for each attention backend, all bars at essentially the same height.
Figure 12. The check that the speed was not bought by changing the math. Loss after twenty identical steps, one bar per backend. Any measurable difference here would invalidate every speed comparison in this article; there is none.

7.2 The energy bill is not the same as the time bill

There is one more cost that a latency table hides, and on shared hardware it is a real one. Measured at a sequence length of 16,384, with idle draw subtracted:

BackendLatencyDraw above idleWork per joule
Conventional29.8 ms511 W0.072 TFLOP/J
SDPA math82.7 ms567 W0.023 TFLOP/J
SDPA efficient6.73 ms497 W0.329 TFLOP/J
flash-attn3.11 ms631 W0.560 TFLOP/J
Vendor fused0.80 ms823 W1.681 TFLOP/J

The fastest kernel draws the most power — 823 W above idle against 511 W for the conventional path — and is still 23× more efficient per joule. That looks paradoxical until the reason is stated: the conventional path leaves the chip idle waiting on memory, so it burns power producing nothing. Being fast and being efficient coincide here, because both follow from not doing redundant memory traffic.

Bar chart on a logarithmic axis of teraflops per watt for each backend, with the fastest fused kernel more than twenty times higher than the conventional path.
Figure 13. Work per joule, on a logarithmic axis. Two orders of magnitude separate the worst backend from the best — a wider spread than latency shows. On a shared machine this is the number that decides whether a benchmark disturbs its neighbours.

This matters for a practical reason beyond accounting. When several processes share accelerators, the fastest kernel is also the one that draws the most instantaneous power for the shortest interval, and the slowest is the one that holds elevated power for thirty times as long. Neither situation is obviously better for a co-tenant, which is why a latency-only comparison is incomplete on shared hardware.

This article has treated the algorithm as a single design. In practice it has been rescheduled four times for successive GPU generations, with the non-materializing core unchanged each time. That sequence — and the parallel decomposition used for single-query decoding — is the subject of FlashAttention in Practice.

8. Limitations

  • Reported throughput values are best-case measurements for specific shapes and GPU configurations; end-to-end model gains are smaller and workload dependent.
  • Algorithmic exactness does not imply bitwise identity because tiled reductions change floating-point operation order.
  • Very short sequences may not amortize fusion and scheduling overheads.
  • Dense FlashAttention does not remove the quadratic arithmetic cost required by very long contexts.
  • Hardware-specific kernels require continued retuning as memory systems and matrix units evolve.

The second point is usually stated as a caveat and then left unquantified, which invites the assumption that the tiled path is the less accurate one. Measured against a float64 reference at a sequence length of 8,192, the opposite is true:

PrecisionConventionalFused
FP161.52e-039.54e-04fused is 1.6× more accurate
BF161.25e-027.54e-03fused is 1.7× more accurate

Every fused implementation tested reported the identical maximum error, because they accumulate the softmax and the value product in FP32 regardless of storage precision, and share the same reduction order. The conventional path accumulates in the storage dtype and loses precision doing so.

Bar chart comparing max absolute error of the conventional and fused attention paths in bf16 and fp16; the fused bars are consistently lower.
Figure 14. Maximum absolute error against a float64 reference. Fusion is not a precision compromise to be traded for speed here — it is more accurate at both reduced precisions, because the fused kernels accumulate in FP32 while the naïve path does not.

Error also does not accumulate with sequence length the way long-sum intuition suggests. At FP32, the maximum absolute error stays between 8e-07 and 1.7e-06 from a sequence length of 512 through 8,192 — flat, not linear in NN — because each tile is normalized against a running maximum that only moves forward. And every backend tested was bitwise reproducible across identical calls at every shape, so “exact” here means exact enough that repeated runs agree, not merely close on average.

Grouped log-scale bar chart of maximum absolute error for each backend and precision, with fused backends lower than the conventional path in bf16 and fp16 and all comparable in fp32.
Figure 15. Error against a float64 reference, by backend and storage precision. In FP32 every backend agrees to about 1e-06 and the ordering is noise; the separation appears only at reduced precision, where accumulation dtype matters. That is the whole explanation for the fused advantage above.
Log-log plot of maximum absolute error against sequence length in fp32, with all backends on flat horizontal lines around one part in a million.
Figure 16. Error against sequence length at FP32. The curves are flat across a sixteen-fold change in length, which rules out the intuitive worry that tiling accumulates error in proportion to context. Each tile is rescaled against a running maximum, so error stays bounded rather than growing with the number of tiles.

References

  1. Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, and Christopher Ré. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. NeurIPS 2022.