Pratham Patel
· 16 min read

Why Vanilla Attention Breaks at Scale: The O(n²) Wall

A precise accounting of why standard attention becomes infeasible at long sequences: exact FLOP counts, memory costs, KV-cache growth, and the HBM bandwidth bottleneck that makes memory worse than compute.

The first two blogs built attention from scratch: the alignment model, the Q/K/V abstraction, scaled dot-product, multi-head. Everything fits on a whiteboard. The formula is clean.

Now double the sequence length. Double it again. Watch the cost explode.

This blog is a precise accounting of why vanilla attention fails at scale. Not vague gestures at “quadratic complexity” but exact FLOP counts, byte counts, and a measurement of which bottleneck actually kills you first.


The Running Model, Scaled Up

We’ve been working with three vectors. That was enough to derive the mechanism. It is not enough to understand the scaling problem.

Fix a concrete model: dmodel=512d_\text{model} = 512, h=8h = 8 heads, dk=dv=64d_k = d_v = 64, L=12L = 12 layers, and fp16 throughout (2 bytes per element). We will vary sequence length nn during training and generated context length tt during autoregressive inference.

Two identities will be used repeatedly: hdk=864=512=dmodelh \cdot d_k = 8 \cdot 64 = 512 = d_\text{model} and dk=dv=64d_k = d_v = 64. These look innocent. They are not. Almost every scaling law in this post comes from expanding these two equalities inside the attention formulas.


1. What Actually Scales with Sequence Length?

Before counting anything, let us write the per-head attention computation once:

S=QKdk,P=softmax(S),O=PVS = \frac{QK^\top}{\sqrt{d_k}}, \qquad P = \text{softmax}(S), \qquad O = PV

For one head, the tensors have shapes QRn×dkQ \in \mathbb{R}^{n \times d_k}, KRn×dkK \in \mathbb{R}^{n \times d_k}, VRn×dvV \in \mathbb{R}^{n \times d_v}, SRn×nS \in \mathbb{R}^{n \times n}, PRn×nP \in \mathbb{R}^{n \times n}, and ORn×dvO \in \mathbb{R}^{n \times d_v}.

The source of the trouble is now visible. QQ, KK, VV, and OO all scale like ndn \cdot d, but SS and PP scale like n2n^2. This is the whole story in one line — the trouble starts the moment we materialize pairwise interactions between all positions. The quadratic term is not a side effect. It is built into the object we are computing.

Numerical check

At n=512n = 512:

n2=5122=262,144n^2 = 512^2 = 262{,}144

At n=4,096n = 4{,}096:

n2=4,0962=16,777,216n^2 = 4{,}096^2 = 16{,}777{,}216

The sequence length grew by a factor of 4,096512=8\frac{4{,}096}{512} = 8, but the number of pairwise interactions grew by 16,777,216262,144=64=82\frac{16{,}777{,}216}{262{,}144} = 64 = 8^2. This is the quadratic growth law: doubling sequence length multiplies pairwise interactions by 4, and multiplying length by 8 multiplies interactions by 64.


2. Bottleneck 1: Compute

Start with the arithmetic. This is the bottleneck people usually mention first, and for good reason: the FLOP count really does blow up.

2.1 FLOPs in QKQK^\top

The score matrix before scaling is QKQK^\top.

QQ has shape (n,dk)(n, d_k), KK^\top has shape (dk,n)(d_k, n), and their product has shape (n,n)(n, n). By the definition of matrix multiplication, each entry is a dot product:

[QK]ij=r=1dkQirKjr[QK^\top]_{ij} = \sum_{r=1}^{d_k} Q_{ir} K_{jr}

One dot product of length dkd_k costs dkd_k multiplications and dk1d_k - 1 additions. Using the standard FLOP convention that a multiply and an add each count as one floating-point operation, this is approximately 2dk2d_k FLOPs per entry. There are n2n^2 entries, so:

FLOPs(QK)=2dkn2\text{FLOPs}(QK^\top) = 2 d_k n^2

For our running model, dk=64d_k = 64:

FLOPs(QK)=264n2=128n2\text{FLOPs}(QK^\top) = 2 \cdot 64 \cdot n^2 = 128 n^2

