Repository navigation
perf(moe): scan the hybrid eviction pool in chunks and add fetch rounding modes - #778
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Speed up victim selection in
_ensure_experts_hybrid_kernelwithout changing its results, fix a race in its route rewrite, and addFREETOKEN_HYBRID_FETCH_ROUNDto choose how many misses a hybrid decode step fetches.Why main spills
Phase 2 loads the whole slot pool (
next_pow2(pool slots)wide) into the registers of one CTA, then scans it once per route to mask the slots this step uses. On a 23,197-slot pool (Qwen3.8-Flash-Next NVFP4, 80 GB) that is 32,768 int64 usages plus ids: the cubin uses 32 registers and a 2,472 B stack, and 63% of its SASS is STL/LDL. One fetch costs 77 us inside the kernel.Change
step, the largest usage in the pool, so after a CTA barrier the argmin never picks them.Routes, copy plan and cache state are bit-identical to main (lockstep over the same random routes, pools of 2,048 to 23,197 slots and per-layer pools).
Fix
Phase 3 rewrites
expert_ids[i]in place. In the PTX every thread loadsexpert_ids[i]and then indexesslot_for_idwith it, while only thread 0 stores the rewritten slot id; a warp that loads after that store indexes with a slot id. A barrier between the loads and the store fixes it. Without it, a 23,197-slot pool withclamp=Falseand back-to-back calls (nocopy_missingin between) faults with an illegal memory access on main; with only this barrier added to main's kernel the same 72,000 calls run.FREETOKEN_HYBRID_FETCH_ROUND
For M misses and the benched fetch fraction f (PCIe / CPU bandwidth), the step fetches about f * M experts and the CPU computes the rest:
min-wait(default, current behaviour): of floor(f * M) and floor(f * M) + 1, take the one that makes the PCIe fetch and the CPU compute finish closest together.stochastic: floor(f * M), plus one with probability equal to the fractional part.prefer-fetch: ceil(f * M), so any miss fetches at least one expert into the cache.Test
test_hybrid_ensure_matches_lru_referenceruns the kernel against a Python model of the LRU contract, call by call from an empty to a full pool, for a pool that fits one tile, one that needs two, and per-layer pools, with clamp on and off and all three rounding modes. Before the barrier, the full parametrized run faulted in the two-tileclamp=Falsecases.Results
H100 80GB, Xeon 8480+, CUDA 13.0, torch 2.14.1, Triton 3.8.0.
nvidia/Qwen3.8-Flash-Next-NVFP4, AIME25 problem 1, T 1.0 / top-p 0.95 / top-k 20, decode tok/s median of 5 runs after one warm-up. A/B on main cc56b6b; after rebasing onto c5b5b72 the lockstep and the tests were run again.stochasticprefer-fetchKernel time for one fetch at 23,197 slots: 76.9 -> 7.7 us. With the default rule a single miss is not fetched, so at 23,197 slots the kernel rarely reaches Phase 2.