Chapter 02Fixed size, but everything smears

Linear attention: move the nonlinearity

Softmax attention cannot be factorised because the exponential sits on top of the dot product. Move the nonlinearity to before the dot product — apply it to \(q\) and \(k\) separately — and the entire computation re-associates into a single fixed matrix. The memory stops growing. Something else starts breaking.

Carried over from chapter 1

The KV cache grows in \(O(N)\) and every decode step reads all of it, at an arithmetic intensity of about 1 FLOP per byte. The state is unbounded and the machine is idle.

The substitution

Write softmax attention with the exponential named as a similarity function \(\text{sim}\):

\[ o_t \;=\; \frac{\sum_{i\le t}\ \text{sim}(q_t,k_i)\ v_i}{\sum_{i\le t}\ \text{sim}(q_t,k_i)},\qquad \text{sim}(q,k)=\exp\!\big(q\cdot k/\sqrt d\big) \]

Nothing about the structure of attention requires \(\text{sim}\) to be that particular function. It requires only that \(\text{sim}\) be non-negative, so the weights form a genuine average. Choose instead a similarity that factorises:

\[ \text{sim}(q,k) \;=\; \phi(q)\cdot\phi(k), \qquad \phi(x)=\mathrm{elu}(x)+1 \;>\;0 \]

elu(x)+1 equals \(x+1\) for \(x>0\) and \(e^{x}\) for \(x\le 0\): smooth, strictly positive, and cheap. Any elementwise positive map works; this one is the original choice.

\(\phi\) is applied to each vector on its own, before they meet. That one change is the whole chapter, because it makes the numerator re-associable.

The re-association

Matrix multiplication is associative: \((AB)C = A(BC)\). Same result, wildly different intermediate. With \(\phi\) folded into \(q\) and \(k\) so we can stop writing it:

\[ \underbrace{\big(q_t\,k_i^{\top}\big)\,v_i}_{\text{score first}} \;=\; \underbrace{q_t\,\big(k_i^{\top} v_i\big)}_{\text{state first}} \]

On the left, \(q_t k_i^{\top}\) is a scalar score, and you need one per key — an \(N\times N\) matrix over a whole sequence. On the right, \(k_i^{\top} v_i\) is an outer product: a \(d\times d\) matrix, the same size no matter how many tokens you have seen. And outer products can be summed before the query ever arrives.

SCORE FIRST · (q kᵀ) v · what softmax must do QN×d × Kᵀd×N = AN×N × VN×d = O The intermediate is a square of side N. It grows with the conversation. Work: 2N²d + 2N²d STATE FIRST · q (kᵀ v) · what factorising allows Kᵀd×N × VN×d = Sd×d then Q × S = O The intermediate is a square of side d. It is the same size forever. Work: 2Nd² + 2Nd²
The same product, bracketed two ways. Both rows compute identical numbers. The top row builds an N×N object on the way; the bottom row builds a d×d one. With d = 64 and N = 8,192, that is the difference between a 67-million-entry intermediate and a 4,096-entry one. Softmax attention is forced into the top row, because \(\exp(q\cdot k)\) cannot be split into a factor depending only on \(q\) times a factor depending only on \(k\).

The recurrence you get for free

Because the numerator is now a sum of outer products, it can be accumulated one token at a time. Same for the denominator. Two running quantities, both fixed size:

\[ S_t = S_{t-1} + k_t^{\top} v_t \;\in\; \mathbb{R}^{d\times d}, \qquad z_t = z_{t-1} + k_t \;\in\; \mathbb{R}^{1\times d} \] write
\[ o_t \;=\; \frac{q_t\,S_t}{q_t\,z_t^{\top}} \] read

That is a recurrent neural network. Not "like" one — it is one, with a linear update and a \(d\times d\) hidden state. The paper that introduced this was titled Transformers are RNNs, and it meant it literally: given the factorised similarity, the transformer and the RNN are the same computation written in two orders.

# the whole of linear attention, prefill and decode
q = qkv[:, :, :d  ].view(b,t,h,d_head).transpose(1,2)
k = qkv[:, :, d:2*d].view(b,t,h,d_head).transpose(1,2)
v = qkv[:, :, 2*d:].view(b,t,h,d_head).transpose(1,2)

k = F.elu(k) + 1                 # φ, applied to k alone
q = F.elu(q) + 1                 # φ, applied to q alone
k = k.transpose(-1,-2)

S, z = cache if cache is not None else (0.0, 0.0)
S = S + k @ v                    # fold this token into the state
z = z + k                        # and into the normaliser

o     = q @ S                     # read: one d×d matmul, no loop over history
denom = q @ z
o     = o / denom
return self.o_proj(o.transpose(1,2).reshape(b,t,d)), (S, z)

There is no past_kv to concatenate and no history to scan. The two highlighted lines replaced the KV cache with a matrix that never changes shape.

What that costs, precisely

Softmax attentionLinear attentionCrossover
State, per head per layer\(2Nd\) numbers\(d^2 + d\) numbers\(N = d/2\)
Decode FLOPs per token\(\sim 4Nd\)\(\sim 4d^2\)\(N = d\)
Prefill FLOPs, length \(N\)\(\sim 4N^2 d\)\(\sim 4Nd^2\)\(N = d\)
Growth in \(N\)linear memory, linear timeconstant, constant

