LLM Inference Engine Comparison Guide

You can run production-quality large language models on your own hardware. But which inference engine should you actually use? Ollama is the easiest to start with, llama.cpp gives you maximum control over every byte, vLLM handles production-scale throughput, and SGLang is purpose-built for agent pipelines. This guide compares all four with real benchmarks, a quantization deep dive, cloud pricing in Rand, migration paths between engines, and a decision framework so you pick the right one on the first try.

TL;DR

  • Ollama — one-command setup, 100+ models, ~41 TPS. Use it for development and single users. You’ll outgrow it at around 5 concurrent users.
  • llama.cpp — the reference engine. Runs on anything from a Raspberry Pi to an H100, supports every quantization format. Use it for edge, CPU-only, and mixed GPU/CPU setups.
  • vLLM — 793 TPS peak, continuous batching, PagedAttention. The default for production APIs serving 10+ concurrent users.
  • SGLang — disaggregated prefill/decode and RadixAttention. The best choice for multi-turn agent pipelines and structured output.
  • Cost — on-prem RTX 4090 breaks even against RunPod in ~8 months of 24/7 use. Renting wins for sporadic workloads.
  • Migration is cheap — all four expose OpenAI-compatible APIs. You usually swap one URL.

What You’ll Learn

  • Engine Comparison

    Ollama vs llama.cpp vs vLLM vs SGLang — features, trade-offs, and when to use each.

  • Quantization Deep Dive

    GGUF vs AWQ vs GPTQ vs FP8 — what they cost you in quality and speed.

  • Cloud vs On-Prem Pricing

    RunPod, Lambda, Vast.ai, and on-prem costs in Rand — with break-even analysis.

  • Real Benchmarks

    Throughput, latency, and concurrency numbers — and how to read them honestly.

  • Decision Framework

    A flowchart, three real personas, and a migration path so you can change your mind later.

Why This Choice Matters More Than You Think

Running a local LLM is no longer the hard part. Model weights are a download away, GPUs are affordable, and every framework ships a Docker image. The hard part — the decision that quietly determines your costs, your latency, and how far you can scale — is choosing which inference engine to build on.

Pick the wrong one and you’ll hit one of two walls. Either you’ll hit a throughput ceiling in production because your engine can’t batch requests, or you’ll burn a month building infrastructure you never needed because you reached for a distributed serving framework to run a model on your laptop.

There are four serious options in 2026, and they are not interchangeable:

Engine One-liner Origin
Ollama Docker for LLMs — one command and you’re serving Wraps llama.cpp with model management
llama.cpp The reference implementation everything else is built on ggerganov, 2023 — C/C++ from scratch
vLLM The production serving engine UC Berkeley SkyLab, 2023
SGLang Built for agent and compound AI workloads UC Berkeley SkyLab, 2024

One thing to understand up front: these engines are layered, not competing. Ollama uses llama.cpp under the hood. vLLM and SGLang share research lineage. When you choose, you’re choosing an abstraction level and an operational profile, not picking a winner in a horse race.

This guide covers the engine comparison, quantization (which matters more than most guides admit), real benchmark numbers and how to interpret them, current GPU cloud pricing in Rand, three worked scenarios from laptop to production cluster, the migration path when you outgrow your first choice, and the mistakes that actually cost teams time.

The Engine Comparison

Each engine occupies a distinct niche. Here’s the full picture:

Ollama vs llama.cpp vs vLLM vs SGLang Comparison Matrix

Ollama — The Docker of LLMs

Ollama wraps model downloading, quantization, and serving into a single CLI. One command to install, one command to run. It auto-detects your GPU, manages memory, and gives you an OpenAI-compatible API out of the box. The model library carries quantized versions of every major open model — Llama, Qwen, Mistral, Gemma, DeepSeek — and pulling one is as easy as ollama pull qwen3:14b.

Under the hood it’s llama.cpp, but with the sharp edges removed. That’s the point: you give up configurability in exchange for not thinking about configuration at all.

