Skip to content

CUDA streaming: per-size-class expert caches, keep cache warm across prefills#504

Open
iCreil wants to merge 1 commit into
antirez:mainfrom
iCreil:fix/cuda-streaming-mixed-expert-cache
Open

CUDA streaming: per-size-class expert caches, keep cache warm across prefills#504
iCreil wants to merge 1 commit into
antirez:mainfrom
iCreil:fix/cuda-streaming-mixed-expert-cache

Conversation

@iCreil

@iCreil iCreil commented Jul 5, 2026

Copy link
Copy Markdown

Fixes #503.

See the issue for the full analysis and benchmarks. Summary of the three changes, all in ds4_cuda.cu:

1. Per-size-class expert caches. g_stream_expert_cache becomes a fixed array of 4 caches keyed by (gate_expert_bytes, down_expert_bytes). note_size() is replaced by class_index() (assigns a free slot on first sight; recycles class 0 in the — never observed — case of more than 4 distinct sizes). Runtime caps and cap-notice dedup move to per-class arrays. prepare() operates on the class cache; cross-class transitions no longer release anything.

2. Conditional release in begin_compact_load. The unconditional release_all() on the allow_global_cache=0 path becomes: try ensure_slots() first; on allocation failure release the expert caches and retry once. Long-prompt VRAM pressure is still handled, short prompts keep the warm cache.

3. Prefill batch reads the cache. prepare_selected_batch now passes allow_global_cache=1 plus a new allow_cache_evict=0 mode: batch loads copy hits device-to-device and may append to free capacity, but never evict valid slots (an LRU cache smaller than a long prompt's expert union would otherwise be fully cycled, destroying the decode working set). The single-token decode path keeps full LRU behaviour (allow_cache_evict=1).

Notes:

  • The memset over the struct containing a std::vector is replaced by explicit member resets (vector().swap()), which also fixes a latent leak of the slots buffer.
  • Budget semantics: a count budget now applies per size class, so classes can overcommit in aggregate; the lazily-growing model-range arena can then fail mid-decode. A shared byte budget across classes would be safer — left out of scope here to keep the diff focused, happy to follow up. Practical guidance for the 96GB + Flash q2-q4 case is in the issue (budget 9472, reserve ≥ 12 GB).
  • Tested on RTX PRO 6000 Blackwell 96GB / CUDA 13.1 / make cuda-generic with Flash q2-imatrix (uniform: behaviour unchanged) and Flash q2-q4-imatrix (fixed: ~0.9 → ~25 t/s sustained decode, 97% cache hit rate per DS4_CUDA_STREAMING_EXPERT_CACHE_VERBOSE).

…prefills

Mixed-quant GGUFs (e.g. the official Flash q2-q4-imatrix) interleave layers
whose routed experts have different byte sizes. The CUDA expert cache was a
single global slab keyed on the last-seen size: every q2<->q4 layer
transition reset the runtime cap and released the whole cache, so decode on
mixed models was stuck at ~1 t/s with a permanent cap-notice flood.

Additionally, prefill batch loads released the entire resident expert cache
unconditionally before allocating their staging buffers, forcing a full
cache re-warm on every request (this also affects uniform models).

Changes:
- keep up to 4 expert caches, one per (gate,down) byte-size class; runtime
  caps and cap notices are tracked per class
- begin_compact_load: try the staging allocation first and only sacrifice
  the expert cache when VRAM is actually short (retry once)
- let the prefill batch path read the global cache (hits are device-to-
  device copies instead of host uploads), appending on miss only while
  free capacity remains, never evicting: a long prompt cannot cycle out
  the decode working set

On an RTX PRO 6000 Blackwell 96GB with the official Flash q2-q4-imatrix
GGUF, a 200-word completion goes from not finishing within 400s (~0.9 t/s)
to ~25 t/s sustained decode with --ssd-streaming-cache-experts 9472 and
DS4_CUDA_STREAMING_EXPERT_CACHE_RESERVE_GB=12 (97% cache hit rate per the
VERBOSE stats). Uniform-quant GGUFs keep their existing behaviour.
giannisanni pushed a commit to giannisanni/neutronstar that referenced this pull request Jul 7, 2026
…fills

Port of iCreil's PR against upstream main, applied to the glm-local CUDA
streaming stack (comments translated to English):

1. Per-size-class expert caches (4 classes keyed on gate/down expert bytes).
   Mixed-quant models no longer thrash the cache on layer-size transitions.
   For this fork's GLM merge it means the Q2_K MTP draft layer (blk.78) can
   be cached alongside the IQ2_XXS layers instead of falling back to model
   views, and the slab-budget gate now sees a per-class budget.
2. Prefill batch loads try their staging allocation before releasing the
   resident expert cache, and only sacrifice it when VRAM is truly short.
   Short chat prompts no longer throw away the warm decode working set.
3. Prefill loads may append to free cache capacity but never evict valid
   slots, so long prompts cannot cycle out the decode working set. Prefill
   hits become device-to-device copies.

Tested on the 4060 Ti / GLM-5.2 rig: one-shot regression clean (0.33 t/s,
correct output), two-turn chat clean (turn 2 resume 0.35/0.32 t/s, memory
low point 7.7GB, no OOM).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CUDA ssd-streaming: expert cache thrashes on mixed-quant GGUFs (decode ~0.9 t/s); prefill drops the whole cache every request

1 participant