Key Takeaway
The vLLM and SGLang numbers for DeepSeek-V4.1-Flash are not comparable. vLLM reports about 5x agentic throughput on SemiAnalysis AgentX. SGLang reports 35 to 873 tokens per second at batch size 1 on four GB300s, mostly through speculative decoding with simulated acceptance. What transfers is the method: shrink the KV cache, trim layers, fuse kernels, then measure end to end.
Two numbers landed in my feed within a month of each other: 5x and 35 to 873. Both are about the same model, DeepSeek-V4.1-Flash, and both look like the answer to the question of which engine is faster. They are not. Before I could say anything useful I had to find out what each number was measured against.
This is a reading, not a benchmark: I did not run either engine. I read the vLLM post of 7 October 2026, the SGLang kernel optimisation post, the Hugging Face model card and the earlier vLLM AgentX post, and I quote only figures I saw on those pages. Where the pages disagree with each other or with the headline, I say so.
What makes this model different to serve
The model card describes a causal encoder-decoder with 40 layers: a 20-layer encoder followed by a 20-layer decoder, 8B activated parameters per token in prefill and 16B in decode, a 196B-parameter Engram memory, and a global KV cache of 890 bytes per token, about a quarter of DeepSeek-V4-Flash. The weights are MIT licensed and the context window is up to one million tokens. SGLang's post derives the 890 from FP4 entries of 288 bytes of main KV plus 68 bytes of indexer K, pooled across layers.
Two consequences drive everything that follows. A long prompt mostly runs through the first 20 layers, because the decoder takes its global KV from the encoder output, while generated tokens still run all 40. And the sliding-window KV is a separate, uncompressed FP8 cache covering the last 128 positions in each of 40 layers, which prefix caching has to deal with. Both engines spent their three weeks on those two facts.
Why the headline numbers cannot be compared
The two posts answer different questions. Putting their set-ups side by side shows that the only thing they share is the model name. Note also that the scouting date I started from, 10 September, is the LMSYS day-0 post that SGLang's page links to, not the optimisation write-up itself, which carries no date of its own on the page.
| Concern | vLLM post | SGLang post |
|---|---|---|
| Workload | SemiAnalysis AgentX: long multi-turn agentic coding sessions | Random 4,096 input tokens, 1,024 output, batch size 1 |
| Hardware | GB200 for TTFT, GB300 for kernels and the AgentX chart | 4x GB300 |
| Headline | About 5x high throughput and 1.9x low latency (the TL;DR says 5.3x under a 150 TPS constraint) | 35.2 to 873.6 tokens per second |
| Baseline | vLLM's own day-0 result, three weeks earlier | SGLang's first working build |
| Quality caveat | SWA bounded replay is approximate; GSM8K and GPQA gaps within about 1.5 standard errors | Acceptance is simulated at length 5.5, so output quality is not evaluated |
So 873 is not a faster number than 5x; it is a different axis. A batch-size-1 decode figure with speculative decoding says how quickly one stream can talk to itself. A throughput figure under an interactivity floor says how many sessions a GPU can carry. AgentX itself, per the earlier vLLM post, has a median input of 142K tokens, a median output of 444 tokens and a prefix-cache hit rate above 96 per cent, a workload that is almost all prefill and cache reuse. Decode speed barely appears in it.
vLLM: skip the half of the model you do not need
The biggest single idea in the vLLM post is SWA bounded replay, on by default behind a --[no-]swa-bounded-replay flag. On a prefix-cache hit of length H, vLLM caches only global KV and reruns tokens H minus 128 up to H to rebuild the sliding-window KV, clipping the window at the replay start. On the decoder side, layer 20 runs on every token to produce global KV, while layers 21 to 39 run only on each request's last 128 tokens. For long prompts that skips nearly half the model.
# vLLM: bounded replay is ON by default (per the vLLM post).
# Serve command from the model card; no other launch flags are published.
vllm serve "deepseek-ai/DeepSeek-V4.1-Flash"
# Wrong: comparing outputs across runs with replay on and expecting identical text.
# Right: turn the approximation off for regression and replay tests.
vllm serve "deepseek-ai/DeepSeek-V4.1-Flash" --no-swa-bounded-replay
# What the replay does, as described in the post (H = prefix-cache hit length):
# encoder: cache global KV only, rerun tokens [H - 128, H) to rebuild SWA KV
# decoder: layer 20 on every token; layers 21-39 only on the last 128 tokens
# CUDA graphs: layers 0-20 on the full batch, 21-39 on the trimmed batch (PIECEWISE)The trimmed layers are small, so run eagerly they are bound by kernel launch cost rather than compute. vLLM captured layers 0 to 20 as a CUDA graph on the full batch and layers 21 to 39 separately on the trimmed batch, using a breakable PIECEWISE graph. With graphs, decoder replay cut prefill compute time by 30 to 40 per cent. Without them, short prompts got slower than the baseline, by up to 12 per cent at 1K tokens on DEP2.
Bounded replay trades exactness for speed. vLLM says the output is not bit-exact and reports GSM8K and GPQA gaps within about 1.5 standard errors. Those are two benchmarks, not your workload. If you compare outputs between runs or replay traffic for regression tests, switch the flag off first and know why a diff appears.
The TTFT claims also need care. The summary says about 30 per cent lower TTFT from bounded replay, while the figure caption says nearly 70 per cent at around 100K throughput from three optimisations combined. The measurements are a median of three runs on GB200 with prefix caching off. I would quote neither without saying which one.
vLLM: the kernel list, and the gap between kernel and end-to-end
Most of the remaining vLLM gain comes from integrating DeepSeek's fused kernels. The table lists what each one fuses and the effect the post reports, and the unit matters: some figures are per kernel, some per layer, some end to end.
| Kernel | What it fuses | Reported effect |
|---|---|---|
| Mega-mHC | mHC post step, delayed-pre step and RMSNorm | 1.14 to 1.51x vs the TileLang version, GB200 |
| Mega-Gate | MoE router gate GEMM, expert scoring, bias, top-k | 1.18 to 1.31x kernel speedup at medium batch sizes |
| MegaAttention with NVFP4 KV | Query RoPE, sparse attention, inverse RoPE, FP8 cast in one launch | KV 45% smaller than FP8, 1.45x kernel efficiency |
| Sparse MQA logits (DeepGEMM) | Scores only the 16K candidate positions | Per layer 1.2x at 8K and 14 to 23x at 512K; end-to-end decode only 3 to 6% on 4x GB300 |
| Low-latency fused WO-A | Inverse RoPE, FP8 quantisation, WO-A batch GEMM, MXFP8 requantisation | Up to about 2.1x fused path; up to 6 to 7% lower inter-token latency at low concurrency |
| mHC multistream overlap | Next block's coefficients computed on a side CUDA stream at small batch | About 4% lower latency in the TP4 low-latency setup |
Read the sparse MQA row twice. A 14 to 23x layer speedup at 512K context sounds like a revolution, and it turns into a 3 to 6 per cent decode gain end to end, because the layer is a small share of a decode step. The honest summary of this table is that no single kernel produced 5x. The 5x is a chart in the post, with the underlying data not given in the text, and it compounds a smaller KV footprint, trimmed layers, graphs and many small fusions.
The configuration split is the part that generalises. For low latency, vLLM used TP4 with FlashInfer attention; it tried MegaAttention there, but the gain was small and FlashInfer was faster. For high throughput it used DEP2, data-parallel attention with experts split across GPUs, because DP avoids replicating the shared KV latent that TP would duplicate, and session affinity keeps prefix-cache locality across turns. NVFP4 KV nearly halves per-request KV against FP8, which raises concurrency per GPU.
SGLang: a ladder from 35 to 873 tokens per second
SGLang's post is a cumulative ladder on four GB300s with attention at TP4, and it is more useful than the headline because every rung has a number. Read it rung by rung and the title stops meaning what it seems to mean.
| Step | Change | Tokens per second |
|---|---|---|
| 1 | First working build (plain decode, batch size 1) | 35.2 |
| 2 | MXFP8 GEMM, scale layout rearranged at load time | 117.8 |
| 8 | mHC statistics overlap on two streams | 186.4 |
| 10 | GEMV, norm and Engram gate fusion (last plain-decode step) | 203.3 |
| 11 | DSpark speculative decoding on, EP4 (random 4k/1k, simulated accept length 5.5) | 558.2 |
| 12 | Verify and MoE fusion and overlap | 718.8 |
| 15 | C2 verify compression fusion | 853.5 |
| 16 | MoE TP4 with padding | 873.6 |
Step 2 is the lesson. Some dense FP8 weights had fallen back to a slower path because their block and scale layout did not match the fast kernel, and fixing the layout took the build from 35.2 to 117.8, about 3.3 times (my arithmetic from their table). The authors say checking which kernel a GEMM actually dispatches to was worth more than tile tuning. The rest of plain decode, steps 3 to 10, added another 1.7 times by fusing small operations and overlapping the mHC statistics on a second stream.
The jump from 203.3 to 558.2 at step 11 is speculative decoding switching on, not a kernel. DSpark uses a block size of 5 with three draft blocks, and the post's own benchmark table gives 223.50 tokens per second with it off and 853.49 with it on, both EP4. The last rung, 873.63, comes from moving MoE from EP4 to TP4 with the expert intermediate dimension padded from 576 to 640 per GPU, a same-round gain of 2.22 per cent. Most of the 25x in the title is therefore speculation with a simulated accept length.
Configs you can actually run
Only SGLang published a full launch command. The vLLM post gives one flag and describes TP4 and DEP2 in prose, so the vLLM block below uses the model card's own serve command and marks everything else as a gap rather than inventing flags. The SGLang block is copied from the post's reproduction section, with the environment variables it lists.
# vLLM: not published as commands. The post names the shapes only:
# low latency TP4 + FlashInfer attention
# high throughput DEP2 (DP attention, experts split across GPUs) + session affinity
# Check your installed version's docs for the exact flags before copying anyone's.
# SGLang: reproduction block from the post (4x GB300, attention TP4).
export MODEL_PATH=/path/to/DeepSeek-V4.1-Flash
export SGLANG_RAGGED_VERIFY_MODE=static
export SGLANG_SIMULATE_ACC_LEN=5.5 # simulated accept length, NOT measured
export SGLANG_SIMULATE_ACC_METHOD=match-expected
CUDA_VISIBLE_DEVICES=0,1,2,3 PYTHONPATH="$PWD/python" MAX_JOBS=16 python -m sglang.launch_server --model-path "$MODEL_PATH" --served-model-name deepseek-ai/DeepSeek-V4.1-Flash --tp 4 --ep-size 1 --trust-remote-code --moe-a2a-backend none --moe-runner-backend flashinfer_mxfp4 --mem-fraction-static 0.80 --max-total-tokens 33554432 --chunked-prefill-size 4096 --cuda-graph-bs-decode 1 2 4 8 16 32 64 --max-running-requests 128 --speculative-algorithm DSPARK --speculative-dspark-block-size 5 --skip-server-warmup --reasoning-parser deepseek-v41 --random-seed 42 --decode-log-interval 10 --host 127.0.0.1 --port 30021
# EP4 comparison in the post: change --ep-size 1 to --ep-size 4.Notice what the SGLang command pins: the random seed, 33,554,432 max total tokens, chunked prefill of 4,096, CUDA graphs for decode batch sizes 1 to 64, and a simulated accept length through environment variables. Remove the simulation and the 873 figure is no longer the one you will see. The post also states that none of its results use the new kernels DeepSeek released with V4.1, so the two posts are not even optimising the same kernel set.
Both engines assume Blackwell. The vLLM kernel numbers are on GB200 and GB300 and the SGLang ladder is on 4x GB300. If you serve on other hardware, treat every multiplier here as a direction to test, not a number to promise.
Where the numbers break
Four things in the sources should stop you from copying a conclusion. First, the vLLM page disagrees with itself: the TL;DR says 5.3x under a 150 TPS constraint while the body and figure say about 5x, and the constraint is never restated in the body. Second, the 1.9x and 5x figures exist only in a chart, so I cannot check them against text. Third, SGLang's final results table lists one fewer accept-length value than it has columns, so the final accept lengths are ambiguous. Fourth, SGLang throughput excludes full prefill and is measured from first to last streamed event.
The operational risks are separate. Bounded replay makes outputs non-reproducible against an exact baseline. The Engram layers rely on transparent huge pages for up to 10 times faster lookup kernels in prefill, and vLLM added a fallback for systems that lack enough huge pages, which means a host with the wrong memory configuration quietly takes the slow path. SGLang's DSpark numbers assume an accept length that real traffic may not reach: the ceiling is 6, and your prompts decide the real value.
My own mistake while reading is a useful warning. I first took 35 to 873 as one engine getting 25 times faster at serving. It is a single stream, with speculation, on simulated acceptance, against a first build that had a layout bug. Every one of those qualifiers was on the page; none was in the title.
What to do on Monday
You do not need GB300s to use what these posts teach. Do the following, in order, on whatever you serve.
- Write down your own workload shape before any tuning: median input, median output, prefix-cache hit rate and turns per session. AgentX publishes those four for its traces; yours decide whether decode or prefill and cache reuse matter.
- Confirm which kernel your biggest GEMMs dispatch to. SGLang's 3.3x came from a layout mismatch, not from tuning.
- Put the KV footprint per token in your capacity sheet and recompute concurrency per GPU. A smaller KV cache buys concurrency before it buys speed.
- Report two configs, not one: a low-latency one and a high-throughput one, as vLLM did with TP4 and DEP2.
- Run any bounded or approximate optimisation against your own evaluation set with the flag on and off before turning it on in production.
# bench-card.yaml: fill this in before you quote any serving number.
engine: vllm | sglang # exact version or commit
model: deepseek-ai/DeepSeek-V4.1-Flash
hardware: 4x GB300 # chip, count, interconnect
parallelism: TP4 | DEP2 | EP4
kv_dtype: fp8 | nvfp4
speculation: off | dspark # if on: simulated or measured accept length?
approximations: swa-bounded-replay on | off
workload:
input_tokens: 4096 # SGLang: random ids, seed 42, no chat template
output_tokens: 1024 # temperature 0, ignore_eos true
concurrency: 1 # or the session count for an agentic trace
prefix_cache: cleared before each run
runs: 6 # one warm-up discarded; report the median
metric: decode tokens/s | TPGS at P90 interactivity above 50 tok/s
excludes: full prefill? # SGLang measures first to last streamed event
quality_checked: yes | no # no means the speed number is unverifiedThe card above is my template for recording a comparison. It copies the fields SGLang pinned, because a result without them cannot be reproduced and cannot be compared with the next post that appears.
A serving speedup is a claim about a workload, a baseline and a metric, and only then about an engine. vLLM and SGLang both got DeepSeek-V4.1-Flash fast in three weeks by the same boring route: make the KV cache small, skip the layers you can, fuse the small kernels, and check what actually dispatches. Copy the route, not the number.

