Training is a one-time cost. Serving is forever. ChatGPT costs OpenAI more in inference per month than the original GPT-4 training run. vLLM, TGI, Triton, and continuous batching are the difference between a $10K/month and a $1M/month bill at the same QPS — same model, same accuracy.
Learning Objectives
After this lesson, you will be able to:
Compare three ways to send predictions (REST, gRPC, and streaming) and pick the best one for your use case
Explain how LLM serving systems like vLLM keep GPUs busy using batching, memory management, and speculative decoding
Design a serving setup that balances speed, volume, cost, and reliability
You have made it to one of the most practical lessons in ML engineering. If you have ever wondered how AI products actually work in real life (not just in notebooks), this is where it all clicks. Let's dive in.
The most common serving pattern. Simple, universal, works with any client:
REST (HTTP/JSON) Flow
Pros: Universal compatibility, easy debugging (curl), human-readable, load balancer support.
Cons: JSON serialization overhead, no streaming, higher latency than binary protocols.
Best for: Web applications, mobile apps, low-throughput APIs, prototyping.
Try it! Open a terminal and use curl to send a JSON request to a free public prediction API (or a local Flask server running a simple model). Watch the request-response cycle in action. Try sending 5 requests in rapid succession and see how response time changes under load -- this is exactly the problem that batching solves.
Essential for LLMs that generate tokens one at a time:
Server-Sent Events Streaming Flow
Pros: User sees output as it generates (time-to-first-token matters), better perceived latency.
Cons: Connection management complexity, harder to load balance, client must handle partial responses.
Best for: LLM chat interfaces, real-time generation, any autoregressive model.
What Do You Think?
A user sends a prompt to a ChatGPT-style service. The full response takes 5 seconds to generate. With streaming, the first token appears in 200ms. Without streaming, the user waits 5 seconds for the full response. Which feels faster to the user?
Time-to-first-token (TTFT) is often more important than total generation time for user experience. A system that streams the first token in 200ms feels responsive, even if the full response takes 5 seconds. This is the same psychology behind progressive loading in web applications.
Try it: Compare serving patternsInteractive
Loading visualization...
Select a serving protocol above and step through the inference request lifecycle. Compare REST, gRPC, and streaming side by side. Adjust the concurrency slider to see how throughput and latency change under different loads.
Step through this visualization to watch the KV cache grow token by token and see how PagedAttention reclaims the memory that naive contiguous allocation leaves stranded.
Traditional batching waits for a batch to complete before starting new requests. Continuous batching (iteration-level scheduling) is smarter:
Continuous Batching Timeline
Step
Slot 1
Slot 2
Slot 3
Step 1
Req A token 5
Req B token 3
Req C token 8
Step 2
Req A token 6
Req B token 4
Req C DONE -> Req D token 1
Step 3
Req A token 7
Req B token 5
Req D token 2
When request C finishes, request D immediately takes its slot in the batch. No request waits for the slowest request to finish. This keeps the GPU saturated at all times.
Use a small "draft" model to generate N candidate tokens quickly, then verify all N in parallel with the large model:
Draft model (fast): generates 5 tokens speculatively
Large model (slow): verifies all 5 in one forward pass
If first 3 are correct: accept 3 tokens in one step (3x speedup!)
If all 5 are correct: accept all 5 (5x speedup!)
This exploits the fact that many tokens are predictable (common phrases, code patterns, structured output). The speedup is proportional to the acceptance rate.
The newest production architectures split the prefill stage (processing the input prompt — compute-bound, parallel) from the decode stage (generating tokens one at a time — memory-bound, sequential). Running both on the same GPU forces one to wait for the other; separating them onto different pools lets each scale independently.
Production systems implementing this pattern:
Mooncake (Moonshot AI / Kimi) — separates prefill and decode clusters, shares a global KV-cache pool over RDMA.
DistServe (Peking University, 2024) — disaggregated serving with per-stage goodput optimization, 4-7x more requests per GPU at the same SLO.
Splitwise (Microsoft) — phase splitting on different hardware classes (compute-heavy prefill on A100, memory-heavy decode on cheaper H100-MIG slices).
SGLang v0.3+ — adds disaggregated mode plus chunked prefill that interleaves long-prompt processing with active decode batches.
The win is that prefill and decode have wildly different optimal batch sizes and KV-cache footprints. Co-locating them caps throughput at the lower of the two.
What Do You Think?
A vLLM replica receives a mix of short chat turns (50-token prompts) and long document-summarization requests (8K-token prompts). Without disaggregation, what is the most common symptom users will notice?
Serving N fine-tuned variants of the same base model used to mean N replicas. Modern systems load the base model once and swap LoRA adapters per request, paying only the cost of the rank-r matrices (typically 50-200 MB per adapter vs. 13 GB per full model).
vLLM --enable-lora — supports hundreds of concurrent LoRA adapters with negligible overhead.
S-LoRA (Stanford, 2023) — unified paging for adapters and KV cache; 4x more adapters per GPU than naive swapping.
Punica. Kernel-level fusion of multiple LoRAs in a single CUDA call (SGMV — Segmented Gather Matrix-Vector).
TGI multi-LoRA. Same pattern for HuggingFace stacks.
This pattern is what makes per-customer fine-tuning economically feasible: 500 enterprise customers can each have a custom adapter served from one shared base-model fleet.
Every model prediction follows the same path from request to response. Understanding each step reveals where latency hides and where optimization pays off:
The journey starts with a trained model artifact stored in a model registry -- a file containing the architecture definition, learned weights, and any preprocessing artifacts (tokenizers, scalers). This artifact was produced by the training pipeline and promoted through evaluation gates.
At startup, the serving framework (vLLM, TorchServe, Triton) loads the model weights into GPU memory. For a 70B model in FP16, this means transferring 140 GB from disk to GPU -- a cold start that takes 30-60 seconds. Replicas stay warm to avoid this latency on user requests.
The model is exposed as a network endpoint. REST (HTTP/JSON) for universality, gRPC (HTTP/2 + Protobuf) for low-latency microservices, or SSE/WebSocket for streaming LLM tokens. A load balancer distributes incoming requests across model replicas.
A user request hits the API gateway, which handles authentication, rate limiting, and routing. The request is validated (correct schema, input size limits) and placed in a queue. For dynamic batching, the request waits up to N milliseconds for other requests to form a batch.
Tests · Compare no-batching, static, and dynamic strategies. Verify that dynamic batching provides better throughput than no batching while keeping latency lower than large static batches.
For LLMs, the KV cache is often the memory bottleneck:
Multi-Query Attention (MQA): Share K/V heads across attention heads (Llama 2 uses GQA, a middle ground)
Sliding window attention: Only cache the last N tokens (Mistral)
KV cache quantization: Quantize cached keys/values to INT8 or FP8
Prefix caching: Hash and reuse the KV cache for common prompt prefixes (system prompts, RAG templates) — vLLM and SGLang ship this out of the box; cache hit rates of 30-60% are typical in chat workloads.
Cross-request offload: Page cold KV-cache blocks to host memory or NVMe (LMCache, Mooncake) so popular conversations stay resident.
Before signing off on hardware, work the numbers. The cell below estimates KV-cache memory per request, total memory headroom, and the maximum concurrent batch size for a vLLM-style replica — the exact math your platform team needs in their capacity plan.
Training and serving are fundamentally different workloads. Training optimizes for throughput (process data as fast as possible), while serving optimizes for latency (respond to each request in milliseconds)
LLM serving uses specialized techniques for efficiency. PagedAttention manages KV cache memory dynamically, continuous batching maximizes GPU utilization, and speculative decoding predicts future tokens to reduce latency
Choose your serving pattern based on requirements. REST APIs for simple request-response, gRPC for low-latency microservices, and streaming (SSE/WebSocket) for LLM token-by-token generation
Cost, latency, throughput, and availability form a quadrilateral tradeoff. You cannot optimize all four simultaneously; production systems must make explicit choices about which dimensions matter most for their use case
Before moving on, you should be able to answer these in your own words. If any feel shaky, scroll back.
You now know how to serve models at scale with modern inference systems. Next up: Model Compression & Efficiency -- how quantization, pruning, and distillation make models smaller and faster without (much) quality loss.
Raw input is transformed into model-ready tensors. For text: tokenization (splitting into subword tokens, converting to IDs). For images: resizing, normalization. For tabular data: feature lookup from the feature store, scaling, encoding. Preprocessing must match training exactly.
The preprocessed tensor batch is fed through the model. For LLMs, this involves iterative autoregressive generation -- each token depends on all previous tokens. vLLM uses PagedAttention for memory efficiency and continuous batching to keep the GPU saturated.
Raw model output (logits, token IDs, or embeddings) is converted to a human-readable response. For LLMs: decode token IDs back to text. For classifiers: map class indices to labels, attach confidence scores. Safety filters and PII detection run here too.
The formatted response is sent back to the client. For streaming, tokens are sent as they are generated via Server-Sent Events. Latency, token counts, model version, and confidence scores are logged for monitoring. The full trace enables debugging any future issues.