vLLM 0.17.0 MXFP4 Patches for DGX Spark: Qwen3.5-35B-A3B 70 tok/s, gpt-oss-120b 80 tok/s (TP=2)

Hi everyone,

I am currently working on successfully deploying vLLM 0.17.0 in a DGX Spark environment (GB10 / SM121) using MXFP4 quantization. I’ve put together a set of patches that fix SM121-specific issues and add BF16 → MXFP4 online quantization for attention layers and lm_head. Here are the results on a 2-node DGX Spark cluster (TP=2, single request, vllm bench serve with 1024 input / 128 output tokens):

Model Vanilla Patched Speedup
Qwen3.5-35B-A3B 42.85 tok/s 70.68 tok/s +65%
openai/gpt-oss-120b 51.82 tok/s 80.88 tok/s +56%

The patches also enable TP=1 single-GPU inference, which vanilla vLLM cannot do on GB10 for MXFP4 models.

What’s included

  • BF16 → MXFP4 online quantization for MoE experts, attention (qkv_proj, o_proj), and lm_head via Marlin backend
  • SM121 device support for CUTLASS MoE kernels
  • Fix for weight_loader ndim mismatch with BF16 per-expert tensors
  • GDN (Gated Delta Net) Triton kernel fix for Qwen3.5
  • FlashInfer CUTLASS header bug fix

Installation

Takes just a few minutes — create a venv, pip install vLLM, apply two patch files, done. Full instructions and patches are on GitHub:

GitHub: https://github.com/namake-taro/vllm-custom

This work builds on the efforts of this community who identified and documented the SM121 software support gaps. Feedback and bug reports are welcome.

Are you planning to open PRs to upstream repos (vLLM and CUTLASS)? I see that the SFA/SFB issue is still present in the latest CUTLASS version.

Nice. Would MXFP4 optimizations @christopher_owen worked on ( GitHub - christopherowen/spark-vllm-mxfp4-docker · GitHub ) overlap with this or can they be combined?

Thanks for the suggestion. I’m planning to submit a PR to CUTLASS for the SFA/SFB fix — it’s a straightforward copy-paste bug that should be easy to merge.

For the vLLM patches, the MXFP4 online quantization changes are fairly extensive and SM121-specific, so they would need significant refactoring to meet upstream code quality standards.

The GDN kernel change is more of a tuning adjustment for Qwen3.5’s dimension sizes rather than a bug fix, so it would need discussion with the FLA developers about the original design intent first.

As for the Triton patch, the explicit allocator setting via ContextVar appears to be an intentional design — the root issue is really about Ray’s thread-level ContextVar propagation.

So for now, everything except the CUTLASS fix will stay as out-of-tree patches.

You may want to coordinate with @christopher_owen on this one, because he is trying to submit his prior MXFP4 optimizations upstream.

I’ll test your patches and may include them as a mod in our community docker (I already have one for the triton allocator bug) if you don’t mind, as our current MXFP4 build is broken in the cluster setup.

Thanks! I’ve looked at @christopher_owen’s work. His patches extensively customize the CUTLASS kernels themselves, while mine improve the Marlin backend, so they don’t conflict. However, combining them would require unifying the quantization dispatch logic, which could be non-trivial.

Regarding the CUTLASS SFA/SFB fix — since @christopher_owen has already patched all three affected files (sm120, sm100, sm103), it would make sense for him to submit the upstream PR.

@eugr Feel free to include the patches in your community docker — happy to have them used.

Correction on TP=1 for gpt-oss-120b: After further testing, I found that TP=1produces malformed output for chat completions (Harmony protocol). Basic text completion works, but the structured reasoning tokens are corrupted — likely due to FP4 quantization errors affecting control token generation. The vllm bench serve benchmarks only measure throughput and don’t catch this. TP=2 remains unaffected. I’ve updated the README accordingly.

Can you benchmark with llama-benchy?
I’m especially interested in higher context depths:

llama-benchy --base-url http://spark:8000/v1 --model openai/gpt-oss-120b --enable-prefix-caching --depth 0 4096 8192 16384 32768 65535 100000"

