Model serving frameworks (TorchServe, Triton)
WHY do we need a dedicated serving framework?
WHY not just Flask + model.predict()? A naive Flask app:
- runs inference one request at a time → GPU sits idle 95% of the time,
- has no batching → you pay per-request overhead again and again,
- reloads / crashes lose the model; no versioning or A/B,
- gives you no metrics (latency p99, throughput, GPU util) out of the box,
- can't easily mix CPU/GPU, multiple models, or multiple frameworks.
Serving frameworks solve exactly these. The two dominant ones:
| TorchServe | NVIDIA Triton | |
|---|---|---|
| Origin | PyTorch (Meta+AWS) | NVIDIA |
| Native models | PyTorch (.mar) |
Framework-agnostic: TensorRT, ONNX, PyTorch, TF, Python, custom |
| Killer feature | Simple PyTorch handlers | Dynamic batching, multi-model, concurrent model instances, GPU-optimal |
| Protocols | HTTP + gRPC | HTTP + gRPC (Triton's v2 inference protocol, which KServe adopts) |
| Best when | Pure-PyTorch shop | Heterogeneous / max GPU utilization |
The core idea that makes serving fast: DYNAMIC BATCHING
Deriving why batching helps — from first principles
Model total latency per request has two parts:
- Fixed overhead (kernel launch, data transfer, framework glue) — paid per batch.
- Marginal compute per sample.
Serve requests one-by-one:
Serve them as one batch of size :
Why? The fixed cost is paid once for the whole batch instead of times. Speedup:
HOW dynamic batching actually works (Triton & TorchServe both do this):
- Requests arrive asynchronously.
- Server holds them in a queue up to
max_batch_sizeOR untilmax_queue_delay(e.g. 5 ms) elapses. - Whichever hits first → pad/stack into a batch → single forward pass → split results back.
Throughput of a single model instance

TorchServe: how it's built
# my_handler.py — the 3 lifecycle steps you override
class MyHandler(BaseHandler):
def preprocess(self, data): # bytes/JSON -> tensor
return torch.tensor(...)
def inference(self, x): # forward pass (batched!)
return self.model(x)
def postprocess(self, y): # tensor -> JSON response
return y.argmax(1).tolist()Batching config lives per model: batch_size, max_batch_delay (ms).
Multiple worker processes give concurrency; each holds a model copy.
Triton: how it's built
# config.pbtxt (sketch)
max_batch_size: 32
dynamic_batching { max_queue_delay_microseconds: 5000 }
instance_group [ { count: 2, kind: KIND_GPU } ]Triton runs many backends (TensorRT for peak GPU speed, ONNX Runtime, PyTorch, Python) behind one unified server. Its HTTP/gRPC API follows the v2 inference protocol that Triton defines (later adopted by KServe and other serving stacks) — not the other way around.
Worked Examples
Common Mistakes (Steel-manned)
Flashcards
What problem does dynamic batching solve on a GPU?
Formula for batching speedup and its ceiling
When does batching barely help?
Two knobs that trigger a batch to flush in dynamic batching
max_batch_size reached OR max_queue_delay timeout elapsed, whichever comes first.What is a TorchServe .mar file?
Concurrency vs batching?
Triton's biggest differentiator vs TorchServe?
What is a Triton ensemble?
Which latency metric matters for SLAs and why?
Relationship between queue delay and achievable batch size?
max_queue_delay lets more requests accumulate → bigger → higher throughput but higher queue-wait latency .Who defines the "v2 inference protocol" Triton uses?
Is Throughput ≈ B/(c+Bm) an application of Little's Law?
Recall Feynman: explain to a 12-year-old
A trained model is like a super-fast robot chef. If the chef cooks one plate, walks it to the table, comes back, and cooks the next plate — that's slow because the walking (overhead) happens every time. A serving framework is the smart kitchen manager: it waits a tiny moment, gathers a tray of orders, cooks them all at once, then delivers them together. TorchServe is a kitchen that only cooks pasta (PyTorch) but is easy to run. Triton is a big kitchen that cooks pasta, pizza, and sushi (any model type) and runs many chefs at once. The manager decides how long to wait for the tray — wait too long and hungry customers get grumpy (high latency); don't wait at all and the chef wastes trips (low throughput).
Connections
- Model quantization & TensorRT — the real fix when batching hits its ceiling.
- ONNX & model interchange — the format Triton loves.
- Little's Law & queueing theory — governs queue depth vs arrival rate (distinct from the batch-throughput formula here).
- Latency SLAs & p99 — why tail latency governs serving config.
- Autoscaling & Kubernetes (KServe) — KServe implements Triton's v2 inference protocol; scales instances horizontally.
- A-B testing & model versioning — served via TorchServe/Triton version routing.
- GPU architecture & kernel launch overhead — origin of the fixed cost .
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, model ko train karna ek baar ka kaam hai, par usse serve karna matlab restaurant chalane jaisa hai — same prediction ko fast, reliably, bahut saare users ko ek saath dena hota hai. Agar aap simple Flask app banaoge to GPU 95% time khaali baithta hai, kyunki ek time pe sirf ek request process hoti hai. Isiliye serving frameworks — TorchServe aur Triton — use karte hain, jo batching, concurrency, versioning aur monitoring khud handle karte hain.
Sabse important idea hai dynamic batching. GPU ek bus jaisa hai, taxi nahi — ek passenger ke liye poori bus chalana waste hai. Framework thoda wait karta hai (max_queue_delay), kuch requests ikattha karta hai, aur sab ka ek saath forward pass chalata hai. Fixed overhead (kernel launch, data transfer) ek hi baar pay hota hai poore batch ke liye, isliye speedup milta hai: , aur maximum tak. Yaad rakho — agar model compute-heavy hai (bada LLM, ), to batching zyada help nahi karega; wahan quantization ya TensorRT chahiye. Throughput bas "kitni requests, kitne time me" ka simple ratio hai — ye Little's Law nahi hai; Little's Law alag cheez hai jo queue kitni gehri banegi wo batati hai.
TorchServe simple hai aur sirf PyTorch models ke liye best hai — aap ek handler likhte ho
(preprocess → inference → postprocess) aur .mar file bana dete ho. Triton zyada powerful
hai: koi bhi framework (TensorRT, ONNX, TF, PyTorch, Python) chala sakta hai, ek saath multiple
model copies (concurrency) aur ensembles (tokenizer→model→postprocess ek hi call me) support
karta hai. Ek chhoti si baat: Triton apna v2 inference protocol khud define karta hai, aur
KServe usse adopt karta hai — ulta nahi. Rule of thumb: pure PyTorch shop → TorchServe; mixed
models + max GPU utilization → Triton. Aur hamesha p99 latency dekhna, sirf average nahi.