AI engineering and inferenceLoading progress…

Prefill interference

Trace static and iteration-level schedules and explain chunked prefill, preemption and latency tradeoffs.

A big prompt can stall everyone else

Admitting a new request means running its prefill, which is much more work than one decoding step. If the scheduler squeezes a long prompt into one iteration, that iteration takes a long time and every request that was about to get its next token waits for it. In the toy cost model (1 ms per decoding sequence, 0.1 ms per prefill token) a 2000-token prompt turns a 4 ms iteration into 204 ms.

requests decoding4new prompt tokens2000
normal iteration
4 ms
with the whole prompt
204 ms
cost model: 1 ms per decoding sequence + 0.1 ms per prefill token the 4 running requests wait 204 ms for their next token instead of 4 ms: ×51

Packing a large prompt into one iteration keeps the hardware busy, but every request that was waiting for its next token pays the delay. Throughput and individual latency targets can conflict.

Change the prompt length and the number of running requests.

Predict first. Does maximising prompt tokens in one iteration necessarily minimise every request's delay?

Chapter context

Requests arrive at different times and finish after different output lengths. Scheduling decides which work enters each iteration and how waiting prompts compete with ongoing generation.

Chapter reference: math, worked example & code

This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.

Math and notation

In a simplified equal-cost decode schedule, utilization = active sequence-iterations / available sequence-iterations. This omits prefill costs and assumes uniform iteration time. A real scheduler needs measured compute, memory and latency constraints.

Worked example

Two slots serve requests A,B,C with output lengths 2,1,2. Static groups run A+B for two iterations, then C alone for two: 5 active slots out of 8 available. Continuous admission runs A+B, then A+C, then C: 5 active slots out of 6. Utilization rises from 62.5% to about 83.33% in this toy schedule; real token costs are not equal.

Reference code

The following example uses the Python standard library and does not call a remote service.

from math import isclose
static=[['A','B'],['A',None],['C',None],['C',None]]
continuous=[['A','B'],['A','C'],['C',None]]
util=lambda rows: sum(x is not None for row in rows for x in row)/(2*len(rows))
assert util(static) == .625
assert isclose(util(continuous),5/6)

Python examples are provided for study and local execution; this page does not execute Python.

Common mistakes

Continuous batching does not guarantee a fixed improvement. Chunked prefill changes scheduling granularity, not the model's context dependencies. Include cache movement or recomputation costs when comparing preemption policies.