Update: TP=1 issue with gpt-oss-120b on SM121 has been fixed

I have identified and fixed the root cause of the malformed output when using TP=1.

The issue was a shared memory race condition in the Marlin MoE 256-thread kernel on SM121.

The Marlin MoE GEMM kernel has two thread configurations: a 128-thread configuration (thread_k=64, thread_n=128) and a 256-thread configuration (thread_k=128, thread_n=256). With gpt-oss-120b at TP=1, the w2 GEMM reduction dimension (intermediate size) N=2944 is large enough that the kernel selects the 256-thread configuration. However, on SM121, this 256-thread kernel suffers from a shared memory race condition that silently corrupts the w2 GEMM output, resulting in garbage data being generated.

With TP=2, the weight matrices are split across two GPUs, so N≈1472 — small enough that the kernel stays on the safe 128-thread configuration. This is why TP=2 worked correctly while TP=1 did not.

The vllm_all.patch also includes a fix for the Mxfp4Config.from_config() method, which was ignoring the modules_to_not_convert field from the model config. This caused attention layers and lm_head to be incorrectly FP4-quantized.

These fixes have already been uploaded to GitHub.

I ran a benchmark.
Is this correct?

(gpt-oss-120b, TP=2)

model test t/s peak t/s ttfr (ms) est_ppt (ms) e2e_ttft (ms)
openai/gpt-oss-120b pp2048 6069.01 ± 23.80 338.71 ± 1.32 337.46 ± 1.32 376.50 ± 1.84
openai/gpt-oss-120b tg32 81.48 ± 0.88 84.40 ± 0.91
openai/gpt-oss-120b ctx_pp @ d4096 6525.09 ± 8.25 628.99 ± 0.79 627.73 ± 0.79 666.10 ± 0.63
openai/gpt-oss-120b ctx_tg @ d4096 80.13 ± 0.91 83.01 ± 0.94
openai/gpt-oss-120b pp2048 @ d4096 4966.58 ± 11.64 413.61 ± 0.96 412.36 ± 0.96 449.10 ± 1.49
openai/gpt-oss-120b tg32 @ d4096 79.14 ± 0.74 81.98 ± 0.77
openai/gpt-oss-120b ctx_pp @ d8192 6198.81 ± 15.13 1322.81 ± 3.22 1321.55 ± 3.22 1356.29 ± 2.42
openai/gpt-oss-120b ctx_tg @ d8192 78.51 ± 0.76 81.32 ± 0.78
openai/gpt-oss-120b pp2048 @ d8192 4190.41 ± 4.28 489.99 ± 0.50 488.74 ± 0.50 522.48 ± 0.86
openai/gpt-oss-120b tg32 @ d8192 78.33 ± 0.17 81.14 ± 0.17
openai/gpt-oss-120b ctx_pp @ d16384 5385.96 ± 5.79 3043.24 ± 3.27 3041.98 ± 3.27 3072.47 ± 3.25
openai/gpt-oss-120b ctx_tg @ d16384 75.48 ± 0.46 78.19 ± 0.48
openai/gpt-oss-120b pp2048 @ d16384 3292.80 ± 14.03 623.23 ± 2.64 621.97 ± 2.64 648.81 ± 3.67
openai/gpt-oss-120b tg32 @ d16384 75.04 ± 0.56 77.73 ± 0.59
openai/gpt-oss-120b ctx_pp @ d32768 4108.69 ± 1.93 7976.54 ± 3.75 7975.29 ± 3.75 7995.49 ± 2.89
openai/gpt-oss-120b ctx_tg @ d32768 70.18 ± 0.50 72.70 ± 0.52
openai/gpt-oss-120b pp2048 @ d32768 2278.84 ± 7.01 899.97 ± 2.76 898.71 ± 2.76 918.86 ± 1.76
openai/gpt-oss-120b tg32 @ d32768 70.13 ± 0.25 72.64 ± 0.26
openai/gpt-oss-120b ctx_pp @ d65535 2753.45 ± 15.87 23803.09 ± 137.76 23801.84 ± 137.76 23819.95 ± 137.03
openai/gpt-oss-120b ctx_tg @ d65535 63.29 ± 0.86 65.56 ± 0.89
openai/gpt-oss-120b pp2048 @ d65535 1437.48 ± 4.75 1425.98 ± 4.70 1424.73 ± 4.70 1443.27 ± 3.95
openai/gpt-oss-120b tg32 @ d65535 62.48 ± 2.32 64.72 ± 2.40
openai/gpt-oss-120b ctx_pp @ d100000 2018.79 ± 1.23 49535.91 ± 30.08 49534.66 ± 30.08 49559.65 ± 27.89
openai/gpt-oss-120b ctx_tg @ d100000 59.56 ± 0.28 61.69 ± 0.29
openai/gpt-oss-120b pp2048 @ d100000 1011.85 ± 7.16 2025.37 ± 14.26 2024.12 ± 14.26 2052.57 ± 14.74
openai/gpt-oss-120b tg32 @ d100000 57.97 ± 1.40 60.10 ± 1.41

