Exercises — Model serving frameworks (TorchServe, Triton)
Before we start, one reminder of the two numbers everything hangs on — because you will earn every symbol here, not memorise it.
The figure below makes this concrete. On the left we serve requests one at a time: notice the amber "overhead" block of height is redrawn four separate times — we pay the fixed cost on every single request. On the right we batch the same 4 requests: there is one amber block, then four cyan compute blocks stacked on top. The area we saved — three missing amber blocks — is exactly where the speedup comes from. Keep this picture in mind for every exercise: batching deletes repeated overhead, nothing else.

Two more symbols will show up the moment a request travels over a network, so let us earn them now rather than meet them cold in an exercise:
Level 1 — Recognition
Exercise 1.1 (L1)
Which serving framework is framework-agnostic (runs TensorRT, ONNX, PyTorch, TF, Python behind one server): TorchServe or Triton?
Recall Solution
Triton. TorchServe is PyTorch-first (it consumes .mar archives). Triton was built by
NVIDIA to run many backends behind one unified HTTP/gRPC server, which is exactly why you
pick it for a mixed stack. See the comparison table in the parent note.
Exercise 1.2 (L1)
In the latency budget , which term is the queue wait — the time a request sits waiting for its batch to fill?
Recall Solution
The term is the queue wait (defined above). It is the time the request spends idle in
the queue before the batch is dispatched. It grows when you raise max_queue_delay, and it is
the term that quietly fattens your p99 tail (see Latency SLAs & p99).
Exercise 1.3 (L1)
Name the file that packages a TorchServe model, and name the file that configures a Triton model.
Recall Solution
- TorchServe: the ==
.mar== (Model Archive) — serialized weights + a handler class + metadata, built withtorch-model-archiver. - Triton: the ==
config.pbtxt== inside the model repository — declares inputs/outputs,max_batch_size,dynamic_batching,instance_group.
Level 2 — Application
Exercise 2.1 (L2)
A model has ms overhead and ms/sample. You serve a batch of . Compute (a) serial time, (b) batched time, (c) the speedup .
Recall Solution
(a) Serial — each of the 10 requests pays the full : (b) Batched — pay once, then per sample: (c) Speedup: What it looks like: the overhead bar of height is drawn once instead of ten times — that saved area is your speedup (exactly the s01 figure, at instead of 4).
Exercise 2.2 (L2)
Same model (, ). What is the theoretical ceiling on speedup, no matter how large the batch grows?
Recall Solution
Take the limit as in . Divide top and bottom by : So at we already got of a ceiling — very close, more batching barely helps.
Exercise 2.3 (L2)
For that same model at , what is the throughput (requests/sec per instance)?
Recall Solution
Throughput = batch size ÷ time to process the batch. The batch time is ms s. Why this and not Little's Law? This is just "how many requests come out per second from one instance running back-to-back batches." Little's Law (, see Little's Law & queueing theory) is a different question — how deep the queue grows — and we don't need it here.
Level 3 — Analysis
Exercise 3.1 (L3)
Two models, same :
- Model A (cheap CNN): ms, ms.
- Model B (huge LLM layer): ms, ms.
Compute the speedup for each and explain which model batching helps and why.
Recall Solution
Model A: Ceiling — huge headroom, batching is a big win.
Model B: Ceiling — essentially no win.
Why the difference: speedup comes entirely from amortising the fixed . When (Model A) the fixed cost dominates, so paying it once instead of times saves a lot. When (Model B) the batch time is basically all per-sample compute, which scales with anyway — nothing to amortise. For Model B you need Model quantization & TensorRT or more instances, not bigger batches.
Exercise 3.2 (L3)
Requests arrive at requests/sec. Your max_queue_delay is set to ms.
(a) Roughly how many requests accumulate in one delay window? (b) What delay would you need to
gather a full batch of ?
Recall Solution
(a) In a window of seconds, arrivals : (Only ~2 gathered — nowhere near a big batch.)
(b) Solve for : What this means: to fill a batch of 16 at this arrival rate you must let each request wait up to 40 ms in the queue (this is the queue wait ). Only do that if your Latency SLAs & p99 budget allows a 40 ms queue tax — the delay is a dial trading latency for throughput.
Level 4 — Synthesis
Exercise 4.1 (L4)
Your model: ms, ms/sample. Network round trip ms. Your SLA says p99 latency must be ms. Requests arrive at /s. Assume worst-case a request waits the full queue delay , and you dispatch batches of size (i.e. one full window's arrivals). Find the largest queue delay (hence largest batch ) that still meets the SLA, and the resulting throughput.
Recall Solution
Step 0 — fix ONE unit so nothing is ambiguous. Everything below is in milliseconds (ms). The arrival rate is req/s. Convert it to ms: req/s req/ms (because 1000 requests spread over 1000 ms is one request every millisecond). Working in ms keeps , , and all in the same unit so we never mix seconds and ms.
Step 1 — WHAT is the batch size in terms of the delay? A window of ms, at 1 req/ms, gathers requests. So numerically (a 15 ms window gathers 15 requests). This is why and share a number here — the arrival rate is exactly one per ms.
Step 2 — WHAT is the worst-case latency? Assemble the budget defined earlier: Substitute , , , (all in ms):
Step 3 — WHY set it equal to the SLA? We want the largest , so push latency right up to the cap of 60 ms: Take ms requests per batch (round down to stay under the SLA).
Step 4 — throughput at , batch time ms s:
Since arrivals are 1000/s, one instance is not enough — you'd run
concurrent instances (Triton instance_group count: 3) to keep up.
This is the synthesis: batching alone can't absorb the load, concurrency finishes the job.
Exercise 4.2 (L4)
You have a pipeline: Python tokenizer → ONNX BERT → TensorRT reranker, all on one GPU. Which framework, and how do you configure it so the three stages happen in one server call with no extra network hops?
Recall Solution
Framework: Triton. Only Triton runs Python, ONNX (ONNX & model interchange) and
TensorRT (Model quantization & TensorRT) backends behind one server.
Configuration: define an ensemble — a DAG in config.pbtxt that chains
tokenizer → BERT → reranker, wiring each stage's outputs to the next stage's inputs. The
client makes one request; Triton runs all three stages internally on the GPU.
Why it wins: TorchServe would force all three into PyTorch handlers (losing TensorRT's
speed), and a naive microservice split would cost 3 network hops — three times the
tax. The ensemble collapses that to one hop.
Level 5 — Mastery
Exercise 5.1 (L5)
A team reports: "We switched from batch to and throughput barely moved, but our p99 latency doubled and we started getting occasional GPU OOM crashes." The model has ms, ms/sample. Diagnose all three symptoms quantitatively and prescribe a fix.
Recall Solution
Symptom 1 — throughput barely moved. Compute throughput at each batch: Gain: only , about +9%. Why? (5 vs 2), so the speedup ceiling is — there was almost nothing to amortise. Batching was the wrong lever.
Symptom 2 — p99 doubled. Bigger needs a longer max_queue_delay to fill, and the
batch compute itself grows: at the batch takes ms versus
ms at . Every request in that big batch waits for the whole 322 ms pass →
the tail latency balloons.
Symptom 3 — OOM. Activation memory scales with ; the batch the activation memory, overflowing GPU RAM on the largest inputs.
Prescription: this is a compute-bound model (). The right levers are (i) Model quantization & TensorRT to shrink (and memory), (ii) more concurrent instances to raise throughput without inflating any single request's latency, and (iii) roll back to a modest batch (say ) that respects the SLA. Bigger batches were never going to help a model whose ceiling is .
Exercise 5.2 (L5)
Design question. You must serve two models on one GPU: a cheap CNN (, ms) and a heavy LLM (, ms). You have a strict p99 ms on the CNN and a lenient p99 ms on the LLM. Sketch a Triton config strategy: batch sizes, delays, and instance groups, and justify each choice from the maths.
Recall Solution
CNN (cheap, overhead-dominated). Ceiling → batch aggressively.
But the SLA is tight (50 ms). Budget: . If ms,
we have ms for queue delay + compute. Pick e.g. : compute ms,
leaving ms for queue delay . So max_batch_size: 16,
max_queue_delay_microseconds: 15000 (15 ms, just under the 16 ms headroom). One or two
instances suffice — batching does the heavy lifting here because the ceiling is .
LLM (compute-dominated). Ceiling → batching is almost useless, so do not chase a huge batch. The lenient 800 ms SLA instead lets you lean on modest batches + concurrency + quantization. Concretely:
- Batch size: pick a modest . Compute ms, comfortably
inside 800 ms even after adding a queue delay and . Going bigger buys almost
no speedup (ceiling ) while eating GPU memory — so
max_batch_size: 8. - Queue delay: with a generous 800 ms budget you can afford a longer window to actually fill
, e.g.
max_queue_delay_microseconds: 20000(20 ms). Even stays far under 800 ms. - Instance group: the real throughput lever here is replication, not batching. If GPU
memory permits, run
instance_group { count: 2, kind: KIND_GPU }so two LLM copies process batches in parallel — doubling throughput without touching any single request's latency. - Quantization: apply Model quantization & TensorRT to cut (say 30 → 12 ms) and the memory footprint; this is what makes both a bigger effective throughput and the extra instance affordable.
Sharing one GPU. Use Triton's per-model instance_group and let dynamic batching run
independently per model. The CNN's tight SLA drives its batch/delay (aggressive batch, short
15 ms delay); the LLM's compute cost drives its instance count (modest batch, replicate +
quantize). This is the whole lesson: match the lever to the model's -vs- regime, per
model, on shared hardware — see also Autoscaling & Kubernetes (KServe) for scaling instances
beyond one GPU.
Recall One-line summary to test yourself
Batching helps ::: only when overhead dominates compute, capped at ; when use quantization + more instances instead. The queue delay ::: is added directly to every request's latency (queue wait ), so it trades throughput for tail latency. Concurrency vs batching ::: concurrency = many model copies in parallel (more memory); batching = one copy, many inputs in one pass; combine both.