Strengths

  • One-command install: curl -fsSL https://ollama.com/install.sh | sh
  • 100+ pre-packaged models, all tested against the runner
  • Auto GPU detection and memory management — it offloads layers until VRAM runs out, then falls back to CPU
  • Built-in OpenAI-compatible API on port 11434
  • Cross-platform: macOS (Metal), Linux (CUDA/ROCm), Windows
  • Modelfile system for custom system prompts, templates, and parameters
  • Zero-dependency daemon — no Python environment to manage

Limitations

  • ~41 TPS peak throughput — versus vLLM’s 793 on comparable hardware
  • No continuous batching; requests are handled serially by default (a background queue exists but it isn’t real batching)
  • P99 latency balloons under concurrency — measured at 673ms vs vLLM’s 80ms in Red Hat’s benchmark
  • GGUF quantization only — you can’t serve AWQ/GPTQ/FP8 checkpoints directly
  • Limited observability: no Prometheus metrics endpoint out of the box

Use Cases

  • Local development — test prompts, evaluate models, prototype chains
  • Single-user workstations — coding assistants, document analysis, private chat
  • Small internal tools — 1-5 concurrent users on a shared box
  • CI and evaluation pipelines — spin up, run a model suite, tear down
  • Learning — the fastest way to understand what local LLMs can and can’t do

llama.cpp — Maximum Control

The reference implementation for LLM inference. It was written in C/C++ from the ground up to run on everything from a Raspberry Pi to an H100, and it still has the broadest hardware support of any engine: CUDA, Metal, Vulkan, SYCL, and bare CPU with NEON and AVX kernels.

llama.cpp’s real superpower is quantization control. It supports the full GGUF zoo — Q4_K_M, Q5_K_M, Q6_K, Q8_0, and more — plus layer-by-layer GPU offloading. If you’ve ever wanted to run a 13B model on a 6GB card by splitting layers between VRAM and system RAM, this is the engine that lets you do it.

Strengths

  • Runs on CPU, GPU, Apple Silicon, Vulkan, SYCL — and hybrid CPU+GPU splits
  • Every GGUF quantization level, with fine-grained control over which layers offload where
  • Smallest memory footprint of any engine — ideal for constrained hardware
  • Built-in HTTP server with /completion, /chat/completion, and embeddings endpoints
  • Perplexity and benchmark tooling included
  • llama-server supports parallel slots for basic multi-request handling

Limitations

  • Full GPU support usually means building from source (though prebuilt binaries have improved)
  • ~38 TPS — marginally behind Ollama, which wraps it with fewer per-call overheads
  • No continuous batching — --parallel slots help, but they’re not PagedAttention
  • Configuration is manual: context size, batch size, KV cache type, offload layers — all yours to tune
  • Model acquisition is your job (convert HF checkpoints to GGUF yourself)

Use Cases

  • Edge deployment — ARM boards, Raspberry Pi, embedded and industrial hardware
  • CPU-only servers — no GPU available, run it on the hardware you already have
  • Mixed GPU/CPU — fill VRAM, spill the rest into system RAM without OOM
  • Custom quantization — trade accuracy against memory at a granularity nobody else offers
  • Privacy-critical offline use — no telemetry, single static binary, air-gap friendly

vLLM — Production Serving

Built for one thing: high-throughput production serving. Two innovations carry it. PagedAttention treats KV cache memory like operating-system pages, so multiple sequences share blocks instead of each reserving its own contiguous buffer — this is where the 19x throughput gap over Ollama comes from. Continuous batching recomputes the batch every iteration, slotting new requests in as others finish rather than waiting for the whole batch to drain.

Strengths

  • 793 TPS peak throughput — 19x faster than Ollama on an A100 (Red Hat benchmark)
  • PagedAttention: near-zero KV cache waste, more sequences per GPU
  • Continuous batching: the GPU never sits idle waiting for a slow sequence
  • Full OpenAI-compatible API — chat, completions, embeddings, and more
  • Tensor parallelism and pipeline parallelism for multi-GPU
  • AWQ, GPTQ, FP8, and INT8 quantization support with kernel-level acceleration
  • Speculative decoding, LoRA serving, structured output via guided decoding
  • Prometheus metrics endpoint — you can actually monitor it

