Skip to the lesson
FixTheRobot Explore Robotics / LLM / LM9 Evaluation, Inference and Reliability / LM9-T5

LLM · Theory · LM9

LM9-T5 — Inference Latency and Memory

Generating text is fast to start and slow to continue, for different reasons. Knowing which limit you are at tells you what to change.

  • Lesson 5 of 6
  • Module 9: Evaluation, Inference and Reliability
  • Theory
  • Difficulty: Advanced
  • About 8 minutes to read
  • Prerequisite: LM9-T4

Accuracy is not the only property users meet: they wait for the first word, then for each next one, and they pay for the hardware. Inference cost has two phases with different bottlenecks, and simple arithmetic predicts both to within a factor of two. This lesson derives the estimates, shows how batching, quantisation and the key-value cache change them, and explains how to measure latency without fooling yourself. The notebook LM9-L3 measures time to first token and tokens per second on a tiny model.

Learning objectives

After this lesson, you should be able to:

  • Distinguish prefill from decode and the metrics of each.
  • Estimate time to first token from FLOPs and tokens per second from memory bandwidth.
  • Explain the effect of batching, quantisation and the KV cache on throughput and memory.
  • Measure latency properly: warm-up, repetition, percentiles.

The idea: two phases, two bottlenecks

To answer a request the system first reads the whole prompt, then writes the answer one token at a time.

Prefill and decodeA timeline. First a wide block labelled prefill: all prompt tokens are processed in one pass, limited by arithmetic; its end marks the time to first token. Then many narrow blocks labelled decode steps: one token each, limited by reading the weights from memory; the rate in tokens per second is set by these.prefill: whole prompt in one passcompute-bound (2·P·n FLOPs) t1t2t3t4t5t6t7t8t9t10 decode: one token per step, memory-bound (reads all weights each step) ▼ first tokentime to first token (TTFT) Two phases with different bottleneckstokens per second = 1 / time per decode step
Prefill processes many tokens at once and is limited by arithmetic. Decode produces one token per step and is limited by memory bandwidth.

Prefill

All prompt tokens are processed in a single parallel pass (they are known in advance, so the causal mask allows it). The work is about 2·P FLOPs per token, spread over many tokens at once: the hardware is busy with arithmetic. The time until the first output token is the time to first token (TTFT); it grows roughly in proportion to the prompt length.

Decode

Each new token needs a pass through the whole model, but only for one position. The arithmetic is tiny; the cost is reading every weight from memory to do it. So the speed of decoding, in tokens per second, is set by how fast the hardware can stream the weights: memory bandwidth divided by the model size in bytes. Smaller weights (quantisation) directly speed it up.

Batching

If several requests are decoded together, the weights are read once per step for all of them. Throughput (total tokens per second) rises almost in proportion to the batch size until the arithmetic becomes the limit; latency per request rises only slightly. The price is memory: each request has its own KV cache.

The KV cache

At each decode step the model needs the keys and values of all previous tokens. Recomputing them would repeat the prefill at every step, so they are stored (Module 10). The cache grows with the sequence and with the number of concurrent requests. Reading it is also memory traffic in every step, so long contexts slow down decoding.

What to measure

  • TTFT (for interactivity) and time per output token (for streaming speed).
  • Throughput: tokens per second across all requests, at a stated batch size.
  • Memory: weights plus KV cache at the target concurrency and context length.
  • Tail latency: the 95th and 99th percentile, not only the mean.

The mathematics: roofline estimates

Let the model have P parameters stored at b bytes each, run on hardware with peak arithmetic rate F (FLOP/s) at utilisation u, and memory bandwidth W (bytes/s).

prefill and decode estimates
TTFT≈2⋅P⋅npromptF⋅u\mathrm{TTFT}\approx \frac{2\cdot P\cdot n_{\text{prompt}}}{F\cdot u}
tstep≈P⋅bWt_{\text{step}}\approx \frac{P\cdot b}{W}
tokens/s≈WP⋅b\text{tokens}/s\approx \frac{W}{P\cdot b}
nprompt
number of prompt tokens.
F · u
achieved arithmetic rate; u is typically 0.3 to 0.6 for large matrix products.
W
memory bandwidth; ~10¹² bytes/s for a high-end accelerator.
tstep
time of one decode step for a single sequence; the weights are read once.