2.2 FLOPs in Scaling, Softmax, and PVPV

Now add the rest of the attention computation. The dot-product score matrix is not the whole story.

Scaling. Dividing each entry of QKQK^\top by dk\sqrt{d_k} is one elementwise operation per entry:

FLOPs(scale)=n2\text{FLOPs}(\text{scale}) = n^2

Softmax. For each of the nn rows, we need one pass to find the row maximum, one pass to exponentiate shifted scores, one pass to sum exponentials, and one pass to divide by the sum. That is approximately 4n4n scalar operations per row, hence

FLOPs(softmax)4n2\text{FLOPs}(\text{softmax}) \approx 4n^2

Value mixing. The output matrix is PVPV with shape (n,n)(n,dv)(n,dv)(n, n) \cdot (n, d_v) \to (n, d_v). By the same dot-product counting as above:

FLOPs(PV)=2dvn2\text{FLOPs}(PV) = 2 d_v n^2

Since dv=64d_v = 64:

FLOPs(PV)=128n2\text{FLOPs}(PV) = 128 n^2

2.3 Total FLOPs Per Head

Summing the four terms:

FLOPshead=2dkn2+n2+4n2+2dvn2\text{FLOPs}_\text{head} = 2d_k n^2 + n^2 + 4n^2 + 2d_v n^2

Factor out n2n^2 by the distributive law of multiplication over addition:

FLOPshead=n2(2dk+2dv+5)\text{FLOPs}_\text{head} = n^2(2d_k + 2d_v + 5)

Substitute dk=dv=64d_k = d_v = 64:

FLOPshead=n2(128+128+5)=261n2\text{FLOPs}_\text{head} = n^2(128 + 128 + 5) = 261 n^2

For all h=8h = 8 heads:

FLOPsattn=8261n2=2,088n2\text{FLOPs}_\text{attn} = 8 \cdot 261 n^2 = 2{,}088 n^2

To keep the scaling law readable, we approximate this as

FLOPsattn2,048n2=4dmodeln2\boxed{\text{FLOPs}_\text{attn} \approx 2{,}048\, n^2 = 4 d_\text{model} n^2}

The approximation comes from ignoring the small softmax/scaling constant and using h2dk+h2dv=4dmodelh \cdot 2d_k + h \cdot 2d_v = 4d_\text{model}.

2.4 Compare to the FFN

The raw number 2,088n22{,}088 n^2 does not mean much by itself. The right comparison inside a transformer block is the feed-forward network.

The standard FFN maps dmodel4dmodeldmodeld_\text{model} \to 4d_\text{model} \to d_\text{model}. For one token, the first linear layer costs 2dmodel4dmodel=8dmodel22 \cdot d_\text{model} \cdot 4d_\text{model} = 8 d_\text{model}^2 FLOPs, and the second linear layer costs the same 8dmodel28 d_\text{model}^2, so the FFN cost per token is 16dmodel216 d_\text{model}^2. Across nn tokens:

FLOPsFFN=16dmodel2n\text{FLOPs}_\text{FFN} = 16 d_\text{model}^2 n

With dmodel=512d_\text{model} = 512:

FLOPsFFN=165122n=4,194,304n\text{FLOPs}_\text{FFN} = 16 \cdot 512^2 \cdot n = 4{,}194{,}304\, n

2.5 The Compute Crossover

Attention is quadratic in nn. The FFN is linear in nn. So there must be a sequence length at which attention stops being the secondary cost and becomes the dominant one.

Using the clean approximation FLOPsattn2,048n2\text{FLOPs}_\text{attn} \approx 2{,}048 n^2:

2,048n2=4,194,304n2{,}048 n^2 = 4{,}194{,}304 n

Cancel one factor of nn from both sides:

2,048n=4,194,3042{,}048 n = 4{,}194{,}304

Divide both sides by 2,0482{,}048:

n=2,048n = 2{,}048

So the approximate compute crossover is

n4dmodel=2,048\boxed{n^* \approx 4 d_\text{model} = 2{,}048}

If we keep the exact 2,088n22{,}088 n^2 coefficient, the crossover is slightly lower:

n=4,194,3042,0882,009n^* = \frac{4{,}194{,}304}{2{,}088} \approx 2{,}009

