Read SGLang from frontend program to GPU step: RadixAttention, scheduling, constrained decoding, speculative execution, parallelism, and production boundaries.
Personalize this lesson
Adapt explanations and teaching visuals to your background and preferred voice.
An LLM request can be a single prompt, or it can be a program: retrieve evidence, ask for several candidates, select one, call a tool, then generate a structured answer. A server that treats every step as an unrelated text completion repeats prefixes and loses the structure the application already knows.
SGLang co-designs two layers for that workload. Its frontend lets Python code describe generation, choices, roles, and control flow. Its SGLang Runtime (SRT) turns those requests into continuously batched GPU work, with a radix-tree key-value (KV) cache, grammar-aware decoding, speculative execution, and distributed model execution. The frontend is optional: an OpenAI-compatible request can enter SRT directly. The runtime is the common path either way.
This chapter follows one request from a frontend expression or HTTP payload through tokenization, prefix matching, scheduling, model execution, and streamed output. Keep three signals in view: time to first token (TTFT), inter-token latency (ITL), and tokens per second. SGLang's optimizations change those signals by changing how much state is reused and how much work the scheduler can overlap.
The original SGLang paper describes a frontend language plus a runtime for efficient execution of structured language-model programs.[1] The current repository describes SGLang as a serving framework for language and multimodal models, with an OpenAI-compatible API, broad model support, and hardware backends.[2] Those descriptions point to a useful distinction:
| Entry path | Reader writes | SGLang must optimize | Good fit |
|---|---|---|---|
| Frontend program | @sgl.function, sgl.gen, sgl.select, roles, control flow | Shared prefixes, parallel program branches, output dependencies | Agents, few-shot programs, extraction, multi-turn workflows |
| OpenAI-compatible API | JSON chat or completion request | Batching, KV reuse, sampling, streaming | Drop-in model endpoint |
| Offline engine | Python calls against sgl.Engine | Batch admission and reusable cache state | Evaluation, replay, test fixtures |
| RL rollout client | Requests from a trainer or environment | Throughput, weight refresh, deterministic boundaries | Post-training and verifiable rewards |
The program surface can make repeated context visible. A system prompt, demonstrations, and retrieved documents become a shared prefix instead of three independently serialized strings. The runtime still needs an exact token match, so application-level similarity is not enough. A changed space, chat-template token, adapter identifier, or cache salt can split the prefix.
SGLang also serves requests that never use the frontend. This matters when comparing engines. A benchmark that sends plain OpenAI requests measures SRT's serving path, not the convenience or scheduling hints supplied by the frontend. A program benchmark adds branch parallelism, variable binding, and prefix reuse to the workload.
The public sgl module exposes a few operations with clear semantics. sgl.gen asks for model tokens, sgl.select chooses among explicit strings, and role helpers add chat-template boundaries. A decorated function becomes an SglFunction; the interpreter walks its expressions and sends generated spans to a backend. The source code for these pieces lives under python/sglang/lang/ in the local clone.
1import sglang as sgl
2
3@sgl.function
4def classify_and_explain(s, question):
5 s += sgl.system("Return concise, evidence-backed answers.")
6 with s.user():
7 s += question
8 s += "\nLabel: "
9 s += sgl.select("label", ["bug", "feature", "question"])
10 s += "\nExplanation: "
11 s += sgl.gen("explanation", max_tokens=64, stop="\n")One generation site has a finite choice set. Another is open-ended but has a stop condition. Its result contains named values such as label and explanation, so application code can use one generation to shape the next prompt. A frontend program can also run several branches in parallel or bind a cached prefix for a batch.
The frontend doesn't make the model deterministic. Sampling parameters still apply, and a select choice can be scored with the configured method. It also doesn't guarantee that a JSON-looking prompt returns valid JSON. Use json_schema or a regex constraint when output syntax is part of the contract.
SGLang's interpreter keeps a stream executor and a program state. The stream executor submits text and generation operations to the backend; the state holds named variables and assembled text. This split lets one program use a local engine, a remote runtime endpoint, or a test backend without changing its control flow. It also means that a bug can live above SRT: an unexpected branch, a missing variable, or a chat-template mismatch may never reach the scheduler.
For an online request, the path is roughly:
The process split can vary with server mode and parallelism, but responsibilities stay recognizable. Entrypoints parse requests and return protocol responses. The tokenizer manager owns tokenization and detokenization boundaries. The scheduler chooses which request tokens run next. A radix cache supplies reusable KV locations. A model runner loads weights, builds a forward batch, selects attention backends, and returns logits or sampled tokens. Grammar and sampling code constrain or select the next token before the stream is updated.[3]
Debugging starts at the handoff between stages. High TTFT with low GPU utilization points to tokenization, queue wait, prefix lookup, or admission before kernel work. Rising ITL after a model update points to forward-batch shapes, attention backend selection, collective wait, and grammar or speculative work. Malformed output calls for separating model logits from grammar masking and detokenization.
SRT maintains active requests and schedules a batch repeatedly. Decodes usually need one new token per request. Prefills need many prompt tokens. Chunked prefill divides a long prompt so it doesn't monopolize a step while decodes wait. The exact flags and defaults change by release, so inspect Scheduler.init_chunked_prefill and the server arguments for the clone's version.
Consider a teaching trace with budget six:
| Step | Ready work | Tokens admitted | State after step |
|---|---|---|---|
| 0 | R0 decode needs 1; R1 prefill needs 9 | R0=1, R1=5 | R0 advances; R1 has 4 prompt tokens left |
| 1 | R0 decode needs 1; R1 prefill needs 4 | R0=1, R1=4 | R1 finishes prefill |
| 2 | R0 and R1 decode | R0=1, R1=1 | Both requests continue generation |
The numbers are an invariant exercise, not a performance claim. The scheduler spends a bounded token budget while keeping decode work eligible. A larger prefill slice may improve TTFT for new prompts but can delay existing decodes. A smaller slice can protect ITL but stretch prompt processing.
SGLang also has an overlap path. CPU scheduling, token movement, and GPU execution can be pipelined when the backend and flags permit it. Overlap hides some host work, but it adds queues and synchronization. A race in request state, a delayed grammar update, or a stale KV location can become a correctness bug rather than a simple latency regression.
RadixAttention is SGLang's name for using a radix tree to find reusable KV-cache prefixes. It isn't an attention kernel. The tree stores token-key paths and references to KV locations; an attention backend later reads those locations. The distinction matters when reading performance reports or debugging a hardware-specific kernel.
Suppose two requests share tokens A B C D and then diverge:
| Request | Token sequence | Reused path | New path |
|---|---|---|---|
R0 | A B C D E F | A B C D | E F |
R1 | A B C D X Y | A B C D | X Y |
The tree contains one shared path for the first four tokens, then two branches. A request's prefix match returns the longest cached path and the corresponding token-to-KV indices. The scheduler can skip prefill for that matched region and allocate new KV slots only for the suffix.
The local RadixKey stores token IDs plus an optional extra key. Current source uses extra keys for namespaces such as LoRA adapters, cache salt, version, or retrieval context, and the cache's match operation checks compatible keys before comparing tokens. This keeps cache namespaces disjoint as cache partitioning, not an authorization boundary.
The match is exact and page-aware. If a cache policy or backend requires aligned units, a partial final unit isn't reusable as a complete prefix. The practical result is simple: stable system prompts and demonstrations help; per-request timestamps and random IDs near the front of a prompt destroy hits.
A prefix cache needs ownership rules alongside its lookup map. While a request is active, its matched KV locations are protected. When a request finishes or releases a branch, nodes can become evictable. The eviction policy can be LRU or another configured strategy, but it must never reclaim state still referenced by a live request.
The source path is visible in python/sglang/srt/mem_cache/radix_cache.py:
match_prefix finds the longest compatible path and returns KV indices.insert adds a new token path after forward computation.evict removes unprotected branches according to the selected policy.This sequence exposes a common failure. If a request's KV reference is released too early, another request can overwrite memory while the first is still decoding. If references never drop, free KV capacity shrinks until admission stalls. Instrument active references, evictable tokens, match lengths, and allocator failures together.
The cache is local to an engine or replica unless a higher-level routing layer coordinates it. A load balancer that sends identical prompts to different replicas may see low hit rates even when each replica has a healthy tree. Cache-aware routing can improve locality, but adding replicas still changes hit probability and warm-up behavior.
Many applications need valid JSON, a tool-call schema, a regular-language field, or one label from a known set. Asking a model to "please emit JSON" is a prompt preference, not a syntax guarantee. SGLang can carry a regex, JSON schema, or choice constraint through sgl.gen and its server APIs.[4]
At each decoding step, a grammar engine tracks an automaton state. It computes the set of tokens that keep the partial output valid, and the sampler masks other logits. The model still chooses among allowed tokens. The grammar doesn't add facts to the model or repair a semantically wrong field.
1import sglang as sgl
2
3@sgl.function
4def extract(s, passage):
5 s += "Extract one entity from this passage:\n" + passage
6 s += sgl.gen(
7 "record",
8 json_schema='{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}',
9 max_tokens=64,
10 )SGLang supports multiple grammar backends in the repository. The selected backend, tokenizer, vocabulary, and schema shape affect CPU work and compatibility. A grammar with many alternatives can take more time to update than a small enum. Measure grammar preparation, per-step mask time, and end-to-end latency rather than assuming all constrained requests cost the same.
The original SGLang work also introduced compressed finite-state-machine techniques for faster structured decoding.[1] The enduring design is the boundary: grammar state stays on the control side, while logits remain on the model side. That makes constraints inspectable and lets the runtime reuse model KV state even when grammar states differ.
Failure handling needs an explicit policy. If no token satisfies the grammar, the request should return an error or a clearly marked incomplete result. Retrying with a looser grammar can change semantics. Don't silently strip braces or run a best-effort JSON parser and call the result valid.
Autoregressive decoding normally produces one token per target-model forward step. Speculative decoding asks a cheaper draft path for several candidates, then lets the target model verify them in one pass. Accepted tokens advance the sequence faster; rejected tokens fall back to a target-sampled token.
SRT exposes several speculative algorithms, including EAGLE, EAGLE3, n-gram lookup, standalone draft models, and DFlash-family methods in the cloned version. The enum and worker registry live under python/sglang/srt/speculative/. The exact flags and model requirements are version-specific, so check SpeculativeAlgorithm and its validator before enabling a mode.[5]
An abstract step looks like this:
| Phase | Work | State |
|---|---|---|
| Draft | Propose y1 ... yk from a cheap path | Draft tokens and optional draft KV |
| Verify | Target scores candidates with one batched forward | Target logits and acceptance tests |
| Commit | Keep accepted prefix, sample after first rejection | Main KV extends by accepted tokens |
EAGLE-2 and EAGLE-3 research explores draft generation from target-model representations and tree-shaped candidates.[6][7] DFlash-family work targets faster diffusion-style or parallel draft paths in newer SGLang releases.[8] The implementation must still preserve token ordering, position IDs, grammar state, and cache ownership.
Speculation helps when acceptance is high and draft work is cheaper than target work. It can hurt when the model is uncertain, prompts are short, grammar masks reject candidates, or draft KV consumes capacity needed by ordinary requests. Track acceptance length, draft and verify latency, rejected tokens, extra memory, and ITL by model and workload.
SGLang maps model work onto hardware through several parallelism axes. Names vary by model, but the trade-offs are stable:
| Axis | What is split | Helps with | Costs |
|---|---|---|---|
| Tensor parallelism (TP) | Matrix dimensions across devices | Fit and per-request compute | Per-layer collectives |
| Pipeline parallelism (PP) | Layer ranges across stages | Fit depth across nodes | Pipeline bubbles and stage sync |
| Data parallelism (DP) | Full model replicas or request groups | Independent throughput | Weight memory and routing |
| Expert parallelism (EP) | Mixture-of-Experts experts | Expert capacity and load balance | Token routing and all-to-all |
| Context or sequence paths | Sequence positions or attention work | Long contexts and bandwidth | Extra communication and layout constraints |
The runtime has to align these choices with attention backends, quantization, CUDA graphs, and model-specific layers. A TP setting that fits weights can still reduce ITL if collectives dominate. A DP replica can raise throughput while lowering prefix-cache locality. Read the parallelism docs and benchmark on the target topology before copying a flag set.[9]
Prefill and decode have different resource shapes. Prefill consumes many prompt tokens and tends to be compute-heavy. Decode consumes one or a few tokens per request and repeatedly reads KV memory, making it more latency and memory sensitive. Prefill-decode (P/D) disaggregation places them on separate worker pools and transfers KV state between them.[10]
The P/D path can protect decode latency from a long prompt and let each pool scale for its own traffic. It adds transfer bytes, synchronization, routing, and a cache locality decision. A prompt that is prefetched on one node but decoded on another needs a reliable connector and enough bandwidth. If transfer time exceeds the compute saved, colocated execution wins.
P/D disaggregation is not a free "scale out" switch. Test queueing at both pools, transfer latency, failure recovery, and cache hit rate. A decode worker that loses its prefill connection needs a clear retry or cancellation path so stale KV state isn't treated as valid.
SGLang's main advantage appears when request structure matches runtime structure. Choose the smallest example that exposes that match:
| Application | Runtime opportunity | What can break |
|---|---|---|
| Agent loop | Reuse system and tool instructions; batch independent branches | Branches diverge early, tool latency dominates |
| Retrieval-augmented generation | Cache stable prompt and template prefix | Retrieved chunks change order or include random IDs |
| Structured extraction | Grammar masks invalid tokens | Schema is large or semantically underspecified |
| Reasoning and self-consistency | Run candidate branches together | More samples multiply KV and verification cost |
| Multi-turn chat | Keep conversation prefix hot | Context trimming or replica routing loses locality |
| Multimodal chat | Share batching and model runner infrastructure | Media preprocessing, transfer, and model support vary |
| RL rollout | High-throughput generation and weight updates | Version skew between trainer and rollout workers |
The paper's evaluations span agent control, logical reasoning, few-shot tasks, JSON decoding, retrieval-augmented generation, and multi-turn chat.[1] Treat those workloads as evidence for mechanisms, not a current throughput guarantee. A release, GPU, model, tokenizer, and request mix can change the winner.
| Dimension | Strength | Boundary or weakness |
|---|---|---|
| Programming model | Frontend names generation sites and branches | Extra abstraction can hide a prompt or state bug |
| Prefix reuse | Radix tree finds exact shared token paths | Small prompt changes or replica routing erase hits |
| Scheduling | Continuous batching, chunked prefill, and overlap share a step | Queue policy can trade TTFT, ITL, and fairness |
| Structured output | Grammar constraints enforce syntax during sampling | Grammar preparation and per-step masks add CPU work |
| Speculation | Accepted draft tokens reduce target-model steps | Low acceptance turns draft work into overhead |
| Hardware | Multiple attention backends and parallelism axes | Compatibility matrix changes quickly by model and accelerator |
| Scale | P/D and distributed execution separate bottlenecks | KV transfer, collectives, and routing add failure surfaces |
| Ecosystem | OpenAI API, frontend, model integrations, and RL clients | API compatibility doesn't mean identical sampling or error behavior |
Most incidents fit one of four boundaries:
Separate those boundaries in telemetry. A single end-to-end latency number can't tell whether a prefix hit helped or whether a grammar mask consumed the saved time.
Suppose a service reports low GPU utilization, high TTFT, and almost no radix hits.
max_prefill_tokens or chunked-prefill settings.Now invert the symptoms: high GPU utilization, good radix hits, but ITL spikes when a long prompt arrives. That points toward prefill scheduling, chunk size, collective wait, or P/D routing. Increasing cache size won't fix a token-budget policy that lets prefill consume every step.
The SGLang paper came from researchers and engineers around UC Berkeley and the LMSYS community, including Lianmin Zheng, Liangsheng Yin, Zhiqiang Xie, Ying Sheng, Ion Stoica, Joseph Gonzalez, and collaborators.[1] LMSYS is a nonprofit organization that incubates open-source systems and research projects; SGLang is one of its flagship projects.[11] The current repository is a multi-contributor project rather than a single-vendor SDK, so support and performance depend on model, hardware, and release paths.
| Field | Current project fact |
|---|---|
| Origin | UC Berkeley and LMSYS researchers built SGLang around language-model programming and efficient serving. The founding paper names Lianmin Zheng, Liangsheng Yin, Zhiqiang Xie, Ying Sheng, and collaborators.[1] |
| Stewardship | LMSYS, a nonprofit research organization, incubates SGLang under the sgl-project GitHub organization.[11] |
| Contributor path | MAINTAINER.md and CODEOWNERS define merge on-call, code-owner, write, CI, and review responsibilities.[12] |
| Source license | Core SGLang source is Apache-2.0.[13] |
| Commercial boundary | Sponsors, vendors, and consultants can support or integrate SGLang without owning its nonprofit-hosted repository. |
| Asset boundary | Model checkpoints, tokenizers, datasets, and third-party kernels can carry licenses separate from SGLang's source license. |
Read the history as a sequence of co-design decisions:
| Research or release thread | Runtime idea it motivates |
|---|---|
| SGLang frontend plus runtime paper | Treat a multi-call LM program as one schedulable workload |
| RadixAttention | Reuse exact token prefixes through a tree of KV locations |
| Compressed grammar decoding | Keep structured output constraints on the decoding path |
| v0.4 scheduler and cache-aware work | Reduce host scheduling overhead and preserve locality as traffic grows[14] |
| EAGLE-2 and EAGLE-3 | Draft and verify multiple candidate tokens[6][7] |
| DFlash-family research | Add newer parallel draft paths to SGLang's speculative registry[8] |
| P/D and distributed runtime docs | Separate prompt compute from decode memory traffic when topology supports it[10] |
The code keeps these ideas modular enough to inspect. A paper explains why a mechanism helps. A release guide explains which flags and models expose it. The local source shows ownership, state transitions, and failure handling. Read all three before making a production claim.
This walkthrough uses the official SGLang repository at commit f8e62a9224815cc9c6fc56b940eb7fde791a8870.[2]
Read one request through these files:
python/sglang/lang/api.py: public function, gen, select, role, regex, and JSON-schema operations.python/sglang/lang/ir.py: expression nodes and the program representation passed to a backend.python/sglang/lang/interpreter.py: program state, stream executor, batch execution, and optional prefix tracing.python/sglang/srt/entrypoints/http_server.py: online request boundary and server routes.python/sglang/srt/managers/tokenizer_manager.py: tokenization, request state, and output handoff.python/sglang/srt/managers/scheduler.py: token pools, chunked prefill, overlap setup, admission, and scheduling loop.python/sglang/srt/mem_cache/radix_cache.py: match_prefix, insert, reference protection, and eviction.python/sglang/srt/model_executor/model_runner.py: model loading, forward-batch setup, attention backends, and parallel state.python/sglang/srt/constrained/grammar_manager.py: grammar backend selection and constrained token state.python/sglang/srt/speculative/spec_info.py: speculative algorithm registry and draft/verify capability checks.python/sglang/srt/disaggregation/prefill.py and decode.py: P/D role boundaries and KV transfer lifecycle.Keep these invariants beside the source:
Start with a tiny local engine or mock backend. Verify the frontend program returns named values. Then send the same tokenized prompt twice and compare matched-prefix length and prefill work. Add a third request with one changed system-prompt token to prove that reuse is exact.
For serving tests, record:
| Layer | Minimum signals |
|---|---|
| Input | token count, template version, media preprocessing time |
| Admission | queue wait, matched-prefix tokens, allocated KV tokens |
| Scheduler | batch token count, prefill chunks, overlap delay, preemptions |
| Model runner | forward time, attention backend, collectives, GPU utilization |
| Grammar or speculation | mask time, accepted draft length, rejection count |
| Output | TTFT, ITL, finish reason, detokenization time |
Change one mechanism at a time. Compare four fixed configurations: plain decoding, radix cache on, grammar on, and speculation on. For P/D, compare colocated and disaggregated modes with the same prompt mix. Keep quality and correctness checks beside latency, because a faster malformed JSON response is a regression.
Explain the runtime path without looking at the diagram. Then answer why a stable system prefix helps two requests, why a grammar can increase CPU time, and why a P/D split can lose to colocated execution on a bandwidth-limited network.
Answer every question, then check your score. Score 75% or higher to mark this lesson complete.
8 questions remaining.
SGLang: Efficient Execution of Structured Language Model Programs
Zheng, L., Yin, L., Xie, Z., et al. · 2024 · NeurIPS 2024
SGLang Source Repository
SGLang Project · 2026
SGLang Documentation
SGLang Project · 2026
Structured Outputs
SGLang Project · 2026
Speculative Decoding
SGLang Project · 2026
EAGLE-2: Faster Inference of Language Models with Dynamic Draft Trees
Li, Y., Wei, F., Zhang, C., and Zhang, H. · 2024
EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Test
Li, Y., Wei, F., Zhang, C., & Zhang, H. · 2025
DFlash: Block Diffusion for Flash Speculative Decoding
Chen, J., Liang, Y., and Liu, Z. · 2026 · ICML 2026
SGLang Server Arguments and Parallelism
SGLang Project · 2026
Prefill-Decode Disaggregation
SGLang Project · 2026
About LMSYS Org
LMSYS Org · 2026
SGLang Maintainer Roles
SGLang Project · 2026
SGLang Apache License 2.0
SGLang Project · 2026
SGLang v0.4: Zero-Overhead Batch Scheduler, Cache-Aware Load Balancer, Faster Structured Outputs
SGLang Team · 2024
Questions and insights from fellow learners.