lmcache-cli 0.4.5.dev0__py3-none-any.whl → 0.4.6.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lmcache/__init__.py +37 -1
- lmcache/_version.py +3 -3
- lmcache/cli/commands/bench/__init__.py +101 -2
- lmcache/cli/commands/bench/engine_bench/config.py +8 -4
- lmcache/cli/commands/bench/engine_bench/interactive/schema.py +41 -0
- lmcache/cli/commands/bench/engine_bench/request_sender.py +9 -0
- lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +23 -0
- lmcache/cli/commands/bench/engine_bench/workloads/prefix_suffix_tuner.py +637 -0
- lmcache/cli/commands/describe.py +20 -13
- lmcache/cli/commands/kvcache.py +2 -2
- lmcache/cli/commands/ping.py +1 -1
- lmcache/cli/commands/server.py +5 -5
- lmcache/cli/commands/test_cache.py +997 -0
- lmcache/integration/sglang/sglang_adapter.py +24 -12
- lmcache/integration/sglang/utils.py +2 -0
- lmcache/integration/tensorrt_llm/__init__.py +78 -0
- lmcache/integration/tensorrt_llm/tensorrt_adapter.py +377 -0
- lmcache/integration/tensorrt_llm/tensorrt_mp_adapter.py +487 -0
- lmcache/integration/tensorrt_llm/utils.py +94 -0
- lmcache/integration/vllm/lmcache_connector_v1.py +0 -5
- lmcache/integration/vllm/lmcache_connector_v1_085.py +0 -5
- lmcache/integration/vllm/lmcache_mp_connector.py +1202 -0
- lmcache/integration/vllm/lmcache_mp_connector_0180.py +11 -10
- lmcache/integration/vllm/lmcache_mp_connector_0201.py +1195 -0
- lmcache/integration/vllm/utils.py +13 -27
- lmcache/integration/vllm/vllm_ec_adapter.py +194 -0
- lmcache/integration/vllm/vllm_multi_process_adapter.py +230 -59
- lmcache/integration/vllm/vllm_v1_adapter.py +128 -21
- lmcache/non_cuda_equivalents.py +56 -7
- lmcache/storage_backend/serde/cachegen_decoder.py +11 -6
- lmcache/storage_backend/serde/cachegen_encoder.py +9 -6
- lmcache/tools/mp_status_viewer/__main__.py +1 -1
- lmcache/usage_context.py +5 -14
- lmcache/utils.py +51 -0
- lmcache/v1/cache_engine.py +59 -28
- lmcache/v1/check/check_mode_test_l2_adapter.py +50 -7
- lmcache/v1/check/utils.py +1 -6
- lmcache/v1/compute/attention/__init__.py +1 -0
- lmcache/v1/compute/attention/flash_attn.py +3 -2
- lmcache/v1/compute/attention/flash_infer_sparse.py +3 -2
- lmcache/v1/compute/attention/metadata.py +92 -1
- lmcache/v1/compute/attention/triton_kernels/__init__.py +13 -0
- lmcache/v1/compute/attention/triton_kernels/block_sparse_attention.py +559 -0
- lmcache/v1/compute/attention/triton_sparse.py +145 -0
- lmcache/v1/compute/attention/utils.py +74 -7
- lmcache/v1/compute/blend/blender.py +2 -1
- lmcache/v1/compute/models/base.py +17 -2
- lmcache/v1/compute/positional_encoding.py +9 -4
- lmcache/v1/config.py +220 -1
- lmcache/v1/distributed/config.py +5 -3
- lmcache/v1/distributed/eviction_policy/__init__.py +4 -0
- lmcache/v1/distributed/eviction_policy/factory.py +5 -0
- lmcache/v1/distributed/eviction_policy/isolated_lru.py +191 -0
- lmcache/v1/distributed/internal_api.py +8 -0
- lmcache/v1/distributed/l1_manager.py +42 -0
- lmcache/v1/distributed/l2_adapters/__init__.py +2 -1
- lmcache/v1/distributed/l2_adapters/base.py +206 -21
- lmcache/v1/distributed/l2_adapters/config.py +105 -2
- lmcache/v1/distributed/l2_adapters/dax_l2_adapter.py +583 -0
- lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +32 -15
- lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +9 -1
- lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +41 -26
- lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +98 -19
- lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +107 -40
- lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +39 -27
- lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +73 -34
- lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +10 -6
- lmcache/v1/distributed/l2_adapters/raw_block_l2_adapter.py +770 -0
- lmcache/v1/distributed/l2_adapters/s3_l2_adapter.py +1057 -0
- lmcache/v1/distributed/l2_adapters/serde_wrapper.py +649 -0
- lmcache/v1/distributed/quota_manager.py +94 -0
- lmcache/v1/distributed/serde/__init__.py +39 -0
- lmcache/v1/distributed/serde/async_processor.py +218 -0
- lmcache/v1/distributed/serde/base.py +215 -0
- lmcache/v1/distributed/serde/factory.py +69 -0
- lmcache/v1/distributed/serde/fp8.py +103 -0
- lmcache/v1/distributed/serde/utils.py +54 -0
- lmcache/v1/distributed/storage_controllers/eviction_controller.py +133 -6
- lmcache/v1/distributed/storage_controllers/prefetch_controller.py +225 -54
- lmcache/v1/distributed/storage_controllers/store_controller.py +270 -83
- lmcache/v1/distributed/storage_manager.py +126 -17
- lmcache/v1/ec_engine.py +202 -0
- lmcache/v1/gpu_connector/__init__.py +17 -9
- lmcache/v1/gpu_connector/gpu_connectors.py +382 -78
- lmcache/v1/gpu_connector/hpu_connector.py +4 -2
- lmcache/v1/gpu_connector/utils.py +682 -193
- lmcache/v1/gpu_connector/xpu_connectors.py +4 -2
- lmcache/v1/internal_api_server/api_registry.py +5 -12
- lmcache/v1/internal_api_server/api_server.py +17 -6
- lmcache/v1/kv_layer_groups.py +497 -187
- lmcache/v1/lazy_memory_allocator.py +12 -3
- lmcache/v1/lookup_client/lmcache_lookup_client.py +9 -1
- lmcache/v1/lookup_client/mooncake_lookup_client.py +17 -8
- lmcache/v1/memory_management.py +50 -16
- lmcache/v1/metadata.py +35 -31
- lmcache/v1/mp_observability/README.md +5 -0
- lmcache/v1/mp_observability/config.py +74 -3
- lmcache/v1/mp_observability/event.py +33 -1
- lmcache/v1/mp_observability/event_bus.py +53 -1
- lmcache/v1/mp_observability/otel_init.py +91 -17
- lmcache/v1/mp_observability/subscribers/logging/__init__.py +5 -1
- lmcache/v1/mp_observability/subscribers/logging/cb_server.py +119 -0
- lmcache/v1/mp_observability/subscribers/metrics/__init__.py +40 -0
- lmcache/v1/mp_observability/subscribers/metrics/cb_server.py +186 -0
- lmcache/v1/mp_observability/subscribers/metrics/engine.py +69 -0
- lmcache/v1/mp_observability/subscribers/metrics/event_bus.py +79 -0
- lmcache/v1/mp_observability/subscribers/metrics/l0_l1_throughput.py +148 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1_eviction_loop.py +48 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1_failures.py +86 -0
- lmcache/v1/mp_observability/subscribers/metrics/l2.py +35 -2
- lmcache/v1/mp_observability/subscribers/metrics/l2_failures.py +59 -0
- lmcache/v1/mp_observability/subscribers/metrics/l2_throughput.py +193 -0
- lmcache/v1/mp_observability/subscribers/metrics/lookup.py +93 -0
- lmcache/v1/mp_observability/subscribers/metrics/sm_lifecycle.py +166 -0
- lmcache/v1/mp_observability/subscribers/tracing/__init__.py +9 -1
- lmcache/v1/mp_observability/subscribers/tracing/cb_server.py +369 -0
- lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +22 -9
- lmcache/v1/multiprocess/blend_server_v2.py +578 -122
- lmcache/v1/multiprocess/custom_types.py +108 -8
- lmcache/v1/multiprocess/futures.py +18 -10
- lmcache/v1/multiprocess/gpu_context.py +133 -104
- lmcache/v1/multiprocess/http_api_registry.py +44 -0
- lmcache/v1/multiprocess/http_apis/__init__.py +1 -0
- lmcache/v1/multiprocess/http_apis/cache_api.py +263 -0
- lmcache/v1/multiprocess/http_apis/common_api.py +46 -0
- lmcache/v1/multiprocess/http_apis/conf_api.py +59 -0
- lmcache/v1/multiprocess/http_apis/healthcheck_api.py +31 -0
- lmcache/v1/multiprocess/http_apis/quota_api.py +173 -0
- lmcache/v1/multiprocess/http_apis/root_api.py +15 -0
- lmcache/v1/multiprocess/http_apis/status_api.py +25 -0
- lmcache/v1/multiprocess/http_apis/version_api.py +8 -0
- lmcache/v1/multiprocess/http_server.py +12 -63
- lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +4 -25
- lmcache/v1/multiprocess/mq.py +13 -11
- lmcache/v1/multiprocess/protocols/engine.py +5 -2
- lmcache/v1/multiprocess/server.py +162 -30
- lmcache/v1/platform/__init__.py +19 -0
- lmcache/v1/platform/event_notifier.py +242 -0
- lmcache/v1/standalone/__main__.py +2 -1
- lmcache/v1/storage_backend/__init__.py +15 -12
- lmcache/v1/storage_backend/abstract_backend.py +17 -2
- lmcache/v1/storage_backend/connector/hf3fs_adapter.py +275 -0
- lmcache/v1/storage_backend/connector/hf3fs_connector.py +622 -0
- lmcache/v1/storage_backend/connector/hfbucket_adapter.py +55 -0
- lmcache/v1/storage_backend/connector/hfbucket_connector.py +965 -0
- lmcache/v1/storage_backend/connector/mooncakestore_connector.py +281 -65
- lmcache/v1/storage_backend/dax/__init__.py +9 -0
- lmcache/v1/storage_backend/dax/core.py +865 -0
- lmcache/v1/storage_backend/gds_backend.py +31 -3
- lmcache/v1/storage_backend/local_cpu_backend.py +81 -5
- lmcache/v1/storage_backend/local_disk_backend.py +35 -18
- lmcache/v1/storage_backend/maru_backend.py +2 -1
- lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +11 -6
- lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +6 -5
- lmcache/v1/storage_backend/nixl_storage_backend.py +264 -30
- lmcache/v1/storage_backend/path_sharder.py +5 -7
- lmcache/v1/storage_backend/pd_backend.py +479 -40
- lmcache/v1/storage_backend/pd_backend_async.py +1664 -0
- lmcache/v1/storage_backend/plugins/dax_backend.py +278 -804
- lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +346 -1122
- lmcache/v1/storage_backend/raw_block/__init__.py +42 -0
- lmcache/v1/storage_backend/raw_block/core.py +1476 -0
- lmcache/v1/storage_backend/raw_block/key_codec.py +168 -0
- lmcache/v1/storage_backend/remote_backend.py +2 -1
- lmcache/v1/storage_backend/storage_manager.py +26 -7
- lmcache/v1/system_detection.py +2 -2
- lmcache/v1/transfer_channel/nixl_channel.py +51 -2
- lmcache/v1/transfer_channel/transfer_utils.py +4 -6
- lmcache/v1/utils/json_utils.py +56 -0
- lmcache/v1/utils/router_discovery.py +54 -0
- {lmcache_cli-0.4.5.dev0.dist-info → lmcache_cli-0.4.6.dev0.dist-info}/METADATA +2 -1
- {lmcache_cli-0.4.5.dev0.dist-info → lmcache_cli-0.4.6.dev0.dist-info}/RECORD +176 -115
- {lmcache_cli-0.4.5.dev0.dist-info → lmcache_cli-0.4.6.dev0.dist-info}/WHEEL +0 -0
- {lmcache_cli-0.4.5.dev0.dist-info → lmcache_cli-0.4.6.dev0.dist-info}/entry_points.txt +0 -0
- {lmcache_cli-0.4.5.dev0.dist-info → lmcache_cli-0.4.6.dev0.dist-info}/licenses/LICENSE +0 -0
- {lmcache_cli-0.4.5.dev0.dist-info → lmcache_cli-0.4.6.dev0.dist-info}/top_level.txt +0 -0
lmcache/__init__.py
CHANGED
|
@@ -15,7 +15,43 @@ except ImportError:
|
|
|
15
15
|
__version__ = "unknown"
|
|
16
16
|
|
|
17
17
|
logger = init_logger(__name__)
|
|
18
|
-
|
|
18
|
+
# Standard
|
|
19
|
+
|
|
20
|
+
__all__ = ["__version__", "torch_dev", "torch_device_type"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# --------------------------
|
|
24
|
+
# Device detection
|
|
25
|
+
# --------------------------
|
|
26
|
+
def _detect_device() -> tuple[Any, str]:
|
|
27
|
+
"""
|
|
28
|
+
Detect the available accelerator and return the corresponding torch
|
|
29
|
+
device module and device type string.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
tuple[Any, str]: A tuple of (torch_device_module, device_type_string),
|
|
33
|
+
e.g. ``(torch.cuda, "cuda")`` or ``(torch.xpu, "xpu")``.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
RuntimeError: If no supported accelerator is found (checked CUDA, XPU, HPU).
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
# Third Party
|
|
40
|
+
import torch
|
|
41
|
+
except ImportError:
|
|
42
|
+
return None, "cpu" # fallback,CLI-only
|
|
43
|
+
|
|
44
|
+
if hasattr(torch, "xpu") and torch.xpu.is_available():
|
|
45
|
+
return torch.xpu, "xpu"
|
|
46
|
+
elif hasattr(torch, "hpu") and torch.hpu.is_available():
|
|
47
|
+
return torch.hpu, "hpu"
|
|
48
|
+
else:
|
|
49
|
+
# Fallback: always return torch.cuda for backward compatibility
|
|
50
|
+
# with existing tests and code paths that assume CUDA is the default.
|
|
51
|
+
return torch.cuda, "cuda"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
torch_dev, torch_device_type = _detect_device()
|
|
19
55
|
|
|
20
56
|
|
|
21
57
|
# --------------------------
|
lmcache/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.4.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 4,
|
|
21
|
+
__version__ = version = '0.4.6.dev0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 4, 6, 'dev0')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g168bbb976'
|
|
@@ -28,12 +28,28 @@ from lmcache.cli.commands.bench.engine_bench.stats import (
|
|
|
28
28
|
from lmcache.cli.commands.bench.engine_bench.workloads import create_workload
|
|
29
29
|
from lmcache.logging import init_logger
|
|
30
30
|
|
|
31
|
+
# Gated for slim install — TestCacheCommand pulls torch/MP runtime.
|
|
32
|
+
_TEST_CACHE_IMPORT_ERROR: ImportError | None = None
|
|
33
|
+
try:
|
|
34
|
+
# First Party
|
|
35
|
+
from lmcache.cli.commands.test_cache import TestCacheCommand
|
|
36
|
+
except ImportError as _exc:
|
|
37
|
+
_TEST_CACHE_IMPORT_ERROR = _exc
|
|
38
|
+
TestCacheCommand = None # type: ignore[assignment,misc]
|
|
39
|
+
|
|
31
40
|
logger = init_logger(__name__)
|
|
32
41
|
|
|
33
42
|
|
|
34
43
|
class BenchCommand(BaseCommand):
|
|
35
44
|
"""CLI command for sustained performance benchmarking."""
|
|
36
45
|
|
|
46
|
+
def __init__(self) -> None:
|
|
47
|
+
super().__init__()
|
|
48
|
+
# None on slim install; _register_kvcache registers a stub instead.
|
|
49
|
+
self._kvcache_delegate = (
|
|
50
|
+
TestCacheCommand() if TestCacheCommand is not None else None
|
|
51
|
+
)
|
|
52
|
+
|
|
37
53
|
def name(self) -> str:
|
|
38
54
|
return "bench"
|
|
39
55
|
|
|
@@ -52,9 +68,10 @@ class BenchCommand(BaseCommand):
|
|
|
52
68
|
inner = parser.add_subparsers(
|
|
53
69
|
dest="bench_target",
|
|
54
70
|
required=True,
|
|
55
|
-
metavar="{engine}",
|
|
71
|
+
metavar="{engine,kvcache}",
|
|
56
72
|
)
|
|
57
73
|
self._register_engine(inner)
|
|
74
|
+
self._register_kvcache(inner)
|
|
58
75
|
|
|
59
76
|
def _register_engine(
|
|
60
77
|
self,
|
|
@@ -99,6 +116,7 @@ class BenchCommand(BaseCommand):
|
|
|
99
116
|
"long-doc-permutator",
|
|
100
117
|
"long-doc-qa",
|
|
101
118
|
"multi-round-chat",
|
|
119
|
+
"prefix-suffix-tuner",
|
|
102
120
|
"random-prefill",
|
|
103
121
|
],
|
|
104
122
|
help="Workload type.",
|
|
@@ -262,6 +280,36 @@ class BenchCommand(BaseCommand):
|
|
|
262
280
|
help="Benchmark duration in seconds (default: 60).",
|
|
263
281
|
)
|
|
264
282
|
|
|
283
|
+
# --- Prefix-suffix-tuner workload args ---
|
|
284
|
+
psf_group = parser.add_argument_group(
|
|
285
|
+
"prefix-suffix-tuner workload options",
|
|
286
|
+
)
|
|
287
|
+
psf_group.add_argument(
|
|
288
|
+
"--psf-context-length",
|
|
289
|
+
type=int,
|
|
290
|
+
default=8000,
|
|
291
|
+
help="Total tokens per request (prefix + breaker + suffix) "
|
|
292
|
+
"(default: 8000).",
|
|
293
|
+
)
|
|
294
|
+
psf_group.add_argument(
|
|
295
|
+
"--psf-prefix-ratio",
|
|
296
|
+
type=float,
|
|
297
|
+
default=0.8,
|
|
298
|
+
help="Fraction of context-length used by the prefix (default: 0.8). "
|
|
299
|
+
"Must be in (0.0, 1.0). The remainder (minus a 32-token breaker) is "
|
|
300
|
+
"the shared suffix.",
|
|
301
|
+
)
|
|
302
|
+
psf_group.add_argument(
|
|
303
|
+
"--psf-thrash",
|
|
304
|
+
type=float,
|
|
305
|
+
default=20.0,
|
|
306
|
+
help="Size in GB of the KV-cache tier to overflow (default: 20.0). "
|
|
307
|
+
"The workload sizes its prefix pool to slightly more than this, "
|
|
308
|
+
"so every pass-2 request misses that tier and falls through to "
|
|
309
|
+
"the next one. Use the L0 (HBM) size for vanilla vLLM baselines, "
|
|
310
|
+
"or the L1 (LMCache DRAM) size for tiered baselines.",
|
|
311
|
+
)
|
|
312
|
+
|
|
265
313
|
# --- Random-prefill workload args ---
|
|
266
314
|
rp_group = parser.add_argument_group(
|
|
267
315
|
"random-prefill workload options",
|
|
@@ -281,8 +329,59 @@ class BenchCommand(BaseCommand):
|
|
|
281
329
|
|
|
282
330
|
parser.set_defaults(func=self.execute)
|
|
283
331
|
|
|
332
|
+
# ------------------------------------------------------------------
|
|
333
|
+
# kvcache bench target — end-to-end MP cache sanity test
|
|
334
|
+
# ------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
def _register_kvcache(
|
|
337
|
+
self,
|
|
338
|
+
subparsers: argparse._SubParsersAction,
|
|
339
|
+
) -> None:
|
|
340
|
+
"""Register ``lmcache bench kvcache``. Delegates to
|
|
341
|
+
:class:`TestCacheCommand`, or registers a stub on slim install.
|
|
342
|
+
"""
|
|
343
|
+
if self._kvcache_delegate is None:
|
|
344
|
+
subparsers.add_parser(
|
|
345
|
+
"kvcache",
|
|
346
|
+
help="(requires full lmcache install)",
|
|
347
|
+
description=(
|
|
348
|
+
"End-to-end sanity test for the LMCache MP cache server. "
|
|
349
|
+
"Requires the full `lmcache` package; not available in "
|
|
350
|
+
"the `lmcache-cli` install."
|
|
351
|
+
),
|
|
352
|
+
).set_defaults(func=self.execute)
|
|
353
|
+
return
|
|
354
|
+
parser = subparsers.add_parser(
|
|
355
|
+
"kvcache",
|
|
356
|
+
help=self._kvcache_delegate.help(),
|
|
357
|
+
description=(
|
|
358
|
+
"End-to-end sanity test for the LMCache MP cache server: "
|
|
359
|
+
"runs LOOKUP / STORE / RETRIEVE against a live MP server "
|
|
360
|
+
"and verifies KV cache checksums."
|
|
361
|
+
),
|
|
362
|
+
)
|
|
363
|
+
self._kvcache_delegate.add_arguments(parser)
|
|
364
|
+
parser.set_defaults(func=self.execute)
|
|
365
|
+
|
|
366
|
+
def _bench_kvcache(self, args: argparse.Namespace) -> None:
|
|
367
|
+
"""Dispatch ``lmcache bench kvcache`` to ``TestCacheCommand``."""
|
|
368
|
+
if self._kvcache_delegate is None:
|
|
369
|
+
print(
|
|
370
|
+
"ERROR: `lmcache bench kvcache` needs the full LMCache "
|
|
371
|
+
"package, but only the `lmcache-cli` shell is installed.\n"
|
|
372
|
+
" Install the full package with `pip install lmcache` "
|
|
373
|
+
"and try again.\n"
|
|
374
|
+
f" Original import error: {_TEST_CACHE_IMPORT_ERROR}",
|
|
375
|
+
file=sys.stderr,
|
|
376
|
+
)
|
|
377
|
+
sys.exit(1)
|
|
378
|
+
self._kvcache_delegate.execute(args)
|
|
379
|
+
|
|
284
380
|
def execute(self, args: argparse.Namespace) -> None:
|
|
285
|
-
handlers = {
|
|
381
|
+
handlers = {
|
|
382
|
+
"engine": self._bench_engine,
|
|
383
|
+
"kvcache": self._bench_kvcache,
|
|
384
|
+
}
|
|
286
385
|
handler = handlers.get(args.bench_target)
|
|
287
386
|
if handler is None:
|
|
288
387
|
print(
|
|
@@ -93,7 +93,7 @@ def auto_detect_model(engine_url: str) -> str:
|
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
def _fetch_lmcache_status(lmcache_url: str) -> dict:
|
|
96
|
-
"""Fetch ``/
|
|
96
|
+
"""Fetch ``/status`` from the LMCache HTTP server.
|
|
97
97
|
|
|
98
98
|
Returns:
|
|
99
99
|
Parsed JSON response.
|
|
@@ -104,7 +104,7 @@ def _fetch_lmcache_status(lmcache_url: str) -> dict:
|
|
|
104
104
|
url = lmcache_url.rstrip("/")
|
|
105
105
|
if not url.startswith(("http://", "https://")):
|
|
106
106
|
url = f"http://{url}"
|
|
107
|
-
status_url = f"{url}/
|
|
107
|
+
status_url = f"{url}/status"
|
|
108
108
|
|
|
109
109
|
logger.debug("Fetching LMCache status from %s", status_url)
|
|
110
110
|
|
|
@@ -125,7 +125,7 @@ def _find_model_meta(
|
|
|
125
125
|
"""Find the GPU metadata entry matching *model_name*.
|
|
126
126
|
|
|
127
127
|
Args:
|
|
128
|
-
gpu_meta: The ``gpu_context_meta`` dict from ``/
|
|
128
|
+
gpu_meta: The ``gpu_context_meta`` dict from ``/status``.
|
|
129
129
|
model_name: Model name to match.
|
|
130
130
|
|
|
131
131
|
Returns:
|
|
@@ -148,7 +148,7 @@ def _find_model_meta(
|
|
|
148
148
|
def resolve_tokens_per_gb(lmcache_url: str, model_name: str) -> int:
|
|
149
149
|
"""Query the LMCache server and compute tokens per GB of KV cache.
|
|
150
150
|
|
|
151
|
-
Fetches ``/
|
|
151
|
+
Fetches ``/status``, finds the model entry matching
|
|
152
152
|
*model_name*, and computes::
|
|
153
153
|
|
|
154
154
|
global_bytes_per_token = cache_size_per_token * world_size
|
|
@@ -172,6 +172,10 @@ def resolve_tokens_per_gb(lmcache_url: str, model_name: str) -> int:
|
|
|
172
172
|
data = _fetch_lmcache_status(lmcache_url)
|
|
173
173
|
|
|
174
174
|
gpu_meta = data.get("gpu_context_meta", {})
|
|
175
|
+
if not gpu_meta:
|
|
176
|
+
# CB-only deployments (engine_type="blend") populate
|
|
177
|
+
# cb_gpu_context_meta instead of gpu_context_meta.
|
|
178
|
+
gpu_meta = data.get("cb_gpu_context_meta", {})
|
|
175
179
|
if not gpu_meta:
|
|
176
180
|
raise RuntimeError(
|
|
177
181
|
"No model info returned by LMCache server; "
|
|
@@ -112,6 +112,10 @@ ALL_ITEMS: list[ConfigItem] = [
|
|
|
112
112
|
),
|
|
113
113
|
("long-doc-qa", "Repeated Q&A over long documents (tests KV cache reuse)"),
|
|
114
114
|
("multi-round-chat", "Multi-turn chat with stateful sessions"),
|
|
115
|
+
(
|
|
116
|
+
"prefix-suffix-tuner",
|
|
117
|
+
"Two-pass sequential workload demonstrating tiered KV cache reuse",
|
|
118
|
+
),
|
|
115
119
|
("random-prefill", "Prefill-only requests fired simultaneously"),
|
|
116
120
|
],
|
|
117
121
|
phase=PHASE_REQUIRED,
|
|
@@ -317,6 +321,43 @@ ALL_ITEMS: list[ConfigItem] = [
|
|
|
317
321
|
condition=_workload_is("multi-round-chat"),
|
|
318
322
|
phase=PHASE_WORKLOAD,
|
|
319
323
|
),
|
|
324
|
+
# ── Phase 3: prefix-suffix-tuner ──────────────────────────────────
|
|
325
|
+
ConfigItem(
|
|
326
|
+
key="psf_context_length",
|
|
327
|
+
display_name="Context length (tokens)",
|
|
328
|
+
description="Total tokens per request (prefix + breaker + suffix).",
|
|
329
|
+
input_type="int",
|
|
330
|
+
default=8000,
|
|
331
|
+
condition=_workload_is("prefix-suffix-tuner"),
|
|
332
|
+
phase=PHASE_WORKLOAD,
|
|
333
|
+
),
|
|
334
|
+
ConfigItem(
|
|
335
|
+
key="psf_prefix_ratio",
|
|
336
|
+
display_name="Prefix ratio",
|
|
337
|
+
description=(
|
|
338
|
+
"Fraction of context-length used by the prefix. Must be in "
|
|
339
|
+
"(0.0, 1.0). The remainder (minus a 32-token breaker) is the "
|
|
340
|
+
"shared suffix."
|
|
341
|
+
),
|
|
342
|
+
input_type="float",
|
|
343
|
+
default=0.8,
|
|
344
|
+
condition=_workload_is("prefix-suffix-tuner"),
|
|
345
|
+
phase=PHASE_WORKLOAD,
|
|
346
|
+
),
|
|
347
|
+
ConfigItem(
|
|
348
|
+
key="psf_thrash",
|
|
349
|
+
display_name="Target tier size (GB)",
|
|
350
|
+
description=(
|
|
351
|
+
"Size in GB of the KV-cache tier to overflow. The prefix pool "
|
|
352
|
+
"is sized to slightly more than this, so every pass-2 request "
|
|
353
|
+
"misses the targeted tier. Use the L0 (HBM) size for vanilla "
|
|
354
|
+
"vLLM, or the L1 (LMCache DRAM) size for tiered baselines."
|
|
355
|
+
),
|
|
356
|
+
input_type="float",
|
|
357
|
+
default=20.0,
|
|
358
|
+
condition=_workload_is("prefix-suffix-tuner"),
|
|
359
|
+
phase=PHASE_WORKLOAD,
|
|
360
|
+
),
|
|
320
361
|
# ── Phase 3: random-prefill ───────────────────────────────────────
|
|
321
362
|
ConfigItem(
|
|
322
363
|
key="rp_request_length",
|
|
@@ -111,6 +111,7 @@ class RequestSender:
|
|
|
111
111
|
"""
|
|
112
112
|
submit_time = time.time()
|
|
113
113
|
first_token_time = 0.0
|
|
114
|
+
first_chunk_time = 0.0
|
|
114
115
|
tokens: list[str] = []
|
|
115
116
|
num_input_tokens = 0
|
|
116
117
|
num_output_tokens = 0
|
|
@@ -119,6 +120,9 @@ class RequestSender:
|
|
|
119
120
|
response = await self._create_stream(messages, max_tokens)
|
|
120
121
|
|
|
121
122
|
async for chunk in response:
|
|
123
|
+
if not first_chunk_time:
|
|
124
|
+
first_chunk_time = time.time()
|
|
125
|
+
|
|
122
126
|
# Extract usage from final chunk
|
|
123
127
|
usage = getattr(chunk, "usage", None)
|
|
124
128
|
if usage is not None:
|
|
@@ -136,6 +140,11 @@ class RequestSender:
|
|
|
136
140
|
tokens.append(content)
|
|
137
141
|
|
|
138
142
|
finish_time = time.time()
|
|
143
|
+
if first_token_time == 0.0 and num_output_tokens > 0:
|
|
144
|
+
# Empty-content stream (common with max_tokens=1, e.g. the
|
|
145
|
+
# single token is EOS): use first chunk arrival as TTFT —
|
|
146
|
+
# closer to engine prefill completion than finish_time.
|
|
147
|
+
first_token_time = first_chunk_time or finish_time
|
|
139
148
|
successful = first_token_time > 0.0
|
|
140
149
|
ttft = (first_token_time - submit_time) if successful else -1.0
|
|
141
150
|
request_latency = finish_time - submit_time
|
|
@@ -30,6 +30,10 @@ from lmcache.cli.commands.bench.engine_bench.workloads.multi_round_chat import (
|
|
|
30
30
|
MultiRoundChatConfig,
|
|
31
31
|
MultiRoundChatWorkload,
|
|
32
32
|
)
|
|
33
|
+
from lmcache.cli.commands.bench.engine_bench.workloads.prefix_suffix_tuner import (
|
|
34
|
+
PrefixSuffixTunerConfig,
|
|
35
|
+
PrefixSuffixTunerWorkload,
|
|
36
|
+
)
|
|
33
37
|
from lmcache.cli.commands.bench.engine_bench.workloads.random_prefill import (
|
|
34
38
|
RandomPrefillConfig,
|
|
35
39
|
RandomPrefillWorkload,
|
|
@@ -43,6 +47,8 @@ __all__ = [
|
|
|
43
47
|
"LongDocQAWorkload",
|
|
44
48
|
"MultiRoundChatConfig",
|
|
45
49
|
"MultiRoundChatWorkload",
|
|
50
|
+
"PrefixSuffixTunerConfig",
|
|
51
|
+
"PrefixSuffixTunerWorkload",
|
|
46
52
|
"RandomPrefillConfig",
|
|
47
53
|
"RandomPrefillWorkload",
|
|
48
54
|
"create_workload",
|
|
@@ -52,6 +58,7 @@ _WORKLOAD_NAMES = (
|
|
|
52
58
|
"long-doc-permutator",
|
|
53
59
|
"long-doc-qa",
|
|
54
60
|
"multi-round-chat",
|
|
61
|
+
"prefix-suffix-tuner",
|
|
55
62
|
"random-prefill",
|
|
56
63
|
)
|
|
57
64
|
|
|
@@ -135,6 +142,22 @@ def create_workload(
|
|
|
135
142
|
seed=config.seed,
|
|
136
143
|
)
|
|
137
144
|
|
|
145
|
+
if config.workload == "prefix-suffix-tuner":
|
|
146
|
+
psf_workload_config = PrefixSuffixTunerConfig.resolve(
|
|
147
|
+
tokens_per_gb_kvcache=config.tokens_per_gb_kvcache,
|
|
148
|
+
context_length=args.psf_context_length,
|
|
149
|
+
prefix_ratio=args.psf_prefix_ratio,
|
|
150
|
+
thrash=args.psf_thrash,
|
|
151
|
+
)
|
|
152
|
+
return PrefixSuffixTunerWorkload(
|
|
153
|
+
config=psf_workload_config,
|
|
154
|
+
request_sender=request_sender,
|
|
155
|
+
stats_collector=stats_collector,
|
|
156
|
+
progress_monitor=progress_monitor,
|
|
157
|
+
seed=config.seed,
|
|
158
|
+
model_name=config.model,
|
|
159
|
+
)
|
|
160
|
+
|
|
138
161
|
if config.workload == "random-prefill":
|
|
139
162
|
rp_workload_config = RandomPrefillConfig.resolve(
|
|
140
163
|
request_length=args.rp_request_length,
|