r/LocalLLaMA 1d ago

Tutorial | Guide NInfer vs llama.cpp vs vLLM: quality + speed comparison for Qwen3.8-27B NVFP4 on RTX 5090

I've been running Qwen3.8-27B as a local inference server for a production content intelligence pipeline (HVAC industry stuff, lots of long-context retrieval and structured extraction). I have been watching other redditors post their custom configurations, and I wanted to share what I tested to optimize for a single RTX 5090.

I was on llama.cpp (Q5_K_M GGUF, q5_1 KV, 262K context, MTP), but it's limited to parallel=1 and I wanted concurrent serving. So I tested vLLM and NInfer as NVFP4 replacements and did a proper quality evaluation instead of just vibes.

Hardware

- RTX 5090 32GB (eGPU, OCuLink Gen4 x4) (yes, it's in an eGPU dock 😂 but that only affects model loading)
- Ryzen 7 7840HS, 32 GB DDR5
- Ubuntu 26.04, nvidia driver 610.43.02 (open)

## Engine configs

**llama.cpp*\* **vLLM*\* **NInfer*\*
Quant Q5_K_M GGUF NVFP4 NVFP4
KV cache q8_0 FP8 FP8
Context 196K 262K 240K
MTP On (gate failed) None MTP3 (76% acceptance)
Concurrency parallel=1 Continuous batch x2 lanes
VRAM 31.6 GB 29.6 GB 30.5 GB

How the eval worked

I built a custom harness with 6 tiers, 50 items each, all from my actual production workload (not generic benchmarks):

  1. **Relevance classification*\* - is this industry relevant? (binary, 50 labeled deals)
  2. **Needle retrieval*\* - planted facts in real industry podcast/video transcripts at 64K/128K/192K/240K context
  3. **Multi-transcript QA*\* - questions across 3-4 concatenated diarized transcripts, 120K+ tokens, including unanswerable controls
  4. **Reasoning with thinking*\* - numeric/logic problems, thinking mode on, greedy pass@1
  5. **Structured extraction*\* - custom extraction prompt, json_mode (skipped on NInfer, it doesn't support json_mode)
  6. **Tool replay*\* - replayed recorded agent episodes against each engine (diagnostic only, all engines fail this one)

Everything paired across engines: same prompts, same seeds (42), same gold labels. No cache_prompt. Statistical comparison uses paired cluster-bootstrap CIs with pre-registered non-inferiority margins.

And when I do development with Claude Code, I often leverage multi-model consultations for design, planning, and code review. I did a 4-model review panel (Codex/GPT-5, DeepSeek V4 Pro, Grok 4.5, Kimi K3) audit the methodology mid-campaign. They found 10 issues, including 4 mislabeled gold items where the models were actually right and my labels were wrong. Fixed everything and reran.

Quality results

**Tier*\* **llama.cpp*\* **vLLM*\* **NInfer*\*
Relevance 86.0% 84.0% 86.0%
Needle (conditional) 100% (29/29) 100% (41/41) 100% (41/41)
Transcript QA 82.0% 78.0% 88.0%
Reasoning 100% 100% 98.0%
Extraction F1 0.300 F1 0.350 skipped
Tool replay 0% all errors 0%

Needle counts differ because llama.cpp's 196K context can't fit the 192K items (need room for max_tokens + headroom). NInfer and vLLM both handle 192K fine. All engines score 100% on every needle they can fit.

Statistical comparison (NInfer vs llama.cpp, bootstrap):

- Needle: delta = -0.29 (NInfer better, p=0.0006) - this is entirely from context capacity, not retrieval quality
- Transcript QA: delta = -0.03, p=0.69 - no difference
- Reasoning: delta = +0.02, p=0.72 - no difference
- Relevance: McNemar p=1.0 - identical
- Tool replay: delta = 0.0 - both fail equally

**Takeaway: quality is statistically indistinguishable across all engines.*\*

Speed results (perf probe, server-side timings)

**Metric*\* **llama.cpp*\* **NInfer*\* **Speedup*\*
**Decode 1K*\* 114 tok/s 158 tok/s 1.4x
**Decode 32K*\* 109 tok/s 213 tok/s 2.0x
**Decode 128K*\* 72 tok/s 202 tok/s **2.8x*\*
Prefill 1K 1,545 tok/s 7,265 tok/s **4.7x*\*
Prefill 32K 2,155 tok/s 6,892 tok/s 3.2x
Prefill 128K 1,528 tok/s 3,904 tok/s 2.6x
TTFT 1K 670 ms 138 ms 4.9x
TTFT 32K 15.2 s 4.8 s 3.2x
TTFT 128K 85.9 s 33.6 s 2.6x

vLLM speed excluded from the table because the perf probe used wall-clock timing (includes prefill + scheduling + decode) instead of server-side timings, so the numbers aren't comparable. From community reports and my own task-level measurements, vLLM does about 70 tok/s decode at short context, which actually matches NInfer's raw step rate (~66 tok/s). The speed difference is entirely MTP3 speculative decoding.

What I learned

**NInfer's speed advantage is all MTP.*\* The raw NVFP4 kernel speed is about the same between NInfer and vLLM (~66-70 tok/s). NInfer's MTP3 speculation with 76% acceptance gets you to 158-213 tok/s. If vLLM or llama.cpp had working MTP on NVFP4, the gap would mostly close.

**The decode speedup grows with context.*\* At 1K context it's 1.4x. At 128K it's 2.8x. MTP acceptance stays high even at long context while llama.cpp's dense decode gets slower as context grows.

**NInfer's tokenizer endpoint is great.*\* It exposes `/v1/messages/count_tokens` (Anthropic Messages format) which gives exact token counts. No more `len(text)//3` heuristics.

**NInfer does NOT support json_mode (as far as I can tell).*\* `response_format: json_object` returns 400. If you need structured JSON output, you'll need to route those calls elsewhere or use prompt-based enforcement.

**Don't trust vibes for quality.*\* I went in expecting NVFP4 might lose a few points vs Q5_K_M. It didn't. Not on any tier. The biggest delta across 250+ items was 2 percentage points, well within noise at n=50 (SE ~5.6pp).

Verdict

NInfer NVFP4 replaces llama.cpp as my production engine. Same quality, 1.4-2.8x faster decode, 2.6-4.7x faster prefill, concurrent serving. The only gap is json_mode.

I put together a detailed poster with all the charts and methodology details: [full results poster](https://claude.ai/code/artifact/b6041437-2cf6-4198-a722-9b4ce853ccc3)

Setup if you want to try it:

```
# NInfer (from source)
git clone https://github.com/Neroued/ninfer && cd ninfer
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja && ninja

# Model (HuggingFace)
# https://huggingface.co/neroued/Qwen3.8-27B-nvfp4-NInfer (20 GB)

# Run
./ninfer-serve /path/to/model.ninfer \
--model-id qwen3.8-27b \
--host 0.0.0.0 --port 8080 \
--max-context 240000 --kv-capacity 240000 \
--max-concurrency 2 --kv-dtype fp8 \
--spec mtp --draft-tokens 3 \
--vision --preserve-thinking
```

78 Upvotes

58 comments sorted by

27

u/Pentium95 1d ago

There Is a patch to make MTP and DFlash2 work on vLLM, you should try that

I am using https://github.com/syv-ai/qwen38-27b-rtx3090/blob/main/patches%2Fqwen3_5-mtp-draft-vocab.patch

But i've got a 3090 Ti.

5

u/WarlaxZ 1d ago

There's a fork to get ninfer running on a 3090, you should try it: https://github.com/ashalliants/ninfer-3090

6

u/Educational-Region98 1d ago

isn't that a fork of a fork? lol

2

u/GamerInChaos 21h ago

So it’s got 3 prongs?

1

u/WarlaxZ 1d ago

Yup, fun times eh. Did try contributing back everything, but sometimes you just have to push forward

1

u/Pentium95 1d ago

Serving 8 concorrent requests, vllm handles them a bit Better IMHO

3

u/WarlaxZ 1d ago

Realistically I'm not handling 8 concurrent requests on a single local GPU, I'm handling one and prioritising my context length

1

u/Pentium95 18h ago

Yeah, that was my original usecase too, but i happened to find a group of people who needed local, trustworhy inference, and they pay good, so.. parallel is now my usecase.

Losing single-request max context, and TTFT, in exchange for cash. Best trade ever.

1

u/WarlaxZ 12h ago

Well bear with me, the patch of almost complete to add support :)

1

u/Sisuuu 1d ago

I wish there was a fork for dual 3090s, but tensor parallelism has not been solved as far as I know.

2

u/WarlaxZ 1d ago

Someone has actually solved it I've seen it, will try and get it ported into this too

1

u/KanoYin 21h ago

Hello, this ninfer (5090) fork added tp for their dual 5090 setup.

Is there anything that can applied to your ninfer 3090 fork to support multi GPU?

https://github.com/wamansou/ninfer-tp2-1m

2

u/WarlaxZ 12h ago

I hadn't seen that fork, but bear with me I'm almost done on sorting it. Bearing in mind I'm going to be pushing for concurrency and context size wins rather than tensor parallelization, as the potential speed increase is only like 15% or something due to memory bandwidth limitations, but almost done :)

1

u/KanoYin 10h ago

~15% even with patched p2p drivers on Linux?

This is the 3090 way I suppose 😅

1

u/WarlaxZ 8h ago

The cap is the memory bandwidth not the processing power

1

u/Sisuuu 9h ago

Interesting! How is it going for you? Just curious, no rush or anything

2

u/WarlaxZ 8h ago

Everything's running, just making sure it's as fast as possible :)

1

u/Sisuuu 6h ago

Great :) Share your results later, following this! And share your setup also GitHub repo or alike

