The Roofline Model — Are You Compute-Bound or Memory-Bound?

Posts 1 and 2 kept drawing a line between two worlds: workloads limited by how fast the GPU can compute, and workloads limited by how fast it can move data. The roofline model is the single tool that tells you which world you're in for any given operation — and therefore which optimizations will help and which are a waste of time. It is the most useful mental model in performance engineering, and it fits on one chart.

Every optimization decision in inference comes down to one question: is this operation limited by compute or by memory bandwidth? Optimizing the wrong one is effort with zero payoff — adding compute to a memory-bound kernel does nothing. The roofline model answers the question quantitatively using just two hardware numbers and one property of your workload. This post builds it from scratch and shows why LLM inference lives on a specific part of the chart.

Arithmetic intensity: the one number that matters

The key property of any operation is its arithmetic intensity: the ratio of compute to data movement.

Arithmetic intensity = FLOPs performed / bytes moved from memory

It measures how much useful math you do for each byte you pull out of HBM. A high-intensity operation does lots of arithmetic per byte (good — the expensive memory read is well amortized). A low-intensity operation does little arithmetic per byte (bad — you move a lot of data for a little compute, so you’re starved waiting on memory).

Arithmetic intensity is a property of the operation and how it’s done, and raising it (doing more math per byte read) is the deep goal behind batching, kernel fusion, and FlashAttention — all later posts.

The chart: two ceilings

Plot achievable performance (FLOPS, vertical) against arithmetic intensity (horizontal) and the hardware imposes two limits that together form a “roofline”:

The corner where the slant meets the flat is the break-even intensity. An operation’s intensity places it under one ceiling or the other, and that tells you what to optimize: - Memory-bound (under the slant): the only thing that helps is reducing data movement or raising intensity — smaller data types (quantization), reusing data in shared memory (fusion), or batching. Adding compute does nothing. - Compute-bound (under the flat): now faster arithmetic helps — tensor cores, lower-precision math, better algorithms. Reducing memory traffic does nothing.

This is the model’s payoff: it turns “my kernel is slow” into “my kernel is at X% of the relevant ceiling, and here is which lever moves it.”

Where LLM inference sits

The roofline explains the two phases of LLM inference precisely:

This is why the same model can feel compute-limited while chewing through a long prompt and bandwidth-limited while streaming out the answer — they’re different points on the roofline. And it’s why batching is the master lever for throughput: batching many sequences turns the memory-bound matrix-vector decode into a higher-intensity matrix-matrix operation, sliding it rightward up the slant toward the compute ceiling. The roofline makes “batch to go faster” not folklore but geometry.

The takeaway: the roofline model answers the one question that governs every optimization — compute-bound or memory-bound? — from two hardware numbers (peak FLOPS, memory bandwidth) and one workload property, arithmetic intensity (FLOPs per byte moved). Low intensity → memory-bound (under the slanted ceiling; only less data movement helps — quantization, fusion, batching). High intensity → compute-bound (under the flat ceiling; only faster math helps — tensor cores, lower precision). LLM prefill is compute-bound (matrix-matrix) and decode is memory-bound (matrix-vector), which is why batching — raising intensity — is the master throughput lever.

Key takeaways

Further reading

Sources & References