Runbooks/ LLM Inference Runbook Track D · Scale and speed Deep readQuick learn
  1. 00 Start
  2. /
  3. 01 Tokens
  4. 02 Anatomy
  5. 03 Journey
  6. /
  7. 04 Hardware
  8. 05 Phases
  9. 06 KV cache
  10. /
  11. 07 Attention
  12. 08 Position
  13. 09 Precision
  14. 10 Cache ops
  15. /
  16. 11 Many GPUs
  17. 12 Engines
  18. 13 Decode loop
  19. /
  20. 14 Planning
  21. 15 Production
Document 12 · Quick learn Right tool, different job · A measurement, not a preference · Forty tunes, one copy

Three wastesfour engines, ten flags

Three wastes, four engines, ten flags that matter — and the enterprise capability the standard material leaves out entirely.

“Forty fine-tunes, ninety-nine point eight per cent identical, stored forty times.”
14–24×
an engine over a research library
6.4×
SGLang, where prefix reuse dominates
27 MB
a rank-16 adapter, against 14.96 GiB
560×
forty adapters vs forty full copies
What a serving engine isthree wastes, and one the standard material omits
Idle seats

A finished request keeps its slot until the whole batch is done.

→ continuous batching, checked after every generated token
Reserved but unused memory

Each request reserves the maximum possible cache and uses a fraction.

→ PagedAttention
Repeated work

The same long prefix re-processed for every request.

→ prefix caching or RadixAttention
Duplicated models

Forty fine-tunes of the same base, held as forty full copies.

→ multi-adapter serving — the one the source material skips entirely
Worked14–24× a Transformers baseline · 2–4× prior serving systems · SGLang up to 6.4× on heavy prefix reusewith attribution, always
“Right tool, different job”

A research library is optimised for flexibility and model coverage; a serving engine is optimised for throughput. Both exist for good reasons.

Saying that is a better answer than a list of deficiencies
The four you should be able to compareand what would change your mind
vLLM

Famous for PagedAttention. Widest coverage.

Cache after a request ends: prefix caching available
Portability: wide
Best fit: general API serving, mixed traffic
SGLang

Famous for RadixAttention.

Cache after a request ends: core design — a persistent tree
Portability: wide
Best fit: agents, few-shot, long shared prompts
TensorRT-LLM

Famous for ahead-of-time compilation.

Cache after a request ends: available
Portability: NVIDIA only, and a build step per configuration
Best fit: a stable model set, at volume
TGI

Famous for HuggingFace integration.

Cache after a request ends: available
Portability: wide
Best fit: teams already deep in that stack
The answer in one breath — and the two caveats that make it good

“vLLM for general high-throughput serving. SGLang when the workload has heavy prefix reuse — agents, few-shot pipelines, chat with a long fixed system prompt. Both do continuous batching and paged memory; the difference is what happens to the cache after a request ends. TensorRT-LLM if the model set is stable and the last ten per cent on NVIDIA hardware is worth a build pipeline.”

Then: these projects move fast and copy each other’s good ideas, so a gap you read about six months ago may have closed. And the right answer for a real deployment is a measurement — what fraction of a typical prompt is a repeated prefix. 80–95% on agents; 5–15% on RAG, where the whole argument evaporates
The flags that move somethingforty options, about ten that matter
--max-model-len

The context limit.

Cache per user is directly proportional. Set it from your measured p99, not from what the model supports · doc 06
--gpu-memory-utilization

The fraction of the card the engine may claim.

0.90 default. This is step 2 of the ladder · doc 06
--kv-cache-dtype fp8

One byte per cache element instead of two.

Exactly 2×, with a quality test on long generations attached · doc 10
--quantization

Which weight format, and therefore which kernel.

Frees weight memory that becomes KV budget · doc 09
--enable-prefix-caching

Keep and match shared prefixes.

Costs nothing even where it buys little · doc 10
--enable-chunked-prefill

Slice long prefills, interleave with decode.

On by default in recent versions — confirm it · doc 05
--tensor-parallel-size

How many cards the model is split across.

The smallest that fits, capped at the KV head count · doc 11
--max-num-seqs

Admission control — the running-sequence cap.

Set it below where preemption starts · doc 15
Two symptoms, one lever

“It will not start — out of memory.” Lower --max-model-len, use a quantised model, or turn on an fp8 cache.

