FlashAttention Implementation › FlashAttention Limitations

Michael BrenndoerferUpdated June 30, 20258 min read
Language AI HandbookAll chapters
Part 17 of 67Efficient Attention
Chapter 125FlashAttention Implementation › FlashAttention Limitations
Reading section 7 of 10 · FlashAttention Limitations · 8 minSection 7 of 10 · 8 min

Book contents

Section 7 of 10 · 8 min read

FlashAttention Implementation › FlashAttention LimitationsOne section · Sections in this chapter7 / 10 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Despite its advantages, FlashAttention has limitations that you should understand before deployment.

Numerical Precision Differences

FlashAttention computes attention in a different order than standard attention, which can lead to small numerical differences. While mathematically equivalent, floating-point arithmetic is not associative, so results may differ at the level of floating-point precision.

In[41]:
Code
def demonstrate_numerical_differences():
    """
    Show how reordering affects floating-point results.
    This illustrates why FlashAttention and standard attention
    may have tiny numerical differences.
    """
    # Create deterministic values from the chapter-level plot seed
    values = np.random.randn(100).astype(np.float32)

    # Sum in different orders
    forward_sum = np.sum(values)
    backward_sum = np.sum(values[::-1])
    sorted_sum = np.sum(np.sort(values))

    # Kahan summation (more accurate)
    def kahan_sum(arr):
        s = 0.0
        c = 0.0
        for x in arr:
            y = x - c
            t = s + y
            c = (t - s) - y
            s = t
        return s

    kahan = kahan_sum(values)

    return {
        "forward": forward_sum,
        "backward": backward_sum,
        "sorted": sorted_sum,
        "kahan": kahan,
        "max_diff": max(
            abs(forward_sum - backward_sum), abs(forward_sum - sorted_sum)
        ),
    }


num_results = demonstrate_numerical_differences()
Out[42]:
Console
Floating-Point Summation Order Effects:

  Forward sum:  3.9492628574
  Backward sum: 3.9492607117
  Sorted sum:   3.9492614269
  Kahan sum:    3.9492611885

  Max difference: 2.15e-06

  These tiny differences are expected and acceptable.
  FlashAttention trades exact bit-for-bit reproducibility
  for significant speedups.

Running the same experiment 1000 times with different random values shows that these differences follow a predictable pattern:

Out[43]:
Visualization
Histogram showing distribution of summation differences across many trials. This shows that differences cluster near zero.
Distribution of floating-point differences when summing values in different orders. All methods compute the same mathematical sum, but finite precision causes tiny variations. FlashAttention differences are similarly small, typically below 1e-5 relative error.

No Arbitrary Attention Masks

FlashAttention's tiled computation assumes specific attention patterns. While causal masks and no masks are well-supported, arbitrary attention masks present challenges:

In[44]:
Code
def mask_support_matrix():
    """
    Document mask support across FlashAttention versions.
    """
    masks = {
        "No mask (bidirectional)": {
            "flash1": "✓",
            "flash2": "✓",
            "pytorch_sdpa": "✓",
        },
        "Causal mask": {"flash1": "✓", "flash2": "✓", "pytorch_sdpa": "✓"},
        "Sliding window": {
            "flash1": "Limited",
            "flash2": "✓",
            "pytorch_sdpa": "✓ (via flash2)",
        },
        "Arbitrary boolean mask": {
            "flash1": "✗",
            "flash2": "Limited",
            "pytorch_sdpa": "Falls back to math kernel",
        },
        "Additive attention bias": {
            "flash1": "✗",
            "flash2": "✓ (FlashAttention-2.3+)",
            "pytorch_sdpa": "Falls back",
        },
    }
    return masks


masks = mask_support_matrix()
Out[45]:
Console
Attention Mask Support:

Mask Type                          FA-1     FA-2         PyTorch
-----------------------------------------------------------------
No mask (bidirectional)               ✓        ✓               ✓
Causal mask                           ✓        ✓               ✓
Sliding window                  Limited        ✓  ✓ (via flash2)
Arbitrary boolean mask                ✗  Limited Falls back to math kernel
Additive attention bias               ✗ ✓ (FlashAttention-2.3+)      Falls back

For most transformer applications, causal or no mask covers the use cases. FlashAttention-2 added sliding window support, which enables models like Mistral. For arbitrary boolean masks or complex attention biases, PyTorch falls back to the slower math kernel, losing FlashAttention's benefits.

Hardware Requirements

FlashAttention requires relatively recent NVIDIA GPUs:

