MTP vs DFlash: vLLM and SGLang on Qwen3.8-27B GPTQ-4bit with dual RTX 3090s
Executive summary
This report compares four speculative-decoding configurations on the same Qwen3.8-27B GPTQ-4bit target and dual-RTX-3090 host: vLLM with DFlash, SGLang with DFlash, vLLM with native MTP, and SGLang with native MTP.
For the matched single-request workload, DFlash delivered the highest decode throughput on both engines. vLLM DFlash reached 109.36 tok/s; SGLang DFlash reached 97.54 tok/s. Native MTP reached 92.78 tok/s on vLLM and 86.98 tok/s on SGLang.
- On vLLM, DFlash was 17.87% faster than MTP.
- On SGLang, DFlash was 12.14% faster than MTP.
- Within DFlash, vLLM was 12.12% faster than SGLang.
- Within MTP, vLLM was 6.67% faster than SGLang.
- SGLang delivered the lowest TTFT: 67.40 ms with DFlash and 69.30 ms with MTP.
Recommendation: use vLLM plus DFlash for maximum single-stream decode and higher offered concurrency. Use SGLang plus DFlash when low-concurrency TTFT is the priority and the workload can remain within SGLang's active-request cache limit. MTP is operational on both engines, but it did not beat DFlash in this workload.
Test setup
| Field | Value |
|---|---|
| Target model | btbtyler09/Qwen3.8-27B-GPTQ-4bit |
| Target quantization | GPTQ, 4-bit |
| DFlash draft | z-lab/Qwen3.8-27B-DFlash2 |
| MTP draft | Native MTP weights embedded in the target checkpoint |
| GPUs | 2 × NVIDIA GeForce RTX 3090, 24 GiB each |
| Tensor parallelism | 2 |
| CPU | AMD Ryzen 9 9950X |
| Host interconnect | PCIe/PHB; no NVLink path |
| Context limit | 8,192 tokens |
| Prompt | 1,044 tokens |
| Output | 512 tokens per request |
| Concurrency | 1 for the four-way comparison |
| Warmups | 2 |
| Measured iterations | 5 |
| Sampling | Temperature 0 |
| Streaming | Enabled |
| Measurement | localmaxxing-cli remote endpoint mode |
| Decode metric | Client-observed inter-token throughput |
| TTFT metric | Client-observed first streamed token |
DFlash used four speculative draft tokens, selected by a prior vLLM sweep. MTP used two speculative tokens, matching the checkpoint's validated native-MTP configuration. The external request and measurement path are matched; the speculative methods necessarily perform different internal work.
Four-way single-request results
All values are arithmetic means over five measured iterations.
| Engine | Method | Draft tokens | Mean decode | Mean TTFT | Mean total throughput | Result |
|---|---|---|---|---|---|---|
| vLLM | DFlash | 4 | 109.36 tok/s | 90.51 ms | 326.64 tok/s | Fastest decode |
| SGLang | DFlash | 4 | 97.54 tok/s | 67.40 ms | 293.20 tok/s | Lowest TTFT |
| vLLM | MTP | 2 | 92.78 tok/s | 86.27 ms | 278.14 tok/s | Fastest MTP |
| SGLang | MTP | 2 | 86.98 tok/s | 69.30 ms | 261.70 tok/s | Lowest MTP TTFT |
Decode ranking
- vLLM + DFlash: 109.36 tok/s
- SGLang + DFlash: 97.54 tok/s
- vLLM + MTP: 92.78 tok/s
- SGLang + MTP: 86.98 tok/s
DFlash wins within each engine. The engine gap is smaller with MTP: vLLM leads SGLang by 6.67% with MTP, versus 12.12% with DFlash.
Method comparison within each engine
| Engine | DFlash | MTP | DFlash advantage | TTFT comparison |
|---|---|---|---|---|
| vLLM | 109.36 tok/s | 92.78 tok/s | +17.87% | MTP is 4.68% lower: 86.27 vs 90.51 ms |
| SGLang | 97.54 tok/s | 86.98 tok/s | +12.14% | DFlash is 2.74% lower: 67.40 vs 69.30 ms |
MTP modestly improved vLLM TTFT, but the 4.24 ms reduction did not offset its 16.58 tok/s decode loss. On SGLang, DFlash won both decode throughput and TTFT.
Engine comparison within each method
| Method | vLLM decode | SGLang decode | Decode winner | vLLM TTFT | SGLang TTFT | TTFT winner |
|---|---|---|---|---|---|---|
| DFlash | 109.36 tok/s | 97.54 tok/s | vLLM, +12.12% | 90.51 ms | 67.40 ms | SGLang, 25.53% lower |
| MTP | 92.78 tok/s | 86.98 tok/s | vLLM, +6.67% | 86.27 ms | 69.30 ms | SGLang, 19.67% lower |
The same engine tradeoff appears under both methods: vLLM maximizes decode throughput, while SGLang returns the first streamed token sooner.
Measurement stability
| Engine | Method | Samples | Decode min / P50 / mean / max | Decode stddev | TTFT min / P50 / mean / max | TTFT stddev |
|---|---|---|---|---|---|---|
| vLLM | DFlash | 5 | 109.30 / 109.40 / 109.36 / 109.40 tok/s | 0.05 | 89.99 / 90.61 / 90.51 / 90.75 ms | 0.31 ms |
| SGLang | DFlash | 5 | 97.40 / 97.50 / 97.54 / 97.80 tok/s | 0.15 | 67.18 / 67.26 / 67.40 / 68.02 ms | 0.35 ms |
| vLLM | MTP | 5 | 92.70 / 92.80 / 92.78 / 92.80 tok/s | 0.04 | 85.40 / 86.44 / 86.27 / 86.72 ms | 0.54 ms |
| SGLang | MTP | 5 | 86.90 / 86.90 / 86.98 / 87.10 tok/s | 0.11 | 68.45 / 69.23 / 69.30 / 70.10 ms | 0.63 ms |
All four single-request results were stable. Decode standard deviation stayed at or below 0.15 tok/s, so the ranking is much larger than run-to-run variation.
Speculative acceptance evidence
| Engine | Method | Observed behavior |
|---|---|---|
| vLLM | DFlash | Mean acceptance lengths of approximately 2.66–2.93 in sampled high-concurrency windows; average draft acceptance approximately 41–48% |
| SGLang | DFlash | Sampled acceptance lengths approximately 1.98–3.25; sampled acceptance rates approximately 33–75% |
| vLLM | MTP | Mean acceptance length approximately 2.20–2.26; average draft acceptance approximately 60–63% |
| SGLang | MTP | Sampled acceptance lengths approximately 1.85–2.50; sampled acceptance rates approximately 42–75% |
These engine logs use different aggregation windows and rate definitions. They prove that each speculative path was active, but they are not a precise cross-engine acceptance benchmark.
MTP's higher reported acceptance percentage does not imply higher end-to-end speed. DFlash still wins because acceptance is only one term in the cost equation: draft-model execution, target verification, graph capture, memory movement, and tensor-parallel communication also contribute.
SGLang MTP correctness issue found during testing
The first SGLang MTP attempt was rejected from the comparison. Although the server command explicitly selected an unquantized MTP draft, Qwen3.5's MTP quantization resolver inherited GPTQ from the target. Seven MTP projection tensors were skipped during load, acceptance stayed at zero, and decode fell to approximately 42.3 tok/s.
The resolver was fixed to honor:
--speculative-draft-model-quantization unquant
After the fix:
- Embedded BF16 MTP tensors loaded without missing-parameter warnings.
- Decode and target-verification CUDA graphs were active.
- Acceptance recovered to approximately 1.85–2.50 tokens in sampled windows.
- SGLang MTP reached the valid 86.98 tok/s result reported above.
- The focused SGLang unit suite completed with 27 passing tests.
The invalid 42.3 tok/s attempt is not included in any comparison or ranking.
DFlash concurrency context
Only concurrency one was measured for MTP in this comparison. The existing DFlash sweep additionally measured concurrency 2, 4, 8, and 16:
| Concurrency | vLLM DFlash | SGLang DFlash | vLLM mean TTFT | SGLang mean TTFT |
|---|---|---|---|---|
| 1 | 109.36 tok/s | 97.54 tok/s | 90.51 ms | 67.40 ms |
| 2 | 176.27 tok/s | 164.80 tok/s | 152.99 ms | 100.19 ms |
| 4 | 266.53 tok/s | 294.13 tok/s | 265.78 ms | 104.27 ms |
| 8 | 360.67 tok/s | 268.83 tok/s | 473.80 ms | 3,401.67 ms |
| 16 | 430.67 tok/s | 258.67 tok/s | 902.61 ms | 10,011.75 ms |
SGLang DFlash peaked at concurrency four. Its Mamba state cache admitted five live requests, so offered concurrency 8 and 16 queued in waves. vLLM DFlash continued scaling through concurrency 16. No equivalent MTP concurrency sweep was run, so this section must not be read as a high-concurrency MTP comparison.
Reproduction commands
The common Localmaxxing workload was:
lmx speed-test run <vllm|sglang> \
--mode remote \
--base-url <endpoint> \
--hf-id btbtyler09/Qwen3.8-27B-GPTQ-4bit \
--served-model <served-alias> \
--quantization GPTQ-4bit \
--hardware hardware.json \
--spec-method <dflash|mtp> \
--spec-num-tokens <4|2> \
--prompt-tokens 1044 \
--max-tokens 512 \
--concurrency 1 \
--warmup 2 \
--iterations 5 \
--out <payload.json>
DFlash additionally supplied:
--spec-draft-model z-lab/Qwen3.8-27B-DFlash2
The corrected SGLang MTP server configuration used:
python -m sglang.launch_server \
--model-path /models/Qwen3.8-27B-GPTQ-4bit \
--tp-size 2 \
--context-length 8192 \
--mem-fraction-static 0.87 \
--max-running-requests 4 \
--disable-custom-all-reduce \
--attention-backend fa3 \
--cuda-graph-backend-prefill disabled \
--speculative-algorithm NEXTN \
--speculative-num-steps 2 \
--speculative-eagle-topk 1 \
--speculative-num-draft-tokens 3 \
--speculative-draft-model-quantization unquant \
--mamba-scheduler-strategy extra_buffer
The vLLM MTP server used:
vllm serve /models/Qwen3.8-27B-GPTQ-4bit \
--tensor-parallel-size 2 \
--max-model-len 8192 \
--max-num-seqs 4 \
--gpu-memory-utilization 0.90 \
--enable-prefix-caching \
--speculative-config \
'{"method":"qwen3_next_mtp","num_speculative_tokens":2}'
vLLM normalized the deprecated qwen3_next_mtp spelling to mtp at startup.
Artifacts and validation
The new MTP payloads are:
qwen38-q4-mtp-vllm-k2-c1.jsonqwen38-q4-mtp-sglang-k2-c1.json
Both passed the Localmaxxing API dry-run validator with specDecoding: true, canonical specMethod: "MTP", and specNumTokens: 2. The DFlash values come from the previously validated and published matched-concurrency sweep. No benchmark payload was submitted as part of this comparison.
Conclusion
DFlash is the performance winner for this Q4 target on dual RTX 3090s. It leads native MTP by 17.87% on vLLM and 12.14% on SGLang under the matched single-request workload.
vLLM is the decode-throughput winner under either speculative method. It reaches 109.36 tok/s with DFlash and 92.78 tok/s with MTP. Its DFlash implementation also scales to 430.67 tok/s at concurrency 16 on this host.
SGLang is the latency winner at low concurrency. Its TTFT is 67.40 ms with DFlash and 69.30 ms with MTP, lower than the corresponding vLLM results. SGLang DFlash is particularly strong at concurrency four, but its five-request Mamba-cache cap causes severe queueing beyond that point.
For this model and hardware, choose vLLM + DFlash for peak decode and load scaling, or SGLang + DFlash for tightly bounded low-concurrency latency. Use MTP when avoiding a separate draft checkpoint is operationally valuable; it works correctly after the SGLang quantization fix, but it is not the fastest option measured here.
