Concurrency (max_num_seqs): how many requests decode at once

Result. On 2x DGX Spark (GB10, TP=2), 16 seats decoded +25% more tokens per second in total than 8 seats, each stream ran 34% slower, and on re-sent 15k-token prompts the warm prefix-hit rate fell from 73% to 11%. 8 was kept on 2026-09-30. On an RTX 5090 node, aggregate decode keeps climbing to 9–12 seats.

What the setting is

max_num_seqs = seats: the most requests the scheduler keeps in one running batch. Request 9 on an 8-seat engine waits in the queue, even with KV memory free.

Measured: 8 vs 16 seats on real prompts

2x DGX Spark, vLLM, Qwen3.8 Flash-Next (512-expert MoE, NVFP4), prefill budget 8,192, 4 speculative tokens. 100 real user prompts with ~2k tokens of real context each, 512 out, 32 clients. 3 reps per setting, engine argv verified at launch, 0 errors, 0 preemptions.

Decode tok/s vs seats. 2x DGX Spark, vLLM: 8 seats 147.0 aggregate and 20.3 per stream; 16 seats 183.6 aggregate (+25%) and 13.4 per stream (-34%). RTX 5090 node, vLLM, production log: aggregate 60, 102, 165, 280, 375 tok/s at 1, 2, 3-4, 5-8, 9-12 seats running 2x DGX Spark · vLLM · Flash-Next MoE · 2026-09-30 (2 px per tok/s) 8 seats, aggregate 147.0 16 seats, aggregate 183.6 +25% 8 seats, per stream 20.3 16 seats, per stream 13.4 −34% RTX 5090 node · vLLM · 27B NVFP4 · aggregate by seats running (1 px per tok/s) 1 seat 60 · 60 / stream 2 seats 102 · 51 / stream 3–4 seats 165 · ~47 / stream 5–8 seats 280 · ~43 / stream 9–12 seats 375 · ~36 / stream 5090 per-stream = aggregate ÷ bucket midpoint (1, 2, 3.5, 6.5, 10.5), computed. Spark rows: 3-rep means.
Top: controlled A/B, 2026-09-30. Bottom: production perf log bucketed by running requests, read 2026-10-16. Different models and engines; compare within a panel.
2x DGX Spark, 3-rep mean8 seats16 seatsΔ
Aggregate decode, tok/s147.0183.6+25%
Per-stream decode, tok/s20.313.4−34%
Time to first token, s (32 clients queued)71.940.7−43%
Wall time, 100 requests, s348.4278.8−20%
KV pool used, peak16%31%×2
Warm re-run of 48 × ~15k prompts: prefix hits73%11%
Warm re-run: wall time, s179.6387.6×2.16

TTFT falls at 16 because 32 clients drain through twice the seats, not because a step got faster. Synthetic 2k-token prompts the day before gave 16 seats 1.17x (233.5 → 272.5 tok/s); real prompts gave 1.25x.

Why 8 was kept

Seats vs the KV pool

Seats are a ceiling, not a reservation. vLLM allocates KV blocks as tokens arrive. If seated requests outgrow the pool, vLLM’s V1 scheduler preempts one and recomputes it later (general vLLM behaviour; 0 preemptions in every run on this page). The panel’s question is whether seats × typical context fits the KV pool:

Model, engine, 2x DGX SparkSeatsPoolFull contexts the pool holdsResult
Flash-Next MoE, vLLM, 28 GB pin6—6.52 at 262,144 (vLLM’s own log line)default since 2026-10-05: memory-safe
Flash-Next MoE, vLLM24——rests at 1.7 GiB free idle, inside the 0.8–4.2 GiB band that hung both boxes; deferred
Flash-Next MoE, SGLang61,720,0006.6 at 262,14449.0 tok/s
Flash-Next MoE, SGLang161,482,3045.796.2 tok/s
Flash-Next MoE, SGLang321,140,2884.3588.8 tok/s (−7.7%)
DeepSeek-V4-Flash NVFP4, vLLM8181,7771.39 at 131,072serving; 8 seats fit only if requests average ≤ 22,722 tokens

SGLang rows: 15-min deep-research swarms, 2026-10-05. On that hybrid model every seat reserves ten linear-attention state slots, so the token pool shrinks as seats grow. 32 seats bought queueing and cost cache. Full contexts = pool ÷ window, computed. DeepSeek pool: generation 235, 2026-10-24.

Seats also size vLLM’s CUDA graphs: the largest captured batch is derived as min(seats × decode query length × 2, 512). Raising seats spends graph memory, not KV.

When more seats do not add throughput

Clean-decode windows on the Spark pair do scale with seats: SGLang with the dense 27B and its DFlash2 drafter went 175 tok/s at 8 seats to 290–366 at 25–28 (2026-10-16). The 4-day production log, bucketed by running requests, stays flat at 175–192, and vLLM on the same pair is flat too (127 / 130 / 126 tok/s at 5–8 / 17–24 / 25+). The cause is prefill: its share of wall time grows from 17% to 45% as seats fill, and prefill on GB10 TP=2 costs ~0.49 ms per prompt token. The RTX 5090 node computes prompts at ~5k tok/s, 5–10x cheaper per token, so its seats turn into throughput. See prefill budget.

Defaults in the panel (2026-10-24)

Model on 2x DGX SparkEngineSeatsBasis
Qwen3.8 Flash-Next MoEvLLM6what the 28 GB pin holds at 262,144; throughput at 6 not measured
Qwen3.8 Flash-Next MoESGLang164-point sweep, 2026-10-05
Qwen3.8-27B dense + DFlash2SGLang3298.4 tok/s over 13,210 requests, 2026-10-03/04
DeepSeek-V4-Flash NVFP4vLLM8recipe value; not swept

Rule of thumb from these runs: raise seats while decode is bandwidth-bound and the pool still holds seats × typical context plus the prefixes you want cached. On the Spark pair that limit came at 8 for vLLM on long contexts; on the RTX 5090 node it had not come by 12. Related: context length, speculative tokens, memory budget.

Questions? Let's Chat :)

vllm settings