Attention Complexity › Practical Scaling Limits

Michael BrenndoerferUpdated May 29, 20258 min read
Language AI HandbookAll chapters
Part 13 of 67Self-Attention
Chapter 95Attention Complexity › Practical Scaling Limits
Reading section 5 of 11 · Practical Scaling Limits · 8 minSection 5 of 11 · 8 min

Book contents

Section 5 of 11 · 8 min read

Attention Complexity › Practical Scaling LimitsOne section · Sections in this chapter5 / 11 ↓
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

Given the quadratic complexity, what are the practical limits for attention-based models? The answer depends on hardware constraints, training budget, and inference requirements. This section translates the abstract complexity formulas into concrete numbers for specific GPU configurations and model architectures, giving you the tools to estimate whether a particular context length is feasible for a given setup.

Understanding these limits matters for both research and deployment. When designing a new model, you need to know whether your intended context length fits within your training budget. When deploying an existing model, you need to predict how latency and memory usage will scale with input length. When evaluating a new paper's claims about long-context capability, you need to understand what hardware was required and whether those results are reproducible at your scale.

The two primary constraints are memory (does the computation fit in GPU memory?) and time (is the computation fast enough for the application?). These constraints interact in interesting ways: memory-efficient algorithms like FlashAttention can reduce peak memory usage, which allows longer sequences, but they may have different compute patterns than naive attention and do not always achieve the same throughput. Optimizing for memory and optimizing for speed are related but distinct engineering goals.

GPU Memory Constraints

Modern GPUs have 16GB to 80GB of high-bandwidth memory. With fp16 precision (2 bytes per element), the attention matrix for a single layer with 12 heads at sequence length 8192 requires:

M=h×n×n×b=12×8192×8192×2≈1.5 GBM = h \times n \times n \times b = 12 \times 8192 \times 8192 \times 2 \approx 1.5 \text{ GB}

where:

  • MM: memory in bytes
  • h=12h = 12: number of attention heads
  • n=8192n = 8192: sequence length
  • b=2b = 2: bytes per element (fp16)

For a 12-layer model, that's approximately 18GB just for attention matrices, before accounting for model weights, other activations, or gradients. Training requires additional memory for optimizer states (often 2x the model size for Adam) and gradient accumulation. In practice, the 8192-token context would be infeasible to train on a single GPU with this configuration without memory-reduction techniques.

The memory calculation reveals why the 2048-token context that was standard in GPT-3 represented a real constraint. At 2048 tokens, the same 12-head, 12-layer model requires only about 1.1GB for attention matrices, comfortably fitting on most GPUs alongside the model weights and optimizer states. At 4096 tokens this grows to 4.5GB, at 8192 to 18GB, at 16384 to 72GB. The quadratic scaling is starkly visible in these numbers: each doubling of context length requires four times as much memory for the attention component alone.

In[17]:
Code
def estimate_max_seq_len(
    gpu_memory_gb, d, n_heads, n_layers, batch_size=1, safety_factor=0.7
):
    """
    Estimate maximum sequence length that fits in GPU memory.

    Args:
        gpu_memory_gb: Available GPU memory in GB
        d: Model dimension
        n_heads: Number of attention heads
        n_layers: Number of transformer layers
        batch_size: Batch size
        safety_factor: Fraction of memory to use (leave room for overhead)

    Returns:
        Maximum sequence length
    """
    available_bytes = gpu_memory_gb * 1e9 * safety_factor

    # Attention matrices dominate for long sequences
    # Memory per layer: n_heads * n^2 * 2 bytes (fp16)
    # Also need Q, K, V, output: 4 * n * d * 2 bytes
    # Solving for n given memory constraint

    # Simplified: attention matrices = n_heads * n^2 * 2 * n_layers * batch_size
    # n^2 = available_bytes / (n_heads * 2 * n_layers * batch_size)
    n_squared = available_bytes / (n_heads * 2 * n_layers * batch_size)
    max_n = int(np.sqrt(n_squared))

    return max_n


# Common GPU configurations
gpus = [
    {"name": "RTX 3090", "memory": 24},
    {"name": "A100 40GB", "memory": 40},
    {"name": "A100 80GB", "memory": 80},
    {"name": "H100 80GB", "memory": 80},
]

# GPT-2 style model
model_config = {"d": 768, "heads": 12, "layers": 12}
Out[18]:
Console
Maximum Sequence Length Estimates (batch_size=1):

            GPU     Memory     Max Seq Len
------------------------------------------
       RTX 3090         24 GB           7,637
      A100 40GB         40 GB           9,860
      A100 80GB         80 GB          13,944
      H100 80GB         80 GB          13,944

