Skip to content

hybrid MoE strategy 20-40x slower than offload on an offload-heavy box (24 GB card, PCIe gen 3) #436

Description

@gberasmus87

Summary

On a single 24 GB card with the routed experts offloaded, --moe-strategy hybrid decodes at 0.17-1.02 tok/s. The same tree, same checkpoint, same box, with only --moe-strategy offload instead, decodes at 23.07 tok/s — about 20-40x faster. GPU utilisation during generation is 0% under hybrid and 85% under offload.

hybrid is not a niche choice here: --moe-strategy auto resolves a MoE model to hybrid when a ft bench bw profile recommends it, which it does on this box, and hybrid is what made this configuration fast in the first place — the same box on v0.1.2 serves this checkpoint at 33.5 tok/s under hybrid.

Numbers

Single RTX PRO 4000 Blackwell 24 GB (sm_120), PCIe gen 3 x16, EPYC 7302, CUDA 13.0, torch 2.11.0+cu130, TP=1, lovedheart/Qwen3.8-Flash-Next-NVFP4-FP8 (123 GiB, NVFP4 routed experts + block-FP8 dense), --moe-cache-auto, --kv-reserve-tokens 65536, --moe-prefill-hit-d2d.

tree --moe-strategy decode (end-to-end, every streamed delta) engine-reported decode GPU util
v0.1.2 + local patches hybrid 33.5 tok/s 33.4 - 38.9 74-95%
main + #427 + #428 (b16877b) hybrid never completed a 3-prompt run 0.17 / 0.43 / 0.55 / 0.56 / 0.74 / 1.02 0%
main + #427 + #428 (b16877b) offload 23.07 tok/s 21.3 - 25.9 85%

The hybrid figures come from three independent launches of the new tree, so they are not a cold-start artifact. Both new-tree rows are the same install; the only difference is the strategy flag.

What it looks like while it is slow

  • GPU 0% during generation, weights resident at 21,962 MiB.
  • One CPU thread pinned at ~100% (state=R, wchan=0, ~1,034 s of CPU accumulated) while the other 76 threads sit idle — including the whole CPU MoE executor ready: threads=15 (pinned to cores 0..14) pool. So neither the GPU nor the CPU expert pool is doing the work; something is spinning on the engine's main thread.
  • Under offload on the identical install that same thread reads 3.0% CPU, state=Ssl.

That pattern points at the hybrid split itself. Startup logs --moe-hybrid-max-fetch auto: fetching 27.8% of each decode step's expert misses over PCIe (benched PCIe/CPU bandwidth ratio), the rest on the CPU — and it is exactly the "rest on the CPU" half that appears not to reach the pool.

Ruled out

  • Not JIT compilation. ~/.triton/cache stopped growing (4,015 files / 145 MB, unchanged over a 30 s sample) while that thread burned CPU.
  • Not expert-cache starvation. --moe-cache-auto resolves moe_cache_size=3741 num_pages=1025, versus 4041 on the old build — 7% down, nowhere near enough.
  • Not the deprecated flags. Re-ran with --nvfp4-backend triton removed entirely (quant_backend=None); it still resolves MoE experts: nvfp4 via triton and is still 0.55 tok/s. The offload run above also has that flag removed.
  • Not the loader. feat(qwen4_exp): load block-fp8 dense projections natively #428's reader loads this checkpoint correctly — all buffers match, output is correct, and the offload run proves the same install serves it fine.

Bisect status

I have not bisected. My baseline is v0.1.2 plus local patches rather than upstream main, because main cannot load this checkpoint at all without #428, so the window is v0.1.2..b16877b — roughly 34 commits including the #418 quantization refactor. Each cycle here is a ~5 minute install plus load, so it is a few hours; happy to do it if that is the fastest way to close this out, or to run any specific commit, flag set, or instrumented build you want to point at.

I could not get a Python stack: the box is ptrace_scope=1 and running the engine under py-spy record --subprocesses attached and sampled but never flushed before the window closed.

Why this may not show elsewhere

hybrid only carries real load when the experts genuinely do not fit. On an 80 GB H100 or 2 x 48 GB Ada this model is close to resident and the CPU-assist half of the split barely engages, so the same code path is nearly free. Here it carries 123 GiB of weights against 24 GB of VRAM on a gen-3 board, which is the regime the offload family exists for.

Context: first reported on #428 (#428 (comment)), narrowed to the strategy flag afterwards.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions