Skip to content

Flash Attention v2

Overview

Flash Attention v2 is the improved version of Flash Attention released in 2023. It achieves 2.8x speedup compared to standard attention (vs 2x in v1) with better numerical stability, optimized backward pass, and support for more variants.

  • Announcement: July 2023 (9 months after v1)
  • Key Improvement: 40% faster than v1
  • Main Focus: Backward pass optimization + numerical improvements
  • Adoption: Now standard in PyTorch, HuggingFace, vLLM
  • Backward Compatibility: Yes, mostly compatible with v1

-

Flash Attention v1: Recap

What v1 Solved

Problem: Standard attention needs O(N²) memory for intermediate matrices

Standard Attention:
 - S = Q @ K^T (N × N matrix) → O(N²) memory
 - Softmax(S) → O(N²) memory 
 - P = Softmax(S) @ V
 - High memory, I/O-bound

Flash Attention v1:
 - Block-wise computation (tiling)
 - Keep S, P in fast SRAM cache
 - O(N²) I/O reduced to O(N)
 - 2x faster than standard attention

v1 Characteristics

Strengths:
2x speedup
60% memory reduction
Exact computation (bit-identical)
Forward pass well optimized

Limitations:
Backward pass not fully optimized
Limited head dimension support (≤ 128)
Some numerical precision issues
Not work well with all sequence lengths
Gradient computation overhead

-

Flash Attention v2: Major Improvements

Key Innovation 1: Backward Pass Optimization

FLASH ATTENTION V1 - Backward Pass (Inefficient):

Forward pass: Optimized 
Backward pass: Standard computation 

Backward computation requires:
 - Recompute attention matrix S (O(N²) I/O!)
 - Compute attention gradients
 - Complex synchronization
 - Result: Backward slower than forward (unusual!)

Typical timing:
 - Forward: 10ms (well optimized)
 - Backward: 15-20ms (not optimized)

FLASH ATTENTION V2 - Backward Pass (Optimized):

Apply same I/O-aware principles to backward:
 - Block-wise gradient computation
 - Minimize intermediate materialization
 - Efficient synchronization patterns
 - Result: Backward ≈ forward speed

Typical timing:
 - Forward: 10ms (further optimized)
 - Backward: 10-12ms (now optimized!)

Speedup: 2x faster backward pass!

Key Innovation 2: Block Partitioning Optimization

FLASH ATTENTION V1:

Block strategy:
 - Fixed block size (e.g., 64 × 64)
 - Process Q in blocks of size B_r = 64
 - Process K,V in blocks of size B_c = 64
 - Suboptimal for different hardware

Memory access pattern:
 - Column-wise access to Q (inefficient for memory bandwidth)
 - Thread synchronization overhead
 - Not fully utilizing GPU memory hierarchy

FLASH ATTENTION V2:

Improved blocking:
 - Adaptive block size based on hardware
 - Optimize for memory coalescing
 - Column-wise vs row-wise access selection
 - Better memory bandwidth utilization

Key change: Reorder loop structure!

V1 loop order:
for i in range(0, N, B_r): # Iterate Q blocks
 for j in range(0, N, B_c): # Iterate K,V blocks
 # Process each pair

V2 loop order (optimized):
for i in range(0, N, B_r): # Iterate Q blocks
 for j in range(0, N, B_c): # Iterate K,V blocks in reverse!
 # Better memory pattern for modern GPUs

Result: 40% faster than v1!

Key Innovation 3: Numerical Stability

FLASH ATTENTION V1:

Softmax computation:
 - Subtract max for stability
 - Compute exp
 - Divide by sum
 - Generally stable but edge cases exist

Issues:
 - Accumulation of floating point errors
 - Different precision behavior across platforms
 - Gradient computation could introduce errors

FLASH ATTENTION V2:

Improvements:
 - Better numerical precision in softmax
 - More stable gradient computation
 - Double-precision intermediate calculations
 - Careful handling of small values

Result:
 - Bit-identical to standard attention
 - Fewer numerical errors
 - Better training stability

