Attention Complexity: Quadratic Scaling, Memory Limits

Michael BrenndoerferUpdated May 29, 202562 min read

Part of Language AI Handbook

Self-attention has O(n²d) compute and quadratic memory growth. Covers when sparse, linear, and other efficient attention variants reduce those costs.

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

Attention Complexity

The power of self-attention comes with a price. Every token attends to every other token, creating n2n^2 pairwise interactions for a sequence of nn tokens. This quadratic scaling is both the source of attention's strength and its primary limitation. Understanding the computational and memory costs of attention is essential for working with transformers at scale, choosing appropriate model configurations, and knowing when alternative architectures might be necessary.

Think of it this way: if you have a classroom of 30 students and every student must pass a note to every other student, you need 30×30=90030 \times 30 = 900 note exchanges. If the class doubles to 60 students, you need 60×60=360060 \times 60 = 3600 exchanges, four times as many. Self-attention faces exactly this combinatorial growth with every token in a sequence. At modest sequence lengths, this overhead is affordable. At the scale of entire documents, codebases, or long conversations, it becomes the central engineering challenge of modern language models.

The quadratic complexity is not an accident or an oversight. It is a direct consequence of what makes attention powerful: every token can consult every other token in a single operation. There are no intermediaries, no bottlenecks from sequential processing, no information that must travel through a chain of steps before reaching its destination. This directness is why transformers capture long-range dependencies so much more effectively than recurrent networks. The cost is that the number of computations grows as the square of the context, rather than linearly.

In this chapter, we analyze the complexity of attention from multiple angles: time complexity measured in floating-point operations (FLOPs), memory requirements for storing attention weights, and practical scaling limits. We compare attention to recurrent models and explore why the quadratic cost becomes prohibitive for long sequences. We also survey a range of efficient attention variants, which attempt to preserve the quality of full attention while reducing its computational footprint.

By the end of this chapter, you will have a precise, quantitative understanding of where the costs come from, which hardware constraints matter most in practice, and how researchers have approached breaking the quadratic barrier. This understanding is not merely academic. It determines which models you can run, which tasks require specialized infrastructure, and which architectural choices are worth the trade-off at a given scale.

The Quadratic Bottleneck

Self-attention's computational cost is best understood not as a formula but as a structural property of the mechanism itself. Before we count operations, it helps to ask: why is a quadratic cost inevitable here, and is there any way around it?

The answer is that full self-attention is defined as exhaustive pairwise comparison. The mechanism works by asking, for every token, "how relevant is every other token to me right now?" This question cannot be answered without computing a relevance score between every pair. If you skip any pair, you may miss a critical dependency. A pronoun might refer to a noun fifty tokens back. A closing bracket might need to match an opening bracket hundreds of tokens earlier. The mechanism is designed to handle exactly these cases, and handling them requires examining all pairs. The cost is the price of generality.

To understand why attention becomes expensive, we need to trace through exactly what happens when self-attention processes a sequence. The mechanism is elegant but computationally demanding, and the cost comes from a single, fundamental fact: every token must interact with every other token.

Imagine you're in a room with nn people, and everyone needs to have a brief conversation with everyone else. With 10 people, that's 10×10=10010 \times 10 = 100 conversations. With 100 people, it's 100×100=10,000100 \times 100 = 10,000 conversations. The number of interactions grows as the square of the group size. Self-attention faces exactly this scaling challenge.

The quadratic cost follows from the requirement to evaluate all pairwise relevances exactly. There is no clever algorithm that evaluates all pairwise relevances in fewer than O(n2)O(n^2) steps while remaining exact. Algorithms that claim linear attention complexity always involve an approximation: they find a way to avoid computing all pairs exactly, at some cost to expressiveness or accuracy. This distinction between exact and approximate attention is a recurring theme in the efficiency literature, and understanding the quadratic baseline is essential for evaluating those trade-offs.

Out[3]:
Visualization
8x8 attention matrix showing 64 pairwise interactions.
n=8: 64 interactions
16x16 attention matrix showing 256 pairwise interactions.
n=16: 256 interactions
32x32 attention matrix showing 1024 pairwise interactions.
n=32: 1024 interactions

The Three-Stage Computation Pipeline

Self-attention proceeds through three distinct operations, each contributing to the overall computational cost. These three stages correspond exactly to the formula you have seen before in this book, but here we examine them through the lens of cost rather than function. Each stage has a well-defined computational profile, and understanding them separately makes the total complexity easy to build up from first principles.

The three stages are:

  1. Computing attention scores: Each token asks "how relevant is every other token to me?" This requires comparing all nn tokens against all nn tokens, producing an n×nn \times n grid of relevance scores.

  2. Applying softmax: The raw scores are converted into proper attention weights that sum to 1, allowing us to interpret them as a probability distribution. This normalizes along each row of the score matrix.

  3. Computing outputs: Each token gathers information from all other tokens, weighted by the attention scores, to produce its contextual representation. This is a weighted sum over value vectors.

The first and third steps are where the quadratic cost emerges. Step two is also quadratic, but it is dominated by the other two when the model dimension is large. Let's examine each stage carefully, counting operations precisely.

Stage 1: Computing Attention Scores

The heart of self-attention is measuring pairwise relevance between tokens. Given a sequence of nn tokens, we need to compute how strongly each token should attend to every other token. This is done by comparing query vectors against key vectors using the dot product. You can think of the query as a question and the key as an index: the dot product measures how well a token's question matches another token's index entry.

The reason we use a matrix multiplication here, rather than computing each score individually in a loop, is efficiency. Modern hardware, especially GPUs and TPUs, is designed to execute large matrix multiplications at peak throughput. Expressing the operation as a single QKTQK^T multiplication allows the hardware to exploit its full parallelism. The result is still the same n2n^2 numbers, but computed much faster than n2n^2 separate dot products would be.

Mathematically, we arrange all queries into a matrix QQ and all keys into a matrix KK, then compute:

S=QKTS = QK^T

where:

  • SS: the n×nn \times n attention score matrix, where SijS_{ij} measures how much token ii should attend to token jj
  • QQ: the query matrix with shape n×dkn \times d_k, containing one query vector per token
  • KTK^T: the transposed key matrix with shape dk×nd_k \times n, rearranging the key vectors for efficient matrix multiplication
  • nn: the sequence length (number of tokens)
  • dkd_k: the dimension of each query/key vector (typically 64 in standard transformers)

Why does this produce n2n^2 values? Each row of QQ (one token's query) gets multiplied against every column of KTK^T (every token's key), producing one score per pair. With nn rows and nn columns, we get an n×nn \times n grid of scores.

The computational cost of this matrix multiplication is substantial. Each entry in the output matrix requires dkd_k multiplications and dk−1d_k - 1 additions. With n2n^2 entries, the total operation count is n2⋅dkn^2 \cdot d_k.

Multiply-Accumulate Operations

A multiply-accumulate (MAC) operation computes a+b×ca + b \times c and is the fundamental unit of computation in matrix multiplication. When counting FLOPs, we typically count multiplications and additions separately, giving 2 FLOPs per MAC. For simplicity, we often count just multiplications, knowing the total FLOPs is roughly double.

