Recipe for VLLM dev (no docker container), and Humaneval benchmark results vs RadixArk
Introduction
Hello, thank you for this model checkpoint. I gave it a shot and benchmarked it against the RadixArk that I've been using.
Other benchmarks will probably focus on agentic capabilities. But I decided to benchmark it on Humaneval https://github.com/openai/human-eval just for fun.
Any tuning/ general advice would be greatly appreciated.
Both checkpoints run against the same vLLM build on one RTX PRO 6000 Blackwell (96 GB), no
container. Both keep the PLE n-gram ("Engram") table in pinned DDR5 and stream it to the GPU
over UVA with VLLM_PLE_CPU_OFFLOAD=1.
| tag | checkpoint | on-disk | PLE table in DRAM | KV cache |
|---|---|---|---|---|
| LocalFP4 | nvidia/Qwen3.8-Flash-Next-FP4-Local | 95.1 GiB | ~33 GB nvfp4 | fp8, ~850K-token pool |
| RadixArk NVFP4 | RadixArk/Qwen3.8-Flash-Next-NVFP4 | 135 GB (79.3 GiB in VRAM) | 51.2 GB fp8 | BF16 forced by QSA, ~330K tokens |
Same base model (Qwen3.8-Flash-Next, Qwen4Exp arch), same systemd unit, same GPU and port,
served as Qwen38. Only the weights swap, so the differences below are the checkpoints' and not
the engine's.
Humaneval results
benched.py, 164 problems, k=1, temperature 0.2. LocalFP4 is one of three independent repeats of
the same NVIDIA checkpoint (159 / 159 / 158 passes, a 0.6pp sampling noise floor); RadixArk NVFP4 is a
single run.
| LocalFP4 | RadixArk NVFP4 | |
|---|---|---|
| pass@1 | 0.9634 (158/164) | 0.9817 (161/164) |
| failed tasks | 91, 103, 116, 137, 140, 145 | 99, 137, 145 |
| wrong answers with code in hand | 3 | 1 |
| returned no code at all | 3 (all exhausted a 32,678-token thinking budget) | 2 (API timeouts, never sampled) |
| wall-clock | 589.7 s | 1,985.6 s |
| completion tokens | 247,745 | 227,507 |
| tokens / problem (both solved, no cap) | 928 | 1,337 (+44%) |
| seconds / request | 9.8 | 14.4 (+47%) |
| max reasoning on one problem | 32,678 (hit cap) | 17,052 |
| samples >=20k reasoning tokens | 4/164 | 0/164 |
Both decode at about 115 tok/s, so RadixArk NVFP4's 3.4x wall-clock is extra thinking time, not slower
decode.
The paired result matters more than the score: across all 164 problems the two checkpoints
never disagree on a problem both answered. There is no problem RadixArk NVFP4 solved where all three
NVIDIA repeats failed, and none RadixArk NVFP4 failed where any NVIDIA repeat passed. Both get exactly
one problem wrong with code in hand, HumanEval/145, and 20 follow-up samples at three token
budgets still get it wrong, so quantization is not what's behind that one.
That makes RadixArk NVFP4's 2-task margin a question of whether the sample finished thinking and emitted
code. The NVIDIA checkpoint's failure mode is finish_reason=length with zero content: thinking
never terminates, the budget burns, no code comes out. The three repeats hit it 7/492 times (3
of them in LocalFP4); RadixArk NVFP4 hit it 0/164.
Takeaway
Same capability where it counts: the checkpoints agree on every problem both attempt, and share
one genuine failure (/145). LocalFP4 is 44% cheaper and 47% faster per problem, halves the
pinned DRAM, and fits one card with a 2.5x bigger KV pool. RadixArk never exhausted its thinking
budget into an empty completion, which is what cost LocalFP4 its points. I'd run LocalFP4,
keep a retry-with-larger-budget loop for the occasional runaway thinker, and pin one--max_tokens value before comparing anything.
Where LocalFP4 is stronger
LocalFP4 allows my single card to be capable of higher concurrent tokens performing different
tasks. Whereas RadixArk was limited to 131k, LocalFP4 serves the full 262144 window to multiple agents with
room to spare, because the KV cache is where the two checkpoints diverge hardest:
| LocalFP4 | RadixArk NVFP4 | |
|---|---|---|
| KV dtype | fp8 (validated by the model card) | BF16 forced, the QSA path rejects fp8 |
| weights in VRAM | ~67 GB | ~84 GB |
| KV pool | 868,021 tokens (~3.3x full context) | 330,159 tokens |
| concurrent full-length (262144) requests | 3 | ~1.3 |
| PLE table pinned in DRAM | ~33 GB | 51.2 GB |
So I can run three agents at the full 262K context at once on LocalFP4 (a fourth fits at
~195K each), while the RadixArk deployment barely fits one full-length request and I capped
mine at 131k to get any concurrency out of it. The freed VRAM comes from the n-gram table
being 33 GB instead of 51.2 GB.
More details
The recipe: vLLM dev without a docker container
I do not have docker, so I ran a vLLM dev version by compiling it for my already existing container, so it probably won't work with yours. Including it just for note-keeping if anyone is curious / want to repeat:
The LIL wheels (same build as the release-10152026 image recipe) install into a plain uv venv,
with provenance pinned to exact commits. Every wheel comes from a local-inference-lab GitHub
release tagged <component>-cu134[-sm120]-beta-<full-commit>, so any wheel can be re-derived
from its commit:
| component (branch) | version | commit | release |
|---|---|---|---|
vllm (dev/karmic-kraken) |
0.1.dev21830+g8244d1c44.cu134 |
8244d1c44348ae8a5bbfc9f1b607021652283ed4 |
vllm-jovian-cu134-beta-8244d1c4 |
flashinfer + b12x + jit_cache sm120f (community/karmic-kraken-cu134-sm120) |
0.7.1+lil.cu134.sm120 |
35c0f234c4b82f4b3b30258132c8bf718e222309 |
flashinfer-cu134-sm120-beta-35c0f234 |
instanttensor (main) |
0.1.9 |
747390dc2bd1e0a18b0c7b13a09a38594edf342f |
instanttensor-cu134-beta-747390dc |
nccl / local_inference_nccl (canonical/cu134-nccl2312-amd-turin) |
2.31.2+lil.cu134 |
300f3a73caba7196e436571d9ac34069e81edea4 |
nccl-cu134-sm120-300f3a73 |
lmcache (integration/local-inference-lab) |
pinned by the assembly, not installed | 8ef537d6481b0d7c93277118ffcc6bf7e68d1b22 |
lmcache-cu134-sm120-beta-8ef537d6 |
torch / triton (PyPI, pinned by qwen38-runtime.lock) |
2.14.1+cu130 / 3.8.0 |
— | NGC wheels not needed |
The wheels are built on Ubuntu 24.04 (glibc 2.38, GCC 15) and this box is Debian 12 (glibc 2.36,
GCC 12), which is the one non-obvious part. LD_PRELOAD shims don't work because DT_VERNEED
pins the soname. What works is gating the real libraries per-process:
/opt/glibc239/ libc6 2.39 (libc, libm, ld-linux)
/opt/libstdcxx15/ libstdc++6 15 (GLIBCXX_3.4.32)
# .venv/bin/python is a COPY of the CPython 3.12 ELF (not a symlink), then:
patchelf --set-interpreter /opt/glibc239/.../ld-linux-x86-64.so.2 \
--set-rpath /opt/glibc239/...:/opt/libstdcxx15/...
Every child process inherits the gate. Do not run a blanket patchelf --set-rpath across
site-packages: it clobbers the auditwheel $ORIGIN RPATHs and breaks torch/numpy/scipy/cv2.
Gotchas that actually bit:
- Both attention paths must be FLASHINFER (
--attention-backend FLASHINFER --mm-encoder-attn-backend FLASHINFER). The wheel's_vllm_fa2_C/_vllm_fa3_Cfatbins only
carry sm_80/sm_75 codegen, so on sm_120 the first ViT call dies withcudaErrorUnsupportedPtxVersion. The model card omits these flags, which is correct only
inside the official container, whosevllm_flash_attnbuild does carry sm_120. apt-get install ninja-build. flashinfer JIT-compiles its sampler on the first request and
shells out to ninja; without it the engine fails to initialize.- PLE in DRAM: keep
VLLM_PLE_CPU_OFFLOAD=1explicit and verifyResolved Engram configuration: EngramConfig(cpu_offload=True)in the journal. That pins 51.2 GB (RadixArk)
or ~33 GB (LocalFP4) of host RAM. The disk backend (VLLM_PLE_TABLE_MEMORY=disk+ io_uring)
is the DGX Spark path. I have more host RAM, so I just pin it on the RAM.
Launch: the same env block serves both checkpoints, only the path and a few flags differ.
LocalFP4 adds --quantization modelopt_mixed --kv-cache-dtype fp8 --moe-backend b12x --linear-backend b12x --load-format instanttensor; RadixArk drops all of those (the
NVFP4-experts + BF16 mix auto-resolves, b12x auto-selects, QSA hard-requires BF16 KV) and keeps--gpu-memory-utilization 0.975 plus MTP spec decode'{"method":"mtp","num_speculative_tokens":3}'.
Verify in 30 s: .venv/bin/python -c "import torch, vllm, b12x, flashinfer, instanttensor"
then a Qwen2.5-0.5B serve with FLASHINFER.
Launch vLLM dev flags RadixArk NVFP4
/root/workspace/vllm-project/.venv/bin/vllm serve \
/root/models/hf-cache/models--RadixArk--Qwen3.8-Flash-Next-NVFP4/snapshots/7b719225242aacd3dbd3f9407468c2ee9a9d2594 \
--served-model-name Qwen38 \
--host 127.0.0.1 \
--port 8001 \
--dtype bfloat16 \
--block-size 64 \
--attention-backend FLASHINFER \
--mm-encoder-attn-backend FLASHINFER \
--mamba-cache-mode align \
--mamba-ssm-cache-dtype auto \
--enable-prefix-caching \
--enable-chunked-prefill \
--max-model-len 262144 \
--max-num-seqs 16 \
--max-num-batched-tokens 6019 \
--gpu-memory-utilization 0.975 \
--compilation-config '{"cudagraph_mode":"FULL_AND_PIECEWISE","custom_ops":["all"]}' \
--max-cudagraph-capture-size 64 \
--no-enable-flashinfer-autotune \
--async-scheduling \
--no-language-model-only \
--mm-encoder-tp-mode data \
--mm-processor-cache-gb 0.0 \
--enable-auto-tool-choice \
--tool-call-parser qwen3_xml \
--reasoning-parser qwen3 \
--speculative-config '{"method":"mtp","draft_sample_method":"probabilistic","rejection_sample_method":"standard","num_speculative_tokens":3}'
Launch vLLM dev flags LocalFP4
/root/workspace/vllm-project/.venv/bin/vllm serve \
/root/models/hf-cache/models--nvidia--Qwen3.8-Flash-Next-FP4-Local/snapshots/b9ce18a6887e18c089fdea8509a33e63429a3300 \
--served-model-name Qwen38 \
--host 127.0.0.1 \
--port 8001 \
--dtype bfloat16 \
--quantization modelopt_mixed \
--block-size 64 \
--load-format instanttensor \
--kv-cache-dtype fp8 \
--attention-backend FLASHINFER \
--mm-encoder-attn-backend FLASHINFER \
--mamba-cache-mode align \
--mamba-ssm-cache-dtype auto \
--enable-prefix-caching \
--enable-chunked-prefill \
--max-model-len 262144 \
--max-num-seqs 16 \
--max-num-batched-tokens 6019 \
--gpu-memory-utilization 0.975 \
--moe-backend b12x \
--linear-backend b12x \
--compilation-config '{"cudagraph_mode":"FULL_AND_PIECEWISE","custom_ops":["all"]}' \
--max-cudagraph-capture-size 64 \
--no-enable-flashinfer-autotune \
--async-scheduling \
--no-language-model-only \
--mm-encoder-tp-mode data \
--mm-processor-cache-gb 0.0 \
--enable-auto-tool-choice \
--tool-call-parser qwen3_xml \
--reasoning-parser qwen3 \
--speculative-config '{"method":"mtp","draft_sample_method":"probabilistic","rejection_sample_method":"standard","moe_backend":"b12x","num_speculative_tokens":3}'
Failpoints for HumanEval benchmark
The 10 tasks that failed in any run, k=3, three arms identical except --max_tokens (the 128k
arm covers only the 4 hardest):
| 2048 (10 tasks) | 32768 (10 tasks) | 131072 (4 hardest) | |
|---|---|---|---|
| samples returning no code | 12/30 (40%) | 4/30 (13%) | 4/12 (33%) |
| tokens burned on empty samples | 73,728 | 393,216 | 1,572,864 |
| slowest single request | 20 s | 251 s | 776 s |
An empty completion has two opposite causes, and the log cannot tell them apart since both
produce completion_tokens == reasoning_tokens == budget:
- Starvation, the budget is too small.
/32and/76need 10-45k thinking tokens; at 2048
they go 0/3, at 32768 they go 2-3/3 with 15k-44k tokens of reasoning. - Runaway, the budget is too large.
/99solves in ~500-1,300 reasoning tokens at 2048 and
32768, but at 131072 all 3 samples spiralled to the full budget and returned nothing, about
39 minutes and 1.57M tokens for nothing.
So "just use the whole 262144 context" is actively worse, and 32768 happened to sit between the
two failure modes. Retrying at the same budget does nothing for starvation (3 attempts, 3
identical starvation); a retry helps only when it changes the budget. One more trap: 262144 is
the context window, input plus output, not an output budget. The server rejects a max_tokens
that leaves the prompt no room.
The LocalFP4 runs requested ~32k. RadixArk NVFP4's budget was never recorded, and reasoning length scales
~160x with the flag alone, so RadixArk NVFP4's clean <=17k tail is as consistent with a smaller budget as
with a better-behaved checkpoint. The capability comparison above uses no token counts and
stands; the budget-stability claim needs one re-run of both checkpoints at one recorded budget.
Can you compare it with "nvidia/Qwen3.8-Flash-Next-NVFP4"?