Conversation
Signed-off-by: stw <stw@zurich.ibm.com>
Signed-off-by: Stanislaw Wozniak <stw@zurich.ibm.com>
Signed-off-by: Stanislaw Wozniak <stw@zurich.ibm.com>
Signed-off-by: Stanislaw Wozniak <stw@zurich.ibm.com>
|
This pull request has merge conflicts that must be resolved before it can be |
|
@mgoin - can anyone look into this? This PR results in 2x TTFT reduction for follow up requests on Qwen3.6-35B-A3B which is a significant improvement. Current mamba cache performance is pretty poor, especially compared to llama.cpp to the point that despite having 3x slower initial prefill, llama.cpp still wins over vLLM by being able to utilize the cache more efficiently. That's on DGX Spark. |
|
This is a quickly drafted PoC. I've just updated it with a more extensive description of the idea above in Technical details. If there is interest, I could refactor and clean up the code. |
Revives and continues draft PR vllm-project#46251: on hybrid Mamba models, mamba state slots become LARGE blocks spanning N small attention blocks (N = large_block_factor, derived from the state-page to attention-page ratio), so attention keeps its small block size instead of being inflated to the mamba state span. Align-mode prefix caching then hits at the small-block grain while states are cached at span cadence. Includes the correctness fixes found while reviving the draft: cross-granularity stale-hash eviction (both directions), identity-based block classification in deferred frees, admission-unit scaling, CoW large-to-small expansion with explicit page sizing, granularity- qualified partial-hash indexing, scheduler-side factor derivation from the KV cache specs (worker-stamped config does not survive TP>1), span-based XPU factor recomputation, and the NIXL transfer unit reading the explicit state page size. "all" mode keeps the legacy flat layout unchanged. Combinations that would break silently now surface: sink attention, dtype-skip packing, and CPU offloading of hierarchical states fail loudly; simple KV offload logs a warning once and disables itself, pending follow-up support. Signed-off-by: Daniel May <daniel@danielmay.co.uk> Co-authored-by: s3woz <s3woz@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
Revives and continues draft PR vllm-project#46251: on hybrid Mamba models, mamba state slots become LARGE blocks spanning N small attention blocks (N = large_block_factor, derived from the state-page to attention-page ratio), so attention keeps its small block size instead of being inflated to the mamba state span. Align-mode prefix caching then hits at the small-block grain while states are cached at span cadence. Includes the correctness fixes found while reviving the draft: cross-granularity stale-hash eviction (both directions), identity-based block classification in deferred frees, admission-unit scaling, CoW large-to-small expansion with explicit page sizing, granularity- qualified partial-hash indexing, scheduler-side factor derivation from the KV cache specs (worker-stamped config does not survive TP>1), span-based XPU factor recomputation, and the NIXL transfer unit reading the explicit state page size. "all" mode keeps the legacy flat layout unchanged. Combinations that would break silently now surface: sink attention, dtype-skip packing, and CPU offloading of hierarchical states fail loudly; simple KV offload logs a warning once and disables itself, pending follow-up support. Signed-off-by: Daniel May <daniel@danielmay.co.uk> Co-authored-by: s3woz <s3woz@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
|
Hi @s3woz - apologies for the tag noise above, I've been iterating on a continuation of this draft in a fork as an experiment and didn't realize each cleanup push would ref the thread here, lesson learned! 🙏 The headline is that your design here roughly triples usable capacity (74k to ~245k tokens) with prefix caching turned on for the latest Nemotron 75B model at W4A16 in VRAM-constrained scenarios like 2x24GB consumer GPUs, with fine-grained cache hits intact. I used AI agents heavily in the implementation and as such have been subjecting it to heavy review, and intended to come back to this thread once I had it into a feasible state I was ready to defend. I'm still assessing what's left to get it fit for a PR, it's not there yet, but wanted to provide an early explanation and share the positive results I'd seen. |
|
#45702 is working on improving the cache hit rate for hybrid model. |
Purpose
Note: This is a draft PR and needs testing & verification.
Hybrid models often comprise attention layers with relatively small KVcache states and other layer types (e.g. Mamba, GDN) with relatively large cache states. Current vLLM implementation sets the
block_sizein a way to keep the KVcache allocation unit of block the same for both attention and hybrid cache. This typically involves considerably increasing theblock_sizeof attention, resulting in lower cache hit chances.This PR separates the
block_sizesetting from the size of hybrid cache entries by allowing different sizes of block allocations for attention and SSM layers. This allows to set anyblock_sizefor hybrid models and by default to keep theblock_sizeof the attention, increasing the cache hit ratio.Note: Currently enabled only for
alignmode, to avoid too fast memory filling ofallmode.When running Mamba-based (non GDN) models switch from all to align with:
--mamba-cache-mode alignIn a prefill-oriented synthetic test with potential full cache hits (see test plan below), current PR substantially increases the cache hits (avoids the "steps" in the figure below), and speeds up the prefill (2.7x times) for
Qwen/Qwen3.6-35B-A3B:Technical details
High-level design idea:
class SingleTypeKVCacheManager(ABC):that is set by each layer type accordingly (current PR just uses a flag_is_large_block: booland sets toTruefor SSM layers, but we could make it to keep the exact size).Primary challenge with variable-sized allocations is the KV cache blocks memory fragmentation. To handle this, we pass information about previous allocation for each request to the block_pool, so that it can avoid fragmentation. Note: currently computed with
_last_non_null_block_id(req_blocks), but I would rather just passreq_blocksthere and let the block_pool figure out how to defragment.At this point we have:
Fragmentation avoidance policy:
All of the complexity gets hidden into
block_poolas follows:block_poolimplementation can be used, based on model configuration and defragmentation policy.gpu_model_runnercan be reused with no changes, andnum_blocksfor tensor shape can be determined based onstate_page_bytesfor each layer type. All kernels have their own views of the shared memory.block_poolchecks what was thelast_hit_block_idfor that request and checks if there are small blocks available within that large block. It returns them if they are available, if not it takes small blocks from the next free large block. This bundles together memory blocks from the same request or requests that have a shared prefix, which seems like a viable heuristic. (It's much better than allowing random requests to allocate, as one frequently used small block could lock the entire large block for a long time.)This is one possible implementation. By having a clear API decoupling between the KV cache manager allocations and the underlying
block_poolimplementation, the community could contribute other alternative implementations. (current implementation has a limitation that it handles only two allocation size: large and small, but it opens a path to new alternative implementations with more options)@tdoublep
Test Plan
Test Result