Limitations

  • GPU-only — no CPU inference path at all
  • NVIDIA CUDA required (ROCm support exists but lags behind)
  • Meaningful memory overhead just to boot — long sequences need planning
  • Python dependency stack — heavier operational surface than a static binary
  • Model must be in HuggingFace format (or converted); no GGUF

Use Cases

  • Production API serving — SaaS products, customer-facing endpoints with an SLA
  • Multi-tenant platforms — shared inference infrastructure across customers
  • High-throughput batch — document processing, evaluation runs, RAG corpora
  • Enterprise deployments — monitored, load-balanced, autoscaled on Kubernetes

SGLang — The Agent Engine

SGLang is the newest entrant and the only one designed around how modern LLM applications actually behave: many short calls chained together. Its two headline features are disaggregated prefill/decode — separating prompt processing from token generation so the two phases don’t contend for the same resources — and RadixAttention, a prefix tree over the KV cache that lets shared prompt prefixes be reused across requests instead of recomputed.

That second one matters enormously for agents. If your agent sends the same 4,000-token system prompt and tool definitions on every single call, RadixAttention computes it once and reuses it. For a 50-step agent loop, that’s the difference between paying for 200K prompt tokens and paying for 4K.

Strengths

  • Disaggregated prefill/decode — prefill-heavy agent traffic no longer blocks generation
  • RadixAttention — automatic KV cache reuse across shared prefixes and conversation turns
  • Structured output (JSON schema, regex) and function calling as first-class features
  • FP8 and INT4 quantization with fused kernels
  • Zero-overhead scheduler — less scheduling overhead than vLLM under load
  • OpenAI-compatible API, plus an HTTP server that can batch across heterogeneous requests

Limitations

  • Younger project — smaller community, fewer deployment examples than vLLM
  • GPU-only, NVIDIA CUDA required
  • Smaller model coverage — the long tail of architectures may not be supported
  • Documentation and API stability still maturing
  • Fewer third-party integrations and managed offerings

Use Cases

  • AI agent pipelines — multi-step tool-calling loops with shared context
  • Multi-turn chat — long conversations where prefix reuse compounds fast
  • Structured output at scale — JSON-mode extraction, schema-constrained generation
  • Compound AI systems — chains of LLM calls sharing a KV cache

Quantization: The Trade-off Nobody Explains Properly

Before you pick an engine, you need to understand quantization — because it determines how big a model you can fit, how fast it runs, and how much quality you lose. And the engines don’t just differ in speed; they differ in which quantization formats they accept.

Format Used by Bits/weight Quality loss Notes
Q4_K_M llama.cpp, Ollama ~4.5 Noticeable The default choice — fits 7B in ~4.5GB
Q5_K_M llama.cpp, Ollama ~5.5 Small Sweet spot if you have the VRAM
Q6_K llama.cpp, Ollama ~6.6 Very small Near-lossless for most tasks
Q8_0 llama.cpp, Ollama 8 Negligible Basically FP16 in 8 bits
AWQ vLLM, SGLang 4 Small Activation-aware — protects important weights
GPTQ vLLM, SGLang 4 Small Needs a calibration set; widely available
FP8 vLLM, SGLang 8 ~Zero Native on H100/B200 — fastest path on modern GPUs
INT4 (Marlin) vLLM 4 Small Fused kernels — best 4-bit throughput in vLLM

The practical consequence: an engine choice locks you into a quantization family. If you build your prototype on Ollama pulling qwen3:14b (Q4_K_M GGUF) and later move to vLLM, you don’t move the file — you swap in the AWQ or FP16 checkpoint from HuggingFace. The model is the same, the packaging is different.

How Much Quality Do You Actually Lose?

