🚀 B200 starting at $3.75/hr. The best price you'll find. DC in US West → (Access it from button on top after login).

Get Your B200 →
Start Building
Technical

Continuous Batching Explained: How Modern Servers Keep GPUs Full

Static batching makes every request wait for the slowest one. Here is the scheduling trick that fixed it, where it came from, and the tradeoff nobody mentions.

Author photo
packet.ai Team
August 3, 2026

Continuous batching is the scheduling technique that lets an LLM inference server swap requests in and out of a running batch at every generation step, instead of waiting for the slowest request to finish before starting the next batch. It is the single reason modern inference engines can keep a GPU near full utilization under real, uneven traffic.

Key takeaways

  • Continuous batching schedules at the iteration level, not the request level: a finished sequence's slot is filled by a new request on the very next token step, not the next batch
  • The technique traces to Orca (OSDI 2022), which reported a 36.9x throughput improvement over NVIDIA FasterTransformer at equal latency by introducing iteration-level scheduling
  • Static batching throughput can fall as low as 81 tokens per second under high sequence-length variance, since every request in a batch waits for the slowest one to finish
  • Every major inference engine implements the same core idea under a different name: continuous batching (vLLM, TGI), in-flight batching (TensorRT-LLM), persistent batching (LMDeploy)
  • Continuous batching is a scheduling technique, distinct from PagedAttention, which is a memory management technique; the two are complementary, not the same thing
  • Mixing prefill (compute-heavy) and decode (memory-bound) work in the same batch creates a real tradeoff; chunked prefill, now built into vLLM's V1 engine by default, is the technique most engines use to manage it

Every inference engine that matters in 2026, vLLM, SGLang, TensorRT-LLM, all lean on some form of continuous batching to keep GPUs busy. vLLM batching specifically, meaning continuous batching vLLM implements by default, is the version most engineers encounter first. It is one of the two ideas, alongside PagedAttention, that took LLM serving from research-lab throughput to something that could plausibly serve production traffic. Most explanations of it stop at "new requests fill empty slots," which is true but skips the actual scheduling problem underneath. This post covers how the scheduling works, where the idea came from, and where it still runs into real friction.

This post belongs to the packet.ai LLM serving frameworks cluster. For the broader picture of GPU sizing across model families, see the VRAM requirements guide. For the memory-management half of the story, see the vLLM Docker deployment guide, which covers PagedAttention in depth.

The Static Batching Problem: Why GPUs Sit Idle

Before continuous batching existed, inference servers batched requests the way most people would naturally design it: collect a group of requests, run them together, wait for every single one to finish, then start the next batch. This is static batching, and it has one structural flaw that gets worse as traffic gets more realistic.

LLMs generate output one token at a time, and nobody knows in advance how many tokens a given request will need. A summarization request might finish in 40 tokens. A long-form generation might run for 2,000. Under static batching, every request in a batch is locked to the same clock: the batch cannot return any results and cannot accept any new requests until the single longest-running sequence finishes. Every other GPU slot in that batch sits idle, fully allocated, doing no useful work, for however long the slowest request takes.

Under high sequence-length variance, this is not a small inefficiency. Some benchmarks show static batching throughput collapsing to as low as 81 tokens per second on workloads with mixed short and long generations, a small fraction of what the same hardware can sustain once idle time is removed. The GPU is not the bottleneck here. The scheduling policy is.

Static batching versus continuous batching request timelines Static batching leaves GPU slots idle once a request finishes early, since the whole batch waits for the slowest request. Continuous batching immediately refills a finished slot with a new request, keeping every slot busy. Static batching Every slot waits for the slowest request to finish A B C running running running idle idle batch ends here Continuous batching A finished slot is refilled on the very next step A B C D E F G H I running Same time span, no idle gaps: every slot stays filled

The diagram makes the mechanism concrete. In static batching, once request A or B finishes early, that GPU slot sits idle, grayed out, for the rest of the batch, because the batch as a whole can't restart until C also finishes. In continuous batching, the same slot gets a new request (D, then E, then F) the moment the previous one completes. No slot goes idle. This is the entire throughput gain in one picture.

How Continuous Batching Actually Schedules Requests

Continuous batching, also called iteration-level scheduling or in-flight batching depending on which engine you're reading about, fixes this by making a scheduling decision at every single token generation step instead of once per batch. The mechanism traces to Orca, a serving system presented at OSDI 2022, which proposed exactly this: run the model one iteration at a time, and after every iteration, check whether any sequence has finished. If one has, immediately admit a new request into that now-empty slot. The batch's composition changes continuously across iterations, rather than staying fixed for the batch's entire lifetime.

