Batch B = tokens Input hidden D = Output hidden F = Compute rate × Bandwidth × Overlap = Dot product N =

An interactive edition of the Scaling Book

All About Rooflines

When we run algorithms on hardware, we're bounded by three things: how fast our computer can do math (OPs/second), the bandwidth available for moving data around (bytes/second), and the total memory available to store data (bytes). These "roofline" constraints let us upper and lower bound the time of a given computation.

Whose words are you reading? The prose comes from the original essay, with local changes for the selected hardware and HBM or network bandwidth. ✦ adaptation marks new paragraphs and sections written with OpenAI Codex. Live numbers and interactive figures replace fixed examples. Sources and editorial changes.

Where Does the Time Go?

Let's start with an extremely simple question: why does an algorithm take 50ms instead of 50s or 5ms? What is actually happening within the model that takes substantial time and how long should we expect it to take?

Computation: A deep learning model is effectively a bunch of matrix multiplications, each composed of floating-point multiplication and addition 'operations' (FLOPs). Our accelerator speed determines how long these take to compute. Let C be the compute rate per chip:

Tmath = Computation sAccelerator s/s C

For instance, can perform about in bfloat16, a 16-bit floating point format often used in ML. That means doing 1012 operations will take roughly 1012 / C = at the current compute rate.

✦ adaptation — Published rates are theoretical peaks; real kernels generally achieve less. The compute and bandwidth multipliers model achieved rates as a fraction of peak (0.8 means 80%), or hypothetical faster hardware above 1. Compute also depends on the selected precision. All compute rates here are dense, without structured sparsity.

Compute × C = per chip ·

Communication within a chip: Within an accelerator, tensors need to be transferred between accelerator memory (HBM) and the compute cores. You'll see the bandwidth of this link referred to as "HBM bandwidth." On , this is . Let W be this bandwidth in bytes/s and Q the bytes read and written.

Bandwidth × W =

We measure this in bytes/s and estimate the total communication time with:

Tcomms = Communication bytes Q bandwidth W

Typically (but not always), computation within a single chip can be overlapped with communication within a chip. This means we can lower-bound training and inference time by using the maximum of computation and communication time. We can also upper-bound with their sum. In practice, we optimize against the maximum as the algebra is simpler and we can usually come close to this bound by overlapping our communication and computation.

✦ adaptation — A matmul as a linear map

A matrix multiplication maps a token's vector from one latent space to another. A batch applies the same linear map to many token vectors.

B
Batch size: token vectors, not sequences.
D
Input width: — the input hidden size.
F
Output width: — the output hidden size.

Call the input matrix X, the weight matrix Y, and the output matrix Z. Each row of X is an input vector; each row of Z is its transformed output. The multiplication contracts over D:

X[B, D] ·D Y[D, F] → Z[B, F]
Hidden sizes in a Transformer

In a Transformer's feed-forward network, the up-projection maps the model hidden size D to the intermediate size F. The down-projection reverses these roles. A square weight matrix has D = F.

BF16 uses two bytes per element; INT8 uses one. Integer arithmetic is counted in OPs, floating-point arithmetic in FLOPs. One multiply-add counts as two operations.

Let w be bytes per weight, a bytes per input activation, and o bytes per output. For , w = , a = , o = . The matmul performs approximately 2BDF operations and transfers Q = aBD + wDF + oBF bytes, assuming each input is read once and each output written once.

If we assume we can perfectly overlap communication and computation, when Tmath > Tcomms, we see full utilization from our hardware. We call this being "compute-bound". When Tcomms > Tmath, we tend to be "communication-bound" and at least some fraction of our accelerator s/s is wasted waiting for data to be passed around.

Computation and communication times

Batch B = tokens Input hidden size D = Output hidden size F =

Weight parameters: D × F = .

Weight reads: Activation reads + writes:

Weight traffic is wDF; activation traffic is B(aD + oF). Increasing B reuses the same parameters, while reading and writing more activations.

Overlap (% of shorter duration):
Compute time2BDF / C
communication time(aBD + wDF + oBF) / W
Elapsed time

a = bytes per input; w = bytes per weight; o = bytes per output. C is operations per second; W is HBM bytes per second.

✦ adaptation — Matmul work and communication

Each of the B × F outputs needs D multiplications and roughly D additions: approximately 2BDF = s. Divide by C = to get .

Read input: aBD = .
Read weights: wDF = .
Write output: oBF = .

Total Q = . Divide by W = to get . This assumes each input and weight is read once.

Figure: computation and communication time. HBM traffic consists of weight reads (blue) and activation reads and writes (orange). Both tracks start at zero with perfect overlap; otherwise this illustrative schedule delays the longer task.
Tlower = max(Tmath, Tcomms) =
Tupper = Tmath + Tcomms =

If we optimize with the maximum in mind then the lower and upper bounds differ by at most a factor of 2 since Tmath + Tcomms ≤ 2 × max(Tmath, Tcomms). We then increase accuracy beyond this by modeling 'overlap regions' and overheads, which can be informed by profiling your specific model and target system.

✦ adaptation — Why the maximum, and why a factor of two?

