Hardware: Dell Pro Max GB10 (DGX Spark class, Grace-Blackwell sm_121, ~121.6 GiB
unified LPDDR5X). Server: vLLM cu130-nightly (0.19.2rc1.dev134), NVFP4 weights,
MoE on Marlin, attention TRITON_ATTN, KV fp8, single GPU (TP/PP/DP=1).
Load tool: guidellm 0.6.0, concurrent profile, chat shape (prompt≈512 / output≈256),
180 s per concurrency level. Metric = output (decode) tokens/sec, mean of successful
requests; "engine peak" = vLLM's own Avg generation throughput log at saturation.
The MTP head reuses a single draft layer per position, so acceptance decays fast at the real serving temperature:
| draft position | acceptance @ temp 1.0 |
|---|---|
| pos 0 (1st draft) | ~67% |
| pos 1 (2nd draft) | ~36% |
| pos 2 (3rd draft) | ~16% |
| config | single-stream tok/s (temp 1.0) |
|---|---|
| no MTP (baseline) | ~15–17 |
MTP k=3 (NVIDIA guide default) |
~23.5 |
MTP k=2 (chosen) |
~24.2 |
k=2 is as fast as k=3 and cheaper (one fewer draft forward/step, less draft KV →
better under concurrency). The 3rd draft token (16% accepted) doesn't earn its forward.
Note: greedy/temp=0 reads ~25 tok/s because deterministic sampling inflates acceptance;
use the temp-1.0 number for real expectations.
Saturated aggregate output throughput vs the server-side concurrency cap:
| max_num_seqs | sat. out tok/s | engine peak | KV @ sat | TTFT @ sat | step |
|---|---|---|---|---|---|
| 4 (baseline, sched-tokens 2048) | 47.7 | ~50 | 7.6% | 1.2 s | — |
| 8 | 73.5 | ~82 | 16.6% | 6.6 s | +54% |
| 16 | 99.6 | ~117 | 33.1% | 7.7 s | +36% |
| 32 | 131.3 | 176 | 68.9% | 9.1 s | +32% |
~2.75× aggregate throughput from raising max_num_seqs alone (47.7 → 131 tok/s).
Key reads:
- The bottleneck at the default
max_num_seqs=4was the scheduler cap, not GPU resources — KV cache sat at 7.6% with the GPU ~92% idle on memory. - We are KV-bound, not compute-bound, at 32: engine peak still grew 1.5× from 16→32,
but KV is at 69%, so
maxseq=48(~103% KV) won't fit at this context length. 32 is the practical ceiling for the chat shape. Longer contexts (e.g. coder 4k+) bind KV much sooner (~5–6 concurrent). - Cost is latency: TTFT climbs to ~9 s at saturation — fine for batch/async fan-out, not
for snappy interactive use. Pick
max_num_seqsto your latency SLO, not the max.
| client conc → | 1 | 4 | 8 | 16 | 32 | 64 |
|---|---|---|---|---|---|---|
| maxseq=4 | 23.9 | 47.7 | 46.1 | 45.8 | 45.7 | — |
| maxseq=8 | 24.3 | 46.3 | 73.5 | 71.8 | 71.9 | — |
| maxseq=16 | 23.9 | 48.0 | 74.7 | 99.6 | 91.4 | — |
| maxseq=32 | 23.6 | 47.1 | 67.0 | 99.4 | 131.3 | 136.3 |
Throughput flattens once client concurrency exceeds the server's max_num_seqs (extra
requests queue → TTFT rises, throughput flat). Bold = each config's saturation point.
Does pure batching beat batch+speculation once the GPU is saturated? No — MTP wins.
| config | sustained tok/s | engine peak | KV @ sat | single-stream | TTFT @ sat |
|---|---|---|---|---|---|
| MTP on, maxseq=32 | 136 | 176 | 69% | 24 | 9 s |
| MTP off, maxseq=32 | 96–109 | 134 | 18% | 18 | 12 s |
| MTP off, maxseq=64 | 121 | 192 | 36% | 18 | 15 s |
- The GB10 is memory-bandwidth-bound on decode, so MTP's "more tokens per weight-load" advantage survives batching — MTP-on sustains more than MTP-off even at full occupancy.
- MTP-off briefly peaks higher at maxseq=64 (192 vs 176) thanks to 3.8× better KV efficiency (MTP-on burns 2.15%/seq vs 0.57%/seq for its draft/lookahead slots — which is why MTP-on is KV-capped at ~32), but its sustained throughput is lower and latency is far worse. Not worth it.
- Verdict: keep MTP on (k=2). Best single-stream AND best sustained aggregate throughput.
Enabled --enable-prefix-caching (vLLM auto-sets Mamba cache mode 'all', flagged
experimental). Drove a real shared-prefix load: identical ~900-token system prompt
(SYSTEM.md) + varied user questions, then the exact same request repeated 3×.
| metric | result |
|---|---|
vllm:prefix_cache_hits_total |
0 (across 9,317 queries) |
engine Prefix cache hit rate |
0.0% |
| TTFT, identical request repeated 3× | flat ~690 ms (no improvement) |
Zero cache hits, even for byte-identical repeated prompts. Hybrid-Mamba prefix caching is non-functional/ineffective for Nemotron-3-Super on this nightly — it boots without crashing but provides no reuse. Not a usable lever today. (Watch for it to mature; the recurrent Mamba state is the hard part to prefix-share.)
- Throughput-max (batch / agent fan-out via LiteLLM): MTP k=2 +
--max-num-seqs 32--max-num-batched-tokens 8192→ ~136 tok/s sustained (2.85× the original baseline), TTFT ~9 s under load.
- Latency-sensitive (interactive): MTP k=2 +
--max-num-seqs 4→ ~24 tok/s single-stream, ~47 tok/s aggregate, TTFT ~1.2 s. - Prefix caching: skip (0% hits). MTP-off: skip (MTP-on is strictly better here).