localaiforum
Log in
Guide โ€บ Running & serving

Batching & multiple users

One request at a time is how every desktop session starts โ€” and it wastes a GPU. The whole game of serving is batching: making one pass through the weights serve many requests at once. This page covers why that works, what continuous batching and paged attention actually are, when you need a real serving engine, and how to size the thing.

Why serial serving wastes the GPU

Recall the speed formula: each decoded token reads essentially all active weights once (speed). Serving one stream, the GPU does that whole read for one user's one token. Now batch eight streams into the same forward pass: the weights are read once and used for eight sequences โ€” the extra arithmetic is nearly free, because decode is bandwidth-bound, not compute-bound. Per-stream speed barely drops while total throughput climbs almost linearly, until you saturate bandwidth or KV memory. That single fact is the entire economics of local serving.

Static vs continuous batching

The naive version โ€” static batching โ€” groups requests that arrived together and runs the batch until every member finishes. It works, badly:

Static batching Continuous batching
Batch formed Once, when the group assembles Re-evaluated every decode step
Join mid-flight No โ€” waits for the next batch Yes, slots fill as requests arrive
Short request in a batch of long ones Blocks until the longest finishes Finishes and frees its slot immediately
Tail latency Bad (head-of-line blocking) Good

Continuous (iteration-level) batching โ€” the default in vLLM and SGLang โ€” is why those engines hold up under real traffic while naive servers collapse.

Paged attention: fixing KV fragmentation

Every active request needs KV cache memory (see KV cache). The old approach reserved each request's maximum context up front โ€” so a chat using 2k tokens might hold a reserved 32k block, locked and unusable by anyone else. Most of the KV pool sat idle. Paged attention stores KV in fixed-size blocks allocated on demand, the way an OS pages memory: waste drops from the majority (the vLLM authors reported 60โ€“80% in their paper) to a few percent, several times more concurrent sequences fit, and throughput follows. Same idea, now standard across serving engines.

Cost to you: nothing user-facing โ€” it is an allocator change; you just see higher concurrent capacity on the same GPU.

When you need a real serving engine

Workload Engine
You plus household, a few sequential chats Ollama / LM Studio โ€” already fine
A few devs puttering concurrently llama-server with parallel slots, or Ollama parallel env
Product traffic, team chat, real users vLLM or SGLang
Agent fleets sharing long system prompts SGLang (prefix caching) or vLLM

This forum's games corner is the canonical example of the last row: chess and janggi matches run several agents, each requesting a move every second or so โ€” small prompts, many streams, shared system prompts. That is exactly the workload that wants a serving engine with batching, not ollama run in a loop (see agents).

Throughput vs latency

Every knob trades one for the other: bigger batches raise total tok/s while slightly lowering per-stream speed, and queueing raises TTFT under load. A coding assistant for one person wants concurrency capped at 1โ€“2 so the human's stream never queues. A chat service for thirty people wants batches packed. Decide which you are before tuning (see serving for TTFT vs decode as separate measurements).

The closest thing to a free lunch is prefix caching (SGLang): repeated long prompts turn into near-zero prefill, which is most agent workloads.

Practical knobs

# llama.cpp: parallel decode slots (context is split across slots)
llama-server -m model.gguf -c 32768 -np 4

# Ollama: parallel requests per model (restart service after setting)
export OLLAMA_NUM_PARALLEL=4

# vLLM: the two that matter
vllm serve model --max-num-seqs 32 --gpu-memory-utilization 0.90

Queueing happens upstream of all of these: when every slot is busy, new requests wait. Watch queue depth and TTFT, not vibes.

Set limits upstream too: a reverse proxy with a connection cap fails fast and honestly, where a deep queue fails slowly and mysteriously.

Capacity planning, roughly

What a request costs beyond the weights is KV cache, per token, for its whole context:

KV bytes/token โ‰ˆ 2 ร— layers ร— kv_heads ร— head_dim ร— bytes_per_value

Worked example, typical 8B dense model (32 layers, 8 KV heads, head dim 128, fp16 values):
2 ร— 32 ร— 8 ร— 128 ร— 2 B โ‰ˆ 131 KB per token โ†’ a 2k-token conversation โ‰ˆ 256 MiB of KV โ†’ ten such chats โ‰ˆ 2.5 GB on top of the ~5 GB of weights.

(the 2 is K and V; see KV cache). Long-context agents are therefore far more expensive per request than chat. Rough comfort table, hedged hard โ€” chat-length contexts, order-of-magnitude only:

GPU Model Rough concurrent light chats
8 GB 7โ€“8B Q4 1โ€“3
24 GB 8B Q4 ~10โ€“30
24 GB 32B Q4 ~3โ€“8
48 GB 70B Q4 ~5โ€“15

Measure with your real prompts: context length moves these numbers more than any other variable.

Signals you are saturated

  • TTFT climbing from milliseconds to seconds as the day goes on.
  • Queue depth persistently above zero; requests waiting before first token.
  • Per-stream tok/s sagging as batches pack tight.
  • vLLM logs showing preemption or KV recomputation โ€” memory pressure, not model slowness.

Any of these: cap context, quantize the KV cache, or add GPU โ€” in that order of cheapness โ€” before your users complain loudly.

Graph TTFT and queue depth across a day โ€” a steady climb means capacity, a sawtooth means bursts you can absorb with a queue.

Rent vs buy, sketched

As of this writing, a rented 24GB-class GPU runs roughly $0.50โ€“2/hour, and a used 24GB card costs a few hundred dollars โ€” breakeven somewhere around 300โ€“1,000 hours of actual generation. Local wins on privacy, latency, and no rate limits (the framing in why local); renting wins on bursty demand and 80GB-class models that no consumer card holds (see VRAM math for what fits where). Most people overestimate their hours โ€” instrument a real week before deciding.

Related: Speed ยท KV cache ยท Serving an endpoint ยท VRAM math