If computation takes 5 ms and communication takes 3 ms, perfect overlap finishes in 5 ms; consecutive execution takes 8 ms. Each duration is at most their maximum, so their sum cannot exceed twice that maximum. These bounds use the selected rates; launch overhead, message latency, and data dependencies can make real execution slower.

One way to tell if an operation will be compute or communication-bound is to look at its "arithmetic intensity" or "operational intensity".

Definition: the arithmetic intensity of an algorithm is given by the ratio of the total s it performs to the number of bytes it needs to communicate — within a chip. We write this as Intensity(Computation).

Intensity(Computation) = Computation sCommunication bytes = /byte

Arithmetic intensity measures the "s per byte" of a given operation. To a first order, when our arithmetic intensity is high, Tmath is large compared to Tcomms and we typically use most of the available s/s. When the opposite is true, we spend more time on comms and waste s. The point where this crossover happens is the "peak arithmetic intensity" of our hardware, the ratio of peak accelerator s/s to accelerator bandwidth. We write this as Intensity(Accelerator).

Tmath > Tcomms ⇔ Intensity(Computation) > Intensity(Accelerator)
Intensity(Accelerator) = C / W = /byte

The quantity Intensity(Accelerator) is the arithmetic intensity at which our accelerator achieves its peak s/s. For , this is about /byte, since it can perform and load from HBM. That means if an algorithm has a lower arithmetic intensity, it will be bound by byte loading and thus we won't make good use of our hardware.

Example (dot product):

To compute the dot product of two vectors of length N = in bfloat16 precision, x · y: bf16[N], bf16[N] → bf16[1], we need to load x and y from memory, each of which has 2N bytes, perform N multiplications and N − 1 additions, and write 2 bytes back into HBM.

Intensity(dot product) = (2N − 1) / (4N + 2) = → ½ FLOP/byte

As N → ∞, the dot product has an arithmetic intensity of ½ or, put another way, the dot product does 0.5 floating point operations per byte loaded. This means our arithmetic intensity is lower than that of our hardware and we will be communication-bound.

✦ adaptation — Which compute units perform a dot product?

The relevant ceiling is the VPU's, rather than the MXU's. The original TPU example makes the same distinction: TPU v5p's VPU has a critical intensity around 3 FLOP/byte, so the dot product is still communication-bound. The matmul plots use the matrix multiplication units' throughput.

Visualizing rooflines

We can visualize the tradeoff between memory and compute using a roofline plot, which plots the peak achievable s/s (throughput) of an algorithm on our hardware (the y-axis) against the arithmetic intensity of that algorithm (the x-axis). Here's an example log-log plot:

Throughput ≤ min(C, W · Intensity(Computation))

· the roofline

Batch B = Input hidden size D = Output hidden size F =

Weight parameters: D × F = .

Compute × Bandwidth ×
BW₁ · BW₂ · ()Compute ceiling
Bandwidth-bound at BW₁ and BW₂Compute-bound at BW₁ and BW₂
Arithmetic intensity /byte
Ideal throughput / chip
Fraction of compute ceiling
Figure: the same matmul under two bandwidths. Red: both bandwidths limit throughput. Yellow: only the higher bandwidth reaches the compute ceiling. Green: more bandwidth gives no benefit. The filled dot is the current matmul at BW₁; the outlined dot shows its throughput at BW₂.

Above, as the intensity increases (moving left to right), we initially see a linear increase in the performance of our algorithm (in s/s) until we hit the critical arithmetic intensity of the hardware, at the selected bandwidth. Any algorithm with a lower intensity will be bandwidth (BW) bound and limited by the peak memory bandwidth. Any algorithm to the right will fully utilize our s/s. We can generally improve the performance of an algorithm either by increasing its arithmetic intensity or by increasing the memory bandwidth available.

✦ adaptation — Why does the sloping line turn into a flat roof?

At arithmetic intensity Intensity(Computation), each byte transferred corresponds to Intensity(Computation) operations. A bandwidth of W bytes/s therefore supports at most W · Intensity(Computation) operations/s: doubling intensity doubles this limit. Once W · Intensity(Computation) reaches C, compute throughput becomes the limit. More bandwidth cannot increase throughput beyond C operations/s. Setting W · Intensity(Computation) = C gives the critical arithmetic intensity at Intensity(Computation) = C/W.

✦ adaptation — Why does a bigger batch move the point right?

The same D × F weight matrix is reused for every token. Doubling B doubles the arithmetic but leaves the weight bytes unchanged. Only input and output traffic grows. When weight traffic dominates, this almost doubles operations per byte; at large B, growing activation traffic limits further improvement.

Matrix multiplication

Let's look at our soon-to-be favorite algorithm: matrix multiplication (aka matmul). Given two matrices X and Y, we can write X * Y → Z where X has shape bf16[B, D], Y has shape bf16[D, F], and Z has shape bf16[B, F].

Batch B = Input hidden size D = Output hidden size F =
HBMX, Y →
Z →

For BF16 inputs and output, load 2BD + 2DF bytes, perform approximately 2BDF FLOPs, and write 2BF bytes back to HBM.

