RAD: optimal tiling (Bari et al.)¶
Bari, Hegde, de Veciana, Optimal Scheduling Algorithms for LLM Inference: Theory and Practice (arXiv 2508.01002, POMACS 9(3), SIGMETRICS 2026). This page writes the paper's inference node running its scheduler RAD as a serQ program, states two of its propositions as claims, and proves them in Lean about the program's paths.
The paper¶
The paper models a GPU's batch time from how matrix multiplications are tiled. A batch of \(n\) tokens passes over \(\lceil n/b_{col}\rceil\) output tiles of the linear layers, plus non-linear operations per token and attention terms, its equation (7). A batch whose token count is not a multiple of the tile wastes the rest of the last tile. The paper asks which planner (routing across nodes) and which scheduler (batching on a node) reach the highest request rate the hardware carries.
The serving system it assumes¶
- \(g\) identical inference nodes behind a resource planner (the program writes one node; Theorem 1's bound is per node);
- batch time (7): \(\frac{1}{c_{Lin}}\lceil n/b_{col}\rceil + n/c_{nLin}\) plus attention terms;
- prompts are multiples of the tile, \(\mathrm{LCM}(b_{row}, b_{col}, b_{red})\) (Assumption 3);
- enough memory for \(b_{col}\) active requests of the largest size (Assumption 4);
- RAD with cycle parameter \(N\): Decode Mode when \(b_{col}\) requests decode or none is left to prefill (one token for each decoding request), otherwise Prefill Mode (a chunk of \(b_{lcm}\) tokens of the oldest prefill).
The system in serQ¶
// Bari, Hegde, de Veciana, "Optimal Scheduling Algorithms for LLM
// Inference: Theory and Practice" (arXiv 2508.01002, POMACS 9(3) 2025): one
// inference node running RAD (Algorithm 1, the cycle parameter N taken
// infinite). The output and reduction tiles are b_col = b_row = b_red = 128,
// so b_lcm = 128. The batch time is (7) without its attention terms: a pass
// over each output tile and the non-linear operations per token. Time is in
// µs.
//
// RAD is in Decode Mode when b_col requests decode (|D| = b_col) or none is
// left to prefill (P empty), and decodes every one of them; otherwise it is
// in Prefill Mode and runs a chunk of b_lcm tokens of the oldest prefill.
// A prefill completes only in Prefill Mode, so |D| never exceeds b_col: the
// b_col active requests of Assumption 4.
use "std/args";
let bcol = 128; // b_col = b_row = b_red = b_lcm
let tlin = 4000; // µs per pass over a tile, 1 / c_Lin
let tnl = 5; // µs per token of non-linear operations, 1 / c_nLin
let vpmax = 8; // prompts are 1 .. vpmax tiles (Assumption 3)
let vdmax = 512; // the largest output
fn main() {
let rate = args.number("rate", 0.00002); // requests per µs (20 per second)
device gpu { } // the paper's cost is written whole in `execute`, and no memory binds
// Decode Mode when b_col requests decode or every one does, else Prefill Mode
def in_mode() { running.decoding >= bcol || running.decoding == running.count ? decoding : !decoding }
engine E on gpu {
tokens cap bcol;
schedule {
advance running only (in_mode()) each at most (bcol);
admit waiting only (in_mode()) while (running.preempted == 0) each at most (bcol);
}
execute (tlin * ceil(batch.tokens / bcol) + tnl * batch.tokens);
}
workload {
arrive poisson(rate);
init {
set t0 = now;
set vp = bcol * floor(~uniform(1, vpmax + 1));
set vd = min(floor(~exp(200)) + 1, vdmax);
}
}
server {
run E prefill (cost(E, vp));
run E decode (cost(E, vd));
observe response = now - t0;
}
// A claim reads an iteration in the kernel's words (#437): `tokens` is
// `batch.tokens`, `decoders` `running.decoding`, `residents` `running.count`.
//
// Theorem 1: under any scheduler a node completes at most one token per
// tlin / bcol + tnl µs, so a load above it diverges.
claim token_rate: every iteration of E (served * (tlin + tnl * bcol) <= bcol * now);
// Optimal GeMM tiling (§4.2): with prompts a multiple of b_lcm (Assumption 3),
// every batch fills its tile, unless every request decodes and fewer than
// b_col of them do.
claim optimal_tiling given (vp == bcol * floor(vp / bcol)):
every iteration of E (tokens == bcol || decoders == residents);
}
| Paper | serQ |
|---|---|
| tiles \(b_{col} = b_{row} = b_{red} = b_{lcm} = 128\) | let bcol = 128; |
| batch time (7) without attention | execute (tlin * ceil(batch.tokens / bcol) + tnl * batch.tokens); (µs) |
| Prefill Mode: one chunk of \(b_{lcm}\) of the oldest prefill | tokens cap bcol; and each at most (bcol), with prefills served in admission order |
| Decode Mode iff \(\lvert D\rvert = b_{col}\) or \(P = \emptyset\) | def in_mode() { running.decoding >= bcol \|\| running.decoding == running.count ? decoding : !decoding }, read by advance running only (in_mode()) and admit waiting only (in_mode()) |
| Assumption 3 | set vp = bcol * floor(~uniform(1, vpmax + 1)); |
| \(N = \infty\) | no cycle counter |
A prefill completes only in Prefill Mode, which runs only while fewer than \(b_{col}\) requests decode, so \(\lvert D\rvert\) never exceeds \(b_{col}\). That is Assumption 4's bound on the active requests, without a pool.
The key propositions¶
Let \(S(t)\) count tokens served before an iteration starting at time \(t\). The program uses \(b_{col}=128\), \(t_{Lin}=4000\) µs per tile and \(t_{nl}=5\) µs per token, omitting attention costs.
Theorem 1 — Per-node token-rate bound
For any scheduler obeying the batch budget and tiled cost,
Here the capacity bound is \(128/4640\) tokens per µs. The claim is a pathwise rate bound; divergence above capacity also depends on the arrival process.
Optimal tiling — RAD with no cycle limit
If every prompt length is a multiple of 128, each batch is a full tile unless every resident is decoding:
In the exceptional case, fewer than 128 decoding residents can leave the batch partially filled. The program uses \(N=\infty\).
The propositions in serQ¶
// Theorem 1
claim token_rate: every iteration of E (served * (tlin + tnl * bcol) <= bcol * now);
// Optimal GeMM tiling, under Assumption 3
claim optimal_tiling given (vp == bcol * floor(vp / bcol)):
every iteration of E (tokens == bcol || decoders == residents);
given restricts the claim to workloads whose every request satisfies Assumption 3. The drawn prompt lengths are otherwise any natural numbers in the Lean statement. decoders == residents says no resident was left to prefill.
The proof in Lean¶
examples/papers/Bari.lean proves both generated claims over every path in
their workload families, with up to 500 sessions. Arrival times and output
lengths range over natural numbers; given restricts prompt lengths to
whole tiles. These pathwise statements do not assume a Poisson law.
The rate proof bounds each batch's tokens by its cost. The tiling proof maintains that every remaining prefill is a multiple of 128. In Prefill Mode, the oldest prefill supplies a full tile; in Decode Mode, the engine serves one token per decoding resident. A prefill can finish only while fewer than 128 requests decode, preserving the decoder-count bound.
Random arrivals¶
The stochastic proofs use separate Markov kernels. Their request types
have prompt lengths from 128 to 1 024 in whole tiles and output lengths
from 1 to 512. All files below are under examples/papers/.
Theorem 2 — One node with a fixed slot-arrival distribution
Let \(L\) be the expected number of arriving tokens per slot, with the slot's request list drawn from a finite distribution of these types. If \(L<128\), every reachable state of the job-list chain is positive recurrent.
Proof: BariRecurrent.lean, positive_recurrent.
A full batch changes backlog by arriving work minus 128 tokens. A partial
batch has fewer than 128 decoding requests and no prefill, so its backlog
is bounded. BariStable.lean uses the resulting negative drift to bound
expected hitting times. BariSim.lean proves the one-step projection from
the machine to the job list; BariProgram.lean bounds returns to an empty
engine on the machine chain.
Theorem 2 — Uniform random routing across g nodes
Suppose \(g>0\), each arrival is routed independently and uniformly, and all nodes advance one iteration per shared slot. If \(L<128g\), each node's reachable job-list states are positive recurrent.
If a slot also has positive probability of bringing no requests, the joint machine chain returns to all engines empty in bounded expected time, uniformly over empty starting machines.
Proof: BariNodes.lean, positive_recurrent and return_idle.
The extra empty-slot assumption is required for the joint result. Capacity alone does not imply it when \(g>1\). The construction uses synchronised slots and a finite arrival distribution, not asynchronous Poisson arrivals across nodes.
For a single node with compound Poisson arrivals, BariPoisson.lean proves
bounded expected return time to an empty engine under
where \(\lambda\) is the arrival rate per µs. This result concerns the machine chain, not recurrence of every state of a Poisson job-list chain.
On the run¶
Run for 100 s with seed 1, at 20 and 30 requests per second:
serq run examples/papers/bari_rad.sq --horizon 100000000 --warmup 0 --seed 1
serq run examples/papers/bari_rad.sq --horizon 100000000 --warmup 0 --seed 1 --set rate=0.00003
Claim-table excerpts, 20 requests/s first:
claim kind result
token_rate every iteration holds (23110 iterations)
optimal_tiling every iteration holds (23110 iterations)
claim kind result
token_rate every iteration holds (22170 iterations)
optimal_tiling every iteration holds (22170 iterations)
Theorem 1's bound for this program is \(128/4640\) tokens per µs. With a mean of about 760 tokens per request (576 prompt, about 185 output after the cap at 512) it is about 36 requests per second, and both loads are below it: 1 984 of 2 013 and 2 961 of 3 000 requests end within the horizon.
What it leaves out¶
- Machine-state recurrence. Clocks and ended sessions are retained, so complete machine states do not repeat. The results concern job-list states or returns to the empty-engine set.
- Finite cycles and SLAI. The program uses RAD with \(N=\infty\); finite-cycle draining and the paper's SLAI latency heuristic are not modelled here.
- Attention. The program omits attention costs. The Lean fragment accepts attention terms only with even integer coefficients.
- Multiple engines in serQ. The executable fragment has one engine.
The multi-node planner and synchronised slots are defined in
BariNodes.lean, not in this.sqprogram.