llama-benchy (0.3.4)
date: 2026-03-11 19:55:35 | latency mode: api

Qwen/Qwen3.5-122B-A10B Qwen/Qwen3.5-122B-A10B · Hugging Face This model might work, thanks in advance

OK, so this is definitely the best result for gpt-oss-120b so far. I guess I’ll swap out a separate MXFP4 build for the mod with your patches.

Hi team, thanks for the patch :) I applied it and I am using a single DGX configuration. It is pretty fast indeed, however I think it has some issues in handling the tools correctly. I got some glithces in the output, some randomic chinese words, and tool suddenly stop during mcp calls. I start vllm like this:
export CUDA_HOME=/usr/local/cuda

export LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v12:/usr/local/cuda-13.0/targets/sbsa-linux/lib:$LD_LIBRARY_PATH

# MXFP4 backend
export VLLM_MXFP4_BACKEND=marlin
export VLLM_FASTSAFETENSORS_NOGDS=1

vllm serve Qwen/Qwen3.5-35B-A3B \
--host 0.0.0.0 \
--quantization mxfp4 \
--load-format fastsafetensors \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.70 \
--kv-cache-dtype fp8 \
--enable-prefix-caching \
--enable-auto-tool-choice \
--tool-call-parser qwen3_coder \
--reasoning-parser qwen3 \
--language-model-only \
--max-model-len 262144 \
--max-num-batched-tokens 32768 \
--max-num-seqs 16 \
--max-cudagraph-capture-size 32

It is funny, llama.cpp has MR for nvfp4, vllm has some pathes for mxfp4.

Thanks @amasawa_seiji

First, download the latest patch from the GitHub repository and follow the instructions to execute the commands.
Run the program (with the --tensor-parallel-size 1 option) to verify that it functions correctly.
Once you’ve confirmed successful operation with this basic test, the next step is to incrementally add options one by one while identifying the root cause of the problem.
If you encounter unusual output during the basic test itself, this may indicate that the patch wasn’t applied correctly or that the README.md documentation lacks sufficient environment configuration instructions.

I tried running Qwen3.5-122B-A10B on 2x DGX Spark (TP=2), but it does not fit in memory.

The BF16 model weights are ~234GB (39 shards). With TP=2, each node needs to load ~117GB of BF16 weights before MXFP4 online quantization can convert them to ~35GB. Since each GB10 has only ~119GB usable unified memory, the loading process itself exceeds available memory and triggers OOM.

A pre-quantized checkpoint like Qwen/Qwen3.5-122B-A10B-FP8 (~127GB) could load successfully with --quantization fp8 (~64GB/node), but in that case the MXFP4 patches would not be used.

This model would likely work on TP=4 (4x DGX Spark) with MXFP4 — each node would only need ~59GB for BF16 loading, well within the 119GB limit. If anyone has a 4-node setup, I’d be curious to hear the results.

Could mxfp4 be quantized with four nodes, and then used on two nodes? Fine-Tuning gpt-oss for Accuracy and Performance with Quantization Aware Training | NVIDIA Technical Blog

Why would you do online quantization though? Without calibration the model will lose quality.