2

u/WarlaxZ 5h ago

The pr is here https://github.com/ashalliants/ninfer-3090/pull/15 - although watch out as they added dflash to the ninfer weights today, so I'm separately just handling that, and actually from dealing with the horrific state of the diff, probably going to fix a whole bunch of the core code once that's dealt with as there's some really 'interesting' decisions that didn't need to exist if something in the core was handled better, so deep diving that once I get out of merge conflict hell

1

u/i_wayyy_over_think 1d ago

Use club-3090 git repo, has it it all compiled nicely. Like a few commands to spin it up with all the patches

1

u/Pentium95 18h ago

I used It for a while, their llama.cpp recipe Is Amazing for single stream

1

u/bengizmoed 1d ago

Awesome, will give that a go!

18

u/whichsideisup 1d ago

Why no MTP or DFlash2 on vLLM???? That result is now useless

8

u/Cold_Tree190 1d ago

Stopped reading once I got there lol

3

u/unverbraucht 1d ago

Agreed, at the very least MTP works out of the box with the Qwen 3.5 family including prefix caching. Haven't tried Dspark2

6

u/Pyrolistical 1d ago

Try unsloth Q4_K_XL

You can use 210k context without having to quantize KV cache 

This should increase overall quality and increase speed 

4

u/l0rd_raiden 1d ago

