lmcache-cli 0.4.5.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 +84 -0
- lmcache/_version.py +24 -0
- lmcache/cli/__init__.py +1 -0
- lmcache/cli/commands/__init__.py +34 -0
- lmcache/cli/commands/base.py +157 -0
- lmcache/cli/commands/bench/__init__.py +557 -0
- lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
- lmcache/cli/commands/bench/engine_bench/config.py +245 -0
- lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
- lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
- lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
- lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
- lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
- lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
- lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
- lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
- lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
- lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
- lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
- lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
- lmcache/cli/commands/describe.py +310 -0
- lmcache/cli/commands/kvcache.py +133 -0
- lmcache/cli/commands/mock.py +75 -0
- lmcache/cli/commands/ping.py +113 -0
- lmcache/cli/commands/query/__init__.py +155 -0
- lmcache/cli/commands/query/prompt.py +134 -0
- lmcache/cli/commands/query/request.py +357 -0
- lmcache/cli/commands/server.py +99 -0
- lmcache/cli/commands/tool/__init__.py +63 -0
- lmcache/cli/commands/tool/cache_simulator.py +113 -0
- lmcache/cli/commands/trace/__init__.py +505 -0
- lmcache/cli/commands/trace/dispatch.py +249 -0
- lmcache/cli/commands/trace/driver.py +372 -0
- lmcache/cli/commands/trace/stats.py +289 -0
- lmcache/cli/documents/lmcache.txt +11 -0
- lmcache/cli/main.py +42 -0
- lmcache/cli/metrics/__init__.py +29 -0
- lmcache/cli/metrics/formatter.py +171 -0
- lmcache/cli/metrics/handler.py +94 -0
- lmcache/cli/metrics/metrics.py +161 -0
- lmcache/cli/metrics/section.py +77 -0
- lmcache/connections.py +173 -0
- lmcache/integration/__init__.py +2 -0
- lmcache/integration/base_service_factory.py +165 -0
- lmcache/integration/request_telemetry/__init__.py +1 -0
- lmcache/integration/request_telemetry/base.py +51 -0
- lmcache/integration/request_telemetry/factory.py +113 -0
- lmcache/integration/request_telemetry/fastapi.py +109 -0
- lmcache/integration/request_telemetry/noop.py +35 -0
- lmcache/integration/sglang/__init__.py +2 -0
- lmcache/integration/sglang/sglang_adapter.py +326 -0
- lmcache/integration/sglang/utils.py +39 -0
- lmcache/integration/vllm/__init__.py +1 -0
- lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
- lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
- lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
- lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
- lmcache/integration/vllm/utils.py +433 -0
- lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
- lmcache/integration/vllm/vllm_service_factory.py +339 -0
- lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
- lmcache/logging.py +107 -0
- lmcache/native_storage_ops.pyi +230 -0
- lmcache/non_cuda_equivalents.py +1424 -0
- lmcache/observability.py +1958 -0
- lmcache/storage_backend/serde/__init__.py +1 -0
- lmcache/storage_backend/serde/cachegen_basics.py +210 -0
- lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
- lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
- lmcache/storage_backend/serde/serde.py +75 -0
- lmcache/tools/__init__.py +1 -0
- lmcache/tools/cache_simulator/README.md +392 -0
- lmcache/tools/cache_simulator/__init__.py +1 -0
- lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
- lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
- lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
- lmcache/tools/cache_simulator/lru_cache.py +124 -0
- lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
- lmcache/tools/cache_simulator/simulator.py +795 -0
- lmcache/tools/controller_benchmark/README.md +161 -0
- lmcache/tools/controller_benchmark/__init__.py +1 -0
- lmcache/tools/controller_benchmark/__main__.py +331 -0
- lmcache/tools/controller_benchmark/benchmark.py +660 -0
- lmcache/tools/controller_benchmark/config.py +44 -0
- lmcache/tools/controller_benchmark/constants.py +10 -0
- lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
- lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
- lmcache/tools/controller_benchmark/handlers/base.py +47 -0
- lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
- lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
- lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
- lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
- lmcache/tools/controller_benchmark/handlers/register.py +56 -0
- lmcache/tools/mp_status_viewer/__init__.py +1 -0
- lmcache/tools/mp_status_viewer/__main__.py +95 -0
- lmcache/usage_context.py +417 -0
- lmcache/utils.py +665 -0
- lmcache/v1/__init__.py +2 -0
- lmcache/v1/api_server/__init__.py +2 -0
- lmcache/v1/api_server/__main__.py +537 -0
- lmcache/v1/basic_check.py +112 -0
- lmcache/v1/cache_controller/__init__.py +9 -0
- lmcache/v1/cache_controller/commands/__init__.py +15 -0
- lmcache/v1/cache_controller/commands/base.py +35 -0
- lmcache/v1/cache_controller/commands/full_sync.py +49 -0
- lmcache/v1/cache_controller/config.py +176 -0
- lmcache/v1/cache_controller/controller_manager.py +535 -0
- lmcache/v1/cache_controller/controllers/__init__.py +11 -0
- lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
- lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
- lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
- lmcache/v1/cache_controller/executor.py +463 -0
- lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
- lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
- lmcache/v1/cache_controller/frontend/static/index.html +234 -0
- lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
- lmcache/v1/cache_controller/full_sync_sender.py +475 -0
- lmcache/v1/cache_controller/locks.py +149 -0
- lmcache/v1/cache_controller/message.py +828 -0
- lmcache/v1/cache_controller/observability.py +208 -0
- lmcache/v1/cache_controller/utils.py +679 -0
- lmcache/v1/cache_controller/worker.py +665 -0
- lmcache/v1/cache_engine.py +2058 -0
- lmcache/v1/cache_interface.py +19 -0
- lmcache/v1/check/__init__.py +74 -0
- lmcache/v1/check/check_mode_gen.py +86 -0
- lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
- lmcache/v1/check/check_mode_test_remote.py +155 -0
- lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
- lmcache/v1/check/utils.py +571 -0
- lmcache/v1/compute/__init__.py +2 -0
- lmcache/v1/compute/attention/__init__.py +0 -0
- lmcache/v1/compute/attention/abstract.py +39 -0
- lmcache/v1/compute/attention/flash_attn.py +129 -0
- lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
- lmcache/v1/compute/attention/metadata.py +85 -0
- lmcache/v1/compute/attention/utils.py +14 -0
- lmcache/v1/compute/blend/__init__.py +7 -0
- lmcache/v1/compute/blend/blender.py +168 -0
- lmcache/v1/compute/blend/metadata.py +34 -0
- lmcache/v1/compute/blend/utils.py +63 -0
- lmcache/v1/compute/models/__init__.py +0 -0
- lmcache/v1/compute/models/base.py +141 -0
- lmcache/v1/compute/models/llama.py +9 -0
- lmcache/v1/compute/models/qwen3.py +24 -0
- lmcache/v1/compute/models/utils.py +68 -0
- lmcache/v1/compute/positional_encoding.py +199 -0
- lmcache/v1/config.py +848 -0
- lmcache/v1/config_base.py +848 -0
- lmcache/v1/distributed/api.py +248 -0
- lmcache/v1/distributed/config.py +321 -0
- lmcache/v1/distributed/error.py +64 -0
- lmcache/v1/distributed/eviction.py +192 -0
- lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
- lmcache/v1/distributed/eviction_policy/factory.py +27 -0
- lmcache/v1/distributed/eviction_policy/lru.py +244 -0
- lmcache/v1/distributed/eviction_policy/noop.py +50 -0
- lmcache/v1/distributed/internal_api.py +170 -0
- lmcache/v1/distributed/l1_manager.py +835 -0
- lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
- lmcache/v1/distributed/l2_adapters/base.py +360 -0
- lmcache/v1/distributed/l2_adapters/config.py +385 -0
- lmcache/v1/distributed/l2_adapters/factory.py +205 -0
- lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
- lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
- lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
- lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
- lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
- lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
- lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
- lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
- lmcache/v1/distributed/memory_manager.py +179 -0
- lmcache/v1/distributed/storage_controller.py +39 -0
- lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
- lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
- lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
- lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
- lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
- lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
- lmcache/v1/distributed/storage_manager.py +532 -0
- lmcache/v1/event_manager.py +145 -0
- lmcache/v1/exceptions/__init__.py +16 -0
- lmcache/v1/gpu_connector/__init__.py +126 -0
- lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
- lmcache/v1/gpu_connector/gpu_ops.py +85 -0
- lmcache/v1/gpu_connector/hpu_connector.py +326 -0
- lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
- lmcache/v1/gpu_connector/utils.py +890 -0
- lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
- lmcache/v1/health_monitor/__init__.py +1 -0
- lmcache/v1/health_monitor/base.py +587 -0
- lmcache/v1/health_monitor/checks/__init__.py +1 -0
- lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
- lmcache/v1/health_monitor/constants.py +36 -0
- lmcache/v1/internal_api_server/__init__.py +0 -0
- lmcache/v1/internal_api_server/api_registry.py +59 -0
- lmcache/v1/internal_api_server/api_server.py +120 -0
- lmcache/v1/internal_api_server/common/__init__.py +1 -0
- lmcache/v1/internal_api_server/common/env_api.py +22 -0
- lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
- lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
- lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
- lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
- lmcache/v1/internal_api_server/common/thread_api.py +63 -0
- lmcache/v1/internal_api_server/controller/__init__.py +1 -0
- lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
- lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
- lmcache/v1/internal_api_server/utils.py +43 -0
- lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
- lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
- lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
- lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
- lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
- lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
- lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
- lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
- lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
- lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
- lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
- lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
- lmcache/v1/kv_layer_groups.py +267 -0
- lmcache/v1/lazy_memory_allocator.py +284 -0
- lmcache/v1/lookup_client/__init__.py +25 -0
- lmcache/v1/lookup_client/abstract_client.py +77 -0
- lmcache/v1/lookup_client/async_lookup_message.py +50 -0
- lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
- lmcache/v1/lookup_client/factory.py +251 -0
- lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
- lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
- lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
- lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
- lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
- lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
- lmcache/v1/lookup_client/record_strategies/base.py +327 -0
- lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
- lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
- lmcache/v1/manager.py +539 -0
- lmcache/v1/memory_management.py +2619 -0
- lmcache/v1/metadata.py +114 -0
- lmcache/v1/mp_observability/AGENTS.override.md +21 -0
- lmcache/v1/mp_observability/README.md +204 -0
- lmcache/v1/mp_observability/config.py +340 -0
- lmcache/v1/mp_observability/event.py +100 -0
- lmcache/v1/mp_observability/event_bus.py +313 -0
- lmcache/v1/mp_observability/otel_init.py +145 -0
- lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
- lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
- lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
- lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
- lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
- lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
- lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
- lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
- lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
- lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
- lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
- lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
- lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
- lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
- lmcache/v1/mp_observability/trace/__init__.py +50 -0
- lmcache/v1/mp_observability/trace/codecs.py +255 -0
- lmcache/v1/mp_observability/trace/decorator.py +147 -0
- lmcache/v1/mp_observability/trace/format.py +132 -0
- lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
- lmcache/v1/mp_observability/trace/reader.py +167 -0
- lmcache/v1/mp_observability/trace/recorder.py +300 -0
- lmcache/v1/multiprocess/__init__.py +0 -0
- lmcache/v1/multiprocess/affinity_pool.py +102 -0
- lmcache/v1/multiprocess/blend_server_v2.py +891 -0
- lmcache/v1/multiprocess/config.py +253 -0
- lmcache/v1/multiprocess/custom_types.py +281 -0
- lmcache/v1/multiprocess/futures.py +194 -0
- lmcache/v1/multiprocess/gpu_context.py +511 -0
- lmcache/v1/multiprocess/http_server.py +235 -0
- lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
- lmcache/v1/multiprocess/mq.py +732 -0
- lmcache/v1/multiprocess/protocol.py +86 -0
- lmcache/v1/multiprocess/protocols/README.md +213 -0
- lmcache/v1/multiprocess/protocols/__init__.py +127 -0
- lmcache/v1/multiprocess/protocols/base.py +89 -0
- lmcache/v1/multiprocess/protocols/blend.py +109 -0
- lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
- lmcache/v1/multiprocess/protocols/controller.py +53 -0
- lmcache/v1/multiprocess/protocols/debug.py +34 -0
- lmcache/v1/multiprocess/protocols/engine.py +146 -0
- lmcache/v1/multiprocess/protocols/observability.py +39 -0
- lmcache/v1/multiprocess/server.py +1134 -0
- lmcache/v1/multiprocess/session.py +190 -0
- lmcache/v1/multiprocess/token_hasher.py +441 -0
- lmcache/v1/offload_server/__init__.py +17 -0
- lmcache/v1/offload_server/abstract_server.py +37 -0
- lmcache/v1/offload_server/message.py +30 -0
- lmcache/v1/offload_server/zmq_server.py +122 -0
- lmcache/v1/periodic_thread.py +579 -0
- lmcache/v1/pin_monitor.py +246 -0
- lmcache/v1/plugin/__init__.py +0 -0
- lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
- lmcache/v1/protocol.py +317 -0
- lmcache/v1/rpc/__init__.py +17 -0
- lmcache/v1/rpc/transport.py +105 -0
- lmcache/v1/rpc/zmq_transport.py +213 -0
- lmcache/v1/rpc_utils.py +165 -0
- lmcache/v1/server/__init__.py +2 -0
- lmcache/v1/server/__main__.py +170 -0
- lmcache/v1/server/storage_backend/__init__.py +21 -0
- lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
- lmcache/v1/server/storage_backend/local_backend.py +75 -0
- lmcache/v1/server/utils.py +21 -0
- lmcache/v1/standalone/__init__.py +1 -0
- lmcache/v1/standalone/__main__.py +583 -0
- lmcache/v1/standalone/manager.py +80 -0
- lmcache/v1/standalone/standalone_service_factory.py +86 -0
- lmcache/v1/storage_backend/__init__.py +313 -0
- lmcache/v1/storage_backend/abstract_backend.py +445 -0
- lmcache/v1/storage_backend/audit_backend.py +233 -0
- lmcache/v1/storage_backend/batched_message_sender.py +222 -0
- lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
- lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
- lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
- lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
- lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
- lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
- lmcache/v1/storage_backend/connector/__init__.py +443 -0
- lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
- lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
- lmcache/v1/storage_backend/connector/base_connector.py +379 -0
- lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
- lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
- lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
- lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
- lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
- lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
- lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
- lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
- lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
- lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
- lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
- lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
- lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
- lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
- lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
- lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
- lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
- lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
- lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
- lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
- lmcache/v1/storage_backend/gds_backend.py +1199 -0
- lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
- lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
- lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
- lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
- lmcache/v1/storage_backend/local_disk_backend.py +656 -0
- lmcache/v1/storage_backend/maru_backend.py +734 -0
- lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
- lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
- lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
- lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
- lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
- lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
- lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
- lmcache/v1/storage_backend/p2p_backend.py +788 -0
- lmcache/v1/storage_backend/path_sharder.py +117 -0
- lmcache/v1/storage_backend/pd_backend.py +646 -0
- lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
- lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
- lmcache/v1/storage_backend/remote_backend.py +624 -0
- lmcache/v1/storage_backend/resp_client.py +227 -0
- lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
- lmcache/v1/storage_backend/storage_manager.py +1352 -0
- lmcache/v1/system_detection.py +110 -0
- lmcache/v1/token_database.py +551 -0
- lmcache/v1/transfer_channel/__init__.py +83 -0
- lmcache/v1/transfer_channel/abstract.py +285 -0
- lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
- lmcache/v1/transfer_channel/nixl_channel.py +639 -0
- lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
- lmcache/v1/transfer_channel/transfer_utils.py +63 -0
- lmcache/v1/utils/__init__.py +1 -0
- lmcache/v1/utils/bloom_filter.py +109 -0
- lmcache/v1/utils/cache_utils.py +125 -0
- lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
- lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
- lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
- lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
- lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
- lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Stats collection, aggregation, and export for ``lmcache bench engine``."""
|
|
3
|
+
|
|
4
|
+
# Standard
|
|
5
|
+
from dataclasses import asdict, dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import csv
|
|
8
|
+
import json
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
# First Party
|
|
13
|
+
from lmcache.cli.commands.bench.engine_bench.config import EngineBenchConfig
|
|
14
|
+
from lmcache.logging import init_logger
|
|
15
|
+
|
|
16
|
+
logger = init_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class RequestResult:
|
|
21
|
+
"""Raw per-request result collected by the request sender."""
|
|
22
|
+
|
|
23
|
+
request_id: str
|
|
24
|
+
successful: bool
|
|
25
|
+
ttft: float # time to first token (seconds)
|
|
26
|
+
request_latency: float # total request time (seconds)
|
|
27
|
+
num_input_tokens: int # from server usage report
|
|
28
|
+
num_output_tokens: int # tokens generated
|
|
29
|
+
decode_speed: float # output tokens / decode time (tok/s)
|
|
30
|
+
submit_time: float # absolute timestamp
|
|
31
|
+
first_token_time: float # absolute timestamp
|
|
32
|
+
finish_time: float # absolute timestamp
|
|
33
|
+
error: str # empty string if successful
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class AggregatedStats:
|
|
38
|
+
"""Snapshot of aggregated statistics (running totals)."""
|
|
39
|
+
|
|
40
|
+
total_requests: int
|
|
41
|
+
successful_requests: int
|
|
42
|
+
failed_requests: int
|
|
43
|
+
elapsed_time: float # seconds since benchmark start
|
|
44
|
+
|
|
45
|
+
mean_ttft_ms: float
|
|
46
|
+
mean_decode_speed: float # tok/s
|
|
47
|
+
mean_request_latency_ms: float
|
|
48
|
+
|
|
49
|
+
input_throughput: float # total input tokens / elapsed time
|
|
50
|
+
output_throughput: float # total output tokens / elapsed time
|
|
51
|
+
|
|
52
|
+
total_input_tokens: int
|
|
53
|
+
total_output_tokens: int
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class FinalStats(AggregatedStats):
|
|
58
|
+
"""Final statistics with percentiles. Extends AggregatedStats."""
|
|
59
|
+
|
|
60
|
+
p50_ttft_ms: float = 0.0
|
|
61
|
+
p90_ttft_ms: float = 0.0
|
|
62
|
+
p99_ttft_ms: float = 0.0
|
|
63
|
+
p50_decode_speed: float = 0.0
|
|
64
|
+
p90_decode_speed: float = 0.0
|
|
65
|
+
p99_decode_speed: float = 0.0
|
|
66
|
+
p50_request_latency_ms: float = 0.0
|
|
67
|
+
p90_request_latency_ms: float = 0.0
|
|
68
|
+
p99_request_latency_ms: float = 0.0
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class StatsCollector:
|
|
72
|
+
"""Thread-safe stats aggregation for benchmark results.
|
|
73
|
+
|
|
74
|
+
Receives ``RequestResult`` objects from the request sender,
|
|
75
|
+
maintains running totals, and produces final summaries.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self) -> None:
|
|
79
|
+
self._lock = threading.Lock()
|
|
80
|
+
self._results: list[RequestResult] = []
|
|
81
|
+
self._start_time: float = time.monotonic()
|
|
82
|
+
|
|
83
|
+
# Running accumulators (updated under lock)
|
|
84
|
+
self._successful: int = 0
|
|
85
|
+
self._failed: int = 0
|
|
86
|
+
self._sum_ttft: float = 0.0
|
|
87
|
+
self._sum_decode_speed: float = 0.0
|
|
88
|
+
self._sum_request_latency: float = 0.0
|
|
89
|
+
self._total_input_tokens: int = 0
|
|
90
|
+
self._total_output_tokens: int = 0
|
|
91
|
+
|
|
92
|
+
def on_request_finished(self, result: RequestResult) -> None:
|
|
93
|
+
"""Record a completed request. Thread-safe."""
|
|
94
|
+
with self._lock:
|
|
95
|
+
self._results.append(result)
|
|
96
|
+
if result.successful:
|
|
97
|
+
self._successful += 1
|
|
98
|
+
self._sum_ttft += result.ttft
|
|
99
|
+
self._sum_decode_speed += result.decode_speed
|
|
100
|
+
self._sum_request_latency += result.request_latency
|
|
101
|
+
else:
|
|
102
|
+
self._failed += 1
|
|
103
|
+
self._total_input_tokens += result.num_input_tokens
|
|
104
|
+
self._total_output_tokens += result.num_output_tokens
|
|
105
|
+
logger.debug(
|
|
106
|
+
"Recorded result for %s (successful=%s)",
|
|
107
|
+
result.request_id,
|
|
108
|
+
result.successful,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def reset(self) -> None:
|
|
112
|
+
"""Clear all accumulated results and restart the timer.
|
|
113
|
+
|
|
114
|
+
Used between warmup and benchmark phases so warmup stats
|
|
115
|
+
don't pollute benchmark results. Thread-safe.
|
|
116
|
+
"""
|
|
117
|
+
with self._lock:
|
|
118
|
+
self._results.clear()
|
|
119
|
+
self._start_time = time.monotonic()
|
|
120
|
+
self._successful = 0
|
|
121
|
+
self._failed = 0
|
|
122
|
+
self._sum_ttft = 0.0
|
|
123
|
+
self._sum_decode_speed = 0.0
|
|
124
|
+
self._sum_request_latency = 0.0
|
|
125
|
+
self._total_input_tokens = 0
|
|
126
|
+
self._total_output_tokens = 0
|
|
127
|
+
logger.debug("Stats collector reset")
|
|
128
|
+
|
|
129
|
+
def get_current_stats(self) -> AggregatedStats:
|
|
130
|
+
"""Return current aggregated stats snapshot. Thread-safe."""
|
|
131
|
+
with self._lock:
|
|
132
|
+
successful = self._successful
|
|
133
|
+
failed = self._failed
|
|
134
|
+
elapsed = time.monotonic() - self._start_time
|
|
135
|
+
sum_ttft = self._sum_ttft
|
|
136
|
+
sum_decode = self._sum_decode_speed
|
|
137
|
+
sum_latency = self._sum_request_latency
|
|
138
|
+
total_in = self._total_input_tokens
|
|
139
|
+
total_out = self._total_output_tokens
|
|
140
|
+
|
|
141
|
+
safe_successful = max(successful, 1)
|
|
142
|
+
|
|
143
|
+
return AggregatedStats(
|
|
144
|
+
total_requests=successful + failed,
|
|
145
|
+
successful_requests=successful,
|
|
146
|
+
failed_requests=failed,
|
|
147
|
+
elapsed_time=elapsed,
|
|
148
|
+
mean_ttft_ms=(sum_ttft / safe_successful) * 1000.0,
|
|
149
|
+
mean_decode_speed=sum_decode / safe_successful,
|
|
150
|
+
mean_request_latency_ms=(sum_latency / safe_successful) * 1000.0,
|
|
151
|
+
input_throughput=total_in / max(elapsed, 1e-9),
|
|
152
|
+
output_throughput=total_out / max(elapsed, 1e-9),
|
|
153
|
+
total_input_tokens=total_in,
|
|
154
|
+
total_output_tokens=total_out,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def get_final_stats(self) -> FinalStats:
|
|
158
|
+
"""Compute and return final stats with percentiles.
|
|
159
|
+
|
|
160
|
+
Should be called once after the benchmark completes.
|
|
161
|
+
"""
|
|
162
|
+
with self._lock:
|
|
163
|
+
results = list(self._results)
|
|
164
|
+
|
|
165
|
+
successful_results = [r for r in results if r.successful]
|
|
166
|
+
current = self.get_current_stats()
|
|
167
|
+
|
|
168
|
+
if not successful_results:
|
|
169
|
+
return FinalStats(
|
|
170
|
+
total_requests=current.total_requests,
|
|
171
|
+
successful_requests=current.successful_requests,
|
|
172
|
+
failed_requests=current.failed_requests,
|
|
173
|
+
elapsed_time=current.elapsed_time,
|
|
174
|
+
mean_ttft_ms=current.mean_ttft_ms,
|
|
175
|
+
mean_decode_speed=current.mean_decode_speed,
|
|
176
|
+
mean_request_latency_ms=current.mean_request_latency_ms,
|
|
177
|
+
input_throughput=current.input_throughput,
|
|
178
|
+
output_throughput=current.output_throughput,
|
|
179
|
+
total_input_tokens=current.total_input_tokens,
|
|
180
|
+
total_output_tokens=current.total_output_tokens,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
ttfts = sorted(r.ttft * 1000.0 for r in successful_results)
|
|
184
|
+
decode_speeds = sorted(r.decode_speed for r in successful_results)
|
|
185
|
+
latencies = sorted(r.request_latency * 1000.0 for r in successful_results)
|
|
186
|
+
|
|
187
|
+
return FinalStats(
|
|
188
|
+
total_requests=current.total_requests,
|
|
189
|
+
successful_requests=current.successful_requests,
|
|
190
|
+
failed_requests=current.failed_requests,
|
|
191
|
+
elapsed_time=current.elapsed_time,
|
|
192
|
+
mean_ttft_ms=current.mean_ttft_ms,
|
|
193
|
+
mean_decode_speed=current.mean_decode_speed,
|
|
194
|
+
mean_request_latency_ms=current.mean_request_latency_ms,
|
|
195
|
+
input_throughput=current.input_throughput,
|
|
196
|
+
output_throughput=current.output_throughput,
|
|
197
|
+
total_input_tokens=current.total_input_tokens,
|
|
198
|
+
total_output_tokens=current.total_output_tokens,
|
|
199
|
+
p50_ttft_ms=_percentile(ttfts, 50),
|
|
200
|
+
p90_ttft_ms=_percentile(ttfts, 90),
|
|
201
|
+
p99_ttft_ms=_percentile(ttfts, 99),
|
|
202
|
+
p50_decode_speed=_percentile(decode_speeds, 50),
|
|
203
|
+
p90_decode_speed=_percentile(decode_speeds, 90),
|
|
204
|
+
p99_decode_speed=_percentile(decode_speeds, 99),
|
|
205
|
+
p50_request_latency_ms=_percentile(latencies, 50),
|
|
206
|
+
p90_request_latency_ms=_percentile(latencies, 90),
|
|
207
|
+
p99_request_latency_ms=_percentile(latencies, 99),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def get_all_results(self) -> list[RequestResult]:
|
|
211
|
+
"""Return all raw results for CSV export."""
|
|
212
|
+
with self._lock:
|
|
213
|
+
return list(self._results)
|
|
214
|
+
|
|
215
|
+
def export_csv(self, path: str) -> None:
|
|
216
|
+
"""Write per-request results to a CSV file."""
|
|
217
|
+
results = self.get_all_results()
|
|
218
|
+
fieldnames = [
|
|
219
|
+
"request_id",
|
|
220
|
+
"successful",
|
|
221
|
+
"ttft",
|
|
222
|
+
"request_latency",
|
|
223
|
+
"num_input_tokens",
|
|
224
|
+
"num_output_tokens",
|
|
225
|
+
"decode_speed",
|
|
226
|
+
"submit_time",
|
|
227
|
+
"first_token_time",
|
|
228
|
+
"finish_time",
|
|
229
|
+
"error",
|
|
230
|
+
]
|
|
231
|
+
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
|
232
|
+
with open(path, "w", newline="") as f:
|
|
233
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
234
|
+
writer.writeheader()
|
|
235
|
+
for result in results:
|
|
236
|
+
writer.writerow(asdict(result))
|
|
237
|
+
logger.debug("Exported %d results to CSV: %s", len(results), path)
|
|
238
|
+
|
|
239
|
+
def export_json(self, path: str, config: EngineBenchConfig) -> None:
|
|
240
|
+
"""Write summary JSON with config and aggregated metrics."""
|
|
241
|
+
final = self.get_final_stats()
|
|
242
|
+
output = {
|
|
243
|
+
"config": asdict(config),
|
|
244
|
+
"results": asdict(final),
|
|
245
|
+
}
|
|
246
|
+
with open(path, "w") as f:
|
|
247
|
+
json.dump(output, f, indent=2)
|
|
248
|
+
logger.debug("Exported JSON summary to: %s", path)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _percentile(sorted_data: list[float], p: float) -> float:
|
|
252
|
+
"""Compute the p-th percentile using linear interpolation.
|
|
253
|
+
|
|
254
|
+
Uses the Tensormesh-Benchmark V1 method:
|
|
255
|
+
``k = (len(sorted_data) - 1) * p / 100``
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
sorted_data: Pre-sorted list of values.
|
|
259
|
+
p: Percentile value (0-100).
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Interpolated percentile value. Returns 0.0 for empty data.
|
|
263
|
+
"""
|
|
264
|
+
if not sorted_data:
|
|
265
|
+
return 0.0
|
|
266
|
+
n = len(sorted_data)
|
|
267
|
+
if n == 1:
|
|
268
|
+
return sorted_data[0]
|
|
269
|
+
k = (n - 1) * p / 100.0
|
|
270
|
+
floor_k = int(k)
|
|
271
|
+
ceil_k = min(floor_k + 1, n - 1)
|
|
272
|
+
fraction = k - floor_k
|
|
273
|
+
return sorted_data[floor_k] + fraction * (
|
|
274
|
+
sorted_data[ceil_k] - sorted_data[floor_k]
|
|
275
|
+
)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Workload definitions and factory for ``lmcache bench engine``.
|
|
3
|
+
|
|
4
|
+
Each workload module defines its own config dataclass and workload
|
|
5
|
+
class. The ``create_workload`` factory selects the right workload
|
|
6
|
+
based on ``EngineBenchConfig.workload``, resolves the workload-specific
|
|
7
|
+
config from CLI args, and returns the workload instance.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard
|
|
11
|
+
import argparse
|
|
12
|
+
|
|
13
|
+
# First Party
|
|
14
|
+
from lmcache.cli.commands.bench.engine_bench.config import EngineBenchConfig
|
|
15
|
+
from lmcache.cli.commands.bench.engine_bench.progress import ProgressMonitor
|
|
16
|
+
from lmcache.cli.commands.bench.engine_bench.request_sender import (
|
|
17
|
+
RequestSender,
|
|
18
|
+
)
|
|
19
|
+
from lmcache.cli.commands.bench.engine_bench.stats import StatsCollector
|
|
20
|
+
from lmcache.cli.commands.bench.engine_bench.workloads.base import BaseWorkload
|
|
21
|
+
from lmcache.cli.commands.bench.engine_bench.workloads.long_doc_permutator import (
|
|
22
|
+
LongDocPermutatorConfig,
|
|
23
|
+
LongDocPermutatorWorkload,
|
|
24
|
+
)
|
|
25
|
+
from lmcache.cli.commands.bench.engine_bench.workloads.long_doc_qa import (
|
|
26
|
+
LongDocQAConfig,
|
|
27
|
+
LongDocQAWorkload,
|
|
28
|
+
)
|
|
29
|
+
from lmcache.cli.commands.bench.engine_bench.workloads.multi_round_chat import (
|
|
30
|
+
MultiRoundChatConfig,
|
|
31
|
+
MultiRoundChatWorkload,
|
|
32
|
+
)
|
|
33
|
+
from lmcache.cli.commands.bench.engine_bench.workloads.random_prefill import (
|
|
34
|
+
RandomPrefillConfig,
|
|
35
|
+
RandomPrefillWorkload,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"BaseWorkload",
|
|
40
|
+
"LongDocPermutatorConfig",
|
|
41
|
+
"LongDocPermutatorWorkload",
|
|
42
|
+
"LongDocQAConfig",
|
|
43
|
+
"LongDocQAWorkload",
|
|
44
|
+
"MultiRoundChatConfig",
|
|
45
|
+
"MultiRoundChatWorkload",
|
|
46
|
+
"RandomPrefillConfig",
|
|
47
|
+
"RandomPrefillWorkload",
|
|
48
|
+
"create_workload",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
_WORKLOAD_NAMES = (
|
|
52
|
+
"long-doc-permutator",
|
|
53
|
+
"long-doc-qa",
|
|
54
|
+
"multi-round-chat",
|
|
55
|
+
"random-prefill",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def create_workload(
|
|
60
|
+
config: EngineBenchConfig,
|
|
61
|
+
args: argparse.Namespace,
|
|
62
|
+
request_sender: RequestSender,
|
|
63
|
+
stats_collector: StatsCollector,
|
|
64
|
+
progress_monitor: ProgressMonitor,
|
|
65
|
+
) -> BaseWorkload:
|
|
66
|
+
"""Resolve workload-specific config and create the workload instance.
|
|
67
|
+
|
|
68
|
+
Dispatches on ``config.workload`` to the appropriate workload module,
|
|
69
|
+
resolves the workload-specific config from ``args`` and ``config``,
|
|
70
|
+
and returns the workload instance ready to ``run()``.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
config: Fully-resolved general benchmark config.
|
|
74
|
+
args: Raw CLI args namespace (contains workload-specific flags).
|
|
75
|
+
request_sender: Shared request sender instance.
|
|
76
|
+
stats_collector: Shared stats collector instance.
|
|
77
|
+
progress_monitor: Shared progress monitor instance.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
A concrete BaseWorkload instance.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ValueError: If the workload name is not recognized.
|
|
84
|
+
"""
|
|
85
|
+
if config.workload == "long-doc-permutator":
|
|
86
|
+
ldp_workload_config = LongDocPermutatorConfig.resolve(
|
|
87
|
+
num_contexts=args.ldp_num_contexts,
|
|
88
|
+
context_length=args.ldp_context_length,
|
|
89
|
+
system_prompt_length=args.ldp_system_prompt_length,
|
|
90
|
+
num_permutations=args.ldp_num_permutations,
|
|
91
|
+
vocab_size=8000,
|
|
92
|
+
num_inflight_requests=args.ldp_num_inflight_requests,
|
|
93
|
+
)
|
|
94
|
+
return LongDocPermutatorWorkload(
|
|
95
|
+
config=ldp_workload_config,
|
|
96
|
+
request_sender=request_sender,
|
|
97
|
+
stats_collector=stats_collector,
|
|
98
|
+
progress_monitor=progress_monitor,
|
|
99
|
+
seed=config.seed,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if config.workload == "long-doc-qa":
|
|
103
|
+
ld_workload_config = LongDocQAConfig.resolve(
|
|
104
|
+
kv_cache_volume_gb=config.kv_cache_volume_gb,
|
|
105
|
+
tokens_per_gb_kvcache=config.tokens_per_gb_kvcache,
|
|
106
|
+
document_length=args.ldqa_document_length,
|
|
107
|
+
query_per_document=args.ldqa_query_per_document,
|
|
108
|
+
shuffle_policy=args.ldqa_shuffle_policy,
|
|
109
|
+
num_inflight_requests=args.ldqa_num_inflight_requests,
|
|
110
|
+
)
|
|
111
|
+
return LongDocQAWorkload(
|
|
112
|
+
config=ld_workload_config,
|
|
113
|
+
request_sender=request_sender,
|
|
114
|
+
stats_collector=stats_collector,
|
|
115
|
+
progress_monitor=progress_monitor,
|
|
116
|
+
seed=config.seed,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if config.workload == "multi-round-chat":
|
|
120
|
+
mr_workload_config = MultiRoundChatConfig.resolve(
|
|
121
|
+
kv_cache_volume_gb=config.kv_cache_volume_gb,
|
|
122
|
+
tokens_per_gb_kvcache=config.tokens_per_gb_kvcache,
|
|
123
|
+
shared_prompt_length=args.mrc_shared_prompt_length,
|
|
124
|
+
chat_history_length=args.mrc_chat_history_length,
|
|
125
|
+
user_input_length=args.mrc_user_input_length,
|
|
126
|
+
output_length=args.mrc_output_length,
|
|
127
|
+
qps=args.mrc_qps,
|
|
128
|
+
duration=args.mrc_duration,
|
|
129
|
+
)
|
|
130
|
+
return MultiRoundChatWorkload(
|
|
131
|
+
config=mr_workload_config,
|
|
132
|
+
request_sender=request_sender,
|
|
133
|
+
stats_collector=stats_collector,
|
|
134
|
+
progress_monitor=progress_monitor,
|
|
135
|
+
seed=config.seed,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if config.workload == "random-prefill":
|
|
139
|
+
rp_workload_config = RandomPrefillConfig.resolve(
|
|
140
|
+
request_length=args.rp_request_length,
|
|
141
|
+
num_requests=args.rp_num_requests,
|
|
142
|
+
)
|
|
143
|
+
return RandomPrefillWorkload(
|
|
144
|
+
config=rp_workload_config,
|
|
145
|
+
request_sender=request_sender,
|
|
146
|
+
stats_collector=stats_collector,
|
|
147
|
+
progress_monitor=progress_monitor,
|
|
148
|
+
seed=config.seed,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
raise ValueError(
|
|
152
|
+
f"Unknown workload {config.workload!r}. Available: {', '.join(_WORKLOAD_NAMES)}"
|
|
153
|
+
)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Abstract base class for engine benchmark workloads."""
|
|
3
|
+
|
|
4
|
+
# Standard
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
import asyncio
|
|
7
|
+
import queue
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
# First Party
|
|
11
|
+
from lmcache.cli.commands.bench.engine_bench.progress import ProgressMonitor
|
|
12
|
+
from lmcache.cli.commands.bench.engine_bench.request_sender import RequestSender
|
|
13
|
+
from lmcache.cli.commands.bench.engine_bench.stats import StatsCollector
|
|
14
|
+
from lmcache.logging import init_logger
|
|
15
|
+
|
|
16
|
+
logger = init_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BaseWorkload(ABC):
|
|
20
|
+
"""Abstract base class for all engine benchmark workloads.
|
|
21
|
+
|
|
22
|
+
Owns the internal dispatch loop that calls ``step()`` on the
|
|
23
|
+
concrete workload. Provides a thread-safe ``request_finished``
|
|
24
|
+
callback that enqueues completed requests for the loop to drain.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
request_sender: RequestSender,
|
|
30
|
+
stats_collector: StatsCollector,
|
|
31
|
+
progress_monitor: ProgressMonitor,
|
|
32
|
+
) -> None:
|
|
33
|
+
self._request_sender = request_sender
|
|
34
|
+
self._stats_collector = stats_collector
|
|
35
|
+
self._progress_monitor = progress_monitor
|
|
36
|
+
self._finished_queue: queue.Queue[tuple[str, str]] = queue.Queue()
|
|
37
|
+
|
|
38
|
+
# ------------------------------------------------------------------
|
|
39
|
+
# Abstract methods
|
|
40
|
+
# ------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
async def warmup(self) -> None:
|
|
44
|
+
"""Run warmup requests. Blocks until all warmup is done."""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
async def step(self, time_offset: float) -> float:
|
|
48
|
+
"""Execute one step of the workload.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
time_offset: seconds since benchmark start.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Next wakeup time offset (absolute, from benchmark start).
|
|
55
|
+
Return a negative value to signal that the workload is done.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def log_config(self) -> None:
|
|
60
|
+
"""Log key workload config before the benchmark starts."""
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def on_request_finished(self, request_id: str, output: str) -> None:
|
|
64
|
+
"""Called when a request finishes (from the loop thread).
|
|
65
|
+
|
|
66
|
+
Stateless workloads can implement this as a no-op.
|
|
67
|
+
Stateful workloads use it to record responses in session history.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
# ------------------------------------------------------------------
|
|
71
|
+
# Concrete methods
|
|
72
|
+
# ------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
def run(self) -> None:
|
|
75
|
+
"""Run the full workload: warmup, then the benchmark loop.
|
|
76
|
+
|
|
77
|
+
Blocks until the workload is complete.
|
|
78
|
+
"""
|
|
79
|
+
asyncio.run(self._run_async())
|
|
80
|
+
|
|
81
|
+
async def _run_async(self) -> None:
|
|
82
|
+
"""Internal async implementation of the run loop."""
|
|
83
|
+
self._progress_monitor.log_message("Starting warmup phase")
|
|
84
|
+
await self.warmup()
|
|
85
|
+
|
|
86
|
+
self._progress_monitor.log_message(
|
|
87
|
+
"Warmup complete, starting benchmark",
|
|
88
|
+
)
|
|
89
|
+
self._stats_collector.reset()
|
|
90
|
+
self._drain_finished_queue() # discard warmup completions
|
|
91
|
+
|
|
92
|
+
start_time = time.monotonic()
|
|
93
|
+
|
|
94
|
+
while True:
|
|
95
|
+
self._drain_finished_queue()
|
|
96
|
+
time_offset = time.monotonic() - start_time
|
|
97
|
+
next_wakeup = await self.step(time_offset)
|
|
98
|
+
if next_wakeup < 0:
|
|
99
|
+
break
|
|
100
|
+
sleep_duration = max(0.0, next_wakeup - (time.monotonic() - start_time))
|
|
101
|
+
if sleep_duration > 0:
|
|
102
|
+
await asyncio.sleep(sleep_duration)
|
|
103
|
+
|
|
104
|
+
self._drain_finished_queue() # final drain
|
|
105
|
+
self._progress_monitor.log_message("Benchmark complete")
|
|
106
|
+
|
|
107
|
+
def request_finished(self, result, response_text: str) -> None:
|
|
108
|
+
"""Thread-safe callback matching ``OnFinishedCallback`` signature.
|
|
109
|
+
|
|
110
|
+
Enqueues ``(request_id, response_text)`` for the loop to drain.
|
|
111
|
+
Registered on ``RequestSender.on_finished`` by the orchestrator.
|
|
112
|
+
"""
|
|
113
|
+
self._finished_queue.put((result.request_id, response_text))
|
|
114
|
+
|
|
115
|
+
def _drain_finished_queue(self) -> None:
|
|
116
|
+
"""Drain all completed requests and call ``on_request_finished``."""
|
|
117
|
+
while True:
|
|
118
|
+
try:
|
|
119
|
+
request_id, output = self._finished_queue.get_nowait()
|
|
120
|
+
except queue.Empty:
|
|
121
|
+
break
|
|
122
|
+
self.on_request_finished(request_id, output)
|