Honest answer: it depends on the task, and 4-bit is not “free”. Evaluation suites like lm-eval-harness generally show:

  • Q8_0 / FP8 — indistinguishable from FP16. Use it whenever VRAM allows.
  • Q6_K — a fraction of a point below FP16. Safe for almost everything.
  • Q5_K_M / AWQ-4 — small measurable drops, rarely noticeable in chat and RAG.
  • Q4_K_M / GPTQ-4 — noticeable on math, code, and long-range reasoning; fine for summarisation and casual chat.
  • Below 4 bits (Q3, Q2) — expect real degradation. Only for squeezing a model onto hardware that can’t otherwise hold it.

Lesson: Match the Format to the Engine, Not the Other Way Round

Don’t choose your quantized checkpoint first and then hunt for an engine that runs it. Decide the engine from your workload (below), then use that engine’s native format: GGUF for Ollama/llama.cpp, AWQ or FP8 for vLLM/SGLang. Converting between them later is wasted work.

VRAM Budgeting — A Worked Example

A rough rule of thumb for a decoder-only transformer:

VRAM ≈ (parameters × bytes_per_weight) + KV_cache + overhead

Example: Llama-3.1-8B at Q5_K_M on a 16GB card
  8B × ~5.5 bytes  =  ~4.4 GB weights
  KV cache (4K ctx) =  ~1.0 GB
  Runtime overhead  =  ~1.0 GB
  Total             =  ~6.4 GB  ✅ comfortable

Same model at FP16:
  8B × 2 bytes      =  ~16 GB weights  ❌ doesn't fit

The KV cache term scales with context length and batch size — which is exactly why PagedAttention buys vLLM so much headroom. Two engines can hold the same weights, but only one of them can hold 50 concurrent 8K-context conversations without thrashing.

Performance Benchmarks — And How to Read Them

The canonical public comparison comes from Red Hat’s A100 benchmark (Llama 3.1 8B instruct, single A100-PCIE-40GB, vLLM 0.9.1 vs Ollama 0.9.2), supplemented by community tests for llama.cpp and SGLang:

Metric Ollama llama.cpp vLLM SGLang
Peak throughput 41 TPS ~38 TPS 793 TPS ~750 TPS
P50 latency 24ms ~26ms 8ms 9ms
P99 latency 673ms ~700ms 80ms 85ms
Concurrent users tested 1–256 1–8 1–256 1–256
Batching None None (slots) Continuous Continuous
Memory efficiency Good Best Good (PagedAttn) Good (RadixAttn)
Cold start Seconds Seconds Minutes Minutes
Setup complexity One command Moderate Moderate Moderate

What the Numbers Really Mean

Three caveats before you quote these in a design doc:

  • The 19x gap is a concurrency result, not a single-request result. For one user sending one message, Ollama and vLLM feel similar. The gap opens as you pile on concurrent requests — which is precisely what “production” means.
  • P99 is the number that pages you at 3am. Ollama’s 673ms P99 under load versus vLLM’s 80ms is the difference between “slightly slow” and “users are retrying, which makes it worse”.
  • Workload shape changes the winner. Prefill-heavy agent traffic with long shared prefixes is where SGLang’s RadixAttention can beat vLLM outright. Pure decode-heavy bulk generation, vLLM holds the edge. Benchmark your traffic, not a synthetic suite.

Lesson: Start with Ollama, Graduate to vLLM

Use Ollama for development and prototyping — it’s the fastest way to get started. When you hit the throughput ceiling (around 5 concurrent users), migrate to vLLM. Both expose an OpenAI-compatible API, so your application code doesn’t change — you swap the base URL and the model name.

Hardware Sizing per Engine

The same GPU is a great experience on one engine and a poor one on another. Here’s how to match hardware to engine:

Your Hardware Best engine Realistic model Expected throughput
Laptop CPU only llama.cpp 7B Q4 on 16GB RAM 8–15 TPS
Apple Silicon (M-series) llama.cpp / Ollama 14B Q4, unified memory 20–40 TPS
RTX 3060 12GB Ollama 8B Q5, 14B Q4 25–40 TPS
RTX 4090 24GB Ollama → vLLM 14B AWQ or 8B FP16 45 TPS single; 400+ batched
A100 80GB vLLM / SGLang 70B AWQ comfortably 700+ TPS batched
2× H100 80GB vLLM (tensor parallel) 70B FP16, 405B quantized Thousands TPS
Old Xeon, no GPU llama.cpp 7B Q4_K_M 5–12 TPS
Raspberry Pi 5 llama.cpp 3B Q4 3–8 TPS