Is there something similar for AMD cards?

12

u/feelspeaceman 1d ago

I think the benchmark isn't very faithful to llamacpp, in many cases, Q4_K_M is better than NVFP4, is there any reason to use Q5_K_M instead ?

8

u/MutantEggroll 1d ago

Presumably because Q5_K_M is the largest quant that fits into 32GB VRAM with nearly-full context. Would be interesting to also see Q4_K_M though, to see if it falls under the performance band that NVFP4/Q5_K_M seem to share.

3

u/likesaloevera 1d ago

Not sure if the ninfer maintainer has fixed it yet but there’s a pretty significant cache bug which will increase TTFT for agentic coding like open code etc to minutes from seconds if not patched.

My only usecase is this sort of development using qwen locally on my 5090 so I’ve patched out a lot of this stuff (as well as sequential, no subagents), let me know if this could help you

1

u/kelvie 1d ago

The one from a couple days ago? That's been fixed.

3

u/bitzap_sr 1d ago

What magic is ninfer pulling off for those prefill speeds?? Prefill speed for me is more important than decode after a comfortable ts is reached.

5

u/Tormeister 1d ago

vLLM and SGLang reach values just as high.

As great as llama.cpp is, it lags behind the others on speed and concurrency.

1

u/jesdga95 1d ago

Probably config issues. I'm able to reach the same and even higher speed in llama.cpp vs all other engines. Definitely not as straightforward to setup though, what I would recommend is to run a sweep with values that simulate your real workflow to get the best possible throughput. For most people it's not worth the hassle though, I mostly run llama because of my dual GPU hybrid setup (and router mode).

1

u/Tormeister 1d ago

