Quadratic Attention Bottleneck › Compute Analysis: The Operations

Michael BrenndoerferUpdated June 21, 202510 min read
Language AI HandbookAll chapters
Part 17 of 67Efficient Attention
Chapter 117Quadratic Attention Bottleneck › Compute Analysis: The Operations
Reading section 4 of 13 · Compute Analysis: The Operations · 10 minSection 4 of 13 · 10 min

Book contents

Section 4 of 13 · 10 min read

Quadratic Attention Bottleneck › Compute Analysis: The OperationsOne section · Sections in this chapter4 / 13 ↓
Reading tools Definitions & inline links

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

Article links

Make inline references clickable

We have seen that attention requires storing an n×nn \times n matrix of weights. But how much computation does it take to produce those weights and use them? Understanding the computational cost helps us predict training times, estimate inference latency, and appreciate why efficient attention variants are so valuable.

The answer turns out to be O(n2d)O(n^2 d) floating-point operations per attention layer, where dd is the model dimension. To see where this comes from, let's trace through exactly what happens when attention runs, quantifying each operation precisely.

Building Intuition: What Must Be Computed?

Before diving into the math, consider what attention needs to accomplish for each token in the sequence:

  1. Compare this token to every other token to determine relevance scores
  2. Normalize those scores into a probability distribution
  3. Blend information from all tokens according to those probabilities

For a sequence of nn tokens, step 1 alone requires nn comparisons per token, and we have nn tokens, giving us n×n=n2n \times n = n^2 comparisons. This is the fundamental source of quadratic scaling: the all-pairs comparison cannot be avoided in standard attention. Steps 2 and 3 must process the results of step 1, so they also touch n2n^2 entries.

Now let's quantify each step precisely and see how the costs add up.

Stage 1: Computing Attention Scores

The first operation computes how strongly each query should attend to each key. Mathematically, this is the matrix multiplication QKTQK^T, where QQ contains all query vectors (one per token) and KK contains all key vectors.

When we multiply a matrix of shape (n×dk)(n \times d_k) by a transposed matrix of shape (dk×n)(d_k \times n), we get an (n×n)(n \times n) output. Each entry in this output matrix is a dot product between one query and one key, requiring dkd_k multiplications and dk−1d_k - 1 additions. With n2n^2 such entries, the total operation count for this stage is:

Score FLOPs≈2⋅n2⋅dk\text{Score FLOPs} \approx 2 \cdot n^2 \cdot d_k

where:

  • n2n^2: the number of query-key pairs (one score per pair)
  • dkd_k: the dimension of each query and key vector (typically d/hd / h for hh heads)
  • 22: accounts for both multiplications and additions in each dot product (counted as a single fused multiply-add operation on modern hardware)

The n2n^2 appears because every token must be compared against every other token. There is no shortcut here: we do not know which pairs will have high attention until we compute all the scores. This is the irreducible cost of the all-pairs comparison.

Stage 2: Softmax Normalization

Raw dot products can be any real number, positive or negative, and their magnitudes depend on the model's learned parameters. To use them as weights in a weighted average, we need to convert each row of scores into a probability distribution that sums to 1. The softmax function accomplishes this, converting each row of scores into non-negative weights that sum to exactly 1.

For a row of nn attention scores s1,s2,…,sns_1, s_2, \ldots, s_n, softmax computes:

softmax(si)=esi/dk∑j=1nesj/dk\text{softmax}(s_i) = \frac{e^{s_i / \sqrt{d_k}}}{\sum_{j=1}^{n} e^{s_j / \sqrt{d_k}}}

where:

  • sis_i: the raw dot product score for the ii-th position in the row
  • esi/dke^{s_i / \sqrt{d_k}}: the exponential of the scaled score. This keeps all weights are positive
  • ∑j=1nesj/dk\sum_{j=1}^{n} e^{s_j / \sqrt{d_k}}: the normalizing constant, summing over all positions in the row
  • dk\sqrt{d_k}: the scaling factor that prevents dot products from growing too large in magnitude, which would push softmax into regions of near-zero gradient

The dk\sqrt{d_k} scaling is a detail from the original Transformer paper, motivated by the observation that without it, dot products grow in magnitude with dkd_k, pushing gradients to near zero during training. Scaling by dk\sqrt{d_k} keeps the scores in a range where softmax gradients remain healthy.

For each of the nn rows in the attention matrix, softmax requires exponentiating nn scores, summing those nn exponentials, and dividing each of the nn exponentials by the sum. This contributes roughly 5n5n operations per row. With nn rows:

Softmax FLOPs≈5⋅n2\text{Softmax FLOPs} \approx 5 \cdot n^2

While still quadratic in nn, the softmax cost lacks the dkd_k factor, making it smaller than score computation when dkd_k is large. For typical transformers where dk=64d_k = 64 to 128128, softmax contributes less than 10% of the total attention FLOPs. It is not negligible, but it is not the dominant term.

Stage 3: Computing the Output

The final step uses the normalized attention weights to compute a weighted combination of value vectors. Each token's output is a blend of all tokens' values, weighted by attention. In matrix form, this is:

Output=softmax ⁣(QKTdk)⋅V\text{Output} = \text{softmax}\!\left(\frac{QK^T}{\sqrt{d_k}}\right) \cdot V

where the left factor is the (n×n)(n \times n) attention weight matrix and the right factor VV is the (n×dv)(n \times d_v) value matrix.

