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,571 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Shared utilities for check modes"""
|
|
3
|
+
|
|
4
|
+
# Standard
|
|
5
|
+
from typing import Optional
|
|
6
|
+
import asyncio
|
|
7
|
+
import hashlib
|
|
8
|
+
import threading
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
# Third Party
|
|
12
|
+
import torch
|
|
13
|
+
|
|
14
|
+
# First Party
|
|
15
|
+
from lmcache.utils import CacheEngineKey
|
|
16
|
+
|
|
17
|
+
# Import from lmcache with absolute paths
|
|
18
|
+
from lmcache.v1.memory_management import MemoryFormat, MemoryObj
|
|
19
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
20
|
+
from lmcache.v1.storage_backend.remote_backend import RemoteBackend
|
|
21
|
+
from lmcache.v1.storage_backend.storage_manager import StorageManager
|
|
22
|
+
|
|
23
|
+
DTYPE_MAP = {
|
|
24
|
+
"float32": torch.float32,
|
|
25
|
+
"float16": torch.float16,
|
|
26
|
+
"bfloat16": torch.bfloat16,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
DEFAULT_KV_DTYPE_STR = "bfloat16"
|
|
30
|
+
DEFAULT_OBJ_SIZE = 1024
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _compute_kv_shape(
|
|
34
|
+
obj_size: int,
|
|
35
|
+
) -> tuple:
|
|
36
|
+
"""Compute a kv_shape that yields the given obj_size.
|
|
37
|
+
|
|
38
|
+
The returned shape is in vllm format:
|
|
39
|
+
``(num_layers, 2, num_tokens, num_heads, head_size)``.
|
|
40
|
+
|
|
41
|
+
The final KV_2LTD tensor has
|
|
42
|
+
``2 * num_layers * num_tokens * (num_heads * head_size)``
|
|
43
|
+
elements, which equals *obj_size*.
|
|
44
|
+
|
|
45
|
+
We fix ``num_layers=1, num_heads=1`` and split the
|
|
46
|
+
remaining factor between ``num_tokens`` and ``head_size``
|
|
47
|
+
so that ``num_tokens * head_size = obj_size // 2``.
|
|
48
|
+
"""
|
|
49
|
+
if obj_size % 2 != 0:
|
|
50
|
+
raise ValueError("obj_size must be even (got %d)" % obj_size)
|
|
51
|
+
half = obj_size // 2
|
|
52
|
+
# (num_layers, kv_dim, num_tokens, num_heads, head_size)
|
|
53
|
+
return (1, 2, half, 1, 1)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def parse_kv_dtype(kv_dtype_str: str) -> Optional[torch.dtype]:
|
|
57
|
+
"""Parse a kv_dtype string to a torch.dtype.
|
|
58
|
+
|
|
59
|
+
Returns None if the string is not recognized.
|
|
60
|
+
"""
|
|
61
|
+
return DTYPE_MAP.get(kv_dtype_str)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _get_default_metadata(
|
|
65
|
+
model: str,
|
|
66
|
+
kv_dtype: torch.dtype = torch.bfloat16,
|
|
67
|
+
obj_size: Optional[int] = None,
|
|
68
|
+
) -> LMCacheMetadata:
|
|
69
|
+
"""Get default metadata for testing.
|
|
70
|
+
|
|
71
|
+
When *obj_size* is given the ``kv_shape`` is computed so
|
|
72
|
+
that the resulting KV_2LTD tensor has exactly *obj_size*
|
|
73
|
+
elements. Otherwise a small default shape is used.
|
|
74
|
+
"""
|
|
75
|
+
if obj_size is not None:
|
|
76
|
+
kv_shape = _compute_kv_shape(obj_size)
|
|
77
|
+
else:
|
|
78
|
+
kv_shape = _compute_kv_shape(DEFAULT_OBJ_SIZE)
|
|
79
|
+
return LMCacheMetadata(
|
|
80
|
+
model_name=model,
|
|
81
|
+
world_size=8,
|
|
82
|
+
local_world_size=8,
|
|
83
|
+
worker_id=0,
|
|
84
|
+
local_worker_id=0,
|
|
85
|
+
kv_dtype=kv_dtype,
|
|
86
|
+
kv_shape=kv_shape,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def create_test_key(
|
|
91
|
+
model: str,
|
|
92
|
+
key_id: str = "test_key",
|
|
93
|
+
kv_dtype: torch.dtype = torch.bfloat16,
|
|
94
|
+
) -> CacheEngineKey:
|
|
95
|
+
"""Create a test CacheEngineKey."""
|
|
96
|
+
return CacheEngineKey(
|
|
97
|
+
model_name=model,
|
|
98
|
+
world_size=8,
|
|
99
|
+
worker_id=0,
|
|
100
|
+
chunk_hash=int(hashlib.sha256(key_id.encode()).hexdigest(), 16),
|
|
101
|
+
dtype=kv_dtype,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def create_test_memory_obj_for_storage_manager(
|
|
106
|
+
storage_manager: StorageManager, metadata: LMCacheMetadata
|
|
107
|
+
) -> Optional[MemoryObj]:
|
|
108
|
+
"""Create a test MemoryObj for testing with StorageManager."""
|
|
109
|
+
# The metadata.kv_shape is in vllm format:
|
|
110
|
+
# [num_layers, 2, num_tokens, num_heads, head_size]
|
|
111
|
+
# For KV_2LTD format, we need shape: [2, num_layers, num_tokens, hidden_dim]
|
|
112
|
+
# where hidden_dim = num_heads * head_size
|
|
113
|
+
|
|
114
|
+
vllm_shape = metadata.kv_shape # [num_layers, 2, num_tokens, num_heads, head_size]
|
|
115
|
+
num_layers = vllm_shape[0] # 8
|
|
116
|
+
kv_dim = vllm_shape[1] # 2 (K and V)
|
|
117
|
+
num_tokens = vllm_shape[2] # 16
|
|
118
|
+
num_heads = vllm_shape[3] # 8
|
|
119
|
+
head_size = vllm_shape[4] # 16
|
|
120
|
+
|
|
121
|
+
# Convert to KV_2LTD format shape: [2, num_layers, num_tokens, hidden_dim]
|
|
122
|
+
hidden_dim = num_heads * head_size
|
|
123
|
+
kv_2ltd_shape = torch.Size([kv_dim, num_layers, num_tokens, hidden_dim])
|
|
124
|
+
|
|
125
|
+
memory_obj = storage_manager.allocate(
|
|
126
|
+
kv_2ltd_shape,
|
|
127
|
+
metadata.kv_dtype,
|
|
128
|
+
fmt=MemoryFormat.KV_2LTD,
|
|
129
|
+
eviction=True,
|
|
130
|
+
busy_loop=False,
|
|
131
|
+
)
|
|
132
|
+
return memory_obj
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def create_storage_manager_with_config(
|
|
136
|
+
model: str,
|
|
137
|
+
kv_dtype: torch.dtype = torch.bfloat16,
|
|
138
|
+
obj_size: Optional[int] = None,
|
|
139
|
+
):
|
|
140
|
+
"""Create storage manager with default configuration"""
|
|
141
|
+
# First Party
|
|
142
|
+
from lmcache.integration.vllm.utils import lmcache_get_or_create_config
|
|
143
|
+
from lmcache.v1.event_manager import EventManager
|
|
144
|
+
|
|
145
|
+
config = lmcache_get_or_create_config()
|
|
146
|
+
metadata = _get_default_metadata(model, kv_dtype=kv_dtype, obj_size=obj_size)
|
|
147
|
+
|
|
148
|
+
# Create event manager
|
|
149
|
+
event_manager = EventManager()
|
|
150
|
+
|
|
151
|
+
# Create storage manager
|
|
152
|
+
storage_manager = StorageManager(
|
|
153
|
+
config=config,
|
|
154
|
+
metadata=metadata,
|
|
155
|
+
event_manager=event_manager,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return storage_manager
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def find_remote_backend(storage_manager: StorageManager) -> Optional[RemoteBackend]:
|
|
162
|
+
"""Find remote backend from storage manager"""
|
|
163
|
+
for backend_name, backend in storage_manager.storage_backends.items():
|
|
164
|
+
if isinstance(backend, RemoteBackend):
|
|
165
|
+
return backend
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def wait_put_tasks_complete(
|
|
170
|
+
remote_backend: Optional[RemoteBackend], max_wait_time: float = 5.0
|
|
171
|
+
):
|
|
172
|
+
"""Wait for remote backend put tasks to complete"""
|
|
173
|
+
if remote_backend is None:
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
check_interval = 0.001
|
|
177
|
+
elapsed_time = 0.0
|
|
178
|
+
|
|
179
|
+
while elapsed_time < max_wait_time:
|
|
180
|
+
if not remote_backend.put_tasks:
|
|
181
|
+
break
|
|
182
|
+
time.sleep(check_interval)
|
|
183
|
+
elapsed_time += check_interval
|
|
184
|
+
|
|
185
|
+
# Log warning if timeout
|
|
186
|
+
remaining_tasks = len(remote_backend.put_tasks)
|
|
187
|
+
if remaining_tasks > 0:
|
|
188
|
+
print(
|
|
189
|
+
f"Warning: {remaining_tasks} remote put tasks still "
|
|
190
|
+
f"pending after {max_wait_time}s timeout"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def create_memory_objects_batch(
|
|
195
|
+
storage_manager: StorageManager, metadata: LMCacheMetadata, batch_size: int
|
|
196
|
+
) -> list[MemoryObj]:
|
|
197
|
+
"""Create a batch of memory objects for reuse"""
|
|
198
|
+
memory_objs = []
|
|
199
|
+
for i in range(batch_size):
|
|
200
|
+
memory_obj = create_test_memory_obj_for_storage_manager(
|
|
201
|
+
storage_manager, metadata
|
|
202
|
+
)
|
|
203
|
+
if memory_obj is not None:
|
|
204
|
+
memory_obj.ref_count_up()
|
|
205
|
+
memory_objs.append(memory_obj)
|
|
206
|
+
return memory_objs
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
async def flow_control_check(
|
|
210
|
+
remote_backend: Optional[RemoteBackend], concurrency: int, sleep_count: float = 1.0
|
|
211
|
+
) -> float:
|
|
212
|
+
"""Check flow control and wait if necessary"""
|
|
213
|
+
if remote_backend is None:
|
|
214
|
+
return sleep_count
|
|
215
|
+
|
|
216
|
+
high_watermark = 100 * concurrency
|
|
217
|
+
low_watermark = 10 * concurrency
|
|
218
|
+
current_tasks = len(remote_backend.put_tasks)
|
|
219
|
+
|
|
220
|
+
while current_tasks > high_watermark:
|
|
221
|
+
current_tasks = len(remote_backend.put_tasks)
|
|
222
|
+
if current_tasks > high_watermark:
|
|
223
|
+
# Too many pending tasks, wait before proceeding
|
|
224
|
+
sleep_sec = 0.1 * sleep_count
|
|
225
|
+
current_tasks = len(remote_backend.put_tasks)
|
|
226
|
+
await asyncio.sleep(sleep_sec)
|
|
227
|
+
current_tasks_after_sleep = len(remote_backend.put_tasks)
|
|
228
|
+
if current_tasks_after_sleep > low_watermark:
|
|
229
|
+
sleep_count *= 2.0
|
|
230
|
+
elif current_tasks_after_sleep == 0:
|
|
231
|
+
sleep_count /= 2.0
|
|
232
|
+
continue
|
|
233
|
+
if current_tasks <= low_watermark:
|
|
234
|
+
break
|
|
235
|
+
|
|
236
|
+
return sleep_count
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
async def run_perf_test_with_timeout(func, args_list, timeout=30.0):
|
|
240
|
+
"""Common performance test framework with timeout handling"""
|
|
241
|
+
times = []
|
|
242
|
+
results = [] # Collect results for each operation
|
|
243
|
+
for i, args in enumerate(args_list):
|
|
244
|
+
try:
|
|
245
|
+
start = time.perf_counter()
|
|
246
|
+
result = await asyncio.wait_for(func(*args), timeout=timeout)
|
|
247
|
+
end = time.perf_counter()
|
|
248
|
+
times.append((end - start) * 1000)
|
|
249
|
+
results.append(result)
|
|
250
|
+
print(
|
|
251
|
+
f" Test {i + 1}/{len(args_list)} completed in "
|
|
252
|
+
f"{(end - start) * 1000:.2f}ms"
|
|
253
|
+
)
|
|
254
|
+
except asyncio.TimeoutError:
|
|
255
|
+
print(f" Test {i + 1}/{len(args_list)} timed out after {timeout}s")
|
|
256
|
+
times.append(timeout * 1000)
|
|
257
|
+
results.append(None)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
print(f" Test {i + 1}/{len(args_list)} failed: {e}")
|
|
260
|
+
times.append(0)
|
|
261
|
+
results.append(None)
|
|
262
|
+
|
|
263
|
+
if times:
|
|
264
|
+
return {
|
|
265
|
+
"time_stats": {
|
|
266
|
+
"avg": sum(times) / len(times),
|
|
267
|
+
"max": max(times),
|
|
268
|
+
"min": min(times),
|
|
269
|
+
},
|
|
270
|
+
"results": results,
|
|
271
|
+
}
|
|
272
|
+
else:
|
|
273
|
+
return {"time_stats": {"avg": 0, "max": 0, "min": 0}, "results": []}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _format_throughput(avg_ms: float, obj_bytes: int) -> str:
|
|
277
|
+
"""Format throughput as a human-readable string."""
|
|
278
|
+
if avg_ms <= 0 or obj_bytes <= 0:
|
|
279
|
+
return "N/A"
|
|
280
|
+
bps = obj_bytes / (avg_ms / 1000.0)
|
|
281
|
+
if bps >= 1 << 30:
|
|
282
|
+
return "%.2f GB/s" % (bps / (1 << 30))
|
|
283
|
+
if bps >= 1 << 20:
|
|
284
|
+
return "%.2f MB/s" % (bps / (1 << 20))
|
|
285
|
+
return "%.2f KB/s" % (bps / (1 << 10))
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def print_performance_results(
|
|
289
|
+
stats_data,
|
|
290
|
+
obj_bytes: int = 0,
|
|
291
|
+
throughput_ops: Optional[set] = None,
|
|
292
|
+
):
|
|
293
|
+
"""Print performance results in a formatted table.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
stats_data: list of (op, stats, results, pass_count).
|
|
297
|
+
obj_bytes: size of one object in bytes. When > 0 a
|
|
298
|
+
throughput column is shown for operations listed
|
|
299
|
+
in *throughput_ops*.
|
|
300
|
+
throughput_ops: set of operation name prefixes that
|
|
301
|
+
should show throughput (e.g. {"STORE", "LOAD"}).
|
|
302
|
+
Defaults to common data-transfer operations.
|
|
303
|
+
"""
|
|
304
|
+
if throughput_ops is None:
|
|
305
|
+
throughput_ops = {
|
|
306
|
+
"STORE",
|
|
307
|
+
"LOAD",
|
|
308
|
+
"PUT",
|
|
309
|
+
"GET",
|
|
310
|
+
}
|
|
311
|
+
show_tp = obj_bytes > 0
|
|
312
|
+
|
|
313
|
+
sep_len = 118 if show_tp else 100
|
|
314
|
+
tp_hdr = " | %s" % "Throughput".center(14) if show_tp else ""
|
|
315
|
+
print("\nPerformance Results:")
|
|
316
|
+
print("-" * sep_len)
|
|
317
|
+
print(
|
|
318
|
+
f"| {'Operation':<20} | {'Avg (ms)':>12} "
|
|
319
|
+
f"| {'Max (ms)':>12} "
|
|
320
|
+
f"| {'Min (ms)':>12} "
|
|
321
|
+
f"| {'Pass/All':>10} "
|
|
322
|
+
f"| {'Pass Rate':>10} |" + tp_hdr
|
|
323
|
+
)
|
|
324
|
+
print("-" * sep_len)
|
|
325
|
+
for op, stats, results, pass_count in stats_data:
|
|
326
|
+
total = len(results)
|
|
327
|
+
pass_all = f"{pass_count}/{total}"
|
|
328
|
+
pass_rate = pass_count / total * 100 if total > 0 else 0
|
|
329
|
+
tp_col = ""
|
|
330
|
+
if show_tp:
|
|
331
|
+
is_data_op = any(op.startswith(p) for p in throughput_ops)
|
|
332
|
+
if is_data_op:
|
|
333
|
+
tp_col = " | %s" % _format_throughput(
|
|
334
|
+
stats["avg"],
|
|
335
|
+
obj_bytes,
|
|
336
|
+
).center(14)
|
|
337
|
+
else:
|
|
338
|
+
tp_col = " | %s" % "-".center(14)
|
|
339
|
+
|
|
340
|
+
print(
|
|
341
|
+
f"| {op:<20} "
|
|
342
|
+
f"| {stats['avg']:>12.6f} "
|
|
343
|
+
f"| {stats['max']:>12.6f} "
|
|
344
|
+
f"| {stats['min']:>12.6f} "
|
|
345
|
+
f"| {pass_all:>10} "
|
|
346
|
+
f"| {pass_rate:>9.1f}% |" + tp_col
|
|
347
|
+
)
|
|
348
|
+
print("-" * sep_len)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def validate_get_results(get_results, exist_keys, exist_memories, num_tests):
|
|
352
|
+
"""Validate GET operation results and return statistics"""
|
|
353
|
+
content_valid_count = 0
|
|
354
|
+
for i, result in enumerate(get_results["results"]):
|
|
355
|
+
if result is None:
|
|
356
|
+
print(f" GET for key {exist_keys[i]} returned None result")
|
|
357
|
+
continue
|
|
358
|
+
try:
|
|
359
|
+
if result.tensor is None:
|
|
360
|
+
print(f" GET for key {exist_keys[i]} returned None tensor")
|
|
361
|
+
continue
|
|
362
|
+
|
|
363
|
+
if exist_memories[i].tensor is None:
|
|
364
|
+
print(f" Original memory object {i} has None tensor")
|
|
365
|
+
continue
|
|
366
|
+
|
|
367
|
+
# Compare data content
|
|
368
|
+
data_match = torch.equal(result.tensor, exist_memories[i].tensor)
|
|
369
|
+
|
|
370
|
+
if data_match:
|
|
371
|
+
content_valid_count += 1
|
|
372
|
+
else:
|
|
373
|
+
print(f" GET for key {exist_keys[i]} returned incorrect memory object")
|
|
374
|
+
print(" Data content mismatch detected")
|
|
375
|
+
|
|
376
|
+
except Exception as e:
|
|
377
|
+
print(f" Data comparison failed for key {exist_keys[i]}: {e}")
|
|
378
|
+
# Standard
|
|
379
|
+
import traceback
|
|
380
|
+
|
|
381
|
+
traceback.print_exc()
|
|
382
|
+
|
|
383
|
+
# Calculate pass rates
|
|
384
|
+
not_none_count = sum(1 for r in get_results["results"] if r is not None)
|
|
385
|
+
content_pass_rate = content_valid_count / num_tests * 100
|
|
386
|
+
print(f" Validation (not None): {not_none_count}/{num_tests} passed")
|
|
387
|
+
print(
|
|
388
|
+
f" Validation (content correct): {content_valid_count}/{num_tests}"
|
|
389
|
+
f" passed ({content_pass_rate:.1f}%)"
|
|
390
|
+
)
|
|
391
|
+
return content_valid_count, not_none_count
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
async def run_common_test_framework(
|
|
395
|
+
test_context,
|
|
396
|
+
model: str,
|
|
397
|
+
num_tests: int = 5,
|
|
398
|
+
settle_time: float = 0.0,
|
|
399
|
+
):
|
|
400
|
+
"""
|
|
401
|
+
Common test framework for both storage manager and remote backend tests.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
test_context: A dictionary containing test-specific functions and objects:
|
|
405
|
+
- 'create_test_data_func': Function to create test data
|
|
406
|
+
- 'async_contains_func': Async function for contains operations
|
|
407
|
+
- 'async_put_func': Async function for put operations
|
|
408
|
+
- 'async_get_func': Async function for get operations
|
|
409
|
+
- 'validate_get_func': Function to validate get results
|
|
410
|
+
- 'test_object': The main test object (storage_manager or backend)
|
|
411
|
+
- 'extra_args': Extra arguments for test data creation (optional)
|
|
412
|
+
model: Model name for testing
|
|
413
|
+
num_tests: Number of tests to run
|
|
414
|
+
"""
|
|
415
|
+
print("Testing basic operations...")
|
|
416
|
+
|
|
417
|
+
# Create test data using the provided function
|
|
418
|
+
extra_args = test_context.get("extra_args", [])
|
|
419
|
+
extra_kwargs = {}
|
|
420
|
+
if "kv_dtype" in test_context:
|
|
421
|
+
extra_kwargs["kv_dtype"] = test_context["kv_dtype"]
|
|
422
|
+
if extra_args:
|
|
423
|
+
non_exist_keys, exist_keys, exist_memories, num_tests = test_context[
|
|
424
|
+
"create_test_data_func"
|
|
425
|
+
](test_context["test_object"], *extra_args, model, num_tests, **extra_kwargs)
|
|
426
|
+
else:
|
|
427
|
+
kv_dtype = test_context.get("kv_dtype")
|
|
428
|
+
obj_size = test_context.get("obj_size")
|
|
429
|
+
meta_kw = {}
|
|
430
|
+
if kv_dtype:
|
|
431
|
+
meta_kw["kv_dtype"] = kv_dtype
|
|
432
|
+
if obj_size is not None:
|
|
433
|
+
meta_kw["obj_size"] = obj_size
|
|
434
|
+
metadata = _get_default_metadata(model, **meta_kw)
|
|
435
|
+
non_exist_keys, exist_keys, exist_memories, num_tests = test_context[
|
|
436
|
+
"create_test_data_func"
|
|
437
|
+
](test_context["test_object"], metadata, model, num_tests, **extra_kwargs)
|
|
438
|
+
|
|
439
|
+
# Phase 1: exists test (key does not exist)
|
|
440
|
+
print("Phase 1: Testing exists for non-existing keys...")
|
|
441
|
+
|
|
442
|
+
exists_non_exist_res = await run_perf_test_with_timeout(
|
|
443
|
+
test_context["async_contains_func"],
|
|
444
|
+
[(test_context["test_object"], key) for key in non_exist_keys],
|
|
445
|
+
)
|
|
446
|
+
exists_non_exist_stats = exists_non_exist_res["time_stats"]
|
|
447
|
+
# Validation: All non-existing keys should return False
|
|
448
|
+
exists_non_exist_pass_count = sum(
|
|
449
|
+
1 for r in exists_non_exist_res["results"] if r is False
|
|
450
|
+
)
|
|
451
|
+
pass_rate = exists_non_exist_pass_count / len(non_exist_keys) * 100
|
|
452
|
+
print(
|
|
453
|
+
f" Validation: {exists_non_exist_pass_count}/{len(non_exist_keys)} "
|
|
454
|
+
f"passed ({pass_rate:.1f}%)"
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Phase 2: put test (create new key)
|
|
458
|
+
print("Phase 2: Testing put operations...")
|
|
459
|
+
|
|
460
|
+
put_res = await run_perf_test_with_timeout(
|
|
461
|
+
test_context["async_put_func"],
|
|
462
|
+
[
|
|
463
|
+
(test_context["test_object"], exist_keys[i], exist_memories[i])
|
|
464
|
+
for i in range(num_tests)
|
|
465
|
+
],
|
|
466
|
+
)
|
|
467
|
+
put_stats = put_res["time_stats"]
|
|
468
|
+
# Validation: All PUT operations should return True
|
|
469
|
+
put_pass_count = sum(1 for r in put_res["results"] if r is True)
|
|
470
|
+
pass_rate = put_pass_count / num_tests * 100
|
|
471
|
+
print(f" Validation: {put_pass_count}/{num_tests} passed ({pass_rate:.1f}%)")
|
|
472
|
+
|
|
473
|
+
if settle_time > 0:
|
|
474
|
+
print(" Waiting %.1fs for data to settle..." % settle_time)
|
|
475
|
+
await asyncio.sleep(settle_time)
|
|
476
|
+
|
|
477
|
+
# Phase 3: exists test (key exists)
|
|
478
|
+
print("Phase 3: Testing exists for existing keys...")
|
|
479
|
+
|
|
480
|
+
exists_exist_res = await run_perf_test_with_timeout(
|
|
481
|
+
test_context["async_contains_func"],
|
|
482
|
+
[(test_context["test_object"], key) for key in exist_keys],
|
|
483
|
+
)
|
|
484
|
+
exists_exist_stats = exists_exist_res["time_stats"]
|
|
485
|
+
# Validation: All existing keys should return True
|
|
486
|
+
exists_exist_pass_count = sum(1 for r in exists_exist_res["results"] if r is True)
|
|
487
|
+
pass_rate = exists_exist_pass_count / num_tests * 100
|
|
488
|
+
print(
|
|
489
|
+
f" Validation: {exists_exist_pass_count}/{num_tests} passed ({pass_rate:.1f}%)"
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
# Phase 4: get test (key exists)
|
|
493
|
+
print("Phase 4: Testing get operations...")
|
|
494
|
+
|
|
495
|
+
get_res = await run_perf_test_with_timeout(
|
|
496
|
+
test_context["async_get_func"],
|
|
497
|
+
[(test_context["test_object"], key) for key in exist_keys],
|
|
498
|
+
)
|
|
499
|
+
get_stats = get_res["time_stats"]
|
|
500
|
+
# Validation: Check for non-None results and content correctness
|
|
501
|
+
content_valid_count, not_none_count = test_context["validate_get_func"](
|
|
502
|
+
get_res, exist_keys, exist_memories, num_tests
|
|
503
|
+
)
|
|
504
|
+
# Use content_valid_count as the pass_count for GET operations
|
|
505
|
+
get_pass_count = content_valid_count
|
|
506
|
+
|
|
507
|
+
stats_data = [
|
|
508
|
+
(
|
|
509
|
+
"EXISTS (non-exist)",
|
|
510
|
+
exists_non_exist_stats,
|
|
511
|
+
exists_non_exist_res["results"],
|
|
512
|
+
exists_non_exist_pass_count,
|
|
513
|
+
),
|
|
514
|
+
("PUT", put_stats, put_res["results"], put_pass_count),
|
|
515
|
+
(
|
|
516
|
+
"EXISTS (exist)",
|
|
517
|
+
exists_exist_stats,
|
|
518
|
+
exists_exist_res["results"],
|
|
519
|
+
exists_exist_pass_count,
|
|
520
|
+
),
|
|
521
|
+
("GET", get_stats, get_res["results"], get_pass_count),
|
|
522
|
+
]
|
|
523
|
+
|
|
524
|
+
# Compute per-object byte size for throughput display
|
|
525
|
+
tp_obj_size = test_context.get("obj_size") or DEFAULT_OBJ_SIZE
|
|
526
|
+
tp_kv_dtype = test_context.get("kv_dtype") or torch.bfloat16
|
|
527
|
+
obj_bytes = tp_obj_size * torch.tensor([], dtype=tp_kv_dtype).element_size()
|
|
528
|
+
|
|
529
|
+
# Use common performance results printing
|
|
530
|
+
print_performance_results(stats_data, obj_bytes=obj_bytes)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class EventLoopManager:
|
|
534
|
+
"""Manages a dedicated event loop in a separate thread"""
|
|
535
|
+
|
|
536
|
+
def __init__(self):
|
|
537
|
+
self.loop = None
|
|
538
|
+
self.thread = None
|
|
539
|
+
self._loop_started = threading.Event()
|
|
540
|
+
|
|
541
|
+
def start(self):
|
|
542
|
+
"""Start the event loop in a separate thread"""
|
|
543
|
+
if self.thread is not None and self.thread.is_alive():
|
|
544
|
+
return
|
|
545
|
+
|
|
546
|
+
self.loop = asyncio.new_event_loop()
|
|
547
|
+
self.thread = threading.Thread(target=self._run_loop, daemon=True)
|
|
548
|
+
self.thread.start()
|
|
549
|
+
self._loop_started.wait()
|
|
550
|
+
|
|
551
|
+
def _run_loop(self):
|
|
552
|
+
"""Run the event loop"""
|
|
553
|
+
asyncio.set_event_loop(self.loop)
|
|
554
|
+
self._loop_started.set()
|
|
555
|
+
try:
|
|
556
|
+
self.loop.run_forever()
|
|
557
|
+
except Exception as e:
|
|
558
|
+
print(f"Event loop error: {e}")
|
|
559
|
+
finally:
|
|
560
|
+
self.loop.close()
|
|
561
|
+
|
|
562
|
+
def stop(self):
|
|
563
|
+
"""Stop the event loop and thread"""
|
|
564
|
+
if self.loop and not self.loop.is_closed():
|
|
565
|
+
self.loop.call_soon_threadsafe(self.loop.stop)
|
|
566
|
+
if self.thread and self.thread.is_alive():
|
|
567
|
+
self.thread.join(timeout=5.0)
|
|
568
|
+
|
|
569
|
+
def get_loop(self):
|
|
570
|
+
"""Get the event loop"""
|
|
571
|
+
return self.loop
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
import abc
|
|
5
|
+
|
|
6
|
+
# Third Party
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
# First Party
|
|
11
|
+
from lmcache.v1.compute.attention.metadata import LMCAttnMetadata
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AttentionInterface(metaclass=abc.ABCMeta):
|
|
15
|
+
@abc.abstractmethod
|
|
16
|
+
def forward_contiguous(
|
|
17
|
+
self,
|
|
18
|
+
query: torch.Tensor,
|
|
19
|
+
key: torch.Tensor,
|
|
20
|
+
value: torch.Tensor,
|
|
21
|
+
output: torch.Tensor,
|
|
22
|
+
attn_metadata: "LMCAttnMetadata",
|
|
23
|
+
**kwargs,
|
|
24
|
+
) -> torch.Tensor:
|
|
25
|
+
"""
|
|
26
|
+
Perform forward pass of the attention mechanism.
|
|
27
|
+
"""
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def init_attn_metadata(
|
|
32
|
+
self,
|
|
33
|
+
input_ids: torch.Tensor,
|
|
34
|
+
**kwargs,
|
|
35
|
+
) -> "LMCAttnMetadata":
|
|
36
|
+
"""
|
|
37
|
+
Initialize attention metadata.
|
|
38
|
+
"""
|
|
39
|
+
raise NotImplementedError
|