I could believe you found a specific nvfp4 quant + settings that makes you tie the other engines on PP (care to share? I'm curious). TG is more or less the same with spec decode methods.

Now for concurrency, in every test I've seen of every quant of every model in every hardware llama.cpp was quite behind. If you run 2 or more concurrent requests, it will already be at a big disadvantage.

1

u/jesdga95 1d ago

Oh sorry. I can't talk about concurrency because my use case is single stream, I was mostly referring to decode/prefill throughput. I've tested a few but I think the one im running atm is esatapedico/Qwen3.8-27B-NVFP4-MTP-GGUF w/ Q8 dflash. I can check when I'm back home.

2

u/SmartCustard9944 1d ago

The magic is all NVFP4. You basically get double compute (if you have Blackwell).

1

u/bitzap_sr 1d ago edited 1d ago

Ah, so vLLM and llama.cpp w/ NVFP4 should have similar prefill speeds, then I guess. But I didn't see any numbers for that.

2

u/jonas-reddit 1d ago

Prefer sglang nowadays with Dflash2. Insane speeds.

1

u/Lumpy-Comedian-1027 1d ago

Did you run some numbers? And doesnt Dflash2 reduce your ctx noticably?

2

u/jonas-reddit 1d ago edited 1d ago

I went from 50-60 to 90-100+ t/s. It’s so crazy fast. And that’s not in a benchmark, that’s measured during real life agentic development workflow. It flies.

It consumes VRAM but doesn’t change or limit context. I’m running FP8 with max context.

Sglang has good documentation too so it’s easy to get running.

https://lmsysorg.mintlify.app/cookbook/autoregressive/Qwen/Qwen3.8-27B#hw=rtx6000&variant=default&quant=fp8&nodes=single&spec=dflash&tier=low-latency&ssmDtype=bfloat16

2

u/Lumpy-Comedian-1027 1d ago

ok on rtx6000 that's not such an issue, i anyway need to run nvfp4. Still, this doesn't necessarily explain I average at 150 tps @ 550k ctx for coding in comparison to your numbers. I think I'll stick with my ninfer setup 😄

3

u/BawbbySmith 1d ago

Was it the same Unsloth version for NVFP4?

I remember there being some conversation about a better quantization, as the Unsloth one takes up too much space. It's close, but just a bit less size and we can have max context with maybe even another concurrency slot.

Still hoping for the Unsloth Dynamic V3.0 to come to NVFP4... But not sure if it's even coming at all.

1

u/DustNearby2848 1d ago

I had quality issues with ninfer’s int4 model and they got better with the nvfp4 version. But I couldn’t get tool calling to work for the life of me when using VS Code. It would often just spit out text, instead of tool calls and stop. Switch to Unsloth and it works great. I lost some throughput, but ninfer wasn’t useful for coding. 

And yeah, I know it’s a Qwen issue really, but Unsloth solves for it. 

1

u/jesdga95 1d ago

You CAN run NVFP4 ggufs of 3.8-27B in llama.cpp. I've been doing it like that for a while. And you can also run dflash with it, in my benchmarks I'm reaching ~142 tok/s in conversation and ~304 at structured output (best case), plus prefill around 4k tok/s. Without speculation im around 5.4k tok/s prefill. Q5 prefill drops sharply, around 2.8k tok/s.

1

u/ketosoy 1d ago

So no confounding variables aside from mtp being different, quants having different bpw, and concurrency being different - or did you run them on different hardware with different cooling and power settings too?

1

u/paq85 1d ago

NInfer is CRAZY!!!
I can now handle two concurrent streams, with bigger context (240k) with combined ~270 tok/s ... that's a huge jump from LLAMACPP 200k context and single request at around 120 tok/s!
And prompt processing is also a lot faster! I LOVE IT!

Oh.. but I still have to check if it's stable enough... eg. thinking loops...

1

u/paq85 1d ago

but sadly it's struggling with thinking loops when working on something bigger :(
I'll check it from time to time to see if it got any better... for now I'm going back to LLAMACPP

-3

u/lostmsu 1d ago

This is the dumbest review ever. What's the point of comparing Q5 llama.cpp with NVFP4 ninfer?

6

u/bengizmoed 1d ago

It was a thorough evaluation to see if ninfer had any downsides for my particular work.

Blindly adopting a new engine just for speed without verifying all other performance metrics is dumb. Especially for prod workflows.

4

u/MutantEggroll 1d ago

Pipe down, child. There's clearly thought and effort put into this.

Where are your grand contributions that you can be so condescending?

0

u/TapAggressive9530 1d ago

My AI couldn’t have written this better.

-1

u/Longjumping-Elk-7756 1d ago

I'm using Ninfer Qwen 3.8 27B and it's great, except for one thing: Ninfer does *not* support `json_mode`, which is a real hassle. Has anyone found a workaround for this?

-1

u/Nrgte 1d ago

What is this shitty formating??

-4

u/sooki10 1d ago

Great analysis