Transformers from the Weights Up/Part 4 of 20
Transformers from the Weights Up, Part 4: Layers as matrix multiplies: FLOPs, bytes, and arithmetic intensity
A layer is a handful of matrix multiplies. What decides speed is whether the chip is doing those FLOPs or waiting on the weights.
3 Nov 2026
Image 4 is σ(Wx + b) for one input. Stack M inputs (tokens, from any mix of batch and sequence, as Lesson 2 showed) as the rows of X and the same weights serve all of them in one matrix-matrix product. The bias add and the activation touch M · N elements, a rounding error next to the 2MNK of the matmul, and Llama drops biases entirely.
Counting FLOPs: 2N forward, 6N training
Each weight wᵢⱼ appears in exactly one product per token, wᵢⱼ · xⱼ, and that product is added to an accumulator. One multiply plus one add is 2 FLOPs (an FMA counts as 2, which is also how NVIDIA quotes peak numbers). Summed over every matmul weight, the forward pass costs 2 FLOPs per parameter per token, plus attention's context-dependent term from Lesson 2.
Llama-3-8B: 2 × (6.98B layer weights + 0.53B lm_head) ≈ 15.0 GFLOP. The 0.53B embedding is a row lookup, not a matmul.
Two matmuls per layer: one for the gradient with respect to the input (to keep backpropagating), one for the gradient with respect to the weights. Each costs as much as the forward one.
D is the number of training tokens. Meta reports Llama 3 was pretrained on over 15T tokens, so the 8B model is about 6 × 8.03 × 10⁹ × 1.5 × 10¹³ ≈ 7.2 × 10²³ FLOPs.
Bytes, not FLOPs: arithmetic intensity
A chip has two budgets: peak compute π (FLOP/s) and memory bandwidth β (bytes/s). An operation's arithmetic intensity I is FLOPs done per byte moved between memory and the compute units. The roofline model (Williams, Waterman and Patterson, 2009) says the best you can do is the smaller of the two limits.
b is bytes per element (2 in bf16). The ridge point π/β is the intensity a kernel needs to keep the math units busy: 989 × 10¹² ÷ (3.35 × 10¹²) ≈ 295 FLOP/byte on an H100 SXM, and 312 ÷ 2.039 ≈ 153 on an A100. Now plug in the two shapes that matter.
- Intensity at this M
- 1.00 FLOP/B ≈ M when M ≪ 4096
- Attainable throughput
- 3.35 TFLOP/s (0.3% of peak)
- Regime
- memory-bound: more tokens per weight read are nearly free
A real layer on the roofline
Here are Llama-3-8B's seven per-layer weights with the simplest roofline time estimate. At M = 1 every matmul is memory-bound and the layer costs the time to read 436.2 MB. Raise M to 128 and the time barely moves, because the same bytes now serve 128 tokens: that is why serving engines batch many users' decode steps together. Past the ridge, time grows linearly with M and you are buying FLOPs.
- Layer FLOPs
- 436.2 MFLOP
- Layer bytes moved
- 436.4 MB (weights alone 436.2 MB)
- Layer time, × 32 layers
- 130.3 µs × 32 = 4.17 ms
- Time per token
- 4.17 ms (240 tokens/s through the layers)
| Regime | Typical M | Bound by | What helps |
|---|---|---|---|
| Single-user decode | 1 | Memory bandwidth (I ≈ 1) | Fewer bytes per weight (quantization), faster memory, speculative decoding |
| Batched serving decode | tens to hundreds | Bandwidth until M nears the ridge | Continuous batching; KV-cache memory becomes the limit (Lesson 13) |
| Prefill / training | thousands | Compute (I in the hundreds to thousands) | Tensor cores, lower-precision math (fp8, Lesson 15), better kernels |
Why decode is memory-bound, and the tokens/s ceiling
Generating one token at batch 1 means one matrix-vector product against every weight matrix. Each weight is read from memory, used for 2 FLOPs, and not needed again until the next token. So the time per token is at least the bytes of the model divided by bandwidth, no matter how many TFLOPS the chip advertises.
The 15.01 GB is everything streamed per step: 32 layers × 436.2 MB plus lm_head's 1.05 GB (the embedding contributes one 8 KB row). Compute for the same token is 15.0 GFLOP, or 15.2 µs at the H100's peak, against 4.48 ms to read the weights. That 0.3% utilization is the single most important number for understanding local inference.
- Bytes read per token
- 7.505B weights × 16 bits ÷ 8 = 15.01 GB
- Ceiling, batch 1
- 3,350 GB/s ÷ 15.01 GB = 223 tokens/s (4.5 ms/token)
- Tokens generated (slowed)
- 0
Lesson 4: key numbers
- 2MNK
- FLOPs of an M by K times K by N productone multiply plus one add per term
- 2N / 6N
- FLOPs per tokenforward, and training (forward plus backward)
- ≈ 1
- FLOP/byte, bf16 decodeeach weight: 2 bytes, 2 FLOPs
- ≈ M
- intensity with M tokenswhile M is much smaller than K and N
- 295
- H100 SXM ridge (FLOP/byte)989 TFLOPS ÷ 3.35 TB/s
- 153
- A100 80GB ridge (FLOP/byte)312 TFLOPS ÷ 2,039 GB/s
- 15.01 GB
- Llama-3-8B bytes per decode stepbf16, layers plus lm_head
- ≈ 223 tok/s
- H100 batch-1 ceiling, bf16real numbers land below this
Lesson 4: self-check
1. How many FLOPs is Llama-3-8B's gate_proj applied to a 2048-token prompt?
2. Why is a bf16 matrix-vector product's arithmetic intensity about 1, independent of matrix size?
3. What is the batch-1 tokens per second ceiling for Llama-3-8B in Q8_0 on an A100 80GB?
4. An H100 serves 64 users' decode steps in one batch. Roughly how do time per step and total throughput change versus one user?
5. Training uses about 6N FLOPs per token. Where do the extra 4N come from?
6. Fusing SiLU of the gate, times up, into the matmul saves no FLOPs. What does it save, and why does that matter in the decode regime?