You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ft bench bw picks hybrid on Ampere, but offload is 8.3x faster (DeepSeek-V4-Flash, 2x RTX 3090)
Summary
On a 2x RTX 3090 (SM86) host, ft bench bw measures the CPU MoE kernel at 56.2 GB/s vs 12.3 GB/s for PCIe, reports a 4.57x ratio, and selects hybrid. In actual serving, hybrid delivers 0.67 tok/s while forcing --moe-backend offload on the same machine and model delivers 5.58 tok/s.
The auto-selected backend is 8.3x slower than the one it rejected.
The calibrated CPU number does not survive contact with serving: at 0.67 tok/s and ~3.07 GB of expert weights per token, the effective CPU-MoE rate is roughly 2 GB/s, about 28x below what ft bench bw measured for the same kernel, dtype and workload minutes earlier.
The last row is included to show the ceiling is not cache-related: raising memory-ratio from the 0.9 default grew the expert pool from 624 to 779 slots (5.7% -> 7.1% residency) for only +6.6% throughput. Expert-cache residency cannot be pushed much further on this host — the ~24 GB of non-expert weights (167 GB total minus 143 GB of experts) already occupy roughly half of the 48 GB of VRAM, leaving ~10-12 GB for all pools combined.
Doubling the CPU pool from 23 to 44 threads yields only +15%, which is itself evidence that the CPU path is not thread-bound but limited by something else — most likely per-layer synchronisation. With top_k=6 over 43 MoE layers there are 258 expert activations per token, each a separate small GEMV with its own sync point, whereas the benchmark measures a tight loop over one expert.
All runs use --tp-size 2, and #62 documents that OffloadMoeCache is TP-unaware, making TP=2 a regression for offloaded MoE. That applies here too, so the absolute numbers below are pessimistic.
TP=1 is not available as a control on this hardware: the non-expert weights are ~25 GB in total (~12.7 GB per rank at TP=2, confirmed by VRAM accounting), which does not fit a single 24 GB card alongside any cache. Anyone reproducing on >=32 GB cards can and should check TP=1.
The hybrid vs offload comparison is unaffected: both ran at TP=2 with identical flags, so the 8.3x gap is attributable to the backend alone. If anything, #62 means both figures would rise together at TP=1.
Config was otherwise identical across runs (--moe-cache-auto, cache resolved to 624 slots / 5.7% residency in every case), so the difference is attributable to the backend alone.
Why this matters
The wrong call is made silently. --moe-backend auto resolves to hybrid here and produces the same 0.67 tok/s; nothing in the logs or in ft ctl stats points at the backend as the cause. Without a reason to try offload by hand, the reasonable conclusion is that the engine is unusable on this hardware, when in fact it is 8.3x faster one flag away.
Suggestions
Validate the calibration against a short end-to-end decode. After picking a backend, run a few tokens through it and compare achieved throughput to the predicted rate; if the gap exceeds some factor, fall back and warn.
Make the benchmark representative of serving. Measure the CPU path across a full layer sweep with the real synchronisation pattern (258 activations/token), not a single-expert loop. A per-activation fixed cost would then show up in the model.
Consider raising the selection threshold for hybrid on architectures without native FP4, or excluding it there until the CPU-side cost model accounts for per-activation overhead.
Secondary issues found in the same session
-lnccl link failure with --tp-size > 1. The JIT build of freetoken__pynccl.so fails on a clean uv pip install "freetoken[accel]":
/usr/bin/ld: cannot find -lnccl: No such file or directory
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
RuntimeError: ninja exited with status 1
ERROR Backend worker is gone and cannot be restarted; stopping the API server
torch ships nvidia/nccl/lib/libnccl.so.2 without the libnccl.so devel symlink that -lnccl requires. Workaround:
The build could resolve the NCCL path from torch.utils.cpp_extension / the installed nvidia-nccl-* package instead of relying on a system -lnccl.
--moe-cache-size accepts a value that makes startup fail.ft ctl cache reported the moe pool as resizable to 256..828, but --moe-cache-size 828 aborts the backend:
ValueError: DSV4 KV budget -284406580 bytes cannot fit the minimal pool
(31 pages incl. the window working-set floor 31); raise memory_ratio or
lower max_running_req/max_seq_len
The advertised upper bound does not account for the KV/SWA floor, so the value the tool itself reports as the maximum is not actually selectable. Possibly the same budget-solve area as #111 and #150.
Kernel cache directory is not created. Every start recompiles kernels:
freetoken/models/deepseek_v4/weight.py:70: UserWarning: Specified kernel cache
directory could not be created! This disables kernel caching
What worked
For balance: the model itself runs correctly on SM86. fp8 dense weights and fp4 experts are dequantised at runtime with no kernel errors, dsv4_sparse attention is selected automatically, and output quality is fine. The only hard blocker to getting it up was the NCCL symlink above. The problem is backend selection, not Ampere support as such.
ft bench bwpickshybridon Ampere, butoffloadis 8.3x faster (DeepSeek-V4-Flash, 2x RTX 3090)Summary
On a 2x RTX 3090 (SM86) host,
ft bench bwmeasures the CPU MoE kernel at 56.2 GB/s vs 12.3 GB/s for PCIe, reports a 4.57x ratio, and selectshybrid. In actual serving,hybriddelivers 0.67 tok/s while forcing--moe-backend offloadon the same machine and model delivers 5.58 tok/s.The auto-selected backend is 8.3x slower than the one it rejected.
The calibrated CPU number does not survive contact with serving: at 0.67 tok/s and ~3.07 GB of expert weights per token, the effective CPU-MoE rate is roughly 2 GB/s, about 28x below what
ft bench bwmeasured for the same kernel, dtype and workload minutes earlier.Environment
freetoken[accel])deepseek-ai/DeepSeek-V4-Flash-0731(fp8 dense,expert_dtype: fp4)Reproduction
Decode measured over a streamed 60-token completion, first token excluded, after a warmup request.
ft bench bwoutputMeasured results
hybrid(auto-selected)hybridoffloadoffload,--memory-ratio 0.95The last row is included to show the ceiling is not cache-related: raising
memory-ratiofrom the 0.9 default grew the expert pool from 624 to 779 slots (5.7% -> 7.1% residency) for only +6.6% throughput. Expert-cache residency cannot be pushed much further on this host — the ~24 GB of non-expert weights (167 GB total minus 143 GB of experts) already occupy roughly half of the 48 GB of VRAM, leaving ~10-12 GB for all pools combined.Doubling the CPU pool from 23 to 44 threads yields only +15%, which is itself evidence that the CPU path is not thread-bound but limited by something else — most likely per-layer synchronisation. With
top_k=6over 43 MoE layers there are 258 expert activations per token, each a separate small GEMV with its own sync point, whereas the benchmark measures a tight loop over one expert.Note on TP=2 and #62
All runs use
--tp-size 2, and #62 documents thatOffloadMoeCacheis TP-unaware, making TP=2 a regression for offloaded MoE. That applies here too, so the absolute numbers below are pessimistic.TP=1 is not available as a control on this hardware: the non-expert weights are ~25 GB in total (~12.7 GB per rank at TP=2, confirmed by VRAM accounting), which does not fit a single 24 GB card alongside any cache. Anyone reproducing on >=32 GB cards can and should check TP=1.
The
hybridvsoffloadcomparison is unaffected: both ran at TP=2 with identical flags, so the 8.3x gap is attributable to the backend alone. If anything, #62 means both figures would rise together at TP=1.Config was otherwise identical across runs (
--moe-cache-auto, cache resolved to 624 slots / 5.7% residency in every case), so the difference is attributable to the backend alone.Why this matters
The wrong call is made silently.
--moe-backend autoresolves tohybridhere and produces the same 0.67 tok/s; nothing in the logs or inft ctl statspoints at the backend as the cause. Without a reason to tryoffloadby hand, the reasonable conclusion is that the engine is unusable on this hardware, when in fact it is 8.3x faster one flag away.Suggestions
ft ctl statsreportsdecode_tpsbut not expert cache hit rate or the realised CPU-vs-PCIe split. Those would make this diagnosable from the outside, and would also cover the Desktop-UI side of [Feature Request]: Add real-time expert cache hit rate and PCIe transfer telemetry to Desktop UI and CLI #76.hybridon architectures without native FP4, or excluding it there until the CPU-side cost model accounts for per-activation overhead.Secondary issues found in the same session
-lnccllink failure with--tp-size > 1. The JIT build offreetoken__pynccl.sofails on a cleanuv pip install "freetoken[accel]":torch ships
nvidia/nccl/lib/libnccl.so.2without thelibnccl.sodevel symlink that-lncclrequires. Workaround:The build could resolve the NCCL path from
torch.utils.cpp_extension/ the installednvidia-nccl-*package instead of relying on a system-lnccl.--moe-cache-sizeaccepts a value that makes startup fail.ft ctl cachereported the moe pool asresizable to 256..828, but--moe-cache-size 828aborts the backend:The advertised upper bound does not account for the KV/SWA floor, so the value the tool itself reports as the maximum is not actually selectable. Possibly the same budget-solve area as #111 and #150.
Kernel cache directory is not created. Every start recompiles kernels:
What worked
For balance: the model itself runs correctly on SM86. fp8 dense weights and fp4 experts are dequantised at runtime with no kernel errors,
dsv4_sparseattention is selected automatically, and output quality is fine. The only hard blocker to getting it up was the NCCL symlink above. The problem is backend selection, not Ampere support as such.