TensorRT-LLM integration
Adapter shape
lmcache/integration/tensorrt_llm/
├── __init__.py # Optional-import surface
├── utils.py # ENGINE_NAME, lmcache_get_config,
│ # create_trtllm_metadata
├── tensorrt_adapter.py # In-process — engine in TRT-LLM process
└── tensorrt_mp_adapter.py # Multi-process — engine in standalone server
Both adapters subclass TRT-LLM's KvCacheConnectorScheduler and
KvCacheConnectorWorker. The TRT-LLM imports are guarded at module
level only via the package's __init__; nothing in core LMCache
imports the adapter modules. This keeps pip install lmcache unaffected
when TRT-LLM is absent.
The TRT-LLM connector preset registry (PR NVIDIA/TensorRT-LLM#12626) maps:
| Preset | Module | Scheduler | Worker |
|---|---|---|---|
lmcache |
lmcache.integration.tensorrt_llm.tensorrt_adapter |
LMCacheKvConnectorScheduler |
LMCacheKvConnectorWorker |
lmcache-mp |
lmcache.integration.tensorrt_llm.tensorrt_mp_adapter |
LMCacheMPKvConnectorScheduler |
LMCacheMPKvConnectorWorker |
Lifecycle
| Stage | TRT-LLM hook | LMCache call |
|---|---|---|
| Init | worker.register_kv_caches(kv_cache_tensor) |
Build engine via _get_or_create_engine; call gpu_connector.register_kv_caches(kv_cache_tensor) |
| Before scheduling | scheduler.get_num_new_matched_tokens(req, num_computed) |
engine.lookup(tokens) (in-process) or LOOKUP + QUERY_PREFETCH_STATUS (MP) |
| Pre-forward | scheduler.build_connector_meta(scheduler_output) |
LMCacheConnectorMetadata(loads=..., saves=...) |
| Forward | worker.start_load_kv(stream) |
engine.retrieve(tokens, block_ids) |
| Forward | worker.wait_for_save(stream) |
engine.store(tokens, block_ids) |
In-process vs MP
The two modes share the lifecycle but differ in where state lives.
| Aspect | In-process (lmcache) |
Multi-process (lmcache-mp) |
|---|---|---|
| LMCache engine | Singleton inside the TRT-LLM process | Standalone ZMQ server |
| Tensor sharing | Direct (same process) | RawCudaIPCWrapper (cudaIpc + cupy DLPack) |
| Lookup | engine.lookup(tokens) returns chunk count |
LOOKUP enqueues prefetch; QUERY_PREFETCH_STATUS reads result keyed by request_id |
| Configuration | LMCACHE_CONFIG_FILE env var |
Same; plus server_url in connector config (or LMCACHE_SERVER_URL env) |
| Failure mode | One process crash takes down both | Engine survives TRT-LLM crash; multiple TRT-LLM instances can share cache |
| Setup cost | None | Run python -m lmcache.v1.multiprocess.server |
Why not subclass VLLMPagedMemGPUConnectorV3
V3's transfer path is wrong for TRT-LLM:
- V3 uses the in-process kernel (
multi_layer_kv_transfer) with aslot_mappingof token positions and per-layer pointers. TRT-LLM's cross-layer pool is a single base pointer and we want to transfer by block ids, not slot positions. - TRT-LLM needs the MP kernel (
multi_layer_block_kv_transfer) which natively handles single-base-pointer cross-layer withshape_desc.nlwalking layers internally. There is nothing to inherit.
TRTLLMGPUConnector is therefore a standalone GPUConnectorInterface
implementation. It also exposes a bespoke
register_kv_caches(kv_cache_tensor) method called by the worker once
at init — separate from to_gpu/from_gpu. The factory in
lmcache/v1/gpu_connector/__init__.py constructs it from
LMCacheMetadata plus the device, and the adapter wires the pool
tensor in afterwards.
Forcing real LMCache hits in tests
TRT-LLM has its own GPU block reuse. To verify LMCache contributes the
hit (and not TRT-LLM's reuse), the E2E tests size TRT-LLM's pool tiny
(KvCacheConfig(max_tokens=512)) while sending prompts much larger than
512 tokens. The first request fills LMCache and TRT-LLM. The second is
guaranteed-evicted from TRT-LLM's pool and must come from LMCache —
which the test asserts via the lmcache_cached=… new_matched=… log
line on request 3.