-

Detailed Differences

1. Forward Pass Optimization

FLASH ATTENTION V1 Forward:

```cuda
// Simplified v1 structure
for i in range(0, N, B_r):
 // Load Q block
 Q_block = load_from_global(Q[i:i+B_r])

 for j in range(0, N, B_c):
 // Load K,V blocks
 K_block = load_from_global(K[j:j+B_c])
 V_block = load_from_global(V[j:j+B_c])

 // Compute S, P in shared memory
 S = Q_block @ K_block.T
 P = softmax(S)

 // Accumulate output
 O += P @ V_block

FLASH ATTENTION V2 Forward:

// Improved v2 structure with better memory access
for i in range(0, N, B_r):
 // Load Q block ONCE (better locality)
 Q_block = load_from_global(Q[i:i+B_r])

 for j in range(0, N, B_c):
 // Reverse iteration improves memory pattern
 K_block = load_from_global(K[N-j-B_c:N-j])
 V_block = load_from_global(V[N-j-B_c:N-j])

 // Same computation, but:
 // - Coalesced memory access
 // - Better cache utilization
 S = Q_block @ K_block.T
 P = softmax(S)
 O += P @ V_block

Performance difference:

  • V1: 2.0x vs standard
  • V2: 2.8x vs standard (40% faster!)
### 2. Backward Pass Computation

FLASH ATTENTION V1 Backward:

Problem: Must recompute forward to get gradients

  • Recompute S matrix from Q,K (O(N²) computation)
  • Use standard backward algorithm
  • Separate memory access patterns
  • Inefficient!

Timing:

  • Forward: 10ms (optimized)
  • Backward: 20ms (not optimized, 2x slower!)
  • Total for training: 30ms

FLASH ATTENTION V2 Backward:

Solution: Apply I/O-aware principles to gradients too!

Key insight: Recomputation is okay if done efficiently

  • Recompute S block-by-block (I/O-aware)
  • Compute dP, dQ, dK, dV with blocking
  • Minimize intermediate storage
  • Efficient!

Algorithm structure:

# Simplified backward algorithm v2
for i in range(0, N, B_r):
 Q_block = load(Q[i:i+B_r])
 dQ_block = zeros()

 for j in range(0, N, B_c):
 K_block = load(K[j:j+B_c])
 V_block = load(V[j:j+B_c])

 # Recompute S, P for this block (efficient!)
 S = Q_block @ K_block.T
 P = softmax(S)

 # Compute gradients (in-place, minimizing memory)
 dV_block = P.T @ dO
 dP = dO @ V_block.T
 dS = dP * P * (1 - P) # softmax gradient
 dQ_block += dS @ K_block
 dK_block = dS.T @ Q_block
 dV_block += P.T @ dO

Timing:

  • Forward: 10ms (further optimized)
  • Backward: 12ms (now optimized, nearly equal!)
  • Total for training: 22ms (27% faster!)
### 3. Head Dimension Support

FLASH ATTENTION V1:

Limitations:

  • head_dim ≤ 128 supported
  • Larger head dims fall back to standard attention
  • Restricts model architectures

Why the limit:

  • Shared memory constraints (48-96 KB)
  • Need to store S matrix in shared memory
  • S size: B_r × B_c × sizeof(float)
  • For head_dim > 128: too much shared memory
  • Falls back to standard (slow!)

FLASH ATTENTION V2:

Support improved:

  • head_dim up to 256 with optimization
  • Adaptive memory allocation
  • Uses register blocking for larger dims
  • No fallback to standard attention

Technique: Register blocking for large head_dim

  • Store S in registers instead of shared memory
  • More complex indexing, but works
  • Maintains I/O efficiency
  • Enables support for larger heads
### 4. Batch and Sequence Length Support

FLASH ATTENTION V1:

Issues:

  • Performance sensitive to sequence length
  • Uneven performance across different lengths
  • Some lengths perform worse than others

Example (A100, head_dim=64):

  • seq_len=512: 200 TFLOPS
  • seq_len=1024: 220 TFLOPS
  • seq_len=1536: 160 TFLOPS (dip!)
  • seq_len=2048: 210 TFLOPS
  • Inconsistent behavior

FLASH ATTENTION V2:

More consistent:

  • Optimized block sizes for all lengths
  • Consistent performance across range
  • No pathological cases

Example (A100, head_dim=64):

  • seq_len=512: 250 TFLOPS (better)
  • seq_len=1024: 270 TFLOPS (better)
  • seq_len=1536: 270 TFLOPS (no dip!)
  • seq_len=2048: 280 TFLOPS (better)
  • Consistent, faster performance
### 5. Gradient Accumulation

FLASH ATTENTION V1:

Gradient computation:

  • Requires materialization of full attention matrix P
  • dP = dO @ V.T (N × N matrix in memory!)
  • Compute dS = dP P (1 - P)
  • Complex intermediate storage

Memory usage:

  • Attention matrix P: N² elements
  • Gradient matrix dP: N² elements
  • Total: 2N² temporary storage

FLASH ATTENTION V2:

Improved gradient flow:

  • Accumulate gradients without materializing full matrices
  • dP computed in blocks
  • Gradients reduced directly
  • Avoid large intermediate tensors

Memory usage:

  • Only block-size intermediate storage
  • Scales as O(B_r × B_c) instead of O(N²)
  • Huge reduction for long sequences
-

## Performance Comparison: v1 vs v2

### Benchmark Results

Hardware: A100 GPU Model: Llama 2 7B Batch size: 1

v1 v2 Improvement ───────────────────────────────────────────────── Forward Pass 2.0x 2.8x 40% Backward Pass 1.2x 2.0x 67% Full Forward+Back 1.5x 2.4x 60%

For 256 token generation: V1: 6.4s (2.0x speedup) V2: 4.0s (2.8x speedup) Improvement: 37.5%

Batch size: 32 (continuous batching) V1: 3.2 req/sec V2: 4.5 req/sec Improvement: 40%

### Training Speed Improvement

Training Llama 2 7B on 8x A100 GPUs

Metric Without FA With FA v1 With FA v2 Total Speedup ───────────────────────────────────────────────────────────────────── Tokens/sec/GPU 1000 2000 2800 2.8x Batched TFLOPS 150 300 420 2.8x Wall-clock time 100 hours 50 hours 35 hours 2.86x

Full training (1 trillion tokens):

  • Without FA: 100,000 GPU-hours
  • With FA v1: 50,000 GPU-hours
  • With FA v2: 35,000 GPU-hours
  • Savings with v2: 65% reduction!
### Where v2 Excels Most
  1. Backward Pass (Training):

  2. V1: 1.2x improvement

  3. V2: 2.0x improvement (67% better than v1)

  4. Long Sequences:

  5. V1: 2.0x at 4K tokens

  6. V2: 2.8x at 4K tokens (40% better)

  7. Large Head Dimensions:

  8. V1: Falls back to standard at head_dim > 128

  9. V2: Maintains optimization up to head_dim=256

  10. Numerical Stability:

  11. V1: Generally stable, some edge cases

  12. V2: Better precision, more stable gradients
-

## Implementation Differences

### Installation and Usage

```python
# FLASH ATTENTION V1