This is another matrix multiplication: the (n×n)(n \times n) attention weights multiplied by the (n×dv)(n \times d_v) value matrix produces an (n×dv)(n \times d_v) output. Each of the n×dvn \times d_v output elements requires summing across nn weighted values:

Output FLOPs≈2⋅n2⋅dv\text{Output FLOPs} \approx 2 \cdot n^2 \cdot d_v

where:

  • n2n^2: the number of weight-value multiplications (every output position must aggregate from all nn positions)
  • dvd_v: the dimension of each value vector (typically equal to dkd_k in standard transformers)
  • 22: multiply-add pairs as before

This matches the cost of score computation exactly when dv=dkd_v = d_k. We have encountered two expensive O(n2d)O(n^2 d) operations, and they dominate the total cost of attention.

Out[9]:
Visualization
Horizontal bar chart showing FLOPs breakdown: score computation and output computation are roughly equal and much larger than softmax.
Breakdown of attention FLOPs across the three computational stages for a model with d=768 at sequence length n=1024. Score computation and output computation each contribute approximately half of all attention operations. Softmax normalization is negligible at under 0.4 percent. This shows that the two matrix multiplications completely dominate the attention cost.

The visualization confirms our analysis: score computation and output computation each consume roughly half the total FLOPs (about 49.8% each), while softmax contributes less than 0.4%. The two matrix multiplications involving the n×nn \times n attention matrix dominate entirely. Any optimization that targets the softmax step alone will have negligible impact on the overall compute budget.

The Complete Complexity Formula

Combining all three stages, the total computational cost of self-attention for a single head is:

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

where:

  • nn: sequence length (number of tokens)
  • dkd_k: dimension of query and key vectors (typically d/hd / h, where hh is the number of heads)
  • dvd_v: dimension of value vectors (typically equal to dkd_k)
  • dd: model dimension (the full embedding size before splitting into heads)
  • hh: number of attention heads

The simplification to O(n2d)O(n^2 d) holds because dkd_k and dvd_v are both proportional to dd. In standard transformers, each head operates on d/hd/h dimensions, and since we sum across all heads, the total work scales with the full model dimension dd.

Why the Quadratic Term Dominates

The formula O(n2d)O(n^2 d) reveals an important asymmetry in how the two factors affect complexity:

  • Doubling nn quadruples the computation because nn appears squared
  • Doubling dd only doubles the computation because dd appears linearly

This asymmetry determines which factor becomes the bottleneck. For a model with d=768d = 768 processing n=4,096n = 4,096 tokens, the n2=16,777,216n^2 = 16{,}777{,}216 factor dwarfs the d=768d = 768 factor by a ratio of over 20,000 to 1. The quadratic term completely dominates, which is why sequence length is the bottleneck rather than model size when processing long documents.

The practical consequence is that scaling up the model (increasing dd) is far less damaging to inference speed than scaling up the context (increasing nn). A model twice as wide is only twice as slow. A model with twice the context length is four times as slow. This is why researchers are far more willing to increase model dimension than context length when staying within a compute budget.

Putting Numbers to the Formula

Let's make this concrete by computing the actual FLOPs for a GPT-2 style model with d=768d = 768 across a range of sequence lengths:

In[10]:
Code
def attention_flops(n, d):
    """
    Calculate FLOPs for self-attention core operations.

    Args:
        n: Sequence length
        d: Model dimension (used for Q@K^T and attn@V)

    Returns:
        Total FLOPs (counting multiplications and additions)
    """
    # Q @ K^T: (n x d) @ (d x n) = n^2 * d multiplies + n^2 * (d-1) adds
    # Simplified to 2 * n^2 * d for multiply-accumulate
    score_flops = 2 * n * n * d

    # Softmax: exp, sum, divide for each row = ~5 * n^2 operations
    softmax_flops = 5 * n * n

    # attention @ V: (n x n) @ (n x d) = n^2 * d
    output_flops = 2 * n * n * d

    return score_flops + softmax_flops + output_flops


d = 768
flops_by_length = [(n, attention_flops(n, d)) for n in sequence_lengths]
Out[11]:
Console
Attention FLOPs (d=768):

 Sequence Length              FLOPs       GFLOPs
--------------------------------------------------
             512        806,617,088         0.81
           2,048     12,905,873,408        12.91
           8,192    206,493,974,528       206.49
          32,768  3,303,903,592,448      3303.90

The numbers tell the story of quadratic growth. At 512 tokens, a single attention layer performs about 1.6 billion operations, which modern GPUs handle in microseconds. At 2,048 tokens (4x longer), the count grows to roughly 26 billion operations (16x more, confirming the quadratic relationship). By 32,768 tokens, we are approaching 7 trillion operations per layer.

For a complete transformer, multiply these numbers by the layer count. A 12-layer GPT-2 at 4,096 tokens performs over 1.2 trillion attention operations. During training, backpropagation roughly triples this cost (forward pass, backward for computing gradients, backward for applying updates). The computational demands compound rapidly, and attention becomes the dominant bottleneck for long sequences.

In practice, the FLOPs budget for a long-context model is not simply "how long will this take on a single GPU." It also drives decisions about parallelism strategies, batch sizes, gradient checkpointing, and mixed-precision training. Every engineering decision that reduces the effective n2n^2 cost has compounding effects throughout the training pipeline.