For GPT-2's geometry — \(d = 64\) per head, 12 heads, 12 layers — the crossover is around 32 tokens. Past that, linear attention's state is strictly smaller, and the gap opens fast:

ContextKV cache (fp16)Linear state (fp16)Ratio
1,024 tokens37.7 MB1.18 MB32×
16,384 tokens604 MB1.18 MB512×
131,072 tokens4.83 GB1.18 MB4,096×
The engineering consequence that actually matters

A 128×128 fp16 state is 32 KB. An H100 streaming multiprocessor has 228 KB of shared memory. The entire state fits on-chip. Decoding a linear-attention layer never has to touch HBM for its history at all — which is the real source of the throughput numbers you will see quoted in chapter 6, not the FLOP count. Chapter 1's problem was bandwidth; this is a bandwidth fix, and it happens to also be a FLOP fix.

The three-step contract

Softmax and linear attention are the same three operations with different choices at step one:

  1. Make the scores non-negative. Softmax exponentiates. Linear attention applies \(\phi\) to \(q\) and \(k\) separately.
  2. Divide by their sum, so the weights are a distribution.
  3. Take the weighted average of the values.

Only step 1 changed, and the whole cost profile inverted. Worth noting for later: from chapter 3 onward, step 2 gets dropped entirely — modern variants normalise \(q\) and \(k\) to unit length and put an RMSNorm on the output instead of carrying a running denominator. The contract loosens as the field gains confidence that the denominator was doing less work than assumed.

What you gave up, part one: sharpness

\(\exp\) is unbounded and convex, so a single high-scoring key can capture nearly all the attention mass. \(\phi(q)\cdot\phi(k)\) with \(\phi = \mathrm{elu}+1\) is a much flatter, more bounded function of the inputs. The resulting weight distribution is smoother; the model retrieves a blur where softmax could retrieve one item.

This is a real loss but a soft one — a worse approximation to the same kernel, recoverable in part with better feature maps. The second loss is structural and no feature map fixes it.

What you gave up, part two: capacity

Here is the failure, stated exactly. After \(N\) tokens the state holds \(S_N = \sum_{i=1}^{N} k_i^{\top} v_i\). Query it with one of the keys you wrote, \(q = k_j\):

\[ k_j\,S_N \;=\; \sum_{i=1}^{N}\big(k_j\!\cdot\! k_i\big)\,v_i \;=\; \underbrace{\|k_j\|^2\,v_j}_{\text{the answer}} \;+\; \underbrace{\sum_{i\ne j}\big(k_j\!\cdot\! k_i\big)\,v_i}_{\text{every other fact you ever wrote}} \]

The second term vanishes only if \(k_j\) is orthogonal to every other key. In \(\mathbb{R}^d\) you can have at most \(d\) mutually orthogonal directions. So the moment \(N > d\), some pair of keys must overlap, and the overlap leaks one fact's value into another fact's readout.

You can quantify how bad. If the keys behave like random unit vectors in \(\mathbb{R}^d\), each cross term has magnitude around \(1/\sqrt d\), and the \(N\) of them accumulate incoherently, so their total scales as \(\sqrt{N/d}\). The signal is \(1\). Therefore

\[ \text{signal-to-noise on readback} \;\;\approx\;\; \sqrt{\frac{d}{N}} \]

With \(d = 128\): perfectly usable at \(N = 128\), down to 0.18 by \(N = 4{,}096\). Learned keys do better than random ones — the model can allocate directions deliberately, and values that should be confusable are not — but no amount of learning creates a \(129\)th orthogonal direction in a 128-dimensional space. The ceiling is dimensional, not statistical.

THE STATE HAS d² NUMBERS — BUT ONLY d PLACES TO PUT THINGS rank d, not d² Each written association is a rank-1 outer product. A d×d matrix is a sum of at most d independent ones. Write the (d+1)ᵗʰ and something must blur. READBACK SNR ≈ √(d/N) 1.00 ← N = d 0.35 ← N = 8d 0.18 ← N = 32d tokens written, in multiples of d →
The capacity ceiling. The state is a d×d matrix, so it has d² numbers — but every association written into it is a rank-one outer product, and a d×d matrix decomposes into at most d independent rank-one pieces. The state's real capacity is d clean slots, not d². Past that, reading one key returns its value plus a weighted sum of every other value ever stored, and the ratio between them decays like √(d/N).
A note on the "O(N²)" framing, since it confuses everyone

The original linear-attention paper sold the result as \(O(N^2) \to O(N)\), and that framing is easy to misread today. In 2020 a reference implementation really did materialise the full \(N\times N\) score matrix in HBM, and reference autoregressive code often recomputed history with no KV cache at all — so the quadratic term was both real and painful. FlashAttention (2022) later removed the materialisation by tiling the softmax, which killed the memory blow-up but not the \(O(N^2)\) arithmetic and not the \(O(N)\) decode cache. The durable claim from this paper is not the asymptotic one. It is: the decode state can be a constant.

What has to change next

The problem is not that the state is finite. The problem is how a finite state gets written: purely additively, with nothing ever removed. Adding is what made it cheap — you can accumulate outer products in any order and never grow — and adding is exactly what makes facts pile on top of each other once capacity runs out. Chapter 3 keeps the constant-size state and changes the write.