Note the pattern: Ollama and llama.cpp own the low end, vLLM and SGLang own the high end. There’s a genuine middle ground — a single RTX 4090 — where the right answer depends entirely on your concurrency, which is why scenario planning (next section) beats spec sheets.

GPU Cloud Pricing — September 2026

If you don’t want to buy hardware, GPU cloud providers offer on-demand access with per-second billing. Here’s what you’ll pay right now:

GPU Cloud Pricing Comparison September 2026
Provider GPU On-Demand Reserved Notes
RunPod A100 80GB SXM $1.64/hr (R29.50) $0.85/hr (R15.30) Community cloud, per-second billing
RunPod H100 80GB SXM $1.99/hr (R35.80) $1.19/hr (R21.40) Secure cloud, enterprise SLA
RunPod H200 SXM $4.31/hr (R77.60) — 141GB VRAM — fits 70B FP16
RunPod RTX 4090 24GB $0.34/hr (R6.10) $0.20/hr (R3.60) Best entry point for experimentation
Lambda A100 80GB SXM $1.99/hr (R35.80) $1.25/hr (R22.50) 1yr reserved from $1.10/hr
Vast.ai RTX 4090 24GB $0.25/hr (R4.50) $0.16/hr (R2.90) Marketplace — price varies by host
CoreWeave H100 80GB ~$2.49/hr (R44.80) Committed use Kubernetes-native, enterprise
On-Prem RTX 4090 24GB $0.10/hr (R1.80) Same R35K purchase ÷ 20,000 hrs lifecycle
ZAR at R18/USD. Prices from provider sites, September 2026 — verify before committing.

Rent vs Reserve vs Buy

  • Rent on-demand (RunPod, Vast.ai) — sporadic workloads, experimentation, short projects. You pay only while the GPU runs. Default choice until you have a steady load.
  • Reserve (RunPod, Lambda) — predictable workloads running 6+ months continuously. Reserved pricing typically cuts 40-50% off on-demand.
  • Buy on-prem — always-on workloads, data residency, POPIA compliance, or egress costs that spiral. Requires someone to own the hardware.

Lesson: The Break-Even Math

An RTX 4090 costs ~R35,000 to buy. On RunPod Community Cloud it costs R6.10/hr — R53,400/year at 24/7. On-prem amortises to about R1.80/hr (R15,700/year over a 20,000-hour lifespan). Break-even: roughly 8 months of continuous use. Running a few hours a day? Rent. Running always? Buy. Add electricity (~R1.50/hr at 450W under load and SA tariff) and idle draw to the on-prem side of the ledger — it’s still cheaper than cloud at 24/7, but include it.

Cost Modelling: A Worked Monthly Example

Scenario Engine Hardware Monthly cost
1 developer, 4 hrs/day Ollama Own RTX 4090 ~R1,100 (amortised + power)
Team of 5, business hours vLLM RunPod 4090, 8h × 22d R6.10 × 176 = R1,074
Prod API, always on vLLM RunPod A100 on-demand R29.50 × 720 = R21,240
Prod API, always on vLLM On-prem 4090 ~R1,300 (amortised + power)
Agent pipeline, 12h/day SGLang RunPod A100 reserved R15.30 × 360 = R5,508

The always-on production row is where on-prem savings get dramatic — an order of magnitude. The one-developer row is where renting would have been fine too. Match the model to the duty cycle.

Decision Framework

Stop overthinking it. Answer three questions:

LLM Engine Decision Tree Flowchart
If you need… Use this Why
Quick local testing Ollama One command, zero config
Run on a Raspberry Pi llama.cpp Only engine with native ARM CPU support
CPU-only server llama.cpp Built for it; Ollama adds overhead you don’t need
Serve 10+ users vLLM Continuous batching, 19x throughput
AI agent with tool calls SGLang Disaggregated prefill + RadixAttention prefix reuse
Structured JSON output SGLang or vLLM Guided decoding is first-class, not bolted on
Production with an SLA vLLM + RunPod Tried-and-tested, per-second billing, metrics
Maximum model variety Ollama 100+ tested models in the library
Split a model across GPU+RAM llama.cpp Layer-level offload control

