NVIDIA's official NVFP4 build of Qwen3.6 35B-A3B serves the full 262,144-token context on one DGX Spark — but it decodes at 42.6 tok/s against 106.5 for Unsloth's NVFP4-Fast build of the same base model. Two structural reasons, both measured here: it is weight-only NVFP4, so the FP4 tensor-core MoE kernels refuse it and vLLM falls back to MARLIN; and the export ships without the MTP head, so it cannot speculative-decode at all.
* segment sizes marked with an asterisk are estimates pending a measured run
Eval scores
(compare all)Bench card
| 2K | 8K | 32K | 128K | |
|---|---|---|---|---|
| decode tok/s | 76.7 | 73.3 | 63.1 | 40.4 |
| ttft | 370ms | 1.25s | 5.64s | 38.52s |
| prefill tok/s | 6.6k | 7.1k | 6.1k | 3.6k |
| power | — | — | — | — |
includes 1 community run · @lloyd094
median per context · o256
Contributors
Experiments run against this recipe
Case studies that used this recipe as their baseline — what was tried, and whether it changed the recipe.
Designated FP8 as the recommended precision for this model on one Spark: +23% decode and +38% prefill over BF16 at half the footprint (67 -> 36 GiB) and flat power. 4-bit adds ~15% more decode and cuts weights to a third, but with clear diminishing returns — it is the pick only when the memory is the binding constraint.
Overview
This page exists to answer one question honestly: NVIDIA publishes an official NVFP4 quantization of Qwen3.6 35B-A3B and Unsloth publishes an NVFP4-Fast one, so which should a Spark owner download? Measured on the same node on the same day against the same engine build, the answer is Unsloth's, by a factor of 2.5x on decode. This recipe documents the NVIDIA build anyway, because it serves correctly, it is the vendor artifact, and the reasons it loses are structural and worth understanding rather than folklore. The first reason is the kernel path. NVIDIA's artifact is NVFP4A16: the weights are 4-bit but activations stay BF16, with no input_activations block in its compressed-tensors config. vLLM's FP4 MoE backends all want FP4 activations to feed the Blackwell FP4 tensor cores, so every one of them declines and selection falls through to MARLIN, which dequantizes weights back up before a BF16 GEMM. Forcing the issue with --moe-backend flashinfer_cutlass does not work, and the error says exactly why: "kernel does not support quantization scheme QuantKey(u8,scale(f8e4m3fn,static,GroupShape(row=1, col=16)),scale2(f32,static,per_tensor),symmetric)xNone". That trailing xNone is the missing activation quantization. This is a property of the checkpoint, not of sm_121 or of the vLLM version, and it is a different failure from the one the NVFP4-Fast recipe documents for vLLM 0.26.0. The second reason is the draft. config.json here declares mtp_num_hidden_layers: 1, and NVIDIA's own recipe.yaml carefully excludes re:^mtp.* from quantization — but the export then ships zero mtp.* tensors. We checked the weight index directly: 93,426 tensors, none of them MTP. Unsloth's build of the same base model keeps all 19 of them, at BF16, costing 1.573 GiB. So the NVFP4-Fast recipe's single biggest lever — MTP speculative decode, worth +57% there — is simply unavailable on this checkpoint as published. The two effects compound: 42.6 tok/s here against 106.5 tok/s there at a 2,048-token prompt. What this build still does well is context and prefill. The full 262,144-token window serves, and prefill runs at 5,872 tok/s at 2K and 6,431 tok/s at 8K — within a few percent of the Unsloth build, because prefill is compute-bound over a large batch of tokens where MARLIN's dequantize overhead amortizes. It is decode, one token at a time, that pays the fallback kernel on every step: ITL is 23.5 ms here against roughly 9 ms for the fast build at k=3. One thing this page cannot explain. The benchmark rows on this recipe from 2026-07-06 recorded 76.8 tok/s decode for this same checkpoint. Re-run on 2026-07-27 against the same ~/venvs/vllm (reporting 0.24.0), on the same node, decode reads 42.6 tok/s under the harness. Re-serving the same checkpoint at the short 10,240-token context those rows used and timing three warm 256-token completions gave 6.00-6.03 s each, i.e. ~42.5 tok/s — so the context flag is not the cause. (That second check is curl-level, not a full harness run: the server was lost to an ssh teardown before the harness pass completed, so no rows were emitted for it.) The engine reports a healthy configuration in each case: MARLIN, FLASH_ATTN, full CUDA-graph capture. The most likely explanation is an in-place venv or driver change between those dates that the recorded engineVersion does not distinguish, but we did not establish it. The July rows are left in place and dated rather than deleted; today's are the ones this recipe quotes.
- 1 × DGX Spark (GB10, sm_121) — no tensor parallelism, no second node
- Full 262,144-token context serves, at 1.18x concurrency with the KV pool pinned to 6 GiB
- Measured 42.6 tok/s single-stream decode at a 2,048-token prompt — no speculative decode is possible
- Unsloth's NVFP4-Fast build of the same base model measures 106.5 tok/s: prefer it unless you need the vendor artifact
- Weight-only NVFP4A16 — activations stay BF16, so every FP4 MoE backend declines and vLLM falls back to MARLIN
- --moe-backend flashinfer_cutlass is rejected at startup here, and the error names the missing activation quant
- The export ships zero mtp.* tensors despite declaring mtp_num_hidden_layers: 1 — no MTP draft to speculate from
- Prefill is competitive: 5,872 tok/s at 2K, 6,431 tok/s at 8K — the loss is decode-side only
- Weights 21.88 GiB resident, slightly MORE than the Unsloth build (20.58) despite quantizing strictly more
- KV costs ~2x per token versus the Unsloth build: 51,492 tokens/GiB against 101,606
- --kv-cache-memory-bytes 4294967296 will not start; the engine needs 5.08 GiB for one full-context sequence
- Verified 2026-07-27 on vLLM 0.24.0. The 2026-07-06 rows on this page read 76.8 tok/s and did not reproduce — see the overview
Software requirements
- 1 × DGX Spark (GB10, sm_121), NVIDIA driver ≥ 580 (CUDA-13 capable)
- vLLM 0.24.0 with torch 2.11.0+cu130
- The `hf` CLI to fetch the checkpoint (~22 GB, 6 shards)
- No patches and no sitecustomize workarounds — but also no working FP4 MoE kernel; see troubleshooting
- Last verified 2026-07-27 on vLLM 0.24.0: flags below all valid, weights 21.88 GiB, KV pool 308,955 tokens / 1.18x, decode 42.9 / 42.6 / 41.4 tok/s at 512 / 2,048 / 8,192-token prompts
Quick start
- 1
Free the node first
Anything still holding unified memory — a previous vLLM, a leftover Ray cluster — comes out of the same pool.
pgrep -af 'vllm serve|ray::' || echo 'node is clear' ray stop --force 2>/dev/null || true free -gbash - 2
Download the checkpoint
6 safetensors shards, ~22 GB. Only this node needs it — there is no second rank to mirror to.
hf download nvidia/Qwen3.6-35B-A3B-NVFP4 \ --local-dir ~/models/hf/Qwen3.6-35B-A3B-NVFP4bashVerify 6 shards and zero *.incomplete files before serving.
- 3
Check for the MTP head before you plan around it
config.json declares mtp_num_hidden_layers: 1, which reads like the checkpoint can speculative-decode. It cannot — the tensors are not in the export. Check the weight index rather than the config on any checkpoint where speculative decode is part of your plan.
python3 - <<'PY' import json p = '/home/joemuller/models/hf/Qwen3.6-35B-A3B-NVFP4/model.safetensors.index.json' ks = list(json.load(open(p))['weight_map']) print('tensors:', len(ks)) print('mtp tensors:', [k for k in ks if k.startswith('mtp')]) PYbashPrints 93426 tensors and an empty MTP list. The same check against unsloth/Qwen3.6-35B-A3B-NVFP4-Fast prints 19 mtp.* tensors, 1.573 GiB of BF16.
- 4
Serve at the full context
No --speculative-config: there is nothing to draft from. Note the 6 GiB KV pin — the 4 GiB the NVFP4-Fast recipe uses is below this build's floor and fails at startup.
PATH="$HOME/venvs/vllm/bin:$PATH" VLLM_USE_DEEP_GEMM=0 \ TORCHINDUCTOR_COMPILE_THREADS=2 MAX_JOBS=4 \ vllm serve ~/models/hf/Qwen3.6-35B-A3B-NVFP4 \ --served-model-name nvidia/Qwen3.6-35B-A3B-NVFP4 \ --max-model-len 262144 \ --kv-cache-memory-bytes 6442450944 \ --gpu-memory-utilization 0.85 \ --max-num-seqs 4 \ --max-num-batched-tokens 8192 \ --port 8000bashDo not add --moe-backend. Every FP4 backend refuses this checkpoint, so forcing one only turns a slow start into a failed one.
- 5
Confirm what it actually chose
Expect MARLIN on the MoE line. That is not a misconfiguration you can fix — it is the correct fallback for a weight-only FP4 checkpoint — but it is the reason the decode number below is what it is.
grep -E "NvFp4 MoE backend|attention backend|Model loading took|GPU KV cache size|Maximum concurrency" /tmp/vllm.logbashOn vLLM 0.24.0 expect MARLIN, FLASH_ATTN, 21.88 GiB, 308,955 tokens, 1.18x — all five confirmed 2026-07-27. EMULATION instead of MARLIN means stop and read troubleshooting.
- 6
Warm it up before you trust any number
Three requests is enough here. With no speculative-decode kernels to JIT, the cold-start penalty is much smaller than on the NVFP4-Fast recipe, but it is not zero.
for i in 1 2 3; do curl -s http://127.0.0.1:8000/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{"model":"nvidia/Qwen3.6-35B-A3B-NVFP4", "messages":[{"role":"user","content":"Write an LRU cache in Python."}], "max_tokens":256,"temperature":0}' > /dev/null donebash
Key vLLM parameters
| Parameter | Value | Purpose |
|---|---|---|
API usage
Chat completion
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"nvidia/Qwen3.6-35B-A3B-NVFP4",
"messages":[{"role":"user","content":"Explain speculative decoding in two sentences."}],
"max_tokens":256,"temperature":0}'bashTurn thinking off
Every decode number on this page was measured with thinking off, so the tokens counted are answer tokens.
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"nvidia/Qwen3.6-35B-A3B-NVFP4",
"messages":[{"role":"user","content":"Write an LRU cache in Python."}],
"max_tokens":512,"temperature":0,
"chat_template_kwargs":{"enable_thinking":false}}'bashTroubleshooting
- Should I use this or Unsloth's NVFP4-Fast build?
- The MoE backend line says MARLIN, not FLASHINFER_CUTLASS. Did I misconfigure something?
- config.json says mtp_num_hidden_layers: 1 but there is no draft
- vLLM refuses to start: '5.08 GiB KV cache is needed, which is larger than the available KV cache memory (4.0 GiB)'
- I measured this checkpoint faster than 42 tok/s before
- Is prefill slow too?
- Can I graft the missing MTP head on from the Unsloth checkpoint?
Memory budget
Weights are a measured 21.88 GiB resident — slightly more than Unsloth's NVFP4-Fast build of the same base model (20.58 GiB) even though this one quantizes strictly more of the network. That is the first clue to what this checkpoint is: NVIDIA quantizes every Linear it can reach to NVFP4 (group size 16, FP8-E4M3 scales, strategy tensor_group) and exempts only the linear-attention projections, router gates, shared-expert gates, lm_head, embeddings, norms and the 27-block vision tower — a 10,641-entry literal ignore list. Unsloth instead leaves attention q/k/v/o at FP8 with dynamic per-token activation scales. The two land within ~1.3 GiB of each other, so on this model the extra quantization buys almost no memory while costing the FP4 kernel path (see troubleshooting). KV is the other surprise: with the pool pinned at 6 GiB this build yields 308,955 tokens = 1.18x concurrency at the full 262,144-token context, i.e. 51,492 tokens/GiB. The Unsloth build gets 101,606 tokens/GiB with no draft on the same engine — roughly twice as many. A 4 GiB pin, which is what the NVFP4-Fast recipe ships, is not enough here: vLLM refuses to start with 'To serve at least one request with the model's max seq len (262144), 5.08 GiB KV cache is needed'. 6 GiB is the smallest round pin that starts. The segment split below is apportioned from parameter counts, not measured per-tensor; the 21.88 GiB total and the KV figure are read from the engine log.