2.6 Numerical table

nnAttn FLOPs / layerFFN FLOPs / layerDominant
1280.034 B0.537 BFFN
5120.545 B2.147 BFFN
1,0242.190 B4.295 BFFN
2,0488.760 B8.590 BNear tie
4,09635.041 B17.180 BAttention
8,192140.164 B34.360 BAttention

At short sequences, the FFN dominates compute. Around 2K tokens, attention catches up. Beyond that, attention becomes the arithmetic bottleneck. This is the first way vanilla attention breaks.


3. Bottleneck 2: Training Memory

The compute bottleneck hurts. The memory bottleneck usually hurts earlier.

3.1 What gets materialized

This is the crucial implementation detail that the compact formula hides. Standard attention does not merely imply an n×nn \times n interaction pattern — it usually materializes two dense n×nn \times n matrices per head. The first is the score matrix S=QKdkS = \frac{QK^\top}{\sqrt{d_k}} and the second is the attention weight matrix P=softmax(S)P = \text{softmax}(S). Each has exactly n2n^2 elements. In fp16, each element is 2 bytes, so one matrix costs 2n22 n^2 bytes and the pair costs 4n24 n^2 bytes per head, per layer.

3.2 Numerical check

At n=4,096n = 4{,}096:

n2=4,0962=16,777,216n^2 = 4{,}096^2 = 16{,}777{,}216

So one n×nn \times n fp16 matrix costs

216,777,216=33,554,432 bytes2 \cdot 16{,}777{,}216 = 33{,}554{,}432 \text{ bytes}

which is

32 MB32 \text{ MB}

to a good binary-unit approximation.

Two matrices per head means

64 MB/head/layer64 \text{ MB/head/layer}

Multiply by h=8h = 8 heads:

64 MB×8=512 MB/layer64 \text{ MB} \times 8 = 512 \text{ MB/layer}

Multiply by L=12L = 12 layers:

512 MB×12=6,144 MB6 GB512 \text{ MB} \times 12 = 6{,}144 \text{ MB} \approx 6 \text{ GB}

This is only for the two attention matrices — it does not include Q, K, V activations, FFN activations, residual streams, optimizer state, or gradients.

3.3 Memory table

nnOne matrix SS or PPTwo matrices / headAll heads / layerAll heads / 12 layers
5120.5 MB1 MB8 MB96 MB
1,0242 MB4 MB32 MB384 MB
2,0488 MB16 MB128 MB1.5 GB
4,09632 MB64 MB512 MB6 GB
8,192128 MB256 MB2 GB24 GB
16,384512 MB1 GB8 GB96 GB

By 8K tokens, the attention matrices alone already consume tens of gigabytes across layers. This is the second way vanilla attention breaks.

3.4 Why the n×nn \times n matrices dominate everything else

It is worth making the comparison explicit, because otherwise “quadratic memory” can still sound abstract.

One Q, K, or V tensor has shape n×dkn \times d_k (or n×dvn \times d_v). At n=4,096n = 4{,}096 and dk=dv=64d_k = d_v = 64, one such tensor costs 4,096642=524,2884{,}096 \cdot 64 \cdot 2 = 524{,}288 bytes, which is only 0.50.5 MB. So all three linear activations together cost about 1.51.5 MB per head. Compare that to the two dense matrices at 6464 MB per head — the ratio is 641.542.7\frac{64}{1.5} \approx 42.7. At 4K tokens, the n×nn \times n attention matrices are already more than forty times larger than the combined Q, K, and V activations. This is the practical meaning of “quadratic memory dominates linear activations.” The n×nn \times n objects are not just asymptotically larger — they are already dominating by large constants at practical sequence lengths.


4. Why Memory Gets Worse Than Compute: HBM Traffic

Compute tells us how many arithmetic operations happen. It does not tell us how fast the hardware can feed data to those operations.

That is where the real training bottleneck appears. Long-context attention is often limited less by multiplication than by movement.

4.1 SRAM vs HBM