from flash_attn import flash_attn_func

# Basic usage
output = flash_attn_func(q, k, v)

# With dropout
output = flash_attn_func(
 q, k, v,
 dropout_p=0.1,
 causal=True
)

# Note

# FLASH ATTENTION V2

from flash_attn import flash_attn_func # Same import!

# Usage is identical (backward compatible)
output = flash_attn_func(q, k, v)

# But now V2 is automatically used:
# - Faster forward pass
# - Faster backward pass
# - Better support for large head dims
# - More stable gradients

# Verify version
import flash_attn
print(flash_attn.__version__) # Should be >= 2.0

CUDA Kernel Differences

FLASH ATTENTION V1 Kernel Structure:

__global__ void flash_attn_fwd_kernel_v1(...) {
 // Load Q block
 // For each K,V block:
 // Compute S in shared memory
 // Compute softmax
 // Accumulate output
}

__global__ void flash_attn_bwd_kernel_v1(...) {
 // Recompute S (I/O overhead!)
 // Compute gradients
 // Complex synchronization
}

FLASH ATTENTION V2 Kernel Structure:

__global__ void flash_attn_fwd_kernel_v2(...) {
 // Improved block ordering
 // Better memory coalescing
 // Optimized for modern GPU architectures
 // Support for larger head dimensions
}

