Why serving is a different problem from running
Running a model for one person is easy: load weights, generate tokens. Serving means many requests hitting one GPU at once, and the whole game becomes batching — computing multiple users' tokens in the same forward pass so the expensive GPU never idles. A single-stream runtime that gives one user 40 tok/s might serve twenty users at 5 tok/s each (25% of capacity wasted on overhead); a batching server serves those same twenty at 30+ tok/s each. That gap — often 10–24× total throughput — is the entire reason these engines exist.
Two innovations power the modern engines. Continuous batching swaps finished requests out of the batch and new ones in every step, instead of waiting for the whole batch to finish. PagedAttention (vLLM's breakthrough) manages the KV-cache like virtual memory in an OS — in non-contiguous pages — eliminating the memory fragmentation that used to cap how many requests fit. If those terms are new, our KV-cache guide explains the memory they're managing.
The four engines at a glance
| Engine | Best at | Weights | Hardware |
|---|---|---|---|
| vLLM | GPU multi-user serving, the default | FP16, AWQ, GPTQ, FP8 | NVIDIA (AMD/others growing) |
| llama.cpp | Single-user, CPU, edge, Apple Silicon | GGUF | Everything (CPU, NVIDIA, AMD, Metal) |
| SGLang | Shared prefixes, structured output, peak throughput | FP16, AWQ, GPTQ, FP8 | NVIDIA (AMD growing) |
| TGI | Hugging Face ecosystem, managed feel | FP16, AWQ, GPTQ, EETQ | NVIDIA, AMD, Gaudi, Inferentia |
vLLM — the production default
vLLM is where most teams should start and where many should stop. It introduced PagedAttention, has continuous batching, an OpenAI-compatible server out of the box, tensor and pipeline parallelism for multi-GPU, and the broadest day-one model support of any engine — new architectures usually land here first. It serves AWQ/GPTQ quantized weights well, so you rarely pay full FP16 memory. Our vLLM-in-Docker tutorial stands up a team server on one 24GB GPU in about forty minutes.
Weak spots: NVIDIA-centric (though AMD support is maturing), heavier to configure than a desktop runner, and no GGUF — you commit to the AWQ/GPTQ/FP16 world.
llama.cpp — the universal runtime
llama.cpp is the engine inside Ollama and LM Studio, and the one that runs everywhere: pure CPU, NVIDIA, AMD, Apple Metal, even phones and Raspberry Pis. Its GGUF format offers the richest quantization ladder (from 2-bit to 8-bit) and uniquely graceful CPU/GPU hybrid offload, so it runs models too big for your VRAM when the alternatives simply refuse. For a single user or a handful of requests, it's excellent. What it can't do is match vLLM/SGLang batching under real concurrency — its server exists, but heavy multi-user throughput isn't its game.
SGLang — peak throughput for the right workload
SGLang is the newer challenger built for maximum efficiency, and on many benchmarks it matches or beats vLLM. Its signature is RadixAttention: it automatically reuses the KV-cache across requests that share a prefix. If your workload has a big fixed system prompt, a shared RAG context, few-shot examples reused across calls, or multi-turn agents, SGLang can pull meaningfully ahead by computing that shared prefix once. It also has first-class fast structured/JSON output. The trade is maturity and ecosystem breadth — vLLM still supports more models on day one and has more community answers when something breaks.
TGI — the Hugging Face-native option
Text Generation Inference is Hugging Face's production server, powering their own Inference Endpoints. If your team already lives in the HF ecosystem — models, tokenizers, the hub — TGI fits like a glove, with the broadest accelerator support here (NVIDIA, AMD, Intel Gaudi, AWS Inferentia). It has continuous batching and solid throughput; it's a safe, well-supported choice. Outside that ecosystem, vLLM usually offers more model coverage and a larger independent community, which is why it's become the more common default.
How to choose — a decision path
- One user, or CPU / Mac / edge / a model bigger than your VRAM → llama.cpp (via Ollama or LM Studio).
- A team or app serving many users on NVIDIA GPUs → vLLM — start here.
- Heavy shared prefixes (big system prompt, shared RAG, agents) or strict JSON at scale → SGLang.
- Already all-in on Hugging Face, or need Gaudi/Inferentia → TGI.
One honest caveat: for most small deployments the engine is not your bottleneck — the GPU is. Get the hardware and VRAM sizing right first, prototype on Ollama, and graduate to vLLM when real concurrency arrives. Switching engines later is a deployment change, not a rewrite.