82 lines
4.0 KiB
Markdown
82 lines
4.0 KiB
Markdown
# 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](https://github.com/NVIDIA/TensorRT-LLM/pull/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 a
|
|
`slot_mapping` of 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 with
|
|
`shape_desc.nl` walking 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.
|