__global__ void flash_attn_bwd_kernel_v2(...) {
 // I/O-aware gradient computation
 // Minimize intermediate materialization
 // Efficient recomputation
 // Better numerical stability
}

Key difference:
V2 kernels are "GPU architecture aware"
 - Detect GPU type (A100 vs H100)
 - Adapt block sizes and memory patterns
 - Auto-tune for best performance

When to Use v2 vs v1

Use Flash Attention v2

Scenarios where v2 shines:

Training (backward pass matters)
 - 60% faster training than v1

Long sequences (> 1K tokens)
 - Consistent 2.8x speedup vs 2x for v1

Large head dimensions (> 128)
 - v1 might fall back to standard

Inference with batching
 - 40% better throughput

Numerical stability critical
 - Better precision and stability

Modern GPUs (A100, H100)
 - Architecture-optimized kernels

Recommendation: Use v2 by default!

Reasons to Stick with v1

Rare cases where v1 might be preferred:

 Very old GPU hardware (< A100)
 - v1 might have better compatibility

 Legacy code that's heavily tested
 - v1 proven to work, not worth updating

 Custom CUDA kernels built on v1
 - Would need rewrite for v2

 Memory constraints (unlikely but possible)
 - v1 uses slightly less memory in some cases

Recommendation: Migrate to v2 (benefits outweigh costs)

Migration from v1 to v2

Is Migration Needed?

Good news: Flash Attention v2 is backward compatible!

