How do you measure and reduce Time to First Token (TTFT), Inter-Token Latency (ITL), and latency variance in a production LLM API such as a streaming chat endpoint served with vLLM?
Answer
TTFT is the wall-clock time from request arrival to the first streamed token and ITL is the gap between consecutive tokens after that, and they are separate metrics because they are produced by two phases with two different hardware bottlenecks. Prefill processes the whole prompt in one or a few forward passes and is compute-bound, so grows roughly linearly with prompt length; decode generates one token per forward pass and is memory-bandwidth-bound, so
is floored by the time to stream the weights and KV cache out of HBM. Measurement must happen at the streaming boundary and be reported as a distribution, namely p50, p95 and p99 merged from histograms and bucketed by input and output length, because a p99 computed over a mix of 200-token and 100k-token prompts is meaningless. Reduction is metric-specific: prefix caching and chunked prefill cut TTFT, FP8 quantization and speculative decoding cut ITL, and the variance work is almost entirely about removing things that can stall the decode loop, namely unchunked prefills, KV cache preemption, and queueing above roughly 70% utilization. The metric that actually decides capacity is goodput, the request rate you can sustain while both the TTFT and ITL SLOs hold at p99, not raw tokens per second.
(1) Two Phases, Two Bottlenecks: prefill is compute-bound and sets TTFT, decode is bandwidth-bound and sets ITL, so a change that helps one often does nothing for the other.
(2) Measure Distributions, Not Means: record one TTFT scalar plus an ITL vector of length per request, and merge t-digest or HDR histograms across replicas instead of averaging per-replica p99s.
(3) TTFT Is Mostly Queueing Under Load: split server-side spans into and
, because past about 70% utilization the queue term dominates and no kernel optimization will help.
(4) The ITL Tail Is Interference, Not Steady State: p50 ITL reflects the decode step, while p99 ITL reflects the longest single stall, typically a colliding prefill or a preempted request being recomputed.
(5) Knobs Map To Metrics: prefix caching, chunked prefill and admission control for TTFT; quantization, GQA and speculative decoding for ITL; batch caps and prefill-decode disaggregation for variance.
(6) Optimize Goodput: report the sustained rate under both SLOs simultaneously, since raising batch size always trades TTFT and ITL tails for throughput.

Figure 1: TTFT is queue time plus prefill time; ITL is whatever happens between two decode steps of the same request. A single unchunked prefill scheduled into the shared loop stalls every decoding stream at once, which is the dominant source of p99 ITL. Splitting it into token-budgeted chunks bounds the worst stall to one chunk, at a small throughput cost from re-reading weights more often.
Instrument at the streaming boundary rather than inside the model: timestamp request arrival, the first SSE data event, and every subsequent event, then derive TTFT and the ITL vector client-side while the server emits its own queue, prefill and decode spans for the same request ID. The two views disagree in informative ways, because proxy buffering (an nginx deployment with proxy_buffering on) and a slow client event loop both inflate measured ITL with zero GPU involvement. For load testing, drive the endpoint with realistic input and output length distributions, since synthetic fixed-length prompts hide exactly the length-mixing effects that create the real tail; the vLLM benchmark_serving script and NVIDIA GenAI-Perf both report TTFT and ITL percentiles at a fixed request rate, which is the right shape of experiment. The variance checklist beyond scheduling is mundane but produces most of the surprises in practice: KV cache preemption and recompute when cache headroom runs out, ITL growing with batch size as decode drifts toward compute-bound, CUDA graph misses on uncaptured batch shapes, tensor-parallel all-reduce jitter, autoscaler cold starts that load tens of gigabytes of weights, and speculative decoding turning ITL bimodal because accepted-draft steps emit several tokens at once while rejected steps emit one.
Mathematical Formulation:
Where:
is end-to-end request latency,
the time to first token, and
the mean inter-token latency (its reciprocal is the perceived tokens per second of one stream).
is scheduler wait time and
the prompt forward pass; separating them is the single most useful piece of instrumentation, because they need opposite fixes.
and
are prompt and completion token counts,
the parameter count, and
the achieved (not peak) FLOP/s; the factor 2 counts one multiply and one add per parameter per token.
and
are the weight and KV cache bytes read per decode step and
is per-device HBM bandwidth, so this inequality is the bandwidth floor on ITL that no batching removes.
is one verification forward pass and
the expected accepted tokens per step under speculative decoding, which divides effective ITL but widens its distribution.
is offered request rate,
and
the TTFT and ITL SLO thresholds, and
the resulting goodput, the only capacity number worth putting in a dashboard.
Plugging in real hardware makes the two bottlenecks concrete. A 70B model in BF16 needs roughly 140 GB of weights, so on eight H100s with tensor parallelism each device reads about 17.5 GB per decode step against roughly 3.35 TB/s of HBM, giving a floor near 5 ms per token before all-reduce and KV reads, which is why a single stream tops out around 100 to 150 tokens per second in practice. The same eight GPUs prefill a 2048-token prompt in about B FLOPs, roughly 287 TFLOPs, which at an achieved 3.2 PFLOP/s is about 90 ms; a 32k-token prompt is 16 times that, which is exactly why long-context traffic destroys a shared TTFT SLO unless prefixes are cached or prefill is disaggregated. Note that FP8 weights halve
and therefore the ITL floor, but barely move TTFT, while prefix caching can cut TTFT by an order of magnitude on a 1500-token shared system prompt and does nothing for ITL.

Figure 2: Both p50 curves stay flat while the p99 curves bend upward, because queueing and interference hit the tail first. Here the ITL SLO is crossed at a lower rate than the TTFT SLO, so goodput is ITL-bound and the correct response is to cap batch size or disaggregate, not to add prefill compute.
| Technique | Metric it moves | What it costs |
|---|---|---|
| Prefix / KV cache reuse | TTFT p50 and p99 on shared system prompts, often 5x to 10x | HBM held by cached blocks, eviction policy tuning, cross-tenant isolation concerns |
| Chunked prefill | ITL p99 (bounds the worst decode stall to one chunk) | Slightly higher TTFT and a few percent lower throughput from repeated weight reads |
| Prefill-decode disaggregation | Both tails, by giving each phase its own pool and SLO | A KV cache transfer per request, duplicated weights, needs fast interconnect |
| Speculative decoding | ITL p50, roughly divided by accepted tokens per step | Bimodal ITL, wasted FLOPs on rejection, gains shrink at large batch sizes |
| FP8 or INT4 weight quantization | ITL floor, by halving or quartering bytes read per step | Accuracy regression that must be evaluated per task, calibration pipeline |
| Batch cap plus admission control | Latency variance, by refusing to accept work that will miss the SLO | Lower throughput and visible queueing or 429s, so it needs a priority policy |

