These estimates assume 70% of GPU memory is available for attention matrices (the remaining 30% accounts for model weights and other overhead). An RTX 3090 with 24GB can handle approximately 27K tokens, while an A100 80GB extends this to around 50K tokens. These numbers represent theoretical upper bounds for a single sample with batch size 1. In practice, larger batch sizes for training efficiency, gradient storage for backpropagation, and optimizer states further reduce these limits.

Production systems address these constraints through several techniques working in combination. Gradient checkpointing trades computation for memory by discarding intermediate activations during the forward pass and recomputing them during backpropagation. FlashAttention restructures the computation to avoid materializing the full attention matrix in off-chip memory. Model parallelism distributes computation across multiple GPUs, so the effective memory available for the attention matrices scales with the number of devices. Together, these techniques can extend practical context lengths by an order of magnitude or more beyond the naive estimates, at the cost of engineering complexity and, in some cases, increased training time.

Time Constraints

Beyond memory, compute time imposes practical limits. Processing long sequences is slow, and the slowness compounds in important ways during both training and inference. During training, each batch of long sequences requires proportionally more computation, which means more GPU-hours per training step. During autoregressive inference (where the model generates one token at a time), the situation is particularly challenging: each new token is generated by running a full forward pass over the entire accumulated context, so generating the kk-th token requires a context of length kk. This means that generating a 10,000-token response is not just 10 times more expensive than a 1,000-token response: it involves 10,000 forward passes each processing an increasingly long context, creating a super-linear increase in total generation time.

In[19]:
Code
def estimate_attention_time_ms(n, d, n_heads, n_layers, tflops=100):
    """
    Estimate attention computation time.

    Args:
        n: Sequence length
        d: Model dimension
        n_heads: Number of heads
        n_layers: Number of layers
        tflops: GPU throughput in TFLOPS (100 typical for A100)

    Returns:
        Time in milliseconds
    """
    # Total FLOPs for attention (simplified)
    flops_per_layer = 2 * n * n * d  # Q@K^T and attention@V
    total_flops = flops_per_layer * n_layers

    # Convert TFLOPS to FLOPS
    flops_per_second = tflops * 1e12

    # Time in seconds, then milliseconds
    time_seconds = total_flops / flops_per_second
    time_ms = time_seconds * 1000

    return time_ms


# Timing estimates for various sequence lengths
seq_lens_timing = [512, 1024, 2048, 4096, 8192, 16384, 32768]
timing_results = []
for n in seq_lens_timing:
    time_ms = estimate_attention_time_ms(
        n, d=768, n_heads=12, n_layers=12, tflops=150
    )
    timing_results.append({"n": n, "time_ms": time_ms})
Out[20]:
Console
Estimated Attention Time (GPT-2 Small, A100 GPU):

 Sequence Length    Time (ms)      Tokens/sec
---------------------------------------------
             512         0.03      15,894,572
           1,024         0.13       7,947,286
           2,048         0.52       3,973,643
           4,096         2.06       1,986,821
           8,192         8.25         993,411
          16,384        32.99         496,705
          32,768       131.94         248,353

At 512 tokens, attention completes in a fraction of a millisecond. By 4096 tokens, latency grows but remains acceptable for most applications. At 32K tokens, attention alone approaches double-digit milliseconds per forward pass. The "Tokens/sec" column shows throughput in terms of context size processed: higher values indicate more efficient processing for that context length. During training with backpropagation, multiply these times by roughly 3x. For autoregressive generation where the full context is reprocessed for each new token, long contexts compound these costs significantly.

One practical mitigation for generation latency is key-value (KV) caching. Instead of recomputing all key and value vectors from scratch at each generation step, the system stores the key-value pairs from previous steps and extends them with just the new token's contribution. This reduces the per-step cost from O(n2)O(n^2) (recomputing full attention over the growing context) to O(n)O(n) (computing attention between the new query and all cached keys). KV caching trades memory for time: you must store O(n)O(n) cached values for each layer and head, but you avoid recomputing them. At 32K tokens with a large model, the KV cache itself can consume tens of gigabytes, reintroducing the memory constraint even though the per-step compute is now manageable.

Out[21]:
Visualization
Line plot showing attention time in milliseconds increasing quadratically with sequence length.
Attention computation time scaling with sequence length. The quadratic relationship means that processing time grows rapidly as context length increases, creating practical limits for interactive applications.

The timing curve shows why context length is such a hot topic in LLM development. Crossing the 100ms threshold makes interactive applications sluggish. Crossing 1 second makes them impractical for real-time use.