Files

LMCacheRedis KV Connector

The cpp binding is in csrc/redis/*.

The key optimization is multi-threading and batching on the C layer and the python side being awoken through the eventfd API where a callback consumes the completion belonging to a non-blocking submission.

An GET / SET benchmark is in examples/kv_cache_reuse/remote_backends/resp/benchmark_resp_client.py. The python client lives inside of lmcache/v1/storage_backend/resp_client.py.

Install lmcache from source, then run a sanity check:

# Run with defaults: host=127.0.0.1, port=6379, chunk-mb=4.0, num-workers=8, num-keys=500
python benchmark_resp_client.py

# Or customize parameters:
python benchmark_resp_client.py \
    --host localhost \
    --port 6379 \
    --chunk-mb 1.0 \
    --num-workers 8 \
    --num-keys 1280 \
    --username default \
    --password YOUR_PASSWORD

Quickstart

Start up redis with multiple io threads:

git clone https://github.com/redis/redis.git
cd redis
git checkout 8.2
make -j
./src/redis-server --protected-mode no --save '' --appendonly no --io-threads 4

Clear the state between queries

sudo apt install redis-cli
redis-cli -p 6379 FLUSHALL
redis-cli -p 6379 DBSIZE

Deploy LMCache with the custom LMCacheRedis KV Connector

save_unfull_chunk must be off (default is off) and also we must not save the chunk metadata.

The "golden spot" for high throughput transfers for redis is ~4 MB (any higher or lower will cause performance degradation, for a model like meta-llama/Llama-3.1-8B-Instruct, this is around 16 tokens

LMCACHE_CONFIG_FILE=resp.yaml \
vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --kv-transfer-config '{"kv_connector":"LMCacheConnectorV1", "kv_role":"kv_both"}' \
    --no-enable-prefix-caching \
    --load-format dummy

MP Mode (Multiprocess)

MP mode runs LMCache as a separate server process, communicating with vLLM over ZMQ. The RESP connector serves as an L2 adapter, supporting variable-size chunks. See csrc/storage_backends/README.md for the full native backend architecture.

Launch Redis

# Build Redis 8.2 with IO threads
git clone https://github.com/redis/redis.git && cd redis
git checkout 8.2 && make -j
./src/redis-server --protected-mode no --save '' --appendonly no --io-threads 4 --port 6379

Launch LMCache MP Server

lmcache server \
    --l1-size-gb 10 \
    --eviction-policy LRU \
    --chunk-size 16 \
    --l2-adapter '{"type": "resp", "host": "localhost", "port": 6379, "num_workers": 8}' \
    --port 6555

The --l2-adapter JSON accepts these fields:

Field Type Default Description
type str (required) Must be "resp"
host str (required) Redis hostname
port int (required) Redis port
num_workers int 8 C++ worker threads for parallel I/O
username str "" Redis ACL username
password str "" Redis AUTH password
max_capacity_gb float 0 Max L2 capacity in GB for usage tracking (required for L2 eviction)

Launch vLLM with LMCache MP Connector

PORT=8000
vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --kv-transfer-config '{
        "kv_connector": "LMCacheMPConnector",
        "kv_role": "kv_both",
        "kv_connector_extra_config": {
            "lmcache.mp.host": "tcp://localhost",
            "lmcache.mp.port": 6555
        }
    }' \
    --no-enable-prefix-caching \
    --port $PORT \
    --load-format dummy

Test (MP and non-MP mode)

Send the same prompt twice. The first request stores KV cache to Redis via the MP server; the second retrieves it.

PORT=8000
PROMPT="$(printf 'Elaborate the significance of KV cache in language models. %.0s' {1..1000})"

# First request: store
curl -s -X POST http://localhost:${PORT}/v1/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"meta-llama/Llama-3.1-8B-Instruct","prompt":"'"$PROMPT"'","max_tokens":10}'

# Second request with same prefix: retrieve from Redis
curl -s -X POST http://localhost:${PORT}/v1/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"meta-llama/Llama-3.1-8B-Instruct","prompt":"'"$PROMPT"'","max_tokens":10}'

Check Redis to verify data was stored:

redis-cli -p 6379 DBSIZE