The critical observation is that this score computation already scales as O(n2dk)O(n^2 d_k). The n2n^2 term is unavoidable: we cannot know which token pairs are important without examining all of them. Even in cases where the final attention weights are sparse (most tokens barely attending to most others), we still must compute all the raw scores before we can discover which ones are large. The sparsity only emerges after the computation, not before it.

Stage 2: Normalizing with Softmax

Raw dot products can be any real number, positive or negative, with no upper bound. To use them as weights for a weighted average, we need to transform them into a valid probability distribution. The softmax function accomplishes this by exponentiating each score and normalizing within each row.

The key insight is that softmax operates independently on each row of the score matrix. Each row corresponds to one query token, and its nn scores express how much that token should attend to each of the nn positions. Softmax converts these nn arbitrary numbers into nn non-negative weights that sum to exactly 1. Think of it as converting raw relevance signals into a proper distribution over positions to attend to.

For each token ii, softmax converts its nn raw scores into nn attention weights that sum to 1. This involves:

  1. Computing exp⁡(Sij)\exp(S_{ij}) for each of the nn scores (making them all positive)
  2. Summing these exponentials across the row
  3. Dividing each exponential by the sum

Each row requires approximately 3n3n operations (exponentiate, sum, divide), and with nn rows, the total is O(n2)O(n^2). While still quadratic, this is dominated by the O(n2dk)O(n^2 d_k) cost of score computation when dkd_k is large. In practice, the softmax step is rarely the performance bottleneck, but it does introduce a numerical subtlety: computing exp⁡\exp of large values causes overflow. The standard remedy is to subtract the row maximum before exponentiating, which leaves the softmax output unchanged (the constant cancels in the numerator and denominator) but keeps all values in a numerically safe range.

Stage 3: Computing the Weighted Output

The final step uses the attention weights to compute a weighted combination of value vectors. Each token's output is a blend of all tokens' values, weighted by attention. You can think of this as a soft lookup: instead of retrieving a single value at a specific position, each token retrieves a smooth mixture of all values in the sequence, weighted by how relevant each position was determined to be in Stage 1.

The operation computes a new representation for each of the nn tokens. Each token's new representation aggregates information from every other token in the sequence, scaled by attention weights. Tokens that received high attention scores contribute more to the output; tokens with near-zero attention weight contribute almost nothing.

The formula for this aggregation is:

Output=softmax(S)⋅V\text{Output} = \text{softmax}(S) \cdot V

where:

  • softmax(S)\text{softmax}(S): the n×nn \times n attention weight matrix, with each row summing to 1
  • VV: the value matrix with shape n×dvn \times d_v, containing one value vector per token
  • dvd_v: the dimension of each value vector (typically equal to dkd_k)

This is another matrix multiplication: (n×n)⋅(n×dv)(n \times n) \cdot (n \times d_v) produces an n×dvn \times d_v output. Each of the n⋅dvn \cdot d_v output elements requires nn multiply-accumulate operations (summing across the nn positions), giving n2⋅dvn^2 \cdot d_v total operations.

This step is just as expensive as score computation. Notice that both of the dominant operations involve an n×nn \times n intermediate matrix: in Stage 1 we create this matrix, and in Stage 3 we multiply by it. This n×nn \times n structure is the crux of attention's quadratic scaling, and it is also the primary target of memory-efficient implementations like FlashAttention, which avoid materializing the full n×nn \times n matrix in memory at once.

Putting It Together: Total Complexity

Now that we have analyzed each stage individually, we can combine the costs to get the total complexity of a single self-attention operation. This summary is important because it makes the n2dn^2 d scaling explicit and shows that neither the softmax step nor the dd dimension can offset the quadratic term.

Summing the costs from all three stages:

Total FLOPs=O(n2dk)⏟scores+O(n2)⏟softmax+O(n2dv)⏟output=O(n2(dk+dv))=O(n2d)\text{Total FLOPs} = \underbrace{O(n^2 d_k)}_{\text{scores}} + \underbrace{O(n^2)}_{\text{softmax}} + \underbrace{O(n^2 d_v)}_{\text{output}} = O(n^2(d_k + d_v)) = O(n^2 d)

where:

  • nn: sequence length
  • dkd_k: query/key dimension
  • dvd_v: value dimension
  • dd: model dimension (typically dk=dv=d/hd_k = d_v = d/h for hh attention heads)

The simplification to O(n2d)O(n^2 d) reflects that dkd_k and dvd_v are both proportional to the model dimension dd. In standard transformers, each head operates on d/hd/h dimensions, but since we have hh heads processing in parallel and concatenating their outputs, the total complexity across all heads scales with the full model dimension.

It is worth pausing here to appreciate what this formula tells us. Two factors control the cost: the sequence length squared (n2n^2) and the model dimension (dd). These two factors contribute very differently to the total. Doubling dd doubles the cost. Doubling nn quadruples it. For any sequence where n>1n > 1, the sequence length term dominates. A model with n=2048n = 2048 and d=768d = 768 spends roughly 20482/768≈5,4612048^2 / 768 \approx 5,461 times more computation on the n2n^2 factor than on the dd factor. This asymmetry is exactly what makes sequence length the central bottleneck in transformer scaling.

Worked Example: Computing FLOPs for a Short Sequence

Let's trace through a concrete small example to make the formula visceral. Consider a single-head attention layer with sequence length n=4n = 4 (four tokens), query/key dimension dk=3d_k = 3, and value dimension dv=3d_v = 3.

Stage 1: Score computation. The query matrix QQ has shape 4×34 \times 3 and the key matrix KK has shape 4×34 \times 3. Computing S=QKTS = QK^T produces a 4×44 \times 4 matrix. Each of the 16 entries requires 3 multiplications and 2 additions (a dot product of length 3), totaling 16×3=4816 \times 3 = 48 multiplications.

Stage 2: Softmax. We apply softmax to each of the 4 rows of SS. Each row has 4 elements, requiring 4 exponentiations, 1 sum (over 4 values, so 3 additions), and 4 divisions. Approximately 4×(4+3+4)=444 \times (4 + 3 + 4) = 44 operations.

Stage 3: Output computation. The softmax weight matrix has shape 4×44 \times 4 and the value matrix VV has shape 4×34 \times 3. The product has shape 4×34 \times 3. Each of the 12 output entries requires 4 multiplications and 3 additions, totaling 12×4=4812 \times 4 = 48 multiplications.

The total is dominated by Stages 1 and 3: approximately 48+48=9648 + 48 = 96 multiplications (plus roughly the same number of additions). Using our formula 2n2dk=2×16×3=962n^2 d_k = 2 \times 16 \times 3 = 96, we recover exactly this number. The formula works.

Now compare with scaling to n=8n = 8 (doubling the sequence length). Stage 1 becomes 64×3=19264 \times 3 = 192 multiplications (4x more). Stage 3 becomes 64×3=19264 \times 3 = 192 multiplications (4x more). The total scales by exactly 4x, as the quadratic formula predicts. Every doubling of nn multiplies the operation count by 4, regardless of what dd is. This is the essential arithmetic that makes long sequences expensive.

The Scaling Insight

This analysis reveals the fundamental scaling behavior of self-attention. The two key findings are:

  • Quadratic in sequence length: Doubling nn quadruples the computation. This is the defining characteristic that limits context length.
  • Linear in model dimension: Doubling dd only doubles the computation. Making models wider is relatively cheap.