Three Real Scenarios

Frameworks are nice. Here’s how this plays out for three teams that actually exist.

Persona 1 — Solo Dev, One Workstation

Profile: One developer on an RTX 4090 workstation, building a RAG assistant. Usage: 3-6 hours a day, single user, iterating constantly.

Choice: Ollama. Startup time is seconds, model switching is instant, and the OpenAI-compatible endpoint plugs straight into the app. Throughput of 41 TPS is irrelevant — there’s one user.

What changes later: If this becomes a shared internal tool for five people, the same box runs vLLM against an AWQ checkpoint of the same model. Same GPU, same app code, different base URL — and suddenly concurrency isn’t a problem.

Persona 2 — Product Team Shipping an AI Feature

Profile: 50-200 daily active users, an SLA to keep, and a budget that must be predictable. Traffic is bursty — lunchtime peaks, quiet nights.

Choice: vLLM on RunPod. Continuous batching absorbs the peaks; per-second billing means quiet nights cost almost nothing. Deploy with autoscaling so the replica count tracks demand. Wire the Prometheus endpoint into Grafana before launch, not after.

Why not on-prem yet: At this duty cycle (well under 24/7), reserved cloud still beats buying. Revisit when traffic makes the GPU busy most hours of the day — that’s the 8-month break-even flipping in your favour.

Persona 3 — Agent Platform with Tool-Calling Loops

Profile: Agents running 20-60 steps per task, each step sending the same system prompt plus tool schemas. Prefill dominates the workload; conversations are long.

Choice: SGLang. RadixAttention means the shared 6K-token prefix is computed once, not sixty times. Disaggregated prefill/decode keeps generation smooth while new steps get prefilled. Structured output for tool calls is native.

Watch out for: SGLang is younger. Pin your version, keep a vLLM fallback config in the repo, and benchmark with your real prompt distribution — prefix-reuse benefits are workload-dependent and synthetic benchmarks can oversell them.

Deployment Patterns

Development — Ollama on your laptop

Install Ollama, pull a model, point your IDE at localhost:11434. No Docker, no Python env, no config files.

Small team — Ollama in Docker

docker run -d --gpus all \
  -v ollama:/root/.ollama \
  -p 11434:11434 \
  --restart unless-stopped \
  ollama/ollama

Good for 1-5 users. Beyond that, the lack of real batching starts showing.

Production — vLLM behind a load balancer

vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 --port 8000 \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --quantization awq_marlin \
  --enable-metrics        # Prometheus /metrics

Run one replica per GPU, put nginx or a cloud LB in front, scrape the metrics endpoint. Scale on queue depth, not CPU — the GPU is the resource that saturates.

Agent pipeline — SGLang with structured output

python -m sglang.launch_server \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 --port 8000 \
  --tp 1 \
  --context-length 16384

Set a stable, well-ordered system prompt so prefix reuse actually hits. Randomise nothing in the shared prefix — every byte that varies is a cache miss.

Edge / on-prem — llama.cpp

./llama-server -m models/qwen3-14b-q4_k_m.gguf \
  --host 0.0.0.0 --port 8080 \
  -ngl 99 -c 8192 \
  --parallel 4

-ngl 99 offloads as many layers as fit in VRAM; the rest run on CPU. That graceful spill is exactly why llama.cpp wins on constrained hardware.

Migrating Later Is Cheap — If You Do One Thing Right

The best part of this decision: it’s reversible. All four engines expose OpenAI-compatible endpoints, so most migrations are a config change:

# Your app config — that's the whole migration surface
OLLAMA_URL     = "http://localhost:11434/v1"   # dev
VLLM_URL       = "http://gpu-box:8000/v1"      # prod
SGLANG_URL     = "http://gpu-box:8000/v1"      # agents
MODEL_NAME     = "llama-3.1-8b-instruct"       # must exist on target

