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 pairwise interactions for a sequence of 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 note exchanges. If the class doubles to 60 students, you need 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 people, and everyone needs to have a brief conversation with everyone else. With 10 people, that's conversations. With 100 people, it's 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 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.



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:
-
Computing attention scores: Each token asks "how relevant is every other token to me?" This requires comparing all tokens against all tokens, producing an grid of relevance scores.
-
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.
-
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 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 multiplication allows the hardware to exploit its full parallelism. The result is still the same numbers, but computed much faster than separate dot products would be.
Mathematically, we arrange all queries into a matrix and all keys into a matrix , then compute:
where:
- : the attention score matrix, where measures how much token should attend to token
- : the query matrix with shape , containing one query vector per token
- : the transposed key matrix with shape , rearranging the key vectors for efficient matrix multiplication
- : the sequence length (number of tokens)
- : the dimension of each query/key vector (typically 64 in standard transformers)
Why does this produce values? Each row of (one token's query) gets multiplied against every column of (every token's key), producing one score per pair. With rows and columns, we get an grid of scores.
The computational cost of this matrix multiplication is substantial. Each entry in the output matrix requires multiplications and additions. With entries, the total operation count is .
A multiply-accumulate (MAC) operation computes 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 . The 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 scores express how much that token should attend to each of the positions. Softmax converts these arbitrary numbers into 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 , softmax converts its raw scores into attention weights that sum to 1. This involves:
- Computing for each of the scores (making them all positive)
- Summing these exponentials across the row
- Dividing each exponential by the sum
Each row requires approximately operations (exponentiate, sum, divide), and with rows, the total is . While still quadratic, this is dominated by the cost of score computation when is large. In practice, the softmax step is rarely the performance bottleneck, but it does introduce a numerical subtlety: computing 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 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:
where:
- : the attention weight matrix, with each row summing to 1
- : the value matrix with shape , containing one value vector per token
- : the dimension of each value vector (typically equal to )
This is another matrix multiplication: produces an output. Each of the output elements requires multiply-accumulate operations (summing across the positions), giving total operations.
This step is just as expensive as score computation. Notice that both of the dominant operations involve an intermediate matrix: in Stage 1 we create this matrix, and in Stage 3 we multiply by it. This 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 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 scaling explicit and shows that neither the softmax step nor the dimension can offset the quadratic term.
Summing the costs from all three stages:
where:
- : sequence length
- : query/key dimension
- : value dimension
- : model dimension (typically for attention heads)
The simplification to reflects that and are both proportional to the model dimension . In standard transformers, each head operates on dimensions, but since we have 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 () and the model dimension (). These two factors contribute very differently to the total. Doubling doubles the cost. Doubling quadruples it. For any sequence where , the sequence length term dominates. A model with and spends roughly times more computation on the factor than on the 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 (four tokens), query/key dimension , and value dimension .
Stage 1: Score computation. The query matrix has shape and the key matrix has shape . Computing produces a matrix. Each of the 16 entries requires 3 multiplications and 2 additions (a dot product of length 3), totaling multiplications.
Stage 2: Softmax. We apply softmax to each of the 4 rows of . Each row has 4 elements, requiring 4 exponentiations, 1 sum (over 4 values, so 3 additions), and 4 divisions. Approximately operations.
Stage 3: Output computation. The softmax weight matrix has shape and the value matrix has shape . The product has shape . Each of the 12 output entries requires 4 multiplications and 3 additions, totaling multiplications.
The total is dominated by Stages 1 and 3: approximately multiplications (plus roughly the same number of additions). Using our formula , we recover exactly this number. The formula works.
Now compare with scaling to (doubling the sequence length). Stage 1 becomes multiplications (4x more). Stage 3 becomes multiplications (4x more). The total scales by exactly 4x, as the quadratic formula predicts. Every doubling of multiplies the operation count by 4, regardless of what 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 quadruples the computation. This is the defining characteristic that limits context length.
- Linear in model dimension: Doubling only doubles the computation. Making models wider is relatively cheap.
The quadratic term dominates for long sequences. At tokens with (GPT-2 dimensions), the factor far exceeds the 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.
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.

