AI Infra Interviews logo
GPU & Accelerator Architecture / 06
medium★ EssentialNewOpenAIAnthropicTogether AI

Is LLM decode memory-bound or compute-bound? Show me the arithmetic that decides it.

At batch 1 a decode step reads every weight byte to do two FLOPs with it. The intensity is the batch size, the ridge is about 295, and the KV cache puts a ceiling on how far batching can push you. The full chain for a 70B model on H100.

Updated Sep 2026 · Grounded in real AI infrastructure interview loops and written to a senior-engineer editorial bar, with every number worked and every diagram hand-built.

TL;DR: Memory-bound, until the batch is large. Each decode step does 2 FLOPs per parameter per sequence and reads each parameter once, so with bf16 weights the arithmetic intensity is 2 × batch ÷ 2 bytes = batch FLOP per byte. The H100 ridge is 295, so a batch of 1 sits at 1 FLOP/B (0.3% of peak) and a step for Llama 3.1 70B takes 141 GB ÷ 3.35 TB/s = 42 ms per GPU-equivalent. Batching moves the point right almost for free until the KV cache reads, which grow with batch and context, dominate the bytes; at 4k context in bf16 the intensity cannot exceed about 108 no matter the batch.

How to approach it

Ask for the model, the precision and the context length, since each changes a term. Write the FLOPs per step and the bytes per step for one sequence, take the ratio, and compare it with the ridge point. Then add sequences and show how the ratio moves, and add the KV cache term to show where the movement stops. Give a step time in milliseconds at each stage so the interviewer hears tokens per second, not just a classification. Close with the decision: what batch to run and why fp8 changes the answer less than people expect.

A strong answer

A typical situation: a serving fleet reports 100% GPU utilization and 3 TFLOPS of useful work, and a capacity plan is written on the first number. Decode at batch 1 reads every weight to produce one token, and the ratio below says so before anyone opens a profiler.

Memory-Bound vs Compute-Bound Kernels is decided by one ratio, and for decode every term in it is countable.

Per generated token, a dense model does 2 FLOPs per parameter (one multiply, one add, for each weight). Per step, the weights must be read once from HBM regardless of how many sequences are in the batch, because a step for a batch is one matrix multiply per layer with the batch as the M dimension.

model: Llama 3.1 70B, N = 70.6e9 parameters, bf16 weights = 141 GB