In[46]:
Code
def hardware_compatibility():
    """
    Document GPU compatibility for FlashAttention.
    """
    gpus = {
        "Ampere (SM 80)": {
            "examples": "A100, A6000, RTX 3090",
            "flash1": "Full support",
            "flash2": "Full support",
        },
        "Ada Lovelace (SM 89)": {
            "examples": "RTX 4090, L40",
            "flash1": "Full support",
            "flash2": "Full support",
        },
        "Hopper (SM 90)": {
            "examples": "H100",
            "flash1": "Full support",
            "flash2": "Full support + FP8",
        },
        "Turing (SM 75)": {
            "examples": "RTX 2080, T4",
            "flash1": "Limited (FA-1 only)",
            "flash2": "Not supported",
        },
        "Volta (SM 70)": {
            "examples": "V100",
            "flash1": "Limited",
            "flash2": "Not supported",
        },
    }
    return gpus


hw = hardware_compatibility()
Out[47]:
Console
FlashAttention GPU Compatibility:

Architecture         Example GPUs                         FA-1            FA-2
------------------------------------------------------------------------------
Ampere (SM 80)       A100, A6000, RTX 3090        Full support    Full support
Ada Lovelace (SM 89) RTX 4090, L40                Full support    Full support
Hopper (SM 90)       H100                         Full support Full support + FP8
Turing (SM 75)       RTX 2080, T4              Limited (FA-1 only)   Not supported
Volta (SM 70)        V100                              Limited   Not supported

Ampere and newer architectures provide full FlashAttention-2 support. Older GPUs like V100 and T4 have limited support for FlashAttention-1 only. If you're using cloud instances, A100 and H100 are the best choices for FlashAttention workloads. Consumer GPUs from the RTX 30 and 40 series also work well.

Memory Still Matters for Very Long Sequences

While FlashAttention eliminates the O(n2)O(n^2) memory for attention matrices, other parts of the transformer still scale with sequence length:

In[48]:
Code
def total_memory_breakdown(
    n, d_model, n_layers, vocab_size, batch_size=1, dtype_bytes=2
):
    """
    Break down memory usage in a transformer with FlashAttention.
    """
    # Activations per layer (need to store for backprop)
    # - Input to layer: n * d_model
    # - After attention: n * d_model
    # - After FFN: n * d_model
    activations_per_layer = 3 * batch_size * n * d_model * dtype_bytes
    total_activations = activations_per_layer * n_layers

    # KV cache for inference
    kv_cache = 2 * n_layers * batch_size * n * d_model * dtype_bytes

    # Model weights (independent of n)
    # Simplified: attention (4 * d^2) + FFN (8 * d^2) per layer
    weights_per_layer = 12 * d_model * d_model * dtype_bytes
    embeddings = vocab_size * d_model * dtype_bytes
    total_weights = weights_per_layer * n_layers + embeddings

    return {
        "activations_gb": total_activations / 1e9,
        "kv_cache_gb": kv_cache / 1e9,
        "weights_gb": total_weights / 1e9,
        "total_gb": (total_activations + kv_cache + total_weights) / 1e9,
    }


# LLaMA 7B-style model
memory_7b = total_memory_breakdown(
    n=4096, d_model=4096, n_layers=32, vocab_size=32000
)
memory_7b_long = total_memory_breakdown(
    n=32768, d_model=4096, n_layers=32, vocab_size=32000
)
Out[49]:
Console
Memory Breakdown (7B-style model, fp16):

At 4K tokens:
  Activations:  3.22 GB
  KV Cache:     2.15 GB
  Weights:      13.15 GB
  Total:        18.52 GB

At 32K tokens (8x longer):
  Activations:  25.77 GB
  KV Cache:     17.18 GB
  Weights:      13.15 GB (unchanged)
  Total:        56.10 GB

FlashAttention eliminates attention matrix memory, but
activations and KV cache still scale linearly with n.

The breakdown shows that at 4K tokens, a 7B model requires about 13 GB total. At 32K tokens, this grows to nearly 50 GB, a 4x increase. Model weights remain constant, but activations and KV cache scale linearly with sequence length. This explains why even with FlashAttention, very long sequences require substantial GPU memory or additional techniques like gradient checkpointing.

Out[50]:
Visualization
Stacked bar chart showing memory components at different sequence lengths, with activations and KV cache growing while weights remain constant.
Memory breakdown for a 7B parameter model at different sequence lengths. FlashAttention removes the quadratic attention matrix cost, but activations and KV cache still grow linearly with sequence length.

Gradient Computation

FlashAttention's recomputation strategy during the backward pass means gradients take longer to compute than they would with stored attention matrices. This is typically worthwhile because:

  1. The memory savings allow larger batch sizes, which often more than compensate for the recomputation cost
  2. Training is usually memory-bound anyway, so the extra compute fits within memory transfer time
  3. For very long sequences, the alternative (storing attention matrices) would be impossible