FlashAttention: GPU Memory-Bound Speedups for Long-Context LLMs
By Bishwambhar SenThe self-attention mechanism is the core component of the transformer architecture. However, self-attention has a major limitation: its computational and memory complexity scales quadratically with the sequence length:
O(N^2)
For sequence length N, calculating attention requires constructing an N \times N matrix. For long contexts (e.g. 16k or 32k tokens), this matrix is massive, saturating GPU memory (High Bandwidth Memory, or HBM) and slowing down execution.
FlashAttention resolves this memory bottleneck by restructuring the attention calculation to run efficiently on GPU memory hierarchies.
FlashAttention Memory Optimization
The Memory Hierarchy Bottleneck
Standard GPU execution relies on two primary memory tiers:
- HBM (High Bandwidth Memory): Large capacity (e.g. 80GB) but relatively slow access speeds.
- SRAM (Static Random-Access Memory): Fast, on-chip memory located near the GPU cores, but extremely small capacity (e.g. 20MB).
In standard self-attention, intermediate results like the attention matrix S and Softmax outputs P are continuously written to and read from HBM. FlashAttention eliminates these slow read/write cycles by loading inputs in blocks (tiles) directly into SRAM, computing attention locally, and writing the final outputs back to HBM.
Core Mechanisms of FlashAttention
FlashAttention achieves this using three key techniques:
- Tiling: Splitting the Query, Key, and Value matrices into blocks that fit within the SRAM limit.
- Online Softmax: Normalizing softmax exponents across blocks. Standard softmax requires seeing the entire row to find the maximum value:
m_i = \max(x_i)
Online softmax updates the maximum value and scaling factors incrementally as each block is processed, ensuring the final output is mathematically identical to standard softmax.
3. Recomputation in Backward Pass: Instead of storing the large N \times N attention matrix for backpropagation, FlashAttention recomputes it on-the-fly during the backward pass using the tiled inputs stored in SRAM.
Code Verification: Enabling FlashAttention in PyTorch
Using PyTorch's native scaled dot-product attention (SDPA), FlashAttention is enabled automatically when supported by the hardware:
import torch
# Define dimensions: batch, heads, seq_len, head_dim
query = torch.randn(2, 8, 4096, 64, dtype=torch.float16, device="cuda")
key = torch.randn(2, 8, 4096, 64, dtype=torch.float16, device="cuda")
value = torch.randn(2, 8, 4096, 64, dtype=torch.float16, device="cuda")
# Run scaled dot product attention, restricting PyTorch to the FlashAttention backend
from torch.nn.attention import sdpa_kernel, SDPBackend
with sdpa_kernel(SDPBackend.FLASH_ATTENTION):
output = torch.nn.functional.scaled_dot_product_attention(
query, key, value, is_causal=True
)
print("Output shape:", output.shape) # torch.Size([2, 8, 4096, 64])
print("Peak GPU memory (MB):", torch.cuda.max_memory_allocated() / 1024**2)
Performance Impact
- Speed: Up to 3x speedups in training and inference.
- Memory: Drastically reduced memory usage, allowing transformers to scale to much longer context windows on existing hardware.
Conclusion
The honest headline for most engineers is that you do not need to do anything. PyTorch's scaled_dot_product_attention already dispatches to a FlashAttention kernel when the conditions are met, and if you are running a recent version of vLLM, TGI, or the HuggingFace transformers attention implementations, you are almost certainly using it already without having configured anything. The interesting question is usually not how to enable it but why it silently did not engage.
The dispatcher falls back to the slower math backend on conditions that are easy to trip. Float32 inputs are the most common — Flash kernels require fp16 or bf16, so a model you forgot to cast runs the naive path at full speed penalty. A non-None attn_mask will also disqualify it in many versions; use is_causal=True rather than passing an explicit causal mask, which is the single most frequent cause of "I enabled it and nothing got faster." Head dimensions above 128, unsupported GPU architectures (pre-Ampere support is limited and pre-Turing is absent), and certain dropout or bias configurations round out the list. The sdpa_kernel context manager above is worth using during development precisely because it raises rather than silently falling back, which turns a mystery into an error message.
It is also worth calibrating the speedup claim. The 2-3x figures come from long sequences where attention dominates the runtime. At a sequence length of 512 the attention matrix is small enough to be a minor cost, the MLP blocks dominate, and end-to-end gains are often in the low single-digit percentages. FlashAttention is an optimization for the long-context regime specifically; at short contexts it is essentially free but also essentially invisible.
Finally, the memory reduction it delivers is not the whole picture during inference. In autoregressive generation the KV cache typically dwarfs attention working memory, and no amount of tiling touches it — that is what paged attention and quantized KV caches address. If you are hitting OOM while serving long conversations, FlashAttention alone will not save you.