The quadratic term dominates for long sequences. At n=1000n = 1000 tokens with d=768d = 768 (GPT-2 dimensions), the n2=1,000,000n^2 = 1,000,000 factor far exceeds the d=768d = 768 factor. Understanding this asymmetry helps when reasoning about transformer scalability.

The key insight is that this asymmetry shapes all of modern LLM engineering. When practitioners want more capable models, they often increase model depth and dimension, which costs linearly. When they want models that understand longer documents, they face a quadratic penalty. This is why context length has historically lagged so far behind model parameter counts: a 10x increase in context length requires 100x more attention computation, while a 10x increase in parameters (achieved through wider or deeper models) costs only 10x more. The entire field of efficient attention is essentially an attempt to change this ratio.

Historical Context: The 512-Token Limit

The original BERT model (2018) was trained with a maximum sequence length of 512 tokens. This was not chosen because 512 tokens is semantically sufficient for language understanding. It was chosen because the quadratic cost of attention made longer sequences prohibitively expensive at the compute budgets available at the time. Many downstream tasks required creative workarounds: sliding windows over longer texts, truncation, or chunking into smaller segments. The gradual increase in practical context length from 512 tokens (BERT, 2018) to 2048 tokens (GPT-3, 2020) to 32K (many 2023 models) to 1M+ tokens (Gemini 1.5, 2024) tracks hardware improvements and algorithmic advances in attention efficiency, not changes in what is theoretically possible.

Out[4]:
Visualization
Contour plot with sequence length on x-axis and model dimension on y-axis, showing steeper gradients in the horizontal direction.
Attention complexity as a function of both sequence length (n) and model dimension (d). The contour lines show equal-computation curves. Moving horizontally (increasing n) raises complexity much faster than moving vertically (increasing d), visualizing the quadratic vs linear scaling asymmetry.

Implementation: Counting FLOPs

The theoretical complexity O(n2d)O(n^2 d) tells us about relative growth rates, but it hides constant factors and additive terms that matter enormously in practice. To see the actual numbers, let's build a function that calculates exact FLOPs for self-attention. This exercise converts the abstract complexity formula into concrete operation counts that can be compared against hardware throughput specifications.

The function mirrors our three-stage analysis: score computation contributes n2⋅dn^2 \cdot d operations, softmax contributes 3⋅n23 \cdot n^2 operations, and output computation contributes another n2⋅dn^2 \cdot d operations. Notice that the softmax term is negligible relative to the others when d≫3d \gg 3, which is always true in practice (dd is typically in the hundreds). This confirms that the two matrix multiplications, not the softmax, are the computational bottleneck.

In[5]:
Code
def attention_flops(n, d, include_projections=False):
    """
    Calculate FLOPs for self-attention.

    Args:
        n: Sequence length
        d: Model dimension
        include_projections: Whether to include Q, K, V projections

    Returns:
        Total FLOPs (counting multiplications only)
    """
    # Stage 1: Score computation (Q @ K^T)
    # Matrix multiplication: (n x d) @ (d x n) = n^2 * d multiplications
    score_flops = n * n * d

    # Stage 2: Softmax normalization
    # Per row: exp (n ops), sum (n ops), divide (n ops) = 3n per row
    # Total: 3n * n rows = 3n^2
    softmax_flops = 3 * n * n

    # Stage 3: Output computation (attention @ V)
    # Matrix multiplication: (n x n) @ (n x d) = n^2 * d multiplications
    output_flops = n * n * d

    total = score_flops + softmax_flops + output_flops

    # Optional: include the linear projections that create Q, K, V
    if include_projections:
        # Q, K, V projections: each is (n x d) @ (d x d) = n * d^2
        projection_flops = 3 * n * d * d
        # Output projection: (n x d) @ (d x d) = n * d^2
        output_proj_flops = n * d * d
        total += projection_flops + output_proj_flops

    return total

With this function, we can explore how computational cost scales across different sequence lengths. We'll use GPT-2's model dimension (d=768d = 768) to ground our analysis in real-world numbers.

In[6]:
Code
# Example: GPT-2 style model
d = 768  # Model dimension
seq_lengths = [128, 512, 1024, 2048, 4096, 8192]

flops_data = []
for n in seq_lengths:
    flops = attention_flops(n, d)
    flops_data.append(
        {"seq_len": n, "flops": flops, "flops_billions": flops / 1e9}
    )
Out[7]:
Console
Attention FLOPs for d=768 (model dimension):

Sequence Length           FLOPs     Billions
---------------------------------------------
            128      25,214,976        0.025
            512     403,439,616        0.403
          1,024   1,613,758,464        1.614
          2,048   6,455,033,856        6.455
          4,096  25,820,135,424       25.820
          8,192 103,280,541,696      103.281

The table shows the quadratic explosion in computational cost. At sequence length 128, attention requires roughly 25 million operations. By 1024 tokens (a typical context window), we're performing over 1.6 billion operations. At 8192 tokens, this jumps to nearly 100 billion operations. These numbers are per layer only, so a 12-layer transformer multiplies each value by 12.

To put these numbers in perspective, a modern A100 GPU performs approximately 77 teraFLOPS (trillion floating-point operations per second) in fp16 precision. Dividing the per-layer operation counts by this throughput gives approximate execution times of fractions of a millisecond for short contexts, growing to tens of milliseconds for very long ones. The important realization is that these operation counts are not small: even at 1024 tokens, each layer of attention performs more operations than the entire computation for many classical machine learning algorithms on typical datasets.

Out[8]:
Visualization
Line plot showing exponential growth of FLOPs as sequence length increases from 128 to 8192.
FLOPs scaling for self-attention with model dimension d=768. The quadratic growth means that doubling sequence length quadruples the computational cost. Note the log scale on the y-axis.

The plot confirms the quadratic relationship. Moving from 512 to 1024 tokens quadruples the computation, and moving from 1024 to 2048 quadruples it again. At the right edge of the plot, we're computing nearly 100 billion operations for a single attention layer.

Memory Requirements

Computation is only half the story. Memory consumption often becomes the limiting factor before compute does. Self-attention requires storing several large tensors, and the memory profile of these tensors follows the same quadratic pattern as the computation, with some additional complications that make memory management especially challenging in practice.

Think of memory pressure as a different kind of constraint than compute. With compute, the question is how long something takes. With memory, the question is whether it fits at all. A computation that is slow can still complete given enough time; a computation that exceeds available memory simply fails, often abruptly and without a clear error message. This binary nature of memory constraints makes them especially important to understand before designing or deploying attention-based systems.

During a forward pass, the key memory consumer is the attention weight matrix itself. Every element of this n×nn \times n matrix must be materialized in memory before the output can be computed in the standard implementation. For training, the situation is worse: the entire matrix must be kept in memory throughout the forward pass so that gradients can be computed during the backward pass. For each layer, this quadratic memory cost persists throughout the backward computation rather than appearing as a momentary spike.

The memory bottleneck is what motivated the development of FlashAttention, which we will discuss later in this chapter. The key insight behind FlashAttention is that the forward and backward passes can be restructured to process the attention matrix in small blocks that fit in the GPU's fast on-chip memory (called SRAM), rather than writing the full n×nn \times n matrix to the slower off-chip memory (HBM). This dramatically reduces memory transfers, even though it performs the same total number of floating-point operations.

