Module 16 · Part IV · The Modern Architecture

Efficient and Long Attention

The GPU Memory Hierarchy, FlashAttention, Local and Global Layers, Sandwich Normalization, Soft-Capping, and Attention Sinks

By the end you can explain how attention is made cheaper without changing its result, and how long contexts are kept stable.

Builds onModule 15·
Check yourself ▸

Before you start, can you already…

  • ☐turn the 2017 block into a 2024 block and justify each change. (Module 15)

If any is shaky, the linked module teaches it.

Open in Colab
4 Hours·Elective·Advanced·0/6

Attention's cost grows with the square of the sequence length (section 6.6), and its KV cache with every generated token (section 12.2). This module collects the techniques that make long contexts affordable and stable: FlashAttention, which computes exactly the same attention while moving far less data, and small architecture changes, as in Google's Gemma family, that attend locally in most layers, bound the size of numbers, and keep attention sinks.

16.1 Memory, Not Arithmetic: The GPU Hierarchy

A GPU has large, comparatively slow HBM (high-bandwidth memory, the "80 GB" on the box) and small, fast on-chip SRAM, about 20 MB on an NVIDIA A100. Arithmetic happens only on data brought into SRAM.

Dao et al. (2022) pointed out that standard attention is memory-bound: it spends most of its time moving the score matrix, writing it to HBM, reading it back for the softmax, writing the result, and reading it again to multiply by VV. FlashAttention computes the same result without ever storing the full matrix.

Architecture Flowchart
Diagram: Slow HBM (A100: 80 GB, about 1.5–2 TB/s); Load one block of Q and one block of K, V into fast SRAM (about 20 MB, about 19 TB/s); Compute that block's scores and update a running softmax; the full N × N matrix is never stored; Add the block's contribution to the output O, kept in SRAM; Write the finished O to HBM once

In words. Work through the score matrix in blocks that fit in fast on-chip memory, keeping a running maximum, sum and output per row. The full matrix is never stored.

16.2 FlashAttention and the Online SoftmaxRun this section

The softmax seems to need the whole row at once: its sum, and its maximum for safety (section 4.3). The online softmax keeps a running maximum and sum instead, correcting them as each block arrives.

Split a row into blocks and x(2)\mathbf{x}^{(2)}, each with its maximum and sum . With overall maximum m=max⁡(m1,m2)m = \max(m_1, m_2), multiplying by emi−me^{m_i - m} rescales each sum to the common maximum, since ex−mi⋅emi−m=ex−me^{x - m_i} \cdot e^{m_i - m} = e^{x - m}:

ℓ=em1−mℓ1+em2−mℓ2\ell = e^{m_1 - m} \ell_1 + e^{m_2 - m} \ell_2

The outputs combine the same way. With OiO_i the output from block ii alone, the combined output weights each by its share of the total sum:

Onew=diag⁡(em1−mℓ1ℓ)O1+diag⁡(em2−mℓ2ℓ)O2O_{\text{new}} = \operatorname{diag}\left(\frac{e^{m_1 - m} \ell_1}{\ell}\right) O_1 + \operatorname{diag}\left(\frac{e^{m_2 - m} \ell_2}{\ell}\right) O_2

Many queries are processed at once, each row with its own mm and ℓ\ell; diag⁡\operatorname{diag} gives each row its own scale. More blocks repeat the same update against the running result.

Figure 16.2
FlashAttention streams the score matrix in tiles

The N × N score matrix is never stored. Each step loads one tile of keys into fast memory, updates a running maximum, sum and output for every query row, and moves on. Pick a row to follow.

Random scores and scalar values, N = 16. After the last tile the running output equals the exact softmax-weighted average to floating-point precision.
Predict, then try

Follow one row and load the tiles one at a time. Before the last tile, predict whether the running output can already equal the exact answer, and what happens to the running sum whenever a tile brings a new, larger maximum.

In words. A sixteen-by-sixteen score matrix in vertical tiles of keys. Each click loads a tile and updates one query row's running maximum, sum and output, which match the exact attention output once the last tile is in.

The result is exactly standard attention, up to rounding. Extra memory drops from to O(N)\mathcal{O}(N), and HBM traffic falls several-fold, since the score matrix never goes there. The paper reports speedups often quoted as 3×3\times to 5×5\times, and the smaller memory is much of what makes long contexts practical.

Measured (lab 4c, experiments/online_softmax.py). The update is exact for every block size from 44 to 6464 (largest difference 5.96×10−75.96 \times 10^{-7}). It also matches this repository's own causal attention to 1.19×10−71.19 \times 10^{-7}, including the causal mask. The mask needs care: the streaming version has to apply it using each token's position in the full sequence, not its position inside the current block.

Measured (lab 4c). If the scores are small, dropping the rescaling (never subtracting any maximum) also gives the exact answer: the running ratio still computes the same expression. So the correction is about stability, not correctness. Multiply the scores by 4040 and the uncorrected version returns inf or nan, while the corrected one stays exact to 1.2×10−71.2 \times 10^{-7}. This is section 4.3's lesson again: the algebra was never in doubt; the problem is the range of numbers a float can hold.