batch B = 1:
  FLOPs per step = 2 × N = 141 GFLOP
  bytes per step = N × 2 B = 141 GB
  I = 141e9 ÷ 141e9 = 1 FLOP/B
  step time (bandwidth-bound) = 141e9 ÷ 3.35e12 = 42 ms   (on one H100's bandwidth)
  achieved = 141e9 ÷ 0.042 = 3.4 TFLOPS = 0.34% of 989
  sanity: 42 ms per token is 24 tokens/s, the familiar single-stream speed of a 70B split over
          a node when you ignore communication; a full 8-GPU node has 8 × 3.35 = 26.8 TB/s,
          so the step drops to 5.3 ms and about 190 tokens/s

batch B:
  FLOPs = 2 × N × B; bytes ≈ N × 2 (weights, the same)
  I = 2NB ÷ 2N = B FLOP/B
  ridge on H100 (bf16) = 989 ÷ 3.35 = 295, so decode reaches compute-bound at B ≈ 295
  with fp8 weights: I = 2NB ÷ N = 2B, ridge for fp8 compute = 591, so B ≈ 295 again

That last line is worth saying in the room: fp8 halves the bytes and doubles the compute peak, so the batch at which decode crosses the ridge barely moves; what fp8 buys is a 2x shorter step at every batch, not a change in regime.

The weight-only chain overstates how far batching can go, because every sequence brings its own KV cache and the attention kernel reads all of it each step. The KV bytes scale with batch, so they do not amortize:

KV per token (Llama 3.1 70B, bf16) = 2 × 80 layers × 8 KV heads × 128 head dim × 2 B = 320 KB
KV per sequence at 4,096 context = 320 KB × 4,096 = 1.31 GB

bytes per step = weights + B × KV per sequence = 141 GB + B × 1.31 GB
FLOPs per step ≈ 2NB = 141 GFLOP × B   (attention FLOPs add ~ 8%, ignored here)

I(B) = 141B ÷ (141 + 1.31B)
  B = 32:   4,512 ÷ 183  = 25 FLOP/B     step = 183 GB ÷ 3.35 TB/s = 55 ms
  B = 128: 18,048 ÷ 309  = 58 FLOP/B     step = 92 ms
  B = 512: 72,192 ÷ 812  = 89 FLOP/B     step = 242 ms
  B → ∞:   141 ÷ 1.31    = 108 FLOP/B, the asymptote

sanity: 108 is below the 295 ridge, so at 4k context in bf16 no batch makes a 70B decode
        compute-bound on H100; the attention reads take over from the weight reads at
        B = 141 ÷ 1.31 ≈ 108 sequences, where the two terms are equal

Two things follow. First, the useful regime is where the step time is still dominated by the fixed weight read, roughly B < 100 at this context, because there each added sequence is nearly free: batch 32 costs 55 ms per step and produces 32 tokens, batch 1 costs 42 ms and produces 1. Second, the levers that matter at long context are on the KV term: fp8 KV halves 1.31 GB to 0.66 GB and lifts the asymptote to 216; GQA already did this once (8 KV heads instead of 64); MLA in DeepSeek-V3 does it again. The attention kernel itself is a separate memory-bound problem at intensity around 8 FLOP/B with GQA, because each sequence's cache is read by that sequence alone.

rendering diagram…

The serving decision that comes out of this: run decode at the largest batch the TPOT target allows, because throughput rises almost linearly with batch until the KV term takes over. On an 8-GPU H100 node with bf16 weights (17.6 GB per GPU) and a 12 ms TPOT budget, the step is 17.6 GB ÷ 3.35 TB/s = 5.3 ms of weights plus B × 1.31 GB ÷ 26.8 TB/s = B × 49 µs of KV, so B ≈ 137 fits inside 12 ms; that node produces about 11,000 tokens/s. The reversal is short-context, small-model serving on a large-batch offline job, where the batch can climb past the ridge and prefill-style compute limits appear; there, fp8 compute and tensor-core efficiency become the levers.

Prefill, by contrast, is compute-bound from the first request: a 2,000-token prompt is a GEMM with M = 2,000, intensity in the thousands, and it runs near peak. The reversal condition: prefill, or any batch large enough to push intensity past the ridge at 295. Bandwidth-Bound Decode Throughput carries the same arithmetic forward into tokens per second. The two phases want opposite things from the hardware, which is the entire motivation for disaggregated serving.

What interviewers probe next

  • "So why does anyone quote 0.3% utilization as a problem?" It is only a problem if the batch could be higher. At batch 1 the ceiling is 3.4 TFLOPS on one card, and being at 3 TFLOPS is a near-perfect kernel; the fix is scheduling, not CUDA.
  • "How does speculative decoding fit?" It converts one memory-bound step into a verification of k draft tokens, which is a GEMM with M = k per sequence: the same weight bytes now do k× the FLOPs, so intensity rises by k. It only pays while the batch is well left of the ridge.
  • "What about a MoE model?" Replace N with the active parameters for FLOPs and bytes per step, but note every expert touched by any sequence in the batch is read, so at large batch the bytes approach the total parameters while FLOPs stay at active × B: the intensity gain from batching is smaller than for a dense model.
  • "What does H200 change?" 4.8 TB/s and 141 GB: the batch-1 step drops from 42 to 29 ms and the KV asymptote is unchanged, but the memory holds 2x the sequences at the same context.

Common mistakes

  • Answering "memory-bound" as a fact about LLMs rather than about a batch size and a context length.
  • Counting FLOPs per token as N instead of 2N, or forgetting that the batch shares the weight read.
  • Ignoring the KV cache and concluding that batch 300 makes decode compute-bound on H100.
  • Believing fp8 turns decode compute-bound; it shortens the step, the crossover batch stays near 295.

Key takeaways

  • Decode intensity ≈ batch (bf16) or 2 × batch (fp8) from weights alone; ridge ≈ 295, and the crossover batch is ≈ 295 either way.
  • Batch-1 step for a 70B in bf16 is 141 GB ÷ 3.35 TB/s = 42 ms per H100-equivalent; 5.3 ms on a full node.
  • KV bytes scale with batch: at 4k context bf16 the asymptotic intensity is 141 ÷ 1.31 ≈ 108, below the ridge; fp8 KV and GQA or MLA raise it.
  • Serve at the largest batch that fits the TPOT, since throughput is near-linear in batch until the KV term dominates.
That one was free — and so are 10 answers per topic without an account. Signing in doubles that to 20, opens the Plus lessons in the courses, and remembers which topics you keep getting wrong.no card · Google sign-in · nothing to cancel
HOW DID IT GO?
0
READING SIGNED OUT

Signing in doubles your free answers, from 10 to 20 per topic, and the site starts remembering you: mastery per topic, bookmarks, and a next-focus recommendation. Free, no card.

Sign in free

The concepts behind this question

Ranked by how closely each one overlaps this question's topic, so the first card is the thing to read if the answer above moved too fast.

Advanced
🧩 GPU & Accelerator Architecture🔒 Premium
Memory-Bound vs Compute-Bound KernelsEvery kernel is limited by one of two walls: how fast bytes arrive from HBM, or how fast the tensor cores can multiply. Which wall applies is decided by arithmetic intensity against the ridge point, and the two regimes need opposite fixes. Decode, LayerNorm and softmax are memory-bound; prefill GEMMs are compute-bound; the interview question is which one you are looking at and what you would do about it.
Foundational
🧩 GPU & Accelerator Architecture
Roofline ModelThe roofline plots a kernel's attainable throughput against its arithmetic intensity, FLOPs per byte moved from memory. Below the ridge point (peak FLOPS divided by memory bandwidth, about 295 on an H100 in bf16) a kernel is memory-bound and no amount of clever code reaches the peak; above it, compute is the limit. One picture explains why decode runs at under 1% of peak and why fusion and batching are the two levers that move it.
Core
🧮 Napkin Math & CapacitySign in
Arithmetic Intensity by OperationThe roofline says a kernel's ceiling is set by its FLOPs per byte against the hardware's ridge point. This page does the FLOPs-per-byte arithmetic for the operations an LLM actually runs (decode at several batch sizes, prefill, the attention score matmul with and without FlashAttention, LayerNorm, an embedding lookup) so the reader can place any of them on the roofline from first principles and say which lever moves it. The numbers explain why a serving fleet's GPUs report 30% utilization while fully loaded.
Advanced
🧮 Napkin Math & Capacity🔒 Premium
Bandwidth-Bound Decode ThroughputBecause decode reads every weight once per step, its speed is a division: memory bandwidth over bytes per step. That one formula gives single-stream tokens per second for any model on any card, the batch curve that flattens at the ridge point, the effect of quantization, and the point where the KV cache rather than the weights becomes the thing being read. This page derives it, works it for a 70B model on four accelerators, and shows how to read a vendor throughput claim against it.
UP NEXT ON YOUR JOURNEY
FEDITOR'S NOTE

Scored on the derivation that intensity ≈ batch for bf16 weights, the step time at batch 1 from bytes ÷ bandwidth, and the recognition that the KV cache term caps intensity below the ridge at long context.

DISCUSSION · 0

No comments yet — be the first to share your approach.