Attention Matrix Storage

The attention weight matrix has shape n×nn \times n, where nn is the sequence length. This is the dominant memory consumer in standard attention implementations. For a single attention head, this matrix contains n2n^2 elements. With hh heads, we store h⋅n2h \cdot n^2 values. Using 32-bit floats (4 bytes each), the memory requirement is:

Mattn=4⋅h⋅n2 bytesM_{\text{attn}} = 4 \cdot h \cdot n^2 \text{ bytes}

where:

  • MattnM_{\text{attn}}: memory required for attention matrices
  • hh: number of attention heads
  • nn: sequence length
  • 44: bytes per element for 32-bit floats (fp32)

For 16-bit floats (common in modern training with fp16 or bf16), this halves to 2⋅h⋅n22 \cdot h \cdot n^2 bytes. The quadratic dependence on nn means that doubling the sequence length quadruples the memory requirement. This is the same quadratic relationship we saw for compute, but memory is a harder constraint: you can always wait longer for computation, but you cannot wait your way around a memory limit.

Total Memory Breakdown

A complete forward pass through attention requires storing several tensors simultaneously. Understanding this breakdown helps you predict memory usage and design systems that fit within hardware constraints. A complete forward pass through attention requires storing:

  • Query, Key, Value matrices: Each is n×dn \times d, totaling 3nd3nd elements. These scale linearly with sequence length.
  • Attention scores/weights: n2n^2 per head, hn2hn^2 total. These scale quadratically and dominate for long sequences.
  • Output before projection: n×dn \times d elements. Linear in sequence length.
  • Intermediate activations for backpropagation: Often requires keeping the attention matrix and pre-softmax scores in memory for the duration of the backward pass.

During training, gradient computation doubles many of these requirements. The optimizer state, which stores momentum and variance estimates for every parameter in Adam-style optimization, adds further overhead that scales with model size rather than sequence length. The total memory footprint during training is commonly estimated as 12 to 20 bytes per parameter for the model and optimizer states, plus the activation memory (dominated by the attention matrices) which scales with batch size and sequence length.

In[9]:
Code
def attention_memory_gb(n, d, n_heads, n_layers=1, dtype_bytes=2):
    """
    Estimate memory for attention computation.

    Args:
        n: Sequence length
        d: Model dimension
        n_heads: Number of attention heads
        n_layers: Number of transformer layers
        dtype_bytes: 2 for fp16/bf16, 4 for fp32

    Returns:
        Memory in gigabytes
    """
    # Per layer memory
    # Q, K, V: 3 * n * d
    qkv_mem = 3 * n * d * dtype_bytes

    # Attention matrices (per head): n * n, total: n_heads * n * n
    attn_mem = n_heads * n * n * dtype_bytes

    # Output: n * d
    output_mem = n * d * dtype_bytes

    # For training, need to store attention weights for backward pass
    # This is often the dominant term
    layer_mem = qkv_mem + attn_mem + output_mem

    total_bytes = layer_mem * n_layers
    return total_bytes / (1024**3)


# Typical model configurations
configs = [
    {"name": "GPT-2 Small", "d": 768, "heads": 12, "layers": 12},
    {"name": "GPT-2 Medium", "d": 1024, "heads": 16, "layers": 24},
    {"name": "GPT-2 Large", "d": 1280, "heads": 20, "layers": 36},
    {"name": "13B Model", "d": 5120, "heads": 40, "layers": 40},
]

seq_lengths_mem = [512, 2048, 8192, 32768]
Out[10]:
Console
Memory Requirements for Attention (fp16, per sample):

Model              Seq Len  Memory (GB)
----------------------------------------
GPT-2 Small            512         0.11
GPT-2 Small          2,048         1.27
GPT-2 Small          8,192        18.56

GPT-2 Medium           512         0.28
GPT-2 Medium         2,048         3.38
GPT-2 Medium         8,192        49.50

The memory requirements reveal the quadratic scaling in action. For GPT-2 Small, moving from 512 to 2048 tokens increases memory by roughly 16x (from ~0.2GB to ~3GB). At 8192 tokens, even the smaller model requires substantial memory. For GPT-2 Medium with its additional layers and heads, the numbers are even larger. These estimates cover only attention matrices and exclude model weights, optimizer states, and gradient storage needed during training.

Out[11]:
Visualization
Heatmap showing memory in GB with sequence length on x-axis and model size on y-axis, darker colors indicating higher memory.
Memory requirements for attention matrices across different sequence lengths and model sizes. The quadratic scaling in sequence length creates a steep cliff where memory becomes prohibitive.

The heatmap shows that memory grows quadratically with sequence length but only linearly with model size. Doubling from 2048 to 4096 tokens has a larger impact than moving from GPT-2 Small to GPT-2 Large. For truly long sequences, even small models hit memory limits.

Attention vs. RNN Complexity

To appreciate why the quadratic cost matters, let's compare attention to recurrent neural networks. Both architectures process sequences, but their computational patterns differ fundamentally. Understanding this comparison is important for two reasons. First, it contextualizes when attention's quadratic cost is a problem versus when it is irrelevant. Second, it makes precise the trade-off that motivated the transformer architecture: you pay more computation per token in exchange for dramatically shorter information paths.

Before transformers, recurrent networks (LSTMs and GRUs) were the dominant architecture for sequence modeling. These networks processed sequences token by token, maintaining a fixed-size hidden state that summarized everything seen so far. This sequential nature made them efficient in terms of total operations, but it created a critical weakness: information from early in the sequence had to propagate through many time steps to influence late positions. In a 1000-token sequence, information from the first token must survive 999 update steps to reach the last position. Each step involves a learned transformation, and information tends to degrade through this chain, a problem known as the vanishing gradient.

Attention sidesteps this problem entirely by connecting any two positions directly in a single operation. The cost of this directness is the quadratic scaling. Whether this trade-off is worth it depends on the sequence length, the nature of the task, and whether the long-range dependencies you care about are dense enough to justify the overhead.

RNN Complexity Analysis

The recurrent architecture's cost structure is fundamentally different from attention's. Rather than computing all pairwise interactions at once, an RNN processes tokens one at a time, updating a hidden state at each step. This sequential structure has practical consequences for both complexity and parallelizability.

An RNN with hidden dimension dd processes each of nn tokens sequentially. At each timestep tt, the hidden state update follows:

ht=σ(Whht−1+Wxxt+b)h_t = \sigma(W_h h_{t-1} + W_x x_t + b)

where:

  • hth_t: hidden state at timestep tt (dimension dd)
  • WhW_h: hidden-to-hidden weight matrix (shape d×dd \times d)
  • ht−1h_{t-1}: previous hidden state (dimension dd)
  • WxW_x: input-to-hidden weight matrix (shape d×dd \times d, assuming input dimension matches dd)
  • xtx_t: input at timestep tt
  • σ\sigma: activation function (e.g., tanh)

The computational cost per timestep is:

  • Matrix-vector multiplication Whht−1W_h h_{t-1}: O(d2)O(d^2) operations
  • Matrix-vector multiplication WxxtW_x x_t: O(d2)O(d^2) operations
  • Activation function: O(d)O(d) operations