Not measured. The streaming code here checks correctness, not speed. It does not test the 33–5×5\times wall-clock figure, which needs real tiling inside a single fused GPU kernel.

16.3 Local and Global AttentionRun this section

Gemma is Google DeepMind's family of open models. The first Gemma (2024) had embedding scaling and a 256,000-token vocabulary; Gemma 2 (2024) added alternating attention (this section), sandwich normalization (16.4) and logit soft-capping (16.5), all on top of section 15.6's block.

Alternating Local and Global Attention

Full attention's compute grows with N2N^2 and each layer's KV cache with NN, both expensive at 8k to 128k tokens. Gemma 2 alternates two kinds of layer:

  • Local (sliding-window) attention: each token attends only to the W=4096W = 4096 most recent tokens, so the layer's cache never exceeds WW entries.
  • Global attention over the whole sequence.
Architecture Flowchart
Diagram: Layer 1: local window, W = 4096; Layer 2: global attention; Layer 3: local window, W = 4096; Layer 4: global attention; ... alternating to the top of the stack

In words. Cheap local layers look at the last 4096 tokens; the global layers between them see the whole sequence, so information still travels from start to end.

The saving grows with context length and the share of local layers. With Gemma 2's 8192-token context, a local layer holds at most half a global layer's cache. Gemma 3 uses five local layers per global one with W=1024W = 1024; at 128k tokens that keeps about 17% of an all-global cache: .

Figure 16.3
Local layers see a window; global layers see everything

Left: which earlier tokens each query can attend to in the chosen layer. Right: how far back the last token’s view reaches as layers stack. A local layer extends it by one window; the first global layer reaches the start.

Drawings are scaled down to 24 tokens and a window of 4. The cache readout uses the real windows: 4,096 for Gemma 2 and 1,024 for Gemma 3.
Predict, then try

A token in a stack of local layers with window WW can see WW positions back in one layer. Before stacking layers, predict how far back the last token can reach after three local layers, and after one global layer.

In words. On a scaled-down sequence, which tokens each query sees in local and global layers, and how far back the last token reaches as layers stack: one more window per local layer, the very start after the first global layer. The readout gives the cache relative to an all-global stack.

16.4 Sandwich Normalization and Embedding Scaling

Sandwich Normalization: RMSNorm Before and After

Gemma 2 keeps Pre-LN (section 8.5) and adds a second RMSNorm on the sub-layer's output, before the addition, sandwiching the sub-layer:

xmid=SubLayer⁡(RMSNorm⁡pre(xl))\mathbf{x}_{\text{mid}} = \operatorname{SubLayer}(\operatorname{RMSNorm}_{\text{pre}}(\mathbf{x}_l))
xl+1=xl+RMSNorm⁡post(xmid)\mathbf{x}_{l+1} = \mathbf{x}_l + \operatorname{RMSNorm}_{\text{post}}(\mathbf{x}_{\text{mid}})

The first norm steadies the sub-layer's input; the second limits what it adds, so no layer can push a huge update into the residual stream. The skip path xl+\mathbf{x}_l + is still a plain addition, keeping section 8.5's gradient highway intact.

Scaling Embeddings by

GPT-2 and LLaMA feed the embedding row straight into the first layer. Gemma, like the 2017 Transformer, first multiplies it by the square root of the width:

x0=(et⊤WE)×dmodel\mathbf{x}_0 = \left(\mathbf{e}_t^\top W_E\right) \times \sqrt{d_{\text{model}}}

(et⊤WE\mathbf{e}_t^\top W_E is row tt of WEW_E, section 1.5.) Embedding entries start with standard deviation about 1/dmodel1/\sqrt{d_{\text{model}}}; the scale brings them to about 1, matching what the layers add. Otherwise the token's own identity would be small beside the first layer's update and hard for later layers to recover.

16.5 Logit Soft-Capping and QK-NormRun this section

Both the attention scores and the output logits can grow very large in training, causing loss spikes and, at sampling time, overconfidence. Gemma 2 bounds every logit within ±κ\pm\kappa (kappa) using tanh⁡\tanh, which maps any number into (−1,1)(-1, 1):

z~=κtanh⁡(zκ)\tilde{z} = \kappa \tanh\left(\frac{z}{\kappa}\right)

Gemma 2 uses two caps:

  • κ=50.0\kappa = 50.0 for the attention scores, .
  • κ=30.0\kappa = 30.0 for the output logits over the vocabulary.
Architecture Flowchart
Diagram: Raw logit z, any size; Divide by the cap κ, e.g. 30; tanh squashes it into -1 to 1; Multiply by κ again; Capped logit, between -30 and +30

In words. Divide by the cap, squash with tanh, multiply by the cap. Small logits pass almost unchanged; large ones bend smoothly toward the cap instead of being cut off.

Figure 16.5
Soft-capping bends large logits toward the cap

The capped value κ·tanh(z/κ) against the raw logit z, next to a hard clip at κ.