“It starts fine and is slow under load.” Almost always not enough memory left for cache, so the batch stays small and the weight read is amortised over too few tokens. Same three levers, or more hardware.

Noticing that these two apparently different complaints have the same fix is the thing worth demonstrating — it is the chain from document 05 read backwards
Forty fine-tunes, one base copythe enterprise requirement the standard material skips

Forty teams or forty customers each want their own tuned model, and you have four GPUs.

The naive answer

Forty full fine-tunes = forty copies of 14.96 GiB.

598 GiB · at least eight H100s
And 99.8% identical

Every copy nearly the same as every other.

Low-rank adaptation

One base copy plus a thin pair of matrices per adapted layer.

27 MB in bf16, rank 16
Forty adapters

1.07 GiB on top of one 14.96 GiB base.

a 560× difference
Worked4096×16 + 16×4096 per projection · 425,984 per layer × 32 = 13,631,488 parameters = 27 MB bf16rank 16 on the four attention projections
And they batch together

One forward pass can serve requests for different adapters at once, because the base is shared and each request’s delta is applied per row.

So all forty teams share one pool at full utilisation, rather than forty deployments each running at whatever traffic one team generates
Honestly costed

A little throughput from the extra per-row work. A cap on how many adapters can be loaded at once. And quality somewhat below a full fine-tune where the domain shift is large.

The proposal: adapters as the default with a documented escalation path — if a team’s evaluation shows LoRA genuinely underfits, that one gets a full fine-tune as an exception with a named owner, not a precedent
What to watchfour gauges, four timers, eight numbers
Cache utilisation

How full the KV pool is right now.

Pinned at 100% with a growing queue is the thrashing signature
Requests waiting

Queue depth.

Rises before latency does
Preemption count

Evictions mid-generation.

A leading indicator nobody watches
Prefix hit rate

The fraction matched.

The other one nobody watches — and it costs nothing
Queue time

Pure waiting.

Invisible in every GPU metric
Prefill time

Separate from TTFT.

Or you cannot tell queue from prefill
Time to first token

What the user feels as the pause.

queue + CPU + prefill
Inter-token latency

What the user feels as streaming.

Set by the decode loop
Eight numbers, at p50, p95 and p99

Gauges tell you the state of the machine; timers tell you what a user experienced. Emitted separately — the two most teams are missing are queue time, because it is invisible in every GPU metric, and preemption count, because it rises before latency does.

They cover almost every question you will be asked in an incident
Answer it out loudthe ninety-second version, then five questions
“A serving engine exists to remove three wastes: idle slots, which continuous batching fixes by refilling after every token; reserved-but-unused memory, which PagedAttention fixes with fixed blocks handed out on demand; and repeated prefill, which prefix caching fixes. That is worth fourteen to twenty-four times a research library and two to four times the previous generation of serving systems. vLLM is the general default with the widest coverage. SGLang keeps caches after requests finish in a radix tree, which is worth up to 6.4× when prefix reuse dominates and nothing when it does not — so the choice is a measurement, not a preference.”

Five questions, and the spine of each answer

Architect

Why not just run Transformers in a loop?

  • No continuous batching — a research library runs the batch you gave it to completion
  • Contiguous worst-case KV allocation — reserved per request, regardless of what it generates
  • Together those waste most of the GPU: 60–80% by the vLLM team’s measurement
  • 14 to 24 times the throughput of a Transformers baseline
But frame it as “right tool, different job” rather than criticising Transformers. It is optimised for coverage; an engine for throughput. Both exist for good reasons.
Architect

vLLM or SGLang, and what would change your mind?

  • vLLM as the default; SGLang when prefix reuse dominates
  • The difference is what happens to the cache after a request finishes
  • One measurement changes my mind: what fraction of a typical prompt is a repeated prefix
  • 80–95% on agents and chat → SGLang earns its keep. 5–15% on RAG → the argument evaporates
So the answer is a default plus a test, not a preference. And both projects move fast, so re-check the comparison rather than relying on what you read six months ago.
Eng manager

Forty product teams each want a fine-tuned model.

  • One base model and forty adapters, not forty deployments
  • Forty full fine-tunes of an 8B is 598 GiB — eight H100s, each at whatever utilisation one team generates
  • Forty rank-16 adapters is 1.07 GiB on top of one base copy
  • And requests for different adapters batch together in the same forward pass
