Repository: https://github.com/efschu/LMCache
Branch: dev
Latest Release: v0.4.8rc2-dev15
| Asset | SHA256 |
|---|---|
lmcache-0.4.8rc2.dev15-cp312-cp312-linux_x86_64.whl |
ebbf8be0... |
Quick Install:
curl -L -O https://github.com/efschu/LMCache/releases/download/v0.4.8rc2-dev15/lmcache-0.4.8rc2.dev15-cp312-cp312-linux_x86_64.whl
pip install lmcache-0.4.8rc2.dev15-cp312-cp312-linux_x86_64.whl --force-reinstall --no-depsThis fork implements engine-driven KV cache transfer for hybrid multi-group models (e.g., Qwen3.6-27B with GDN/Mamba + Attention groups) in LMCache v1.
The original LMCache lmcache server process allocated 666 MB VRAM per GPU even though it should be a pure CPU-caching daemon. This happens because:
vLLM Worker (GPU)
└─ sends CUDA-IPC handles → LMCache Server
└─ Server opens CUDA-IPC → creates CUDA Primary Context (~550 MB/GPU)
└─ Server executes multi_layer_block_kv_transfer CUDA kernels
Additionally, the engine-driven path was blocked for hybrid models due to a hard check:
def _single_group_block_ids(block_ids: list[list[int]]) -> list[int]:
if len(block_ids) != 1:
raise RuntimeError(
"engine-driven transfer does not support hybrid KV cache groups"
)Qwen3.6-27B has at least 2 groups (Attention + GDN-State), so this check blocked engine-driven mode entirely.
vLLM Worker (GPU)
└─ executes GPU→CPU copy itself (gather_paged_kv_to_cpu)
└─ sends CPU bytes via COMMIT_STORE
LMCache Server (CPU-only, NO VRAM!)
└─ receives CPU bytes, deserializes
└─ stores in L1-RAM / L2-Disk
Added GroupLayoutInfo to track per-group metadata:
block_size,num_layers,hidden_dim_size,dtype_struse_mla(Modified Local Attention flag)tokens_per_block(from EngineGroupInfo)
Extended RegisterEngineDrivenContextPayload with optional group_layouts: list[GroupLayoutInfo].
slice_kv_caches_for_group(): Extract layer subset for one groupgather_paged_kv_multi_group_to_cpu(): Gather all groups to CPU tensorsscatter_cpu_multi_group_to_paged_kv(): Scatter CPU tensors back to GPU paged KV
EngineDrivenTransferContext.register(): Sends per-group layout metadataEngineDrivenTransferContext.submit_store(): Gathers all groups, serializes as pickle blobEngineDrivenTransferContext.submit_retrieve(): Deserializes blob, scatters to all groups
register_kv_cache_engine_driven_context(): Creates per-groupMemoryLayoutDescentries_commit_store_multi_group(): Stores to all Object Groups_prepare_retrieve_multi_group(): Loads all groups, returns serialized blob
Fixed a race condition where multi-group prefetch handles (groups 1-3) were submitted but never awaited:
- Added
multi_group_handles: list[PrefetchHandle]to_PrefetchJobdataclass - In
end_session(): await primary handle + allmulti_group_handlesviaquery_prefetch_status - Call
finish_read_prefetchedfor all groups (group-0 through group-N) - Touch L1 keys only for group-0 (matching original behavior)
# Download from GitHub Releases or build from source
# Latest build: wheelhouse/lmcache-0.4.8rc2.dev15-cp312-cp312-linux_x86_64.whlpip install lmcache-*.whl --force-reinstall --no-deps# Start LMCache server (CPU-only, no GPU memory!)
CUDA_VISIBLE_DEVICES="" lmcache server \
--max-workers 1 \
--max-gpu-workers 1 \
--max-cpu-workers 1 \
--chunk-size 1600 \
--l1-size-gb 10 \
--eviction-policy LRU \
--port 6555 \
--l2-adapter '{"type":"fs","base_path":"/kv-cache", "max_capacity_gb": 250}' &
# Start vLLM with LMCache connector
vllm serve Qwen/Qwen3.6-27B \
--kv-transfer-config '{
"kv_connector": "LMCacheMPConnector",
"kv_role": "kv_both",
"kv_connector_extra_config": {
"lmcache.mp.host": "tcp://localhost",
"lmcache.mp.port": 6555,
"lmcache.mp.mp_transfer_mode": "auto"
}
}'Important: Use CUDA_VISIBLE_DEVICES="" (not NVIDIA_VISIBLE_DEVICES="") to prevent the server from allocating GPU memory.
| Document | Description |
|---|---|
| README.md | This file - Overview and quick start |
| BUILD_GUIDE.md | Step-by-step build instructions |
| BUILD.md | Technical build details and CI/CD |
| Lmcache_engine_driven_multigroup.md | Full design document with protocol specs |
See BUILD.md for detailed build instructions.
# Clone repository
git clone https://github.com/efschu/LMCache.git
cd LMCache
# Build wheel with Docker (GPU required)
docker run --rm \
--gpus all \
--security-opt apparmor=unconfined \
-v $(pwd):/lm \
-v $(pwd)/wheelhouse:/whl \
ghcr.io/efschu/lmcache-manylinux-builder-gpu \
bash -c '
export TORCH_CUDA_ARCH_LIST="8.6;8.9;9.0"
export ENABLE_CXX11_ABI=1
cd /lm
/opt/python/cp312-cp312/bin/pip wheel . --no-deps -w /whl
'
# Install
pip install wheelhouse/lmcache-*.whl --force-reinstall --no-depsThis commit contains the full implementation:
-
lmcache/v1/multiprocess/custom_types.py- Added
GroupLayoutInfomsgspec.Struct for per-group layout metadata - Extended
RegisterEngineDrivenContextPayloadwithgroup_layoutsfield
- Added
-
lmcache/v1/multiprocess/transfer_context/base.py- Added
slice_kv_caches_for_group()helper function - Added
gather_paged_kv_multi_group_to_cpu()for multi-group gather - Added
scatter_cpu_multi_group_to_paged_kv()for multi-group scatter
- Added
-
lmcache/v1/multiprocess/transfer_context/worker_transfer.py- Extended
EngineDrivenTransferContextwith multi-group support - Added
_serialize_multi_group_chunks()and_deserialize_multi_group_chunks() - Modified
register(),submit_store(),submit_retrieve()for multi-group
- Extended
-
lmcache/v1/multiprocess/modules/engine_driven_transfer.py- Extended
register_kv_cache_engine_driven_context()for multi-group registration - Added
_commit_store_multi_group()server-side storage - Added
_prepare_retrieve_multi_group()server-side retrieval
- Extended
-
lmcache/v1/multiprocess/modules/lookup.py- Fixed multi-group prefetch handling: await all group handles before
finish_read_prefetched - Added
multi_group_handlesfield to_PrefetchJobdataclass - Modified
end_session()to callfinish_read_prefetchedfor all groups
- Fixed multi-group prefetch handling: await all group handles before
-
lmcache/v1/multiprocess/group_view.py- Added
EngineGroupInfomsgspec.Struct for engine-side group metadata
- Added
-
lmcache/v1/protocols/engine.py- Extended
PrepareRetrieveResponsewithcpu_data: bytesfield
- Extended
# Run unit tests
pytest tests/
# Run specific multi-group tests
pytest tests/v1/ -k "multi_group or engine_driven"See Lmcache_engine_driven_multigroup.md for the full design document including:
- Detailed data flow analysis - How data flows between vLLM and LMCache
- Protocol specifications - Wire format for engine-driven transfer
- API contracts - Function signatures and expected behaviors
- Migration guide - How to migrate from old transfer mode
┌─────────────────────────────────────────────────────────────────────┐
│ vLLM Worker │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────────┐ │
│ │ Paged KV │───▶│ gather_paged │───▶│ EngineDrivenTransfer │ │
│ │ Cache │ │ _kv_to_cpu() │ │ .submit_store() │ │
│ └─────────────┘ └──────────────┘ └───────────┬───────────┘ │
│ │ │
│ ┌─────────────┐ ┌──────────────┐ │ │
│ │ Paged KV │◀───│ scatter_cpu │◀──────────────┘ │
│ │ Cache │ │ _to_paged_kv │ ┌───────────────────────┐ │
│ └─────────────┘ └──────────────┘ │ EngineDrivenTransfer │ │
│ │ .submit_retrieve() │ │
│ └───────────┬───────────┘ │
└─────────────────────────────────────────────────────┼─────────────┘
│ IPC
▼
┌─────────────────────────────────────────────────────────────────────┐
│ LMCache Server (CPU-only) │
│ ┌──────────────┐ ┌─────────────────┐ ┌───────────────────┐ │
│ │ Lookup │───▶│ finish_read │───▶│ ObjectGroupStore │ │
│ │ Manager │ │ _prefetched() │ │ (all groups) │ │
│ └──────────────┘ └─────────────────┘ └───────────────────┘ │
│ │ │ │
│ │ ┌─────────────────┐ │ │
│ └───▶│ PrefetchJob │ │ │
│ │ (handles for │ │ │
│ │ groups 0..N) │ │ │
│ └─────────────────┘ │ │
│ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ L1 Memory │◀──▶│ LRU Eviction │ │
│ │ (RAM) │ │ Policy │ │
│ └──────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ L2 Disk │ │
│ │ (KV-Cache) │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Savings: ~668 MB per GPU for LMCache server overhead elimination.
Apache 2.0 (same as upstream LMCache)
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Build and test:
pip wheel . --no-deps -w wheelhouse/ - Submit a pull request