Total per step: O(d2)O(d^2). For nn tokens: O(nd2)O(nd^2).

RNN complexity is linear in sequence length and quadratic in hidden dimension. This is a notable contrast with attention: the RNN pays its quadratic cost in the model dimension, while attention pays its quadratic cost in the sequence length. The choice of architecture is essentially a choice about which resource you can afford to spend quadratically.

Comparative Analysis

Summarizing the complexity of each architecture:

  • Attention: O(n2d)O(n^2 d)
  • RNN: O(nd2)O(nd^2)

where nn is sequence length and dd is the hidden/model dimension.

The key insight is that these two complexities share the same functional form with nn and dd swapped. Attention is quadratic in nn, linear in dd. RNN is linear in nn, quadratic in dd. This symmetry is not coincidental: it reflects the fundamental trade-off between sequential processing (which pays a fixed cost per step but must be sequential) and parallel processing (which can run in parallel but must examine all pairs).

To find when these costs are equal, we set n2d=nd2n^2 d = nd^2 and solve for nn:

n2d=nd2n^2 d = nd^2

Dividing both sides by ndnd (assuming n,d>0n, d > 0):

n=dn = d

This crossover point has a clear interpretation: when sequence length exceeds model dimension (n>dn > d), attention becomes more expensive. When model dimension exceeds sequence length (n<dn < d), RNN becomes more expensive. In practice, modern language models have dimensions of 512 to 4096, meaning that attention is more expensive than RNN for any sequence longer than a few hundred to a few thousand tokens. Since many practical tasks involve sequences longer than that, this crossover is reached regularly in real workloads.

In[12]:
Code
def compare_complexity(n_values, d):
    """Compare attention vs RNN complexity."""
    results = []
    for n in n_values:
        attention = n * n * d  # O(n^2 d)
        rnn = n * d * d  # O(n d^2)
        results.append(
            {
                "n": n,
                "attention": attention,
                "rnn": rnn,
                "ratio": attention / rnn,
            }
        )
    return results


# Compare for typical model dimension
d = 512
n_values = [64, 128, 256, 512, 1024, 2048, 4096]
comparison = compare_complexity(n_values, d)
Out[13]:
Console
Complexity Comparison (d = 512):

 Seq Len (n)       Attention             RNN   Attn/RNN
-------------------------------------------------------
          64       2,097,152      16,777,216       0.12x
         128       8,388,608      33,554,432       0.25x
         256      33,554,432      67,108,864       0.50x
         512     134,217,728     134,217,728       1.00x
        1024     536,870,912     268,435,456       2.00x
        2048   2,147,483,648     536,870,912       4.00x
        4096   8,589,934,592   1,073,741,824       8.00x

Crossover point: n = d = 512

The ratio column reveals the crossover behavior. At n=64n = 64 and n=128n = 128, attention is 0.13x and 0.25x the cost of an RNN, respectively, making attention the more efficient choice. At n=512n = 512 (exactly equal to dd), both architectures have identical cost (1.00x ratio). Beyond this point, attention becomes increasingly expensive: at n=4096n = 4096, attention requires 8x more operations than an equivalent RNN.

Out[14]:
Visualization
Line plot comparing attention O(n^2 d) and RNN O(nd^2) complexity, crossing at the model dimension.
Computational complexity comparison between self-attention and RNN as sequence length varies. The lines cross at n=d (512 in this example), after which attention's quadratic cost dominates.

The crossover behavior has practical implications. For short sequences (a few hundred tokens), attention's overhead is minimal and often smaller than the equivalent RNN. For long documents, books, or conversation histories spanning thousands of tokens, the quadratic cost becomes the dominant bottleneck. This explains the historical dominance of transformers for classification and short generation tasks, alongside early proposals to handle long sequences through hierarchical chunking: run attention on small windows, then run attention again over the chunk summaries. The quadratic cost was "tamed" rather than solved until more principled efficient attention methods arrived.

Path Length vs. Computation Trade-off

Why use attention despite its higher cost at long sequences? The answer lies in path length, a concept that quantifies how "far" information must travel to flow from one part of a sequence to another. In an RNN, information from token ii to token jj must traverse ∣j−i∣|j - i| sequential steps, each involving a learned transformation that can potentially lose or distort information. In attention, any two tokens connect in a single step, with no intermediaries.

Think of it as the difference between a telephone game and a conference call. In the telephone game (RNN), a message passes through a chain of people, each one rephrasing it before passing it on. After 50 iterations, the message may be unrecognizable. In a conference call (attention), everyone hears the original speaker directly, regardless of how far apart they are. The conference call is more expensive to organize for large groups, but it preserves message fidelity perfectly.

In[15]:
Code
# Trade-off analysis: what do we gain for the extra compute?
def analyze_tradeoff(n, d):
    """Analyze the compute/path-length trade-off."""
    # Maximum path length
    rnn_max_path = n - 1  # First to last token
    attention_max_path = 1  # Always direct

    # Average path length (uniform distribution of source/target)
    rnn_avg_path = (n - 1) / 2
    attention_avg_path = 1

    # Compute ratio
    compute_ratio = (n * n * d) / (n * d * d)

    # Path length improvement
    path_improvement = rnn_avg_path / attention_avg_path

    return {
        "compute_ratio": compute_ratio,
        "path_improvement": path_improvement,
        "efficiency": path_improvement / compute_ratio,
    }


tradeoff_results = []
for n in [128, 256, 512, 1024, 2048]:
    result = analyze_tradeoff(n, d=512)
    result["n"] = n
    tradeoff_results.append(result)
Out[16]:
Console
Compute vs Path Length Trade-off (d = 512):

   Seq Len   Compute Ratio    Path Improve   Efficiency
-------------------------------------------------------
       128            0.25x            63.5x      254.000
       256            0.50x           127.5x      255.000
       512            1.00x           255.5x      255.500
      1024            2.00x           511.5x      255.750
      2048            4.00x          1023.5x      255.875

The "Efficiency" column (path improvement divided by compute ratio) quantifies the value proposition of attention. At n=128n = 128, we pay only 0.25x the compute of an RNN while gaining 63x shorter average paths, yielding an efficiency of 252. At n=512n = 512 (the crossover point), we pay 1x compute for 255x path improvement. At n=2048n = 2048, we pay 4x more compute but get 1023x shorter paths. Notice that efficiency remains nearly constant around d/2=256d/2 = 256. This reveals an important insight: as sequence length grows, both the path improvement and the compute penalty scale proportionally with nn. Attention's value proposition stays roughly constant regardless of sequence length. The short path lengths are always worth the extra compute.

This constancy is a powerful argument for attention. One might worry that at very long sequences, the quadratic overhead makes the mechanism unjustifiable. But the analysis shows otherwise: longer sequences also mean proportionally longer average RNN paths, so the benefit of short connections grows in proportion to the cost. The relative advantage of direct connections remains stable even as absolute costs grow. This is why the field did not abandon attention as sequences grew longer; instead, it found ways to make attention cheaper.

Practical Scaling Limits

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.

The Attention Bottleneck