Honest costs: a little throughput, a cap on adapters loaded at once, and quality below a full fine-tune where the domain shift is large. Propose adapters as the default with a documented escalation path.
Architect

Which flags would you set first on a new deployment?

  • Three that are free: continuous batching and paged KV come with the engine; chunked prefill on; prefix caching on
  • Then three decisions, in order. --max-model-len from measured p99 — the single biggest lever most teams leave at the default
  • --kv-cache-dtype fp8 for exactly 2×, with a quality test on long generations
  • --max-num-seqs below where preemption starts
Everything else I would leave alone until a measurement tells me otherwise.
Architect

What is TensorRT-LLM for, and when would you not use it?

  • Ahead-of-time compilation — the kernels are built for one model, one precision, one parallel layout
  • Fastest on NVIDIA hardware, and the reason is that it can specialise at build time
  • Not when the model set changes often, because every configuration needs a build
  • Not when portability matters — it is NVIDIA only
So: a stable model set on fixed NVIDIA hardware, at enough volume that the last ten per cent pays for a build pipeline. Otherwise the operational cost outweighs the gain.
Fast answersthe ten that come up most

QIs the OpenAI-compatible API actually compatible?

For the common paths, yes — chat completions, streaming, most sampling parameters. Edge cases differ: log-probabilities, some penalties, tool-calling formats, error shapes. Test the specific features you rely on rather than assuming full parity.

QWhy is my throughput lower than the benchmark?

Usually prompt and output length distribution — published benchmarks often use short fixed lengths, which flatters throughput enormously. Also check whether it measured at saturation while you care about latency at a fixed rate.

QHow many adapters can I load at once?

A configured cap, and the binding constraint is usually not memory — at 27 MB each you could hold hundreds — but the per-step work of applying many different adapters within one batch. Start small, raise it, watch inter-token latency.

QDoes LoRA slow down inference?

A little. The base matrix multiply is unchanged and a small extra product is applied per row. The overhead is modest and roughly independent of how many distinct adapters are in the batch; what matters more is whether your engine has a proper batched-adapter kernel.

QShould we build our own engine?

Almost certainly not. The three wastes are solved, the solutions are subtle — paged attention needs a custom kernel, continuous batching needs careful scheduling — and the projects doing it have large teams. The exception is a genuinely unusual requirement, and even then the answer is a fork or a plugin.

QWhat is chunked prefill and is it on?

Slicing a long prefill into pieces and interleaving them with decode steps, so one huge prompt does not stall every other stream. On by default in recent versions. Confirm rather than assume — a ten-minute check that prevents a class of complaint that never appears in the logs.

QWhy does the engine reserve so much memory at startup?

It allocates the entire KV pool up front, so it never has to allocate during serving — allocation during a decode step would be a latency spike. What looks like grabbing 90% of the card is it claiming its cache pool.

QCan one engine serve several different models?

Generally one model per process, plus as many LoRA adapters of that base as you like. Genuinely different models means separate processes and memory pools, usually separate GPUs. A router in front is the normal pattern — and size each pool from its own traffic.

QThe engine version changed and throughput moved. Normal?

Yes, in both directions, and a real operational hazard. Defaults change, kernels change, scheduling changes. Pin the version, read the release notes for the flags you depend on, and re-run the same benchmark at the same request rate.

QWhat is the difference between block sharing and prefix caching?

Block sharing works between requests running at the same time; prefix caching keeps blocks after a request finishes. The first saves memory; the second saves memory and prefill work. RadixAttention is the persistent version built as the core design.

An engine removes three wastes, and the choice between engines is a measurement rather than a preference. Measure what fraction of a typical prompt is a repeated prefix: 80–95% on agents and chat makes SGLang’s persistent cache worth up to 6.4×; 5–15% on RAG makes the whole argument evaporate. And the flag most teams leave at the default — --max-model-len — is the single biggest capacity lever they have.

Where this goes next

Questions to ask them

Which engine, which version, and is the version pinned?

What is --max-model-len, and was it chosen or inherited?

Is chunked prefill on? Is prefix caching on? What is the hit rate?

Is preemption count monitored?

How do you serve fine-tuned variants — adapters or separate deployments?

When the engine is upgraded, is the benchmark re-run?

← 11 · Many GPUs Read the full chapter → 13 · Decode loop →