FlashAttention computes exact attention without ever writing the matrix of attention scores to GPU main memory. It splits queries, keys and values into tiles that fit in on-chip SRAM, computes each tile's contribution, and merges the contributions with a running softmax that rescales earlier partial results whenever a larger score appears. The output is the same as standard attention up to floating-point rounding. For inference this matters in two different ways. In prefill, which is compute-bound, it removes a memory footprint that grows with the square of the prompt and keeps the tensor cores busy, which makes long prompts feasible and fast. In decode, which is memory-bound, the tiling idea is reused for a different purpose: splitting a long KV cache across many thread blocks so that a single sequence can use the whole GPU's bandwidth, plus native support for grouped-query heads and paged caches. Every major open-source serving engine runs a FlashAttention-family kernel or a close relative.
The bottleneck it attacks#
Standard attention for one head computes , and as separate kernels, writing and , each , to high-bandwidth memory and reading them back. Dao, Fu, Ermon, Rudra and Ré (2022) argued that what made attention slow on GPUs was not FLOPs but these memory reads and writes, and that approximate attention methods had failed to deliver wall-clock speedups because they ignored IO. Their paper proves that standard attention needs HBM accesses, while FlashAttention needs for SRAM size with , and that no exact algorithm can do asymptotically better across all SRAM sizes. For typical head dimensions of 64 to 128 and on-chip memory of order 100 KB per streaming multiprocessor, is well below 1, so far fewer bytes move.
Memory capacity is the more visible effect at inference time. For one Llama 3 8B layer (32 query heads), materialising FP16 scores for a single 32k-token prompt takes bytes = 68.7 GB, nearly a whole H100, for one layer of one request. FlashAttention keeps only two numbers per query row (the running maximum and the running sum), 8.4 MB in FP32 for the same layer and prompt.
The line of work: Milakov and Gimelshein (2018) showed the softmax normaliser can be computed in one pass with a running maximum. Rabe and Staats (2021) showed self-attention needs only memory in practice by chunking with this trick. FlashAttention (2022) fused it into an IO-aware GPU kernel. FlashAttention-2 (Dao, 2023) reworked the loop order and parallelism to reach 50 to 73% of theoretical peak FLOP/s on A100, about 2× FlashAttention. FlashAttention-3 (Shah, Bikshandi, Zhang, Thakkar, Ramani and Dao, 2024) exploited Hopper's asynchronous tensor cores and memory engine to reach up to 740 TFLOP/s in FP16 (75% of H100 peak) and close to 1.2 PFLOP/s in FP8, with 2.6× lower FP8 error than a baseline FP8 attention. For decoding, Flash-Decoding (Dao, Haziza, Massa and Sizov, 2023) added the split-KV scheme described below, reporting up to 8× faster decoding for very long sequences.
The online softmax, with every symbol defined#
Take one query row , keys and values split into blocks and , and block scores . Keep three running quantities: the maximum score seen so far , the sum of exponentials relative to it , and the unnormalised output . Initialise , , , and for each block
After the last block, . The correction factor rescales everything accumulated so far whenever a new maximum appears; subtracting the running maximum keeps every exponential at most 1, so nothing overflows. By induction and is the matching weighted sum, so the result equals exactly in real arithmetic.
The same algebra gives the rule for combining independent partial results, which is what split-KV decoding needs. If chunk produced a normalised output and its log-sum-exp , then
which is a softmax over chunks weighted by how much probability mass each chunk holds.
A worked example by hand
One query, four keys with scores , scalar values , two blocks of two.
Block 1, scores : ; ; . The interim output would be .
Block 2, scores : new maximum , so . Then and .
Result: . Directly, with sum 84.791, and . Our numpy run reproduces both to machine precision.
Prefill: tiles, loop order and the tensor cores#
for each (sequence, head, query tile Q_i of B_r rows) in parallel: # one thread block each
load Q_i into SRAM; m = -inf; l = 0; acc = 0
for each key/value tile j up to the causal limit: # B_c columns per tile
load K_j, V_j into SRAM
S = Q_i K_j^T / sqrt(d) (tensor cores; apply causal mask on the diagonal tile)
m_new = max(m, rowmax(S)); P = exp(S - m_new); c = exp(m - m_new)
l = c * l + rowsum(P); acc = c * acc + P V_j (tensor cores)
m = m_new
write O_i = acc / l and the row log-sum-exp m + log(l) to HBM
This is FlashAttention-2's loop order: the outer parallel loop is over query tiles, so each thread block owns its output tile and no two blocks write the same rows. Causal masking lets tiles strictly above the diagonal be skipped entirely, halving the work. Keys and values are re-read once per query tile, often from L2 cache rather than HBM, and the score matrix never leaves the chip. Because prefill is compute-bound (see the roofline), the goal is to keep the matrix units saturated, which is what FlashAttention-2's work partitioning and FlashAttention-3's overlap of softmax with matrix multiplies achieve.
How much prefill time attention takes depends on prompt length. Its FLOPs are against for the linear layers, so the attention share is . For Llama 3 8B () that is 3.2% at 2,000 tokens, 11.8% at 8k, 34.8% at 32k and 68.1% at 128k. A faster attention kernel barely moves TTFT for chat-length prompts and dominates it for long documents.
Decode: a different bottleneck#
At decode time each sequence contributes one query row per head. The score "matrix" is a vector of length , so there is nothing large to avoid materialising, and the cache must be read exactly once whatever the algorithm. The arithmetic intensity is fixed by the model, as derived in the roofline article: FLOP per byte, 4 for Llama 3 8B in BF16. Decode attention is memory-bound on every GPU, and the only goal is to stream the cache at full bandwidth. Three things stand in the way.
Too little parallelism. A kernel that assigns one thread block per (sequence, head) launches blocks, or if query heads sharing a key-value head are processed together. At batch 1 with 8 key-value heads that is 8 thread blocks on a GPU with 132 streaming multiprocessors, so most of the chip, and most of its memory bandwidth, sits idle. Flash-Decoding splits the key-value sequence into chunks, gives each chunk its own thread block, writes per-chunk outputs with their log-sum-exp, and merges them in a small reduction kernel using the combination rule above. With 1,024-token chunks a 32k-token sequence yields 32 chunks per key-value head, 256 blocks at batch 1. The Flash-Decoding authors report attention up to 50× faster than FlashAttention for long sequences at small batch and end-to-end decoding up to 8× faster, with runtime roughly constant up to 32k tokens on CodeLlama 34B at batch 1. At large batch there is already enough parallelism and splitting adds only reduction overhead, so kernels choose the number of splits from batch size, heads and length.
Reading shared heads more than once. With GQA, query heads share each key-value head. A naive kernel that expands keys and values to heads, or runs one block per query head, reads each cache entry times. GQA-aware kernels load each key-value tile once and multiply it against all query rows, which both cuts bytes by and turns a matrix-vector product into a small matrix-matrix product.
Scattered memory. Paged caches store keys and values in blocks listed in a block table. FlashAttention added paged-cache support in version 2.5, and FlashInfer (Ye et al., 2025) builds its kernels around block-sparse layouts that cover paged caches, shared prefixes and tree-structured speculative decoding; FlashInfer reports 29 to 69% lower inter-token latency than compiler backends on a serving benchmark and 28 to 30% lower latency for long-context inference.
A quick bound shows the stakes. One 32k-token Llama 3 8B sequence holds 4.29 GB of cache, read every step. At the H100's 3.35 TB/s that is 1.28 ms per step at best; a kernel that reaches only a sixth of the chip's bandwidth because it launched 8 thread blocks spends several times that. Decode attention kernels are judged by achieved bandwidth, not FLOP/s.
Toy run: tiling and splitting are exact#
We implemented three versions in numpy (seed 0, FP64): standard attention that materialises the full score matrix, a tiled causal prefill with 64×64 tiles and the online softmax, and split-KV decoding with 1,024-token chunks merged by log-sum-exp. For a 2,048-token causal prefill with , the largest absolute difference between tiled and standard outputs was . For four query heads decoding against a 32,768-token cache, split-KV and standard differed by at most . Both are rounding noise: neither technique approximates anything. The hand example above came out of the same script, matching to the fourth decimal.
Interaction with post-trained models#
Exact, but not bit-identical. FlashAttention changes the order of floating-point additions, so logits differ from a naive implementation in the last bits, and different kernels (prefill versus decode, split versus unsplit, different split counts at different batch sizes) can disagree slightly with each other. This is harmless for serving, but it matters in reinforcement learning: if a rollout engine and a trainer use different attention kernels, the log-probabilities used in importance ratios for GRPO or PPO disagree before any update has happened. Measure the mismatch between rollout and trainer log-probabilities on the same tokens before blaming the algorithm for instability.
FP8 attention needs an accuracy check. FlashAttention-3's FP8 path reduces error relative to naive FP8 attention through block quantisation and incoherent processing, but it is still lower precision than BF16. A fine-tune whose behaviour depends on precise long-range retrieval should be evaluated with the exact kernel and dtype you will serve. See FP8 and FP4 inference.
Architecture features must be supported by the kernel. Head dimensions up to 256, grouped-query attention, sliding windows, ALiBi, logit soft-capping and paged caches are supported in current FlashAttention releases according to its documentation, but each new variant arrives in kernels later than in model code. A post-training change that alters attention (a different head size after pruning, a custom mask, an added bias) can push a model off the fast path and onto a slow fallback without any error.
Packed fine-tuning data and variable-length batches. Fine-tuning pipelines that pack several examples into one sequence rely on variable-length attention so examples do not attend to each other; inference engines use the same variable-length interface to batch prompts of different lengths in prefill without padding. If a fine-tune was trained with packing but without per-example masking, it has seen cross-example attention during training, which no inference kernel will reproduce. That is a training bug that only shows up as a quality gap.
Adapters do not change the attention kernel. LoRA modifies the projections that produce , and , not the attention computation, so FlashAttention is unaffected by adapters, merged or not.
Tuning knobs#
| Knob | Effect | Guidance |
|---|---|---|
| Attention backend | Which kernel library the engine calls | Use the engine's default for your GPU generation; switch only with a benchmark |
| Number of KV splits | Parallelism for long, small-batch decode | Leave on automatic; force more splits only for batch 1, very long context |
| Tile sizes | Shared memory use and occupancy | Set by the library per head dimension and architecture |
| Attention dtype | BF16, FP16 or FP8 | FP8 for prefill-heavy long-context workloads after an accuracy check |
| CUDA graphs | Removes launch overhead for decode | On for decode; capture a range of batch sizes |
Failure modes and how to detect them#
Silent fallback. Symptom: prefill of long prompts is several times slower than expected, or memory use spikes during prefill. The engine fell back to a math implementation that materialises scores, usually because of an unsupported head dimension, dtype, mask or GPU. Check the engine's start-up log for the selected attention backend and profile one long prefill.
Low decode bandwidth at small batch. Symptom: single-user TPOT on long contexts far above (weights + cache bytes) / bandwidth. Splitting is off or capped. Check that the decode kernel supports split-KV for your configuration.
Mismatch between prefill and decode kernels. Symptom: tiny logit differences between the first generated token and later ones that make deterministic tests flaky. Expected behaviour; compare with tolerances, not equality.
Numerical issues in FP16. Models trained in BF16 can produce attention logits or values that overflow FP16. The online softmax protects the exponentials, but the matrix products themselves can still overflow. Serve BF16-trained fine-tunes in BF16.
When it matters#
| Workload | What FlashAttention buys |
|---|---|
| Chat prompts under 2k tokens | Little TTFT change (attention is about 3% of prefill FLOPs for an 8B model); still the safe default |
| Long prompts, RAG, document QA | Large TTFT reduction and no memory; essential |
| Long-context decode at small batch | Split-KV keeps bandwidth high; large TPOT gain |
| Large-batch decode with short contexts | Small gain; the step is dominated by weight reads |
| Shared prefixes across a batch | Add prefix-aware kernels (cascade or Hydragen-style attention, see prefix caching) |
| Need to read fewer cache bytes | Not this; use KV quantisation or eviction |
Minimal implementation#
In PyTorch, scaled_dot_product_attention dispatches to a FlashAttention-style fused kernel on supported GPUs, which covers prefill in simple settings. For a decode step against a paged cache, the flash-attn package's flash_attn_with_kvcache takes the cache, a block table and per-sequence lengths, appends the new keys and values in place, and handles GQA and split-KV internally.
import torch
import torch.nn.functional as F
from flash_attn import flash_attn_with_kvcache
# prefill: q, k, v of shape (batch, heads, seq, head_dim); fused kernel, no N x N tensor
out = F.scaled_dot_product_attention(q, k, v, is_causal=True)
# decode: one new token per sequence against a paged cache
# q_new: (batch, 1, q_heads, head_dim); k_new, v_new: (batch, 1, kv_heads, head_dim)
# k_cache, v_cache: (num_blocks, block_size, kv_heads, head_dim)
# block_table: (batch, max_blocks) int32; cache_seqlens: (batch,) int32 tokens already cached
out = flash_attn_with_kvcache(
q_new, k_cache, v_cache, k=k_new, v=v_new,
cache_seqlens=cache_seqlens, block_table=block_table, causal=True,
)
cache_seqlens += 1
# the merge rule behind split-KV, for reference
def merge(outs, lses): # outs: (splits, ..., d); lses: (splits, ...)
w = torch.softmax(lses, dim=0)
return (w.unsqueeze(-1) * outs).sum(0)
Serving engines wrap these calls: vLLM, SGLang and TensorRT-LLM select FlashAttention, FlashInfer or their own kernels per GPU generation. When POFE evaluates a fine-tune on long-context tasks, it runs the same attention backend that the deployment target will use, so the numbers on the evaluation report are the numbers you will serve.
Further reading#
Dao et al. (2022) is the original paper, with the IO-complexity analysis and lower bound. Dao (2023) gives the FlashAttention-2 loop order and partitioning, and Shah et al. (2024) the Hopper-specific FlashAttention-3 with FP8. The Flash-Decoding post by Dao, Haziza, Massa and Sizov (2023) explains split-KV decoding. Milakov and Gimelshein (2018) and Rabe and Staats (2021) are the algorithmic ancestors. Ye et al. (2025) describes FlashInfer's serving-oriented kernels for paged and shared-prefix caches. The FlashAttention repository documents current support for paged caches, GQA, sliding windows and soft-capping.
Frequently asked questions#
Is FlashAttention exact?
Yes. It computes the same softmax attention as the standard algorithm, only in a different order, using a running maximum and sum to merge tiles. Outputs differ only by floating-point rounding; in our numpy test the largest difference was about 1e-15.
Does FlashAttention speed up decoding?
Not by reducing bytes: decode must read the whole KV cache once whatever the kernel. The decode-time gains come from Flash-Decoding's split-KV parallelism, which lets one long sequence use the whole GPU, from reading each shared GQA head once, and from native paged-cache support.
How much does FlashAttention help time to first token?
It depends on prompt length. For Llama 3 8B, attention is about 3% of prefill FLOPs at 2,000 tokens but 35% at 32k and 68% at 128k, so a faster attention kernel matters most for long documents. It also removes the N squared memory that would make very long prompts impossible.
What is Flash-Decoding?
A decoding kernel that splits the cached keys and values of each sequence into chunks, computes attention for each chunk in parallel, and merges the partial outputs using their log-sum-exp values. Its authors report up to 8x faster decoding for very long sequences.
Why do my RL rollout and trainer log-probabilities differ slightly?
Different attention kernels sum in different orders, so logits differ in the last bits, and prefill, decode and split-KV kernels can disagree with each other. Measure the mismatch on identical tokens before attributing instability in GRPO or PPO to the algorithm.
References#
- Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, Christopher Ré (2022). FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. arxiv.org/abs/2205.14135
- Tri Dao (2023). FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning. arxiv.org/abs/2307.08691
- Jay Shah, Ganesh Bikshandi, Ying Zhang, Vijay Thakkar, Pradeep Ramani, Tri Dao (2024). FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision. arxiv.org/abs/2407.08608
- Tri Dao, Daniel Haziza, Francisco Massa, Grigory Sizov (2023). Flash-Decoding for long-context inference. crfm.stanford.edu/2023/10/12/flashdecoding.html
- Maxim Milakov, Natalia Gimelshein (2018). Online normalizer calculation for softmax. arxiv.org/abs/1805.02867
- Markus N. Rabe, Charles Staats (2021). Self-attention Does Not Need O(n^2) Memory. arxiv.org/abs/2112.05682
- Zihao Ye, Lequn Chen, Ruihang Lai, Wuwei Lin, Yineng Zhang, Stephanie Wang, Tianqi Chen, Baris Kasikci, Vinod Grover, Arvind Krishnamurthy, Luis Ceze (2025). FlashInfer: Efficient and Customizable Attention Engine for LLM Inference Serving. arxiv.org/abs/2501.01005
- Jordan Juravsky, Bradley Brown, Ryan Ehrlich, Daniel Y. Fu, Christopher Ré, Azalia Mirhoseini (2024). Hydragen: High-Throughput LLM Inference with Shared Prefixes. arxiv.org/abs/2402.05099
- Tri Dao and contributors (2025). flash-attention repository README. github.com/Dao-AILab/flash-attention
- NVIDIA (2022). NVIDIA Hopper Architecture In-Depth. developer.nvidia.com/blog/nvidia-hopper-architecture-in-depth/