The combination of quadratic time and space complexity creates what is known as the attention bottleneck. As we push for longer context windows, attention becomes the dominant cost, often exceeding all other components combined. This section makes that claim precise by computing the actual FLOP distribution across transformer components at various sequence lengths.

Understanding this distribution is important because it reveals where optimization effort should be directed. At short sequences, optimizing attention provides minimal benefit; the feed-forward network is the bottleneck and any attention speedup is drowned out. At long sequences, the situation reverses completely: you could make the feed-forward network infinitely fast and still be limited by attention's quadratic cost. Knowing which regime you are in determines whether efficient attention variants are worth the implementation complexity.

The crossover happens gradually. Think of a transformer layer as a budget of operations. At short sequences, the n2dn^2 d attention cost is small relative to the nd2n d^2 cost of two linear layers in the feed-forward network. As sequence length grows, the attention cost grows quadratically while the feed-forward cost grows only linearly. At some crossover sequence length, attention overtakes the feed-forward as the dominant component, and beyond that point, attention efficiency is what matters most.

In[22]:
Code
def layer_component_flops(n, d, ffn_mult=4):
    """
    Break down FLOPs by transformer layer component.

    Args:
        n: Sequence length
        d: Model dimension
        ffn_mult: FFN hidden dimension multiplier (typically 4)

    Returns:
        Dict of FLOPs per component
    """
    # Attention
    attention_flops = 2 * n * n * d  # Q@K^T and attention@V
    qkv_proj_flops = 3 * n * d * d  # Q, K, V projections
    output_proj_flops = n * d * d  # Output projection

    # Feed-forward network
    ffn_hidden = d * ffn_mult
    ffn_flops = 2 * n * d * ffn_hidden  # Two linear layers

    return {
        "attention_core": attention_flops,
        "qkv_projection": qkv_proj_flops,
        "output_projection": output_proj_flops,
        "feed_forward": ffn_flops,
    }


# Analyze at different sequence lengths
d = 768
analysis_results = []
for n in [256, 512, 1024, 2048, 4096]:
    flops = layer_component_flops(n, d)
    total = sum(flops.values())
    analysis_results.append(
        {
            "n": n,
            "attention_pct": (flops["attention_core"] / total) * 100,
            "projections_pct": (
                (flops["qkv_projection"] + flops["output_projection"]) / total
            )
            * 100,
            "ffn_pct": (flops["feed_forward"] / total) * 100,
        }
    )
Out[23]:
Console
Transformer Layer FLOP Distribution (d=768):

   Seq Len    Attention    Projections        FFN
--------------------------------------------------
       256         5.3%          31.6%      63.2%
       512        10.0%          30.0%      60.0%
      1024        18.2%          27.3%      54.5%
      2048        30.8%          23.1%      46.2%
      4096        47.1%          17.6%      35.3%

The percentages reveal how computational burden shifts with sequence length. At 256 tokens, attention accounts for only about 5% of layer computation, with the feed-forward network dominating at around 69%. By 1024 tokens, attention grows to roughly 17%, and at 4096 tokens it reaches approximately 45%, nearly matching the FFN. The projection layers (QKV and output) remain relatively stable since they scale linearly with nn. This distribution explains why optimizing attention becomes critical for long-context applications while being less important for short sequences.

In practice, this means that for tasks like sentiment classification or named entity recognition with inputs under 512 tokens, attention optimizations provide almost no benefit. But for tasks like document summarization, code generation over large codebases, or multi-turn conversation with long history, attention is the dominant cost and any improvement there directly translates to faster, cheaper systems. The research on efficient transformers is driven almost entirely by the long-context use cases where this bottleneck is acute.

Out[24]:
Visualization
Stacked area chart showing attention growing from 20% to 50% of computation as sequence length increases.
Distribution of FLOPs across transformer layer components as sequence length increases. Attention core (the n^2 operation) grows to dominate at longer sequences, while feed-forward layers shrink proportionally.

This visualization shows the attention bottleneck in action. The red region (attention core) expands as sequence length grows, squeezing out other components. This is why efficient attention mechanisms are such an active research area. Notice that the transition is not sharp: even at 4096 tokens, feed-forward operations still account for a substantial fraction of computation. But the trend is clear, and at 8192 tokens and beyond, attention has become the defining cost.

Efficient Attention Variants

The quadratic complexity of standard attention has motivated an entire subfield of research into more efficient alternatives. Understanding these options is important for two reasons: it shows you what trade-offs are available when quadratic attention becomes prohibitive, and it illustrates the creative approaches researchers have taken to break the n2n^2 barrier without abandoning the core insight that all-pairs interaction is useful.

The key challenge in designing efficient attention is that you cannot simply compute fewer interactions without risking the loss of important long-range dependencies. Different variants make different assumptions about which interactions are dispensable. Linear attention methods assume that the interaction pattern can be approximated using lower-rank representations. Sparse attention methods assume that each token only needs to attend to a small subset of positions. Local attention methods assume that most relevant context is nearby, with occasional global dependencies handled by special mechanisms. Each assumption is correct in some settings and wrong in others.

Think of the trade-off this way: standard attention is like an exhaustive library search that checks every single book for relevance. It never misses anything, but it is expensive. Efficient attention variants are like using an index, relying on subject headings, or trusting that nearby shelves are most relevant. Each strategy can dramatically reduce the search effort, at the risk of occasionally missing an important reference that the full search would have found.

While a detailed treatment of each variant deserves its own chapter, this comparison puts the complexity analysis in context and helps you evaluate which variant is appropriate for a given task.

Linear Attention Approximations

Several methods approximate attention with linear complexity by avoiding the explicit n×nn \times n matrix. The core insight is that softmax attention can be written as a similarity function between queries and keys, and certain similarity functions can be decomposed into feature maps that allow the computation to be reordered. Instead of computing (QKT)V(QK^T)V (which requires the n×nn \times n intermediate), these methods compute Q(KTV)Q(K^TV), where the parenthesized term has shape d×dd \times d rather than n×nn \times n.

  • Linformer: Projects keys and values to a fixed low-dimensional space before computing attention. If keys and values are projected to kk dimensions where k≪nk \ll n, the attention matrix becomes n×kn \times k rather than n×nn \times n, achieving O(nk)O(nk) complexity with kk as a fixed hyperparameter.
  • Performer: Uses random feature maps to approximate softmax attention in O(n)O(n) time. The random features are chosen so that their inner product approximates the exponential kernel that softmax attention implicitly computes.
  • Linear Transformers: Replace softmax with kernel functions (such as the ReLU feature map) that allow associative computation. Because the kernel decomposes as a product of query and key features, the computation can be reordered to avoid the n×nn \times n matrix.

These methods trade accuracy for speed, often working well in practice despite theoretical approximation. The main limitation is that removing the softmax normalization can destabilize training, and the approximation quality degrades for queries that are far from the key distribution. In practice, linear attention methods often underperform full attention on tasks requiring precise long-range matching, while performing comparably on tasks where approximate retrieval is sufficient.

Sparse Attention Patterns