Modern GPUs have fast on-chip SRAM and registers with very high bandwidth, and much larger off-chip HBM with much lower bandwidth. The exact numbers vary by device, but the pattern is stable: SRAM is extremely fast and extremely small, while HBM is much larger and much slower. The problem is that SS and PP stop fitting on-chip surprisingly early, and once they spill to HBM, every pass over them becomes a bandwidth problem.

4.2 Exact forward-pass traffic

Let us count the dominant tensor traffic for one head in a standard forward pass. This is the bookkeeping FlashAttention is designed around.

Step 1. Read QQ and KK to compute QKQK^\top:

2ndk2nd_k

elements read.

Write the score matrix SS:

n2n^2

elements written.

Step 2. Read SS to apply softmax:

n2n^2

elements read.

Write PP:

n2n^2

elements written.

Step 3. Read PP and VV to compute PVPV:

n2+ndvn^2 + nd_v

elements read.

Write OO:

ndvnd_v

elements written.

Add everything:

2ndk+n2+n2+n2+ndv+ndv2nd_k + n^2 + n^2 + n^2 + nd_v + nd_v

Since dk=dv=dd_k = d_v = d, this becomes

4nd+3n24nd + 3n^2

elements moved per head in the forward pass.

4.3 Numerical check

For our running model, d=64d = 64 and n=4,096n = 4{,}096.

First compute the linear term:

4nd=44,09664=1,048,5764nd = 4 \cdot 4{,}096 \cdot 64 = 1{,}048{,}576

Now the quadratic term:

3n2=34,0962=316,777,216=50,331,6483n^2 = 3 \cdot 4{,}096^2 = 3 \cdot 16{,}777{,}216 = 50{,}331{,}648

Total:

4nd+3n2=1,048,576+50,331,648=51,380,2244nd + 3n^2 = 1{,}048{,}576 + 50{,}331{,}648 = 51{,}380{,}224

elements moved.

The linear term is only about 1 million elements, while the quadratic term is over 50 million. The ratio is 50,331,6481,048,576=48\frac{50{,}331{,}648}{1{,}048{,}576} = 48, so at 4K tokens, the n2n^2 traffic is already 48 times larger than the ndnd traffic. This is the core reason FlashAttention exists — the bottleneck is not only arithmetic, it is moving those n2n^2 matrices to and from HBM.


5. Bottleneck 3: KV Cache During Autoregressive Inference

The first two bottlenecks are primarily training-time problems. Inference introduces a different one, and in modern long-context generation it is often the decisive one.

5.1 Why the cache exists at all

During autoregressive generation, token t+1t+1 attends over all previous tokens 1,,t1, \ldots, t.

If we recomputed all keys and values from scratch at every generation step, total work over the full generated sequence would become quadratic again. So practical decoders cache the keys and values from previous steps. That cache is what makes autoregressive decoding feasible in the first place.

5.2 Cache bytes per token

For one token, one head, one layer, we store one key vector of length dkd_k and one value vector of length dvd_v, giving dk+dvd_k + d_v elements. In fp16, each element is 2 bytes, so the cost is (dk+dv)2(d_k + d_v) \cdot 2 bytes. Across hh heads and LL layers:

KV bytes/token=Lh(dk+dv)2\boxed{\text{KV bytes/token} = L \cdot h \cdot (d_k + d_v) \cdot 2}

5.3 Numerical check

Substitute the running model:

Lh(dk+dv)2=128(64+64)2L \cdot h \cdot (d_k + d_v) \cdot 2 = 12 \cdot 8 \cdot (64 + 64) \cdot 2

First add the key and value widths:

64+64=12864 + 64 = 128

Then multiply:

1281282=128256=122,048=24,576 bytes12 \cdot 8 \cdot 128 \cdot 2 = 12 \cdot 8 \cdot 256 = 12 \cdot 2{,}048 = 24{,}576 \text{ bytes}

So the cache cost is

24,576 bytes/token24 KB/token\boxed{24{,}576 \text{ bytes/token} \approx 24 \text{ KB/token}}

5.4 Growth table

Context length ttKV cache size
1,02424 MB
4,09696 MB
16,384384 MB
32,768768 MB
65,5361.5 GB
128,0003.0 GB

This is for our small 12-layer model. The important point is not just the number but the scaling law: KV cachet\text{KV cache} \propto t. The cache grows linearly with generated context length and never shrinks unless we explicitly evict or compress it.

