← Plasmient Labs
Code Walk F · layer 05deep dive · serving

Inference & serving
where the GPU lies loudest

Layer 05 of the Frontier Stack, gone deep. Training a model is half the job; turning weights into a fast, cheap API is the other half — and it’s where the company thesis bites hardest. A serving node can pin nvidia-smi at 100% while its goodput — useful tokens per second per dollar — is half of what the silicon can do. This walk is why: the two phases of inference, the batching that keeps slots full, and the speculative trick that spends the idle headroom. Two live animations.

  • the two phasesprefill (compute) · decode (memory)
  • the traputil ~100%, goodput half of peak
  • the fixespaging · batching · spec-decode
  • the open stackvLLM · SGLang · TensorRT-LLM

00 Two phases, two very different machines

Generating from an LLM is really two workloads wearing one costume. First prefill: the whole prompt goes through in a single big pass — a fat matmul that saturates the tensor cores. It’scompute-bound, exactly the work a GPU is built for. Then decode: one token at a time, each step reading the entire model’s weights and the whole KV cache to produce a single token. That’smemory-bound — the tensor cores sit mostly idle while the chip streams gigabytes in and out.

This split is the root of everything on this page. Decode is where models spend most of their time, and decode is where the silicon is starved. Every serving technique below exists to claw back the utilization that memory-bound decode leaves on the floor — and none of it shows up on the one number the dashboards report.

the sentence to rememberPrefill is compute-bound and looks great. Decode is memory-bound and looks identical onnvidia-smi — same 100% — while doing a fraction of the useful work. Serving is the art of hiding decode’s starvation, and measuring it is the whole reason this company exists.

01 Keeping the slots full: continuous batching

A single decode step barely uses the GPU, so you run many requests at once — a batch. The catch: requests are different lengths and finish at different times. With old static batching the whole batch waits for its slowest member; short requests finish early and their slots sit idle. The GPU still reads busy. Continuous batching (a.k.a. iteration-level / in-flight batching) refills a slot the instant its request finishes. Flip between them and watch the goodput gauge:

Six batch slots, requests of varying length. In static mode, finished slots idle (hatched) until the whole batch clears — the left gauge stays pinned high while the right one craters. Switch to continuous and the freed slots backfill instantly. Same silicon, same util reading, very different useful work.
batchingWatch the idle (grey) slots in static mode — the GPU is “busy” but half of it is waiting.
nvidia-smi says
≥1 slot active → the SM is running
goodput · useful occupancy
fraction of slots doing real work

That is the thesis in one widget. The left gauge — what a monitoring dashboard shows — barely moves between the two modes. The right gauge — what’s actually being accomplished — is the entire difference between a serving node that pays for itself and one that burns half its capacity waiting. Continuous batching (from Orca, now standard in vLLM and every serious server) is the single biggest throughput win in inference, and it’s invisible to utilization.

02 Fitting more requests: PagedAttention

Batching only helps if the requests fit. The limit is the KV cache (the tensor you shrank with grouped-query attention over in layer 02). Classic serving reserved one big contiguous block of memory per request, sized for the longest output it mightproduce — so most of it went unused, and fragmentation wasted the rest. Real GPUs ran at a fraction of their possible batch size.

PagedAttention (the idea that launched vLLM) borrows the oldest trick in operating systems:virtual memory. Store the KV cache in small fixed-size pages, allocated on demand and addressed through a page table. No giant reservation, almost no fragmentation — so you fit far more concurrent sequences on the same card, which makes the batching above actually pay off.

  reserved (old way)                     paged (PagedAttention)
  ┌───────────────────────────┐          ┌──┬──┬──┬──┬──┬──┬──┬──┐
  │ req A ████░░░░░░░░░░░░░░░  │          │A0│A1│B0│C0│A2│B1│C1│░░│   pages handed out
  │ req B ██░░░░░░░░░░░░░░░░░  │   →      └──┴──┴──┴──┴──┴──┴──┴──┘   as tokens arrive
  │ req C █░░░░░░░░░░░░░░░░░░  │          page table maps each seq → its pages
  └───────────────────────────┘          almost no waste, no fragmentation
     reserved-for-max = wasted              → many more requests fit

The diagram above sketches the idea; here it is live. Flip between reserved and paged and watch how many requests fit on the same 64-page pool — and where the memory actually goes.

A 64-page GPU memory pool. In reserved mode each request checks out a full contiguous max-size block — most of it stays hatched-amber, reserved-but-never-used — so only a handful fit and the next request is rejected even while pages sit free but scattered. Switch to paged and requests grab small pages on demand from anywhere via a page table: almost no waste, no fragmentation, far more requests resident. Watch the gauges — the pool looks 'allocated' on a dashboard in both modes, but the KV actually in use is where paging wins. Reserved memory looks allocated while most of it never holds a token; paging reclaims that invisible waste so the GPU serves far more concurrent requests — capacity utilization can't see.
allocationReserved mode: every request checks out a full max-size block, most of it never used. Flip to paged and watch how many more requests fit.
KV in usereserved · wastedfree page
requests fit
memory wasted
KV used / allocated
dashboard sees · pages allocated
pool checked out — looks full in both modes
actually working · KV in use
pages holding real tokens

The payoff compounds with §01: paging lets more requests be resident, continuous batching keeps their slots full, and together they turn the memory-bound decode phase from a capacity disaster into a packed, efficient pipeline. Chunked prefill (Sarathi-Serve) adds one more fix — slicing long prompts so a big prefill doesn’t stall everyone else’s decode.

03 Spending the idle headroom: speculative decoding

Here’s the clever one. Since decode is memory-bound, the target model reads all its weights to produce a single token — with the tensor cores mostly idle. What if we gave them something to do? A small, cheapdraft model proposes several tokens; the big target model then verifies all of them in one pass. If the draft was right, you commit several tokens for the price of one expensive pass. Drag the draft-acceptance slider:

A small draft model proposes 5 tokens; the target verifies them in one pass. The accepted prefix (green) commits, plus one bonus token; the first miss (amber) discards the rest. Higher draft acceptance → more tokens per expensive target pass → higher speedup. Same target pass either way — the extra tokens ride along in the memory-bound step’s idle compute.
draft · small model
target · big model verifies
idle
committed output
tokens / target pass
effective speedup
0target passes

This is the thesis flipped into a feature. Speculative decoding wins precisely because decode under-uses the tensor cores: verifying k candidate tokens costs almost the same as generating one, since both are dominated by reading the weights once. It converts the utilization gap directly into throughput — typically 2–3×. Medusa, EAGLE and n-gram drafting are variations on the same insight.

04 Why an observability company cares

the plasmient thread

Look back at the four sections: every serving technique is a fight against memory-bound decode. Continuous batching hides it behind other requests. PagedAttention fits more requests to hide it behind. Speculative decoding spends its idle compute. Each one moves goodput a lot and movesnvidia-smi almost not at all — which means the one number the industry watches is blind to the entire discipline of making inference efficient.

This is the sharpest edge of the whole Frontier Stack: the layer where “busy” and “useful” diverge the most, on the workload that runs every minute of every day. The model core it serves is layer 02; the request flowing through it is the request lifecycle. Across all of it,Plasmient is the instrument that reports goodput next to utilization — and tells you which of that pinned-at-100% silicon was actually working.

Full references — go to the source

The serving papers that define the modern stack, then the open servers that implement them. PagedAttention and Orca are the two to read first — they’re why a GPU can serve thousands of users at once.