Code written for v1 works with v2:
```python
from flash_attn import flash_attn_func

# This code works with both v1 and v2
output = flash_attn_func(q, k, v, causal=True)

# Automatically uses v2 if installed

How to Update

# STEP 1

# Current version
pip install flash-attn

# Specify v2 explicitly
pip install "flash-attn>=2.0"

# Verify installation
python -c "import flash_attn; print(flash_attn.__version__)"

# STEP 2
# v2 is backward compatible with v1

# STEP 3
# - Check training speed (should be faster)
# - Check inference latency (should be better)
# - Run tests to ensure numerical correctness

# Optional
if hasattr(flash_attn, 'flash_attn_kvpacked_func'):
 print("Using Flash Attention v2 (has v2-specific functions)")
else:
 print("Using Flash Attention v1")

-

Real-World Impact: Training Example

Scenario: Fine-tune Llama 2 7B on 100K examples

Setup:
 - Model: Llama 2 7B
 - Batch size: 32
 - Sequence length: 2048
 - Hardware: 8x A100 GPUs
 - Epochs: 3

WITH FLASH ATTENTION V1:

Training time:
 - Tokens processed: 100K × 2048 × 3 = 614.4B tokens
 - Throughput: 200 tokens/sec per GPU (v1 optimized)
 - Total time: 614.4B / 200 / 8 GPUs = ~385 hours
 - Cost: 385 hours × $8 per GPU-hour = $3,080
 - Actual: ~16 days (accounting for overhead)

WITH FLASH ATTENTION V2:

Training time:
 - Same 614.4B tokens
 - Throughput: 280 tokens/sec per GPU (v2, 40% better)
 - Total time: 614.4B / 280 / 8 GPUs = ~275 hours
 - Cost: 275 hours × $8 per GPU-hour = $2,200
 - Actual: ~11 days (accounting for overhead)

Improvement:
 - Time saved: 5 days (31% faster)
 - Cost saved: $880 (29% cheaper)
 - Same quality model!

-

Detailed Algorithm Comparison

Forward Pass Comparison

FLASH ATTENTION V1 - Forward

Algorithm Flash Attention:
for i = 1 to N do
 Load Q_i from HBM // Q block
 m_i  -, l_i  0 // Tracking variables
 for j = 1 to N do
 Load K_j, V_j from HBM
 S_ij  Q_i K_j^T / d

 // Online softmax
 m_ij^new  max(m_i, max_row(S_ij))
 P_ij  exp(S_ij - m_ij^new)
 l_i^new  exp(m_i - m_ij^new) l_i + sum_row(P_ij)

 // Accumulate output
 O_i  exp(m_i - m_ij^new) O_i + P_ij V_j

 // Update tracking
 m_i  m_ij^new
 l_i  l_i^new
 end
 Write O_i to HBM
 Write l_i, m_i to HBM // For backward
end

I/O complexity: O(N × d)
FLOP complexity: O(N² × d)
Peak throughput: 2.0x vs standard

FLASH ATTENTION V2 - Forward

Algorithm Flash Attention V2:
for i = 1 to N step B_r do
 Load Q block from HBM
 m  -, l  0

 for j = N step -B_c do // REVERSED iteration!
 Load K, V blocks from HBM

 S  Q @ K^T (block computation)

 // Improved numerical stability
 m_new  max(m, max_row(S), precision=fp32)
 P  exp(S - m_new)
 l_new  exp(m - m_new) l + sum_row(P)

 // Accumulate output
 O  exp(m - m_new) O + P @ V

 // Update
 m  m_new
 l  l_new
 end
 Write O block to HBM
end

I/O complexity: O(N × d) (same)
FLOP complexity: O(N² × d) (same)
Peak throughput: 2.8x vs standard (40% better!)

Key improvement: Better memory access pattern from reversed iteration
+ Improved numerical precision tracking
+ No separate backward pass overhead

-

Comparison Table: v1 vs v2

Feature Flash Attn v1 Flash Attn v2
──────────────────────────────────────────────────────────
Forward pass speedup 2.0x 2.8x
Backward pass speedup 1.2x 2.0x
Combined training speedup 1.5x 2.4x
Memory reduction 60% 60%
Max head dimension 128 256+
Sequence length support All Better consistency
Numerical stability Good Excellent
Backward compatible N/A Yes (v1 code works)
GPU compatibility A100+ A100+ (better)
Release date May 2022 July 2023
Status Maintained Current standard

Recommendation: Always use v2 (better in every way)

-

Key Differences Summary

Core Algorithm Changes in v2:

1. LOOP STRUCTURE
 - V1: Forward iteration order
 - V2: Reversed K,V iteration (better memory pattern)

2. NUMERICAL PRECISION
 - V1: Float16 tracking
 - V2: Float32 intermediate precision (more stable)

3. BACKWARD PASS
 - V1: Separate optimization
 - V2: Full I/O-aware optimization (like forward)

4. HEAD DIMENSION SUPPORT
 - V1: Limited to 128 (shared memory constraint)
 - V2: Up to 256+ (register blocking)

5. ARCHITECTURE AWARENESS
 - V1: Generic kernel
 - V2: Auto-tunes for specific GPU (A100 vs H100)

Result: 40% faster forward, 67% faster backward

Key Takeaways

Flash Attention v2 is 40% faster than v1 on forward pass 67% faster backward pass (most impactful for training) 2.8x speedup vs standard (vs 2.0x for v1) Backward compatible with v1 code Better support for larger head dimensions (up to 256) Production standard now (use v2 by default)

-

Further Reading

  • Flash Attention v2 Paper: "Flash-2: Faster Causal Attention by Reducing IO with Minimal Overhead"
  • GitHub Repository: github.com/Dao-AILab/flash-attention
  • Blog Post: Improvements explained in detail
  • Related: Flash Attention for v1 details

-