Example (7B model). P = 7·109; F = 1014 FLOP/s at u = 0.4; W = 1012 B/s. A 1,000-token prompt: 2 · 7·109 · 1,000 / (4·1013) = 0.35 s. Decoding in 16 bits (b = 2): tstep = 14 GB / 1 TB/s = 14 ms, 71 tokens/s. In 8 bits: 7 ms, 143 tokens/s. In 4 bits: 3.5 ms, 286 tokens/s.

Batching. With batch B and per-request KV cache of K bytes, a step reads the weights once and each request’s cache: bytes per step = P · b + B · K, and arithmetic 2 · P · B. The step is memory-bound while

batch size where decode becomes compute-bound
B<B∗=F⋅u⋅b2⋅WB<B^{*}=\frac{F\cdot u\cdot b}{2\cdot W}

For the numbers above (16-bit): B* = 4·1013 · 2 / (2 · 1012) = 40. Below 40 concurrent sequences, doubling the batch nearly doubles throughput. With B = 8 and a 1,000-token cache of 0.5 MB per token (500 MB per request): bytes per step = 14 GB + 4 GB = 18 GB, 18 ms per step for 8 tokens, 444 tokens/s in total (56 per request) against 71 for a single request.

Memory. Serving memory = P · b + B · n · 2 · L · d · bKV (Lesson LM8-T3). For the 7B model: 14 GB of weights in 16 bits; the cache of 0.52 MB per token at 16 bits means 8 requests of 4,096 tokens need 17 GB, more than the weights.

Tail latency. Real services see queueing and variable lengths. If the service rate is μ requests per second and the arrival rate λ, utilisation ρ = λ/μ, a simple M/M/1 model gives a mean time in the system of 1/(μ − λ): at ρ = 0.9 it is ten times the service time, at ρ = 0.5 twice. Plan capacity for headroom, not for 100% utilisation.

A worked example: latency calculators

The code reproduces the estimates above and a batched-throughput table, then shows a measurement helper that avoids the usual mistakes (warm-up, repeated runs, percentiles).

import statistics
import time

P, F, U, W = 7e9, 1e14, 0.4, 1e12            # parameters, FLOP/s, utilisation, bytes/s

def ttft(nprompt):
    return 2 * P * nprompt / (F * U)

def tokens_per_second(bytes_per_weight, batch=1, kv_bytes_per_request=0.0):
    step = (P * bytes_per_weight + batch * kv_bytes_per_request) / W        # memory-bound step
    compute = 2 * P * batch / (F * U)                                       # arithmetic for the batch
    return batch / max(step, compute)

print(round(ttft(1000), 2))                                                  # 0.35
for b in (2, 1, 0.5):
    print(b, round(tokens_per_second(b)))                                       # 2 71 / 1 143 / 0.5 286
print(round(tokens_per_second(2, batch=8, kv_bytes_per_request=0.5e6 * 1000)))               # 444
print(round(F * U * 2 / (2 * W)))                                                           # 40  batch where decode turns compute-bound
for batch in (1, 8, 32, 64):
    print(batch, round(tokens_per_second(2, batch)))                         # 71 571 2286 2857 (compute limit at large batch)

def measure(fn, warmup=2, runs=20):
    for _ in range(warmup):
        fn()                                                                 # warm-up: caches, compilation, allocator
    times = []
    for _ in range(runs):
        t0 = time.perf_counter()
        fn()
        times.append(time.perf_counter() - t0)
    times.sort()
    return statistics.median(times), times[int(0.95 * len(times)) - 1]

median, p95 = measure(lambda: sum(i * i for i in range(20000)))
print(median > 0 and p95 >= median)                                          # True

The batch table shows the regime change: throughput grows almost linearly with batch size until about 40 sequences and then flattens at the arithmetic limit (2,857 tokens/s for these numbers), while the cache grows linearly with batch size.

What to change when

Diagnosing inference cost
SymptomBottleneckRemedies
Slow first token on long promptsprefill computeshorter prompts, caching a shared prefix, faster hardware, smaller model
Slow streaming speed (tokens/s)decode memory bandwidthquantise, smaller model, speculative decoding (Module 10), more bandwidth
Low throughput for many usersbatch too small / idle hardwarecontinuous batching, larger batches until compute-bound
Out of memory with long contextsKV cacheshorter context, cache quantisation, grouped-query attention (Module 10), fewer concurrent requests
Good median but bad 99th percentilequeueing, long outputsheadroom, limits on length, priority classes