5.5 A cleaner closed form

Because dk=dvd_k = d_v and hdk=dmodelh d_k = d_\text{model}, the cache formula simplifies in a surprisingly clean way. Start from KV bytes/token=Lh(dk+dv)2\text{KV bytes/token} = L \cdot h \cdot (d_k + d_v) \cdot 2. Since dk=dvd_k = d_v, we have dk+dv=2dkd_k + d_v = 2d_k, so KV bytes/token=Lh2dk2=4L(hdk)\text{KV bytes/token} = L \cdot h \cdot 2d_k \cdot 2 = 4L(h d_k). Now use hdk=dmodelh d_k = d_\text{model}:

KV bytes/token=4Ldmodel\boxed{\text{KV bytes/token} = 4L d_\text{model}}

5.6 Numerical check of the closed form

Substitute L=12L = 12 and dmodel=512d_\text{model} = 512:

412512=24,5764 \cdot 12 \cdot 512 = 24{,}576

bytes per token, exactly matching the earlier derivation.

This form is worth remembering because it shows that, for standard MHA, the per-token KV cost depends only on model depth and width. The head count disappears once we use the conventional relation hdk=dmodelh d_k = d_\text{model}.

5.7 Why this is different from the n2n^2 wall

This is the part that confuses almost everyone. The KV cache is not another version of the training-time n2n^2 wall. It is different in two ways. First, the stored memory grows linearly with context length, O(t)O(t). Second, every new token must read the whole cache accumulated so far, so the per-step bandwidth cost also grows linearly with tt. That is why long-context inference feels slow even when we generate only one token at a time — each step drags a longer and longer KV history through memory. The next blog will derive this bandwidth bottleneck in much more detail. For now, the key fact is simple: training breaks on n2n^2, while inference breaks on the KV cache.


6. Three Problems, Three Lineages of Solutions

Different attention papers look different because they are attacking different bottlenecks. Once we separate the bottlenecks, the literature becomes much easier to parse.

6.1 Compute bottleneck

The first bottleneck is that QKQK^\top and PVPV scale quadratically in sequence length. Typical fixes include sparse attention, local-window attention, linear attention, and state-space replacements — all of which change what pairwise interactions are computed.

6.2 Memory / HBM bottleneck

The second bottleneck is that materializing SS and PP forces large n2n^2 tensors through HBM. The canonical fix here is FlashAttention. It does not change the attention formula — it changes the schedule, tiling the computation so the large matrices are never written to HBM in the first place.

6.3 KV-cache bottleneck

The third bottleneck is that inference stores and rereads one K and one V vector per layer, per head, per token. Typical fixes include Multi-Query Attention (MQA), Grouped-Query Attention (GQA), Multi-head Latent Attention (MLA), sliding-window caches, and KV quantization, all of which change what gets stored across decoding steps.

6.4 Why one fix does not solve the others

This is the unifying insight of the whole post. FlashAttention solves the training memory traffic problem, but it does not shrink the inference KV cache. GQA shrinks the inference KV cache, but it does not remove the quadratic training-time n×nn \times n interaction pattern. Sparse or linear attention reduce quadratic arithmetic, but they may or may not help the KV cache, depending on whether they also change what is stored. So when two papers claim to make attention “efficient,” they may not be addressing the same bottleneck at all.


Summary

Vanilla attention breaks in three distinct ways. First, its arithmetic cost grows as O(n2)O(n^2) because every token interacts with every other token. Second, its training-time memory and HBM traffic are dominated by the dense n×nn \times n score and probability matrices. Third, its autoregressive inference path accumulates a KV cache whose size grows linearly with context and whose bandwidth cost grows with every generated token.

These three bottlenecks are why the literature branches: sparse and linear methods attack quadratic compute, FlashAttention attacks training-time memory traffic, and MQA/GQA/MLA attack the KV cache. The next blog zooms in on that third bottleneck and derives the KV-cache story much more deeply.


Previous: From Soft Alignment to Queries, Keys, and Values
Next: What Can We Actually Modify in Attention?

Enjoyed this post?

Subscribe to get notified when I publish new posts. No spam, unsubscribe anytime.