AI engineering and inferenceLoading progress…

Static batches

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

Static batches wait for the slowest

The simplest scheduler forms a batch of requests, runs it until every request in it is finished, then forms the next. All requests in a batch advance one token per iteration, but they do not need the same number of tokens. When a short request finishes, its slot sits idle until the longest request is done.

slots2request A output tokens2request B output tokens1request C output tokens2
it 1it 2it 3it 4
slot 1AACC
slot 2B
static batches run until their longest request is done iterations = 4, active slot-iterations = 5, capacity = 4 × 2 = 8 utilisation = 62.5% finish iterations: A=2 B=1 C=4

Grey cells are idle: a short request finishes but its slot cannot be given to a waiting request until the whole group is done.

Rows are slots, columns are iterations. Grey cells are wasted. Change the output lengths.

Predict first. Why does one short request not always free capacity for a waiting request?

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.