What Actually Changes When You Migrate

Migration Code change Real work
Ollama → llama.cpp Base URL Download/convert a GGUF with the exact chat template you were using
Ollama → vLLM Base URL + model name Swap GGUF for an AWQ/HF checkpoint; add quantization flag
vLLM → SGLang Base URL Same HF checkpoint usually works; verify supported ops
llama.cpp → vLLM Base URL + model name Lose GGUF; pick AWQ/GPTQ/FP16 equivalent
Any → Any (multi-GPU) Endpoint config Tensor-parallel flags, memory utilisation tuning

Lesson: Keep an OpenAI-Compatible Client

Never bind your application to an engine’s native SDK. Use the standard OpenAI client with a configurable base URL and you retain the freedom to switch engines — or add a cloud fallback — in an afternoon. The one portability trap is model names: qwen3:14b is an Ollama tag, not a portable identifier. Keep a mapping of your logical model names to each engine’s actual identifiers.

Common Pitfalls (That Cost Real Time)

Pitfall Symptom Fix
Serving production traffic on Ollama P99 latency spikes, requests queue Move to vLLM — same API, real batching
Ignoring KV cache in VRAM math OOM at load despite weights “fitting” Budget cache = f(context × batch); use PagedAttention or cap context
Choosing Q4 to “save memory” on an 80GB card Mystery quality drops in evals If VRAM allows, run FP8/Q8 — the savings weren’t worth it
Random tokens in your shared system prompt SGLang slower than expected Stabilise the prefix so RadixAttention can hit
No metrics from day one You find out from users Expose Prometheus metrics (vLLM) or wrap with a proxy that counts
Buying a GPU for a 4h/day workload Break-even never arrives Run the duty-cycle math first — renting wins under ~12h/day
Forcing vLLM onto a CPU-only box It won’t start at all vLLM is GPU-only. Use llama.cpp.
Never re-benchmarking after a model swap New model is 3x slower, no one knows why Different architectures have different decode profiles — retest on model change

Lesson: Benchmark Your Own Traffic

Every number in this guide comes from someone else’s workload. Before you commit, run a short load test with your real prompt lengths, your real concurrency, and your real output lengths. An agent sending 6K-token prompts behaves nothing like a chatbot sending 200-token ones — and no public benchmark will tell you that.

Quick Start Commands

Ollama

# Install
curl -fsSL https://ollama.com/install.sh | sh

# Run a model
ollama run llama3.1:8b

# API available at http://localhost:11434/v1

llama.cpp

# Build with CUDA support
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp && make CUDA=1

# Run the server
./llama-server -m models/llama-3.1-8b.gguf \
  --host 0.0.0.0 --port 8080 \
  -ngl 99 -c 4096

vLLM

# Install
pip install vllm

# Serve a model
vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 --port 8000 \
  --max-model-len 4096 \
  --tensor-parallel-size 1

# API available at http://localhost:8000/v1

SGLang

# Install
pip install sglang[all]

# Serve a model
python -m sglang.launch_server \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 --port 8000 \
  --tp 1

# API available at http://localhost:8000/v1

Conclusion

There’s no single “best” inference engine — there’s the engine that fits your workload, your hardware, and your duty cycle:

  • Ollama for getting started fast, and for anything with one or two users.
  • llama.cpp for edge, CPU, and hybrid setups where every megabyte counts.
  • vLLM for production API serving where concurrency and tail latency are the whole game.
  • SGLang for agent pipelines and multi-turn conversations where prefix reuse compounds.

The good news: they’re all open source, they all expose OpenAI-compatible APIs, and migrating between them is usually an afternoon’s work. Start with Ollama, benchmark your actual workload, and graduate to vLLM or SGLang when the numbers say it’s time — not before, and not three months too late.

Need Help Choosing or Deploying?

We help businesses select and deploy the right LLM inference stack — from single-GPU setups to multi-node production clusters, with monitoring and cost modelling included. We’ll benchmark your workload and get you running on the engine that actually fits.

Related Reading