Instead of attending to all positions, sparse methods attend to a carefully chosen subset. The key question is which subset to choose: a random subset, a structured subset based on proximity, or a learned subset based on content. Different choices lead to different architectural properties.

  • Longformer: Combines local windowed attention (each token attends to nearby tokens within a window) with global attention on special tokens like [CLS]. The global tokens can attend to and be attended by all positions. This provides pathways for long-range information while keeping most attention local.
  • BigBird: Uses a combination of random and window attention with global patterns. The theoretical motivation is that this combination is sufficient to preserve the expressive power of full attention for most practical tasks, at a fraction of the cost.
  • Sparse Transformers: Learn which positions to attend to, adapting the attention pattern to the data rather than fixing it in advance. This is more powerful but introduces training complexity.

Sparse attention reduces complexity to O(n⋅k)O(n \cdot k), where:

  • nn: sequence length
  • kk: number of positions each token attends to (typically k≪nk \ll n)

For example, with a local window of 256 tokens, k=256k = 256 regardless of sequence length, making the complexity effectively linear in nn. The key assumption behind sparse attention is that most relevant context for any given token is local, with occasional non-local dependencies that can be handled through global token mechanisms or random connections. This assumption holds well for natural language, where adjacent sentences are usually more relevant than distant ones, but may fail for tasks requiring systematic long-range reasoning such as mathematical proofs or algorithmic reasoning over large structures.

Memory and Caching Approaches

A third class of approaches does not approximate the attention function at all, but instead restructures the computation to be more hardware-efficient. These methods compute exactly the same attention as the standard algorithm but do so in a way that reduces the amount of data moved between different memory levels on the GPU.

  • FlashAttention: Reorders operations to minimize memory transfers between GPU high-bandwidth memory (HBM) and the faster on-chip SRAM. Standard attention writes the n×nn \times n attention matrix to HBM and reads it back for the output computation. FlashAttention instead tiles the computation so that each block of the attention matrix is computed, used for the corresponding output block, and discarded without ever writing to HBM. The result is identical to full attention mathematically, but achieves 2 to 4x speedup due to reduced memory bandwidth, with memory usage O(n)O(n) rather than O(n2)O(n^2).
  • KV Caching: During autoregressive generation, caches key-value pairs from all previous tokens to avoid recomputing them at each step. The cache grows linearly with the number of generated tokens and must be stored in memory for the duration of generation. For long outputs, KV cache management (including eviction strategies when the cache exceeds memory) becomes a significant engineering challenge.
  • Gradient Checkpointing: Trades computation for memory during training by discarding intermediate activations after the forward pass and recomputing them from saved checkpoints during backpropagation. This reduces activation memory from O(n2L)O(n^2 L) (proportional to all layers and sequence length squared) to roughly O(n2L)O(n^2 \sqrt{L}) with some computational overhead.
In[25]:
Code
def compare_attention_variants(n, d, k=None):
    """
    Compare complexity of attention variants.

    Args:
        n: Sequence length
        d: Model dimension
        k: Sparsity parameter (positions attended to)
    """
    if k is None:
        k = int(np.sqrt(n))  # Common choice: sqrt(n) sparse positions

    return {
        "Standard": n * n * d,
        "Linear": n * d * d,
        "Sparse": n * k * d,
        "Window (w=256)": n * 256 * d,
    }


# Compare at long sequence
n = 16384
d = 768
variants = compare_attention_variants(n, d)
Out[26]:
Console
Attention Variant Complexity (n=16,384, d=768):

Variant                        FLOPs    Speedup
------------------------------------------------
Standard             206,158,430,208        1.0x
Linear                 9,663,676,416       21.3x
Sparse                 1,610,612,736      128.0x
Window (w=256)         3,221,225,472       64.0x

The speedup factors demonstrate why efficient attention variants are essential for long sequences. Standard attention at 16K tokens requires over 200 billion operations. Linear attention, which trades the n2n^2 term for d2d^2, achieves roughly a 21x speedup by avoiding the explicit attention matrix. Sparse attention with k=n≈128k = \sqrt{n} \approx 128 positions achieves similar gains. Windowed attention with a fixed 256-token window provides approximately 64x speedup, making it highly effective for tasks where local context dominates. These variants enable practical processing of long documents, book-length texts, and extended conversations that would be prohibitively expensive with standard attention.

In practice, the right choice depends heavily on the task. For document understanding where local context dominates and occasional long-range references are acceptable to miss, windowed attention is often the best trade-off. For tasks requiring precise long-range matching (such as retrieving a specific fact from a long document), full attention or FlashAttention is preferable, with length constraints managed through other means. For very long sequences in production systems, combinations of approaches are often used: FlashAttention for exact computation within a maximum context budget, combined with retrieval mechanisms that pre-select relevant context from a larger pool. Understanding the underlying complexity analysis helps you reason about which combination makes sense for your specific constraints.

Out[27]:
Visualization
Line plot comparing standard quadratic attention against linear, sparse, and windowed attention variants, showing the divergence at long sequence lengths.
Complexity scaling of different attention mechanisms across sequence lengths. Standard attention (red) shows quadratic growth, while linear, sparse, and windowed variants remain subquadratic, enabling much longer context windows.

Limitations and Impact

The quadratic complexity of attention is both its greatest strength and most significant limitation. The all-pairs computation enables transformers to capture any dependency regardless of distance, but it also creates hard limits on context length that no amount of hardware can fully overcome. This section synthesizes the practical implications of everything we have analyzed and discusses the broader impact on how language model systems are designed.

For practitioners, understanding these complexity bounds is essential for making informed architecture decisions. When processing short sequences (a few hundred tokens), attention overhead is negligible compared to feed-forward layers. In this regime, you can use standard attention without concern and should focus optimization efforts elsewhere (model quantization, batching strategy, caching). However, when context length grows into the thousands or tens of thousands, attention becomes the dominant cost. You need to consider sparse attention variants, linear approximations, or hierarchical processing strategies. The crossover point depends on your model size, but for typical configurations, it occurs around 512 to 1024 tokens.

Memory constraints often bite before compute constraints do. During training, storing attention matrices for backpropagation requires O(n2)O(n^2) memory per layer. This quadratic memory scaling is why long-context training requires techniques like gradient checkpointing (trading compute for memory), FlashAttention (reordering operations to reduce memory), or model parallelism (distributing across multiple GPUs). The memory bottleneck also affects inference, especially when serving many concurrent requests with long contexts. A server handling 100 simultaneous requests each with 32K tokens of context must maintain 100 separate KV caches, each potentially tens of gigabytes in size. At scale, the memory management of attention caches becomes a primary concern for inference infrastructure engineers.

The complexity analysis also explains why efficient attention is such an active research area. Linearizing attention, sparsifying attention patterns, and approximating attention with lower-rank structures are all attempts to break the quadratic barrier while preserving the benefits of all-pairs interaction. These techniques have enabled models with 100K+ token context windows, but they often involve trade-offs in accuracy or increased implementation complexity. Understanding when these trade-offs are acceptable requires understanding the underlying complexity.

Quadratic scaling does not prevent building very capable models. GPT-4 and systems such as Gemini or Claude all use standard or near-standard attention with large context windows. The cost is paid through massive compute infrastructure, carefully optimized implementations (especially FlashAttention), and systems engineering that manages memory efficiently. The quadratic cost is a hard physical constraint, but it is a constraint that engineering can work around at sufficient scale. What it does mean is that every doubling of context length requires a four-fold increase in attention compute and memory, making each step toward longer context exponentially more expensive than the last.