Negative logits behave symmetrically. Gemma 2 uses κ = 50 for attention scores and κ = 30 for output logits.
Predict, then try

With a cap of 30, predict what a logit of 30 comes out as, and whether a logit of 300 can still be told apart from a logit of 100 after capping.

In words. The soft cap plotted against the raw logit, beside a hard clip and the identity line. A slider sets the cap, and a marker follows a chosen logit to its capped value and slope.

What the Cap Does to Small and Large Logits

  • Small logits pass through, since tanh⁡(u)≈u\tanh(u) \approx u near zero: with κ=30\kappa = 30, 5 becomes 4.95.
  • Large logits approach the cap: 30 becomes 22.8 and 100 becomes 29.9.
  • The gradient never reaches zero. The slope 1−tanh⁡2(z/κ)1 - \tanh^2(z/\kappa) is always positive, whereas a hard clip has zero slope beyond the cap, so a clipped logit stops learning.

Gemma 3 replaced soft-capping with QK-norm: RMSNorm on the queries and keys before their dot product, which bounds the scores another way.

16.6 Attention Sinks and Streaming

To stream endless text, a natural idea is to keep only the most recent tokens in the KV cache. But as soon as the first token is evicted, perplexity shoots up, even when it is just the start marker <s>. The cause is the attention sinks of section 14.6 (Xiao et al., 2023): the spare attention parked there gets spread over other tokens and disrupts the layer. The fix is to always keep the first 44 tokens alongside the sliding window; with that, the paper's models kept a stable perplexity over very long streams.

Exercises

Check

Does FlashAttention change the result of attention? What does it change?

Apply

Merge two softmax blocks. Block 1 has maximum m1=2m_1 = 2 and sum ℓ1=3\ell_1 = 3 (computed with its own maximum); block 2 has m2=5m_2 = 5 and ℓ2=1.5\ell_2 = 1.5. What is the combined sum ℓ\ell relative to the overall maximum?

Apply

Gemma 3 uses five local layers (window 1,024) for every global layer. What share of an all-global stack's KV cache does it need at a context of 32,768 tokens?

Apply

With soft-capping at κ=30\kappa = 30, what does a logit of 30 become?

Extend

FlashAttention does the same number of multiply-adds as standard attention, yet runs several times faster. Explain where the time goes in standard attention and what FlashAttention changes.

Core Academic References

  • Dao et al. (2022); Dao (2023): FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. NeurIPS 2022; FlashAttention-2, 2023. arXiv:2205.14135
  • Milakov & Gimelshein (2018): Online normalizer calculation for softmax. arXiv:1805.02867
  • Gemma Team, Google DeepMind (2024): Gemma 2: Improving Open Language Models at a Practical Size. arXiv:2408.00118
  • Gemma Team, Google DeepMind (2025): Gemma 3 Technical Report. arXiv:2503.19786
  • Beltagy, Peters & Cohan (2020): Longformer: The Long-Document Transformer (sliding-window attention). arXiv:2004.05150
  • Xiao et al. (2023): Efficient Streaming Language Models with Attention Sinks (StreamingLLM). arXiv:2309.17453

Measured labs

These labs test what this module says by running real code. Each card states the idea, what the experiment found, and what it does not prove. The numbers come from experiments/results.json, the output of running experiments/run_all.sh on the tiny-transformers reference code at version 137f7ef. The project checks fail if a measured number in the module text stops matching that file.

Lab 4c — FlashAttention's online softmax

What the module says

Section 16.2 explains how FlashAttention computes softmax one block of keys at a time. When a new block has a larger maximum score, the running totals are rescaled, ℓ=em1−mℓ1+em2−mℓ2\ell = e^{m_1-m}\ell_1 + e^{m_2-m}\ell_2, and the output is corrected the same way. The module says the result is exact, the extra memory needed drops from O(N2)\mathcal{O}(N^2) to O(N)\mathcal{O}(N) for NN tokens, traffic to GPU main memory (HBM) falls several times over, and the speedup is often quoted as 3–5×.

What the experiment found

The block-by-block result is exact for every block size from 4 to 64, and matches the repository's own causal attention to within 1.2e-07. A subtler result: with ordinary scores, leaving out the rescaling still gives the right answer, because the math cancels. The rescaling is there to stop overflow, not to make the answer correct. Multiply the scores by 40 and the version without rescaling returns inf/NaN, while the rescaled one still matches to within 1.2e-07.

block sizes 4 to 64
max |Δ| = 5.96e-7

exact for any block size, up to rounding

vs the repository's causal attention
0.000000119209

largest difference

with rescaling, scores ×40
max |Δ| = 0.00000012
without rescaling, scores ×40: finite?
false

the failure the rescaling prevents

What this does not prove

This implementation uses ordinary tensor operations, so it says nothing about speed. It checks the rescaling identity from section 16.2 and the overflow it prevents. The 3–5× speedup the module quotes needs real tiling and a fused GPU kernel, which this lab does not have.

Run it yourself

bash
cd tiny-transformers && .venv/bin/python experiments/online_softmax.py

The code being tested