Implementation: Counting FLOPs
The theoretical complexity 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 operations, softmax contributes operations, and output computation contributes another operations. Notice that the softmax term is negligible relative to the others when , which is always true in practice ( is typically in the hundreds). This confirms that the two matrix multiplications, not the softmax, are the computational bottleneck.
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 totalWith this function, we can explore how computational cost scales across different sequence lengths. We'll use GPT-2's model dimension () to ground our analysis in real-world numbers.
# 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}
)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.281The 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.

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 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 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 , where is the sequence length. This is the dominant memory consumer in standard attention implementations. For a single attention head, this matrix contains elements. With heads, we store values. Using 32-bit floats (4 bytes each), the memory requirement is:
where:
- : memory required for attention matrices
- : number of attention heads
- : sequence length
- : bytes per element for 32-bit floats (fp32)
For 16-bit floats (common in modern training with fp16 or bf16), this halves to bytes. The quadratic dependence on 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 , totaling elements. These scale linearly with sequence length.
- Attention scores/weights: per head, total. These scale quadratically and dominate for long sequences.
- Output before projection: 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.
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]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.

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 processes each of tokens sequentially. At each timestep , the hidden state update follows:
where:
- : hidden state at timestep (dimension )
- : hidden-to-hidden weight matrix (shape )
- : previous hidden state (dimension )
- : input-to-hidden weight matrix (shape , assuming input dimension matches )
- : input at timestep
- : activation function (e.g., tanh)
The computational cost per timestep is:
- Matrix-vector multiplication : operations
- Matrix-vector multiplication : operations
- Activation function: operations
Total per step: . For tokens: .
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:
- RNN:
where is sequence length and is the hidden/model dimension.
The key insight is that these two complexities share the same functional form with and swapped. Attention is quadratic in , linear in . RNN is linear in , quadratic in . 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 and solve for :
Dividing both sides by (assuming ):
This crossover point has a clear interpretation: when sequence length exceeds model dimension (), attention becomes more expensive. When model dimension exceeds sequence length (), 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.
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)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 = 512The ratio column reveals the crossover behavior. At and , attention is 0.13x and 0.25x the cost of an RNN, respectively, making attention the more efficient choice. At (exactly equal to ), both architectures have identical cost (1.00x ratio). Beyond this point, attention becomes increasingly expensive: at , attention requires 8x more operations than an equivalent RNN.

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 to token must traverse 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.
# 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)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.875The "Efficiency" column (path improvement divided by compute ratio) quantifies the value proposition of attention. At , we pay only 0.25x the compute of an RNN while gaining 63x shorter average paths, yielding an efficiency of 252. At (the crossover point), we pay 1x compute for 255x path improvement. At , we pay 4x more compute but get 1023x shorter paths. Notice that efficiency remains nearly constant around . This reveals an important insight: as sequence length grows, both the path improvement and the compute penalty scale proportionally with . 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:
where:
- : memory in bytes
- : number of attention heads
- : sequence length
- : 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.
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}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,944These 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 -th token requires a context of length . 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.
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})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,353At 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 (recomputing full attention over the growing context) to (computing attention between the new query and all cached keys). KV caching trades memory for time: you must store 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.

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 attention cost is small relative to the 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.
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,
}
)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 . 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.

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 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 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 (which requires the intermediate), these methods compute , where the parenthesized term has shape rather than .
- Linformer: Projects keys and values to a fixed low-dimensional space before computing attention. If keys and values are projected to dimensions where , the attention matrix becomes rather than , achieving complexity with as a fixed hyperparameter.
- Performer: Uses random feature maps to approximate softmax attention in 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 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 , where:
- : sequence length
- : number of positions each token attends to (typically )
For example, with a local window of 256 tokens, regardless of sequence length, making the complexity effectively linear in . 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 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 rather than .
- 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 (proportional to all layers and sequence length squared) to roughly with some computational overhead.
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)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 term for , achieves roughly a 21x speedup by avoiding the explicit attention matrix. Sparse attention with 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.

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 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 operations, where is sequence length and is model dimension. The 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 memory, where 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 ( operations), softmax normalization ( operations), and output aggregation ( operations). The softmax is negligible; the two matrix multiplications dominate.
-
Crossover with RNN: Setting shows that attention is cheaper than RNN when , but more expensive when . 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 rather than , 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 to or , where 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 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 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 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 because all 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
Reference
Citation details
Cite or share this article.
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 HandbookStay up to date
Get articles, book updates, and news delivered to your inbox.
No spam, unsubscribe anytime.
Join the community
Sign in to remove popups, track your reading progress, and join the discussion.

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