Orca's own evaluation on a GPT-3 175B model reported a 36.9x throughput improvement over NVIDIA FasterTransformer at the same latency, by this mechanism alone. That is a striking number for what is, underneath the terminology, a fairly simple idea: stop treating the batch as a fixed unit and start treating the GPU's available slots as a resource to refill continuously.

36.9x

Orca's reported throughput gain over FasterTransformer at equal latency

81 tok/s

Static batching throughput under high sequence-length variance, worst case

2022

Year Orca introduced iteration-level scheduling (OSDI)

3

Different names for the same idea across major inference engines

"Dynamic batching" sometimes gets used as a synonym for continuous batching, but they are not quite the same thing, and this matters if you're searching for dynamic batching LLM guidance and landing on continuous batching content instead. Dynamic batching, in the stricter sense, still batches at the request level: it waits for a time window or a batch-size threshold, then launches whatever has arrived, without waiting indefinitely for a full batch. Continuous batching goes a step further and makes the decision at every token, not just at batch-launch time. If you see the two terms used interchangeably, check which mechanism the source is actually describing before trusting the distinction.

Selective Batching: The Half of Orca's Idea Nobody Mentions

Iteration-level scheduling gets most of the attention, but Orca's paper actually proposed two techniques together, and the second one solves a problem the first one creates. Once the batch's composition can change every iteration, sequences in that batch are no longer guaranteed to be at the same point in generation, some may be on their 3rd token, others on their 200th. Standard batched matrix operations assume every sequence in the batch is at the same shape and stage. That assumption breaks the moment iteration-level scheduling is introduced.

Orca's answer, selective batching, is to apply batching only to the operations that tolerate sequences being at different stages, mainly the linear and normalization layers, while running attention separately per sequence, since attention is the one operation whose computation actually depends on each sequence's own accumulated context. This is a real engineering compromise, not a free optimization: it trades some batching efficiency on the attention operation specifically in exchange for making iteration-level scheduling possible at all. Later systems, including vLLM's PagedAttention, took this further by fusing per-sequence attention into a single CUDA kernel that reads from non-contiguous memory, which is a different, complementary solution to the same underlying constraint.

The Prefill and Decode Collision Continuous Batching Doesn't Fully Solve

⚡ Note

This is the part most explanations skip, and it's the part worth understanding if you're actually tuning a production server rather than just reading about the concept.

LLM inference has two distinct phases with very different computational profiles. Prefill processes an entire incoming prompt in one shot and is compute-heavy: it's a large, parallel matrix operation. Decode generates one token at a time for an already-running sequence and is memory-bandwidth-bound: it's a small operation, repeated many times. Continuous batching has to decide what happens when a new request's prefill arrives while other requests are mid-decode.

Run the new prefill immediately, and you delay every currently-decoding sequence in the batch by the time the prefill takes, which can be substantial on a long prompt. Delay the prefill until decode finishes, and the new request's time-to-first-token gets worse. There is no version of continuous batching that makes this tradeoff disappear; different engines just make different choices about where to sit on it. Orca's original design supported hybrid batches, mixing prefill and decode requests in the same iteration, from the start. vLLM's own scheduler initially took the opposite approach by default, prioritizing prefill-only or decode-only batches to optimize time-to-first-token, at some cost to steady-state throughput.

Chunked Prefill: The Fix for the Prefill-Decode Tradeoff

Chunked prefill is the technique most production engines now use to soften this tradeoff instead of picking a side outright. Rather than running an entire prompt's prefill in one uninterrupted pass, chunked prefill splits it into smaller pieces, typically a few hundred to a couple thousand tokens each, and interleaves those pieces with the decode steps of requests already running. Instead of one long prefill blocking every decoding sequence for its full duration, decode work gets a turn between each chunk.

The technique comes from Sarathi-Serve, and it has since been adopted directly into vLLM's own scheduler rather than staying an external add-on: vLLM's V1 engine enables chunked prefill by default wherever possible. The tuning knob that controls it, max_num_batched_tokens, sets the token budget per scheduling step. A smaller budget means decode requests get interrupted less often, which improves inter-token latency; a larger budget lets more prefill through per step, which improves time-to-first-token for the new request. Neither setting eliminates the tradeoff, it just moves where you feel it.

This is worth being direct about: chunked prefill is not a free upgrade over plain continuous batching, it's a more granular way to manage the same underlying prefill-decode tension described above, and getting real benefit from it means tuning the chunk size against your own traffic's prompt-length distribution rather than trusting a default value.

Continuous Batching, In-Flight Batching, Persistent Batching: Same Idea, Different Vendor

If you're comparing inference engines and the terminology feels inconsistent, that's because it genuinely is, not because the underlying mechanism differs meaningfully between them.