Intensity(matmul) = 2BDF2BD + 2DF + 2BF = BDFBD + DF + BF

✦ adaptation — Other precisions

For the selected precision, weights use w = bytes, inputs use a = bytes, and outputs use o = bytes. The same calculation becomes:

Intensity(Computation) = 2BDFaBD + wDF + oBF =

We can get a nice simplification if we assume our "batch size" B is small relative to D and F. Then we get Intensity(Computation) ≈ 2B/w, or Intensity(Computation) ≈ B for BF16.

This is a reasonable assumption for Transformer matmuls since we typically have a local (per-replica) token batch size B < 1024 (note, not sequences) but D and F > 8000. Thus we generally become compute-bound when our per-replica batch size is greater than tokens, a very simple rule!

Takeaway:
✦ adaptation — When does the small-batch approximation break down?

The rule above assumes B is small relative to D and F, so weight transfers dominate HBM traffic. With Intensity(Computation) ≈ 2B/w, we need B ≳ w · Intensity(Accelerator) / 2 = tokens per batch. The takeaway above uses the full byte count for the selected dimensions.

The figures also count input reads and output writes, which use additional HBM bandwidth.

Solving the full inequality gives B ≥ w · Intensity(Accelerator) · DF / [2DF − Intensity(Accelerator) · (aD + oF)], provided the denominator is positive. At large B, activation traffic matters, so intensity saturates at 2DF/(aD + oF) = . If that is no greater than Intensity(Accelerator), no finite batch reaches the compute ceiling.

Actual kernels break these matrices into smaller tiles that fit on-chip memory and may read data more than once. Let bm be a tile’s number of input rows, bk its summed dimension, and bn its number of output columns. For BF16, ignoring output writes and reuse between tiles, intensity is approximately bmbn / (bm + bn). This whole-matrix roofline is optimistic; profiling or a tile-level byte count makes it more accurate.

✦ adaptation — The hardware numbers

Every row uses dense BF16 throughput and . The peak arithmetic intensity Intensity(Accelerator) = C/W uses the published rates, before multipliers.

HardwareDense BF16 / chipIntensity(Accelerator) · FLOP/byte

See exact figures, sources, dates, and conventions.

A Few Problems to Work

These exercises use TPU v5e: 197 TFLOP/s BF16, 394 TOP/s INT8, and 820 GB/s HBM bandwidth. Each question specifies its own precision; B, D, and F follow the shared controls.

Question 1 [INT8 matmul]:

Say we want to do the matmul X[B, D] ·D Y[D, F] → Z[B, F] in int8 precision (1 byte per parameter) instead of bfloat16 (2 bytes per parameter) since TPUs can do matmuls faster in lower precision.

  1. How many bytes need to be loaded from memory? How many need to be written back to memory?
  2. How many total OPs are performed?
  3. What is the arithmetic intensity?
  4. What is a roofline estimate for Tmath and Tcomms? What are reasonable upper and lower bounds for the runtime of the whole operation?

Assume our HBM bandwidth is 820 GB/s and our int8 peak OPs/s is (about 2x bfloat16).

Click here for the answer.
  1. Because we're storing our parameters in int8, we have 1 byte per parameter, so we have BD + DF = loaded from HBM and BF = written back.
  2. This is the same as in bfloat16, but in theory int8 OPs/s should be faster. So this is still 2BDF = OPs.
  3. Arithmetic intensity is 2BDF / (BD + DF + BF) = . If we make the same assumption as above about B ≪ D and B ≪ F, we get an arithmetic intensity of 2B, meaning our rule becomes B > HBM int8 arithmetic intensity / 2. Using the numbers given, our rule is B > . Note that this is basically unchanged!
  4. Tmath = 2BDF / = and Tcomms = (BD + DF + BF) / W = , so a reasonable lower bound is max(Tmath, Tcomms) = and an upper bound is Tmath + Tcomms = .

Question 2 [INT8 weights + BF16 compute]:

In practice we often do different weight vs. activation quantization, so we might store our weights in very low precision but keep activations (and compute) in a higher precision. Say we want to quantize our weights in int8 but keep activations (and compute) in bfloat16. At what batch size do we become compute bound? Assume .

Click here for the answer.

Again assuming B is small, we have 2BDF bfloat16 FLOPs but only DF weight bytes (instead of 2DF in bfloat16). This means we become compute-bound when B > . This is a lot lower, meaning if we can do int8 weight quantization (which is fairly easy to do) but still do bfloat16 FLOPs, we get a meaningful win in efficiency.

Question 3 [exact byte counts]:

Taking the setup from Question 2, make a roofline plot of peak FLOPs/s vs. B for F = D = 4096 and F = D = 1024. Use the exact number of bytes loaded, not an approximation.

Click here for the answer and interactive plot.
B = tokens
D = F = 4096D = F = 1024BF16 compute ceiling

At this batch: (4096) vs (1024).

Both shapes eventually achieve the peak hardware FLOPs/s, but the larger D and F achieve it sooner. D = F = 4096 reaches the ceiling at 137 tokens; D = F = 1024 needs 227 tokens.