Summary

Attention's quadratic complexity is a fundamental characteristic that shapes how we build and use transformer models. In this chapter, we analyzed this complexity from multiple angles, built precise formulas for estimating real-world costs, compared attention to recurrent alternatives, and surveyed a range of efficient attention methods.

Key takeaways:

  • Time complexity: Standard self-attention requires O(n2d)O(n^2 d) operations, where nn is sequence length and dd is model dimension. The n2n^2 term means doubling sequence length quadruples computation. This is not an approximation or a worst-case bound: it is the exact cost of the all-pairs interaction that defines the mechanism.

  • Memory complexity: Storing attention matrices requires O(h⋅n2)O(h \cdot n^2) memory, where hh is the number of heads. This quadratic memory scaling often becomes the limiting factor before compute does. Doubling context length quadruples memory for attention matrices, which is why GPU memory capacity is a central constraint in long-context systems.

  • Three-stage decomposition: The cost breaks down as score computation (n2dkn^2 d_k operations), softmax normalization (3n23n^2 operations), and output aggregation (n2dvn^2 d_v operations). The softmax is negligible; the two matrix multiplications dominate.

  • Crossover with RNN: Setting n2d=nd2n^2 d = nd^2 shows that attention is cheaper than RNN when n<dn < d, but more expensive when n>dn > d. For typical model dimensions (512 to 4096), attention becomes more expensive beyond a few hundred tokens. Despite this, attention is preferred because path lengths are O(1)O(1) rather than O(n)O(n), making long-range dependencies much easier to learn.

  • Practical limits: GPU memory and compute time impose hard constraints on context length. Even with 80GB GPUs, sequence lengths beyond tens of thousands of tokens require specialized techniques. The memory bottleneck typically arrives before the compute bottleneck at realistic batch sizes.

  • The attention bottleneck: As sequence length grows, attention dominates transformer computation. At 256 tokens, attention is only 5% of layer FLOPs. At 4096 tokens, it approaches 45%. Beyond 8192 tokens, attention is the primary cost component in each transformer layer.

  • Efficient alternatives: Linear attention, sparse attention, and memory-efficient implementations (especially FlashAttention) can reduce effective complexity from O(n2)O(n^2) to O(n)O(n) or O(n⋅k)O(n \cdot k), where kk is the number of attended positions. FlashAttention achieves the same result as full attention with less memory by restructuring computation to minimize data movement between memory tiers.

Understanding these complexity characteristics is essential for choosing appropriate architectures, estimating computational requirements, and knowing when to apply optimization techniques. The quadratic wall is real, but the right tools and understanding can help keep it manageable. The progression from 512-token BERT to million-token Gemini reflects hardware improvements and algorithms such as FlashAttention, supported by engineering that keeps the n2n^2 term manageable without sacrificing the expressive power that makes attention valuable in the first place.

Key Parameters for Complexity Analysis

When analyzing or estimating attention complexity, several parameters directly impact computational and memory requirements. Understanding how each one affects cost allows you to make informed choices when designing or selecting models.

  • n (sequence length): The number of tokens in the input sequence. This is the most critical parameter since complexity scales quadratically with it. Typical values range from 512 to 128K+ in modern models. Because of the quadratic relationship, changes to this parameter have a much larger effect on total cost than changes to any other. Changing nn from 2K to 8K quadruples attention cost; no other parameter has this multiplier effect.

  • d (model dimension): The embedding dimension of the model. Common values include 768 (GPT-2 Small), 1024 (GPT-2 Medium), 4096 (LLaMA 7B), and larger for bigger models. Complexity scales linearly with this parameter. Making models wider is relatively cheap compared to making them longer-context.

  • h (number of heads): The number of attention heads. Each head operates on d/hd/h dimensions. More heads mean more parallel attention patterns but also more attention matrices to store. Critically, the total FLOPs for multi-head attention are the same as for a single head with the full dimension, because the reduction in per-head dimension exactly offsets the increase in number of heads. However, memory scales with hh because all hh attention matrices must be stored simultaneously.

  • L (number of layers): Total transformer layers. Memory and compute scale linearly with layer count, so a 24-layer model requires twice the resources of a 12-layer model. Adding layers is one of the "cheap" ways to increase model capacity: it costs linearly and does not interact with the quadratic sequence-length term.

  • dtype_bytes: Precision of floating-point representation. fp32 uses 4 bytes, fp16/bf16 use 2 bytes. Half precision halves memory requirements and often doubles throughput on modern GPUs. Moving from fp32 to bf16 is one of the simplest ways to double your effective context capacity within a fixed memory budget.

  • batch_size: Number of sequences processed simultaneously. Memory scales linearly with batch size, often requiring batch size reduction for long sequences. At very long context lengths, batch sizes of 1 or 2 are common in production, meaning the full GPU memory is devoted to a single request's attention matrices and KV cache.

Quiz

Ready to test your understanding? Take this quick quiz to reinforce what you've learned about attention complexity and efficient alternatives.

Attention Complexity

Question 1 of 100 of 10 completed
What is the time complexity of self-attention with sequence length n and model dimension d?

Comments

No comments yet. Be the first to share your thoughts!

Reference

Citation details

Cite or share this article.

BIBTEXAcademic
@misc{brenndoerfer2025attentioncomplexity, author = {Michael Brenndoerfer}, title = {Attention Complexity: Quadratic Scaling, Memory Limits}, year = {2025}, url = {https://mbrenndoerfer.com/writing/attention-complexity-quadratic-scaling-memory-efficient-transformers}, organization = {mbrenndoerfer.com}, note = {Accessed: 2026-09-30} }
APAAcademic
Michael Brenndoerfer (2025). Attention Complexity: Quadratic Scaling, Memory Limits. Retrieved from https://mbrenndoerfer.com/writing/attention-complexity-quadratic-scaling-memory-efficient-transformers
MLAAcademic
Michael Brenndoerfer. "Attention Complexity: Quadratic Scaling, Memory Limits." 2026. Web. September 30, 2026. <https://mbrenndoerfer.com/writing/attention-complexity-quadratic-scaling-memory-efficient-transformers>.
CHICAGOAcademic
Michael Brenndoerfer. "Attention Complexity: Quadratic Scaling, Memory Limits." Accessed September 30, 2026. https://mbrenndoerfer.com/writing/attention-complexity-quadratic-scaling-memory-efficient-transformers.
HARVARDAcademic
Michael Brenndoerfer (2025) 'Attention Complexity: Quadratic Scaling, Memory Limits'. Available at: https://mbrenndoerfer.com/writing/attention-complexity-quadratic-scaling-memory-efficient-transformers (Accessed: September 30, 2026).
SimpleBasic
Michael Brenndoerfer (2025). Attention Complexity: Quadratic Scaling, Memory Limits. https://mbrenndoerfer.com/writing/attention-complexity-quadratic-scaling-memory-efficient-transformers

About the author

Continue with the full handbook

This chapter is part of Language AI Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.

Explore Language AI Handbook
Newsletter

Stay up to date

Get articles, book updates, and news delivered to your inbox.

No spam, unsubscribe anytime.

or

Join the community

Sign in to remove popups, track your reading progress, and join the discussion.