Serving many models on one CPU, the right schedule changes with load and the right machine code changes with neighbor noise — VELTAIR compiles each layer once into a small Pareto family from locality-greedy to parallelism-heavy, then picks code versions and layer-block sizes on the fly, landing within 10% of interference-free speed.
The argument in one glance
-
Problem
One model cannot fill a server, so tenants must share — and interfere
Inference runs at small batch sizes, leaving a 64-core CPU mostly idle per request: a Threadripper 3990X sustains ~300 queries per second on MLPerf vision models. Sharing the machine across models recovers the throughput — and creates contention on the L3 cache and memory bandwidth that inflates a task’s latency by up to 1.8× under heavy co-location.
-
What people did
Smarter multi-tenant schedulers on specialized hardware
PREMA time-multiplexes an NPU with preemption; AI-MT overlaps compute- and memory-bound layers; Planaria spatially partitions a systolic array per task; datacenter QoS managers like PARTIES partition conventional resources. All schedule fixed binaries at fixed granularities — whole models, or single layers.
-
Why it fell short
Fixed granularity wastes the machine, and fixed code fights the neighbors
Model-wise scheduling allocates for a model’s peak layer, leaving a 47% core-usage gap; layer-wise scheduling tracks needs precisely but collides constantly (high conflict rate, per-layer overhead). Worse, the best block size changes with load — 6 layers at 150 QPS, 11 at 200. And code auto-tuned in isolation bets everything on cache locality, exactly the resource neighbors steal: the optimal binary depends on interference that only exists at runtime.
-
This paper's idea
Adapt both knobs: multi-version code, dynamic-size layer blocks
VELTAIR extends TVM’s auto-scheduler to harvest, in a single search pass, several implementations per layer along the parallelism-locality Pareto frontier — from locality-greedy (fast when quiet) to parallelism-heavy (tolerant when noisy). A runtime scheduler measures interference, picks each block’s version, and forms layer blocks whose size follows a dynamic threshold set by idle cores. Result: 45–71% more QPS at the same QoS than Planaria, half the average latency, within 10% of isolated execution.
Why this problem is worth solving
Inference serving has an occupancy problem. Training saturates machines; serving arrives one query at a time, and a single small-batch inference cannot use 64 cores. The paper’s measurement: an AMD Threadripper 3990X serves around 300 queries per second on MLPerf vision models — one machine, many customers’ worth of capacity. So providers co-locate: multiple models, multiple tenants, one CPU. (This paper deliberately targets general-purpose hardware — the CPUs many companies actually serve on — where resource boundaries are softest.)
Co-location’s tax is performance loss from
contention on shared resources — last-level cache capacity, cache
bandwidth, memory bandwidth — between co-running tasks that hold
separate cores but not separate memories
quality-of-service targets — per-query latency
deadlines; the serving metric is how many queries per second can be
sustained with 95% of tasks meeting their deadline
The paper’s framing insight is that two decisions govern this tax, and both are conventionally frozen. The schedule — at what granularity work claims cores — is fixed per system. And the code — the loop tiling, unrolling, and parallelization of every layer, produced by compilers like TVM (see the TensorIR and AStitch explainers elsewhere on this site) — is auto-tuned once, in isolation, on a quiet machine. Multi-tenancy invalidates the quiet machine. VELTAIR is, by its authors’ account, the first system to treat compilation itself as a serving-time knob.
What people did before, and where it broke
Multi-tenant DNN schedulers. PREMA (temporal multiplexing with preemption), AI-MT (overlapping compute- and memory-heavy layers), and Planaria (spatially splitting a systolic array) advanced accelerator sharing; PARTIES and similar QoS managers partition resources for conventional interactive services. All hold both granularity and code fixed. Some (AI-MT, Planaria) even split layers into sub-layers — which the analysis here shows is overly fine: layer-wise is already conflict-dominated.
The granularity dilemma, measured. Scheduling whole models (first-come-first-served) allocates each model’s peak requirement for its whole runtime; since per-layer needs are jagged, cores idle — a 47% gap versus the per-layer minimum at 75% load. Scheduling single layers tracks the jagged profile precisely but pays a conflict rate an order of magnitude higher (every layer boundary is a reallocation, and concurrent tenants’ reallocations collide) plus per-layer scheduling overhead. Fixed blocks of layers sit between — but the best block size moves with load: 6 layers wins at 150 QPS, 11 at 200. No static choice survives a bursty day.
The compilation blind spot. Auto-tuned kernels (TVM outperforms even Intel’s MKL-DNN library here) are searched on an idle machine. The search rewards big tiles that live in the L3 cache — maximum locality. Under a noisy neighbor, that bet inverts: the cache is contended, big-tile code stalls, and a differently-compiled version of the same layer — smaller tiles, more parallelism — wins. The optimal binary is a function of interference level, which is unknowable at compile time.
VELTAIR: an offline compiler and an online scheduler
-
Single-pass multi-version compiler new
Extends TVM’s auto-scheduler: during ONE search pass per layer (~20 minutes), it harvests candidate implementations along the parallelism-locality Pareto frontier instead of just the single winner — from high-locality/big-tile (best when quiet) to high-parallelism/small-tile (tolerant when noisy). Naively searching per interference level would take five passes (~2 hours); this adds almost nothing.
-
Version selection new
Up to V=5 dominant implementations are kept per layer, each annotated with the interference range where it wins. Benefits saturate at 4 versions; only 3% of layers need all 5 — most need 2–3 to stay within 10% of the per-level optimum, keeping code storage modest.
-
Dynamic layer-block formation new
Per model, compute each layer’s core requirement to meet QoS and the model-wide average. Layers whose requirement exceeds average + threshold are conflict-prone: they become block-starting pivots. Within each block, work is smoothed — peak layers borrow time from cheap ones — so the block’s core claim is flat and modest.
-
Runtime threshold + interference proxy new
The threshold comes from spare capacity: total cores minus all active models’ average needs, distributed proportionally (64 cores, models averaging 12/12/24 → 16 idle → thresholds 4/4/8). A linear-model interference proxy reads system state and selects each block’s code version. Total scheduling overhead: under 0.1 ms per model.
-
Execution engine
Blocks from all tenants queue and execute; conflict rate stays near model-wise scheduling’s low level while core usage stays within 10% of layer-wise’s minimum.
The idea
Interference tolerance is a compilable property. The two code
versions that win at the extremes differ systematically: the
quiet-machine winner uses large tiles (locality — data parked in
the shared L3) and moderate parallelism; the noisy-machine winner
uses small tiles and maximal parallelism (little cache
footprint to lose, more cores to hide latency). These are not two
tricks but two ends of the classic parallelism-locality
trade-off, and the intermediate points form a
the set of options where improving one
objective necessarily worsens the other — here, no version is both
more local and more parallel than another frontier member
Conflict-prone layers should start blocks. The scheduling insight is about shape: a model’s per-layer core requirement is jagged, and conflicts concentrate at the spikes — layers demanding far more cores than the model’s average. The block-formation algorithm walks the layers once, splits at every spike (requirement > average + threshold), and flattens each block internally by letting the spike borrow schedule slack from its cheaper neighbors while the block still meets its share of the QoS budget. Blocks claim cores rarely (low conflict, like model-wise) but claim only what they need (low waste, like layer-wise).
Let spare capacity set the aggression. The threshold — how much a block may exceed the average before being split — is not tuned but computed: idle cores (total minus sum of active models’ averages) are distributed proportionally to each model’s appetite. Heavy load → few idle cores → threshold near zero → many small, frugal blocks; light load → generous thresholds → big blocks that sprint. The system’s granularity breathes with its load, which is exactly what the static block-size study said was needed.
A ResNet-50 query threads through a noisy afternoon
Block = consecutive layers scheduled as one unit; pivot = conflict-prone layer starting a block. Version 1 = high-locality code (big tiles), version 5 = high-parallelism code (small tiles). thres = per-model core threshold from idle capacity. Numbers follow the paper's 64-core Threadripper setup; the query walk is illustrative.
Step 1 of 6
Offline, months earlier: one search, five binaries
Each ResNet-50 layer went through one TVM auto-scheduler pass (~20 min). Along the way, VELTAIR harvested the Pareto frontier: version 1 tiles for the L3 (fastest alone), version 5 splits fine and parallelizes wide (degrades least under contention), versions between interpolate. Each is tagged with the interference range it dominates.
one pass ≈ 20 min vs 5 passes ≈ 2 h kept: ≤5 dominant versions/layer (3% of layers need 5)
Why it works
- Interference changes which loop structure is optimal → compiling one binary per layer bakes in a bet about neighbors → a small Pareto family per layer (locality → parallelism) lets the runtime re-bet every block, at code-selection cost only.
- The auto-scheduler already explores the parallelism-locality space → harvesting the frontier during one pass is nearly free → five interference-specialized versions cost one search, not five.
- Conflicts concentrate at core-requirement spikes → splitting blocks exactly at spikes and smoothing within them gives model-wise conflict rates at layer-wise efficiency → the false dilemma between waste and collisions dissolves.
- The right granularity depends on spare capacity → deriving the threshold from idle cores makes block size track load automatically → no operator-tuned constant to go stale.
- Both runtime algorithms are linear scans plus a linear proxy → under 0.1 ms per model → adaptivity never becomes its own interference source.
What it costs
- Code storage. Up to five binaries per layer — mitigated by keeping only dominant versions (most layers need 2–3; only 3% need five).
- A proxy, not a measurement. Interference level comes from a linear model over observable system state; mispredictions pick suboptimal versions (bounded by the versions’ own robustness).
- CPU-centric mechanics. The knobs (tiles vs threads, L3 behavior, core counts) are general-purpose-hardware concepts; porting to GPUs/accelerators with hard partitions changes the trade-off space.
- QoS model. Per-layer core requirements derive from profiled latencies against per-model QoS budgets; new models need that profiling pass.
- Latency-critical focus. Best-effort co-location is sketched as an extension, not evaluated.
The evaluation
Seven MLPerf-derived models spanning weight classes — light (EfficientNet, MobileNet-V2, Tiny-YOLOv2), medium (ResNet-50, GoogLeNet), heavy (SSD, BERT-Large) — arrive as Poisson streams on a 64-core AMD Threadripper 3990X. Baselines: PREMA-style temporal multiplexing, Planaria-style layer-wise spatial scheduling (adapted to CPU), plus VELTAIR ablations (adaptive scheduling only, adaptive compilation only, full).
Multi-tenant serving strategies on one 64-core CPU
| Property | PREMA-style (temporal) | Planaria-style (layer-wise spatial) | VELTAIR (adaptive both) |
|---|---|---|---|
| What adapts | |||
| QPS at 95% QoS satisfaction | |||
| Average query latency | High under load | Reference | |
| Resource efficiency vs conflicts | Efficient but serialized |
Check your understanding
1. A layer’s auto-tuned kernel uses large tiles that fit the L3 cache. A noisy co-tenant starts thrashing that cache. Predict which alternative compilation of the SAME layer now wins.
2. Model-wise scheduling wastes cores (47% gap) while layer-wise scheduling conflicts constantly. How do VELTAIR’s layer blocks escape the dilemma?
3. Searching optimal code per interference level would take ~2 hours per layer (five auto-scheduler passes). VELTAIR gets five versions in ~20 minutes. What’s the trick?
4. With 64 cores and active models averaging 12, 12, and 24 cores, VELTAIR sets block thresholds of 4, 4, and 8. What behavior does this formula produce as the machine fills up?