Engine Name used Notes
vLLM Continuous batching Paired with PagedAttention for memory management
Hugging Face TGI Continuous batching Same term as vLLM, same underlying mechanism
TensorRT-LLM In-flight batching NVIDIA's own name for the identical iteration-level idea
LMDeploy Persistent batching Same mechanism, third naming convention

None of these are competing techniques. They're the same scheduling idea, implemented independently, named differently by each team. If a benchmark or vendor comparison treats "in-flight batching support" as a differentiator against an engine that has "continuous batching," that's a marketing framing, not a technical one, worth checking closely before trusting the comparison.

Continuous Batching and PagedAttention Are Not the Same Thing

These two ideas get bundled together often enough that it's worth separating them cleanly. Continuous batching is a scheduling technique: it decides which requests run in which iteration. PagedAttention is a memory management technique: it decides how the KV cache for those requests is stored in VRAM. They solve different problems and were developed independently, though vLLM popularized running them together, which is likely why they get conflated. You can have continuous batching without PagedAttention, and in fact Orca did, since PagedAttention came a year later. For the memory-management side of this story in full, the vLLM Docker deployment guide covers it in depth; this post stays on the scheduling half.

Running This on packet.ai

Getting continuous batching right in practice usually means tuning concurrency settings, flags like vLLM's --max-num-seqs, against your actual traffic shape rather than a default value, and that tuning process involves running the same workload several times at different settings to find where throughput actually plateaus.

packet.ai's Dynamic tier is built around a version of the same underlying idea one layer up: instead of one engine sharing GPU slots across requests, the packet.ai scheduler shares physical GPU capacity across multiple tenants' workloads, keeping latency within roughly 2 to 5 percent of a dedicated card while billing by the hour rather than locking you into a fixed allocation. An RTX 6000 Pro on Dynamic runs at $0.66/hr, live in under 5 minutes from API call to SSH-ready, which makes running five or six concurrency experiments in an afternoon a reasonable thing to do rather than an expensive one. Once you've found your actual concurrency ceiling, moving the same workload to a Dedicated instance removes scheduler interference entirely for production traffic.

Frequently asked questions

Continuous batching is a scheduling technique where an inference server checks, at every token generation step, whether any request in the current batch has finished. If one has, a new request is immediately admitted into that slot rather than waiting for the entire batch to complete. It keeps the GPU near full utilization under uneven, real-world traffic instead of idling while it waits for the slowest request in a fixed batch.
Not precisely, though the terms are often used loosely as synonyms. Dynamic batching typically still batches at the request level, launching whatever requests have arrived within a time window rather than waiting for a fixed batch size. Continuous batching makes a scheduling decision at every individual token step, which is a finer granularity. Check which mechanism a source actually describes before assuming the two terms mean the same thing.
There is no meaningful technical difference. Continuous batching is the term vLLM and Hugging Face TGI use; in-flight batching is NVIDIA's name for the identical mechanism in TensorRT-LLM; LMDeploy calls it persistent batching. All three describe the same iteration-level scheduling approach, just named independently by each engine's team.
No. Continuous batching is a scheduling technique that decides which requests run in which iteration. PagedAttention is a memory management technique that decides how the KV cache is stored in VRAM. vLLM popularized running both together, which is why they're often mentioned in the same breath, but they were developed independently and solve different problems. Continuous batching predates PagedAttention by about a year.
All actively developed production inference engines support some form of it: vLLM and Hugging Face TGI call it continuous batching, TensorRT-LLM calls it in-flight batching, and LMDeploy calls it persistent batching. If an engine doesn't support this in some form, it will underperform significantly on any workload with mixed request lengths.
Chunked prefill splits a long prompt's prefill computation into smaller pieces and interleaves them with the decode steps of requests already running, instead of running the entire prefill in one uninterrupted pass that blocks all decoding. It comes from Sarathi-Serve and is now built into vLLM's V1 engine by default. It softens the prefill-decode tradeoff inherent to continuous batching; it doesn't eliminate it.
Any GPU with enough VRAM for your model and target concurrency benefits from continuous batching equally, it's a scheduler-level technique rather than a hardware requirement. For tuning concurrency settings before committing to production hardware, packet.ai's Dynamic tier (for example, an RTX 6000 Pro at $0.66/hr) lets you test different configurations quickly on hourly billing before moving to a Dedicated instance for predictable production latency.

Last reviewed: August 3, 2026. For the memory-management complement to this post, see the vLLM Docker deployment guide. Browse packet.ai GPU cluster options for multi-node inference.

Waste less compute.

Same models. Same API. Fraction of the cost. Start free — no credit card required.

Start Building →

More from the blog