Engineering implications and limitations

Measure the thing the user feels. TTFT and time per output token at the real prompt and output lengths, not a synthetic short prompt.

Warm up and repeat. The first calls include one-off costs (allocator, kernel compilation, caches). Discard them, then take many runs and report median and high percentiles.

Synchronise on accelerators. GPU calls are asynchronous; call the framework’s synchronise function before reading the clock, or you time only the launch.

State the batch size and the hardware. “71 tokens/s” means nothing without them.

Separate model quality from serving speed when quantising. Measure the accuracy loss and the speed gain together (Lesson LM8-T4); a 4-bit model that is 4× faster but 5 points worse may or may not be a good trade.

Limit output length. Decode time grows linearly with the output, so a maximum length caps tail latency and cost.

Estimate before buying hardware. The roofline numbers tell you whether you need more bandwidth (decode-heavy) or more compute (prefill-heavy, large batches).

For robots. A control loop has a deadline. If the model must return a command in 200 ms, budget TTFT plus the tokens of a structured answer (a dozen tokens) and test on the actual on-board hardware, at its thermal limits; fall back to a rule-based safe action if the deadline is missed.

Common misconceptions

“Latency is proportional to model size in every phase.”

Prefill is limited by arithmetic and decode by memory bandwidth; they respond to different changes.

“A faster GPU always speeds up decoding.”

Decode follows memory bandwidth; extra arithmetic capacity does not help a memory-bound step.

“Batching increases the latency of each request proportionally.”

Until the arithmetic limit, a larger batch costs each request little extra time while multiplying throughput.

“The KV cache is a minor detail.”

It can exceed the weights in memory and adds to the traffic of every step.

“Mean latency is enough.”

Users meet the tail; report percentiles.

Engineering takeaways

  1. Prefill is compute-bound: TTFT ≈ 2·P·n/(F·u) (0.35 s for 1,000 tokens on a 7B model). Decode is memory-bound: tokens/s ≈ W/(P·b) (71, 143, 286 at 16, 8, 4 bits).
  2. Batching raises throughput nearly linearly until B* = F·u·b/(2·W) (40 in the example), at the price of KV-cache memory.
  3. KV cache = 2·L·d·n·B·bytes; it can exceed the weights and slows long-context decoding.
  4. Measure TTFT, time per token, throughput and percentiles, with warm-up, repetitions and synchronisation, at stated batch size and hardware.
  5. Choose remedies by bottleneck: quantisation for decode, shorter prompts or prefix caching for prefill, continuous batching for throughput.

References and further reading

See Chapter 4 of the book (parameters and memory of GPT models) and Lesson LM8-T3 for the memory formulas. The hardware figures used here are round illustrative numbers, not the specification of any particular device.

Knowledge check

Three conceptual questions. Write an answer, then reveal the explanation. Your answers stay in your browser.

Question 1

A 13-billion-parameter model is stored in 8 bits on hardware with 2 TB/s memory bandwidth. Estimate the single-stream decode speed.

Explanation

Bytes read per token: 13 GB. Time: 13e9 / 2e12 = 6.5 ms, about 154 tokens per second.

Question 2

A prompt has 2,000 tokens, the model has 7 billion parameters and the hardware delivers 5×10¹³ useful FLOP/s. Estimate the time to first token.

Explanation

2 × 7e9 × 2,000 / 5e13 = 2.8e13 / 5e13 = 0.56 s.

Question 3

Why does doubling the batch size roughly double throughput at small batch sizes but not at large ones?

Explanation

At small batch sizes the step is memory-bound: the weights are read once whatever the batch, so more sequences per step means more tokens for nearly the same time. Beyond the batch size where arithmetic becomes the limit, each additional sequence adds proportional compute time, so throughput flattens.

What’s next

Next: responsible evaluation: how to report what you found, including what you did not test.

Practice this lesson

Evaluate a Tiny GPT End to End

Compare checkpoints and decoding settings on a held-out set, with seeds, confidence intervals and a latency and memory measurement.

Next lesson

LM9-T6 — Responsible Evaluation and Model Limitations

Report limits honestly, test failure cases and avoid conclusions from a few impressive examples.

Nothing below is required to finish this lesson.