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,167 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""
|
|
3
|
+
Filesystem native L2 adapter config and factory.
|
|
4
|
+
|
|
5
|
+
Backed by the native C++ filesystem connector wrapped with
|
|
6
|
+
``NativeConnectorL2Adapter``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Future
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
# Standard
|
|
13
|
+
from typing import TYPE_CHECKING, Optional
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from lmcache.v1.distributed.internal_api import (
|
|
17
|
+
L1MemoryDesc,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# First Party
|
|
21
|
+
from lmcache.logging import init_logger
|
|
22
|
+
from lmcache.v1.distributed.l2_adapters.base import (
|
|
23
|
+
L2AdapterInterface,
|
|
24
|
+
)
|
|
25
|
+
from lmcache.v1.distributed.l2_adapters.config import (
|
|
26
|
+
L2AdapterConfigBase,
|
|
27
|
+
register_l2_adapter_type,
|
|
28
|
+
)
|
|
29
|
+
from lmcache.v1.distributed.l2_adapters.factory import (
|
|
30
|
+
register_l2_adapter_factory,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
logger = init_logger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FSNativeL2AdapterConfig(L2AdapterConfigBase):
|
|
37
|
+
"""
|
|
38
|
+
Config for an L2 adapter backed by the native C++
|
|
39
|
+
filesystem connector.
|
|
40
|
+
|
|
41
|
+
Fields:
|
|
42
|
+
- base_path: directory for storing KV cache files.
|
|
43
|
+
- num_workers: C++ worker threads for I/O (default 4).
|
|
44
|
+
- relative_tmp_dir: relative sub-dir for temp files.
|
|
45
|
+
- use_odirect: bypass page cache via O_DIRECT.
|
|
46
|
+
- read_ahead_size: trigger filesystem readahead by
|
|
47
|
+
reading this many bytes first (optional).
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
base_path: str,
|
|
53
|
+
num_workers: int = 4,
|
|
54
|
+
relative_tmp_dir: str = "",
|
|
55
|
+
use_odirect: bool = False,
|
|
56
|
+
read_ahead_size: Optional[int] = None,
|
|
57
|
+
max_capacity_gb: float = 0,
|
|
58
|
+
):
|
|
59
|
+
self.base_path = base_path
|
|
60
|
+
self.num_workers = num_workers
|
|
61
|
+
self.relative_tmp_dir = relative_tmp_dir
|
|
62
|
+
self.use_odirect = use_odirect
|
|
63
|
+
self.read_ahead_size = read_ahead_size
|
|
64
|
+
self.max_capacity_gb = max_capacity_gb
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def from_dict(cls, d: dict) -> "FSNativeL2AdapterConfig":
|
|
68
|
+
base_path = d.get("base_path")
|
|
69
|
+
if not isinstance(base_path, str) or not base_path:
|
|
70
|
+
raise ValueError("base_path must be a non-empty string")
|
|
71
|
+
|
|
72
|
+
num_workers = d.get("num_workers", 4)
|
|
73
|
+
if not isinstance(num_workers, int) or num_workers <= 0:
|
|
74
|
+
raise ValueError("num_workers must be a positive integer")
|
|
75
|
+
|
|
76
|
+
relative_tmp_dir = d.get("relative_tmp_dir", "")
|
|
77
|
+
if not isinstance(relative_tmp_dir, str):
|
|
78
|
+
raise ValueError("relative_tmp_dir must be a string")
|
|
79
|
+
|
|
80
|
+
use_odirect = d.get("use_odirect", False)
|
|
81
|
+
if not isinstance(use_odirect, bool):
|
|
82
|
+
raise ValueError("use_odirect must be a boolean")
|
|
83
|
+
|
|
84
|
+
read_ahead_size = d.get("read_ahead_size", None)
|
|
85
|
+
if read_ahead_size is not None:
|
|
86
|
+
if not isinstance(read_ahead_size, int) or read_ahead_size <= 0:
|
|
87
|
+
raise ValueError("read_ahead_size must be a positive integer")
|
|
88
|
+
|
|
89
|
+
max_capacity_gb = d.get("max_capacity_gb", 0)
|
|
90
|
+
if not isinstance(max_capacity_gb, (int, float)) or max_capacity_gb < 0:
|
|
91
|
+
raise ValueError("max_capacity_gb must be a non-negative number")
|
|
92
|
+
|
|
93
|
+
return cls(
|
|
94
|
+
base_path=base_path,
|
|
95
|
+
num_workers=num_workers,
|
|
96
|
+
relative_tmp_dir=str(relative_tmp_dir),
|
|
97
|
+
use_odirect=use_odirect,
|
|
98
|
+
read_ahead_size=read_ahead_size,
|
|
99
|
+
max_capacity_gb=float(max_capacity_gb),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def help(cls) -> str:
|
|
104
|
+
return (
|
|
105
|
+
"FS native L2 adapter config fields:\n"
|
|
106
|
+
"- base_path (str): directory for KV "
|
|
107
|
+
"cache files (required)\n"
|
|
108
|
+
"- num_workers (int): C++ worker threads "
|
|
109
|
+
"for I/O (default 4, >0)\n"
|
|
110
|
+
"- relative_tmp_dir (str): relative "
|
|
111
|
+
"sub-dir for temp files (default empty)\n"
|
|
112
|
+
"- use_odirect (bool): bypass page cache "
|
|
113
|
+
"via O_DIRECT (default false)\n"
|
|
114
|
+
"- read_ahead_size (int): trigger fs "
|
|
115
|
+
"readahead by reading this many bytes "
|
|
116
|
+
"first (optional)\n"
|
|
117
|
+
"- max_capacity_gb (float): max L2 capacity "
|
|
118
|
+
"in GB for usage tracking / eviction "
|
|
119
|
+
"(default 0 = disabled)"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _create_fs_native_l2_adapter(
|
|
124
|
+
config: L2AdapterConfigBase,
|
|
125
|
+
l1_memory_desc: "Optional[L1MemoryDesc]" = None,
|
|
126
|
+
) -> L2AdapterInterface:
|
|
127
|
+
"""Create a NativeConnectorL2Adapter backed by the
|
|
128
|
+
C++ filesystem connector."""
|
|
129
|
+
try:
|
|
130
|
+
# First Party
|
|
131
|
+
from lmcache.lmcache_fs import (
|
|
132
|
+
LMCacheFSClient,
|
|
133
|
+
)
|
|
134
|
+
except ImportError as e:
|
|
135
|
+
raise RuntimeError(
|
|
136
|
+
"FS native L2 adapter requires the C++ FS "
|
|
137
|
+
"extension. Build with: pip install -e ."
|
|
138
|
+
) from e
|
|
139
|
+
|
|
140
|
+
# Lazy import to avoid circular dependency
|
|
141
|
+
# First Party
|
|
142
|
+
from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( # noqa: E501
|
|
143
|
+
NativeConnectorL2Adapter,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
assert isinstance(config, FSNativeL2AdapterConfig)
|
|
147
|
+
native_client = LMCacheFSClient(
|
|
148
|
+
config.base_path,
|
|
149
|
+
config.num_workers,
|
|
150
|
+
config.relative_tmp_dir,
|
|
151
|
+
config.use_odirect,
|
|
152
|
+
config.read_ahead_size or 0,
|
|
153
|
+
)
|
|
154
|
+
logger.info(
|
|
155
|
+
"Created FS native L2 adapter: %s (workers=%d, odirect=%s, read_ahead=%s)",
|
|
156
|
+
config.base_path,
|
|
157
|
+
config.num_workers,
|
|
158
|
+
config.use_odirect,
|
|
159
|
+
config.read_ahead_size,
|
|
160
|
+
)
|
|
161
|
+
return NativeConnectorL2Adapter(
|
|
162
|
+
native_client, max_capacity_gb=config.max_capacity_gb
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
register_l2_adapter_type("fs_native", FSNativeL2AdapterConfig)
|
|
167
|
+
register_l2_adapter_factory("fs_native", _create_fs_native_l2_adapter)
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
# Future
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
# Standard
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from typing import TYPE_CHECKING, Optional
|
|
9
|
+
import asyncio
|
|
10
|
+
import copy
|
|
11
|
+
import os
|
|
12
|
+
import threading
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from lmcache.v1.distributed.internal_api import (
|
|
17
|
+
L1MemoryDesc,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# First Party
|
|
21
|
+
from lmcache.logging import init_logger
|
|
22
|
+
from lmcache.native_storage_ops import Bitmap
|
|
23
|
+
from lmcache.v1.distributed.api import ObjectKey
|
|
24
|
+
from lmcache.v1.distributed.l2_adapters.base import L2AdapterInterface, L2TaskId
|
|
25
|
+
from lmcache.v1.distributed.l2_adapters.config import (
|
|
26
|
+
L2AdapterConfigBase,
|
|
27
|
+
register_l2_adapter_type,
|
|
28
|
+
)
|
|
29
|
+
from lmcache.v1.distributed.l2_adapters.factory import (
|
|
30
|
+
register_l2_adapter_factory,
|
|
31
|
+
)
|
|
32
|
+
from lmcache.v1.memory_management import MemoryObj, TensorMemoryObj
|
|
33
|
+
|
|
34
|
+
logger = init_logger(__name__)
|
|
35
|
+
|
|
36
|
+
# Helper function
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def clone_tensor_memory_obj(obj: MemoryObj) -> TensorMemoryObj:
|
|
40
|
+
assert isinstance(obj, TensorMemoryObj), (
|
|
41
|
+
"Only TensorMemoryObj is supported in this mock adapter"
|
|
42
|
+
)
|
|
43
|
+
raw_tensor = obj.raw_tensor
|
|
44
|
+
assert raw_tensor is not None, (
|
|
45
|
+
"The tensor data of the object cannot be None for cloning"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
new_obj = TensorMemoryObj(
|
|
49
|
+
raw_data=raw_tensor.detach().clone(),
|
|
50
|
+
metadata=copy.deepcopy(obj.metadata),
|
|
51
|
+
parent_allocator=None,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return new_obj
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Config class
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class MockL2AdapterConfig(L2AdapterConfigBase):
|
|
61
|
+
"""
|
|
62
|
+
Config for a mock L2 adapter (for testing).
|
|
63
|
+
|
|
64
|
+
Fields:
|
|
65
|
+
- max_size_gb: maximum size in GB.
|
|
66
|
+
- mock_bandwidth_gb: simulated bandwidth in GB/sec.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
max_size_gb: float,
|
|
72
|
+
mock_bandwidth_gb: float,
|
|
73
|
+
):
|
|
74
|
+
self.max_size_gb = max_size_gb
|
|
75
|
+
self.mock_bandwidth_gb = mock_bandwidth_gb
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_dict(cls, d: dict) -> "MockL2AdapterConfig":
|
|
79
|
+
max_size_gb = d.get("max_size_gb")
|
|
80
|
+
if not isinstance(max_size_gb, (int, float)) or max_size_gb <= 0:
|
|
81
|
+
raise ValueError("max_size_gb must be a positive number")
|
|
82
|
+
|
|
83
|
+
mock_bandwidth_gb = d.get("mock_bandwidth_gb")
|
|
84
|
+
if not isinstance(mock_bandwidth_gb, (int, float)) or mock_bandwidth_gb <= 0:
|
|
85
|
+
raise ValueError("mock_bandwidth_gb must be a positive number")
|
|
86
|
+
|
|
87
|
+
return cls(
|
|
88
|
+
max_size_gb=max_size_gb,
|
|
89
|
+
mock_bandwidth_gb=mock_bandwidth_gb,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def help(cls) -> str:
|
|
94
|
+
return (
|
|
95
|
+
"Mock L2 adapter config fields:\n"
|
|
96
|
+
"- max_size_gb (float): maximum size of "
|
|
97
|
+
"the adapter in GB (required, >0)\n"
|
|
98
|
+
"- mock_bandwidth_gb (float): simulated "
|
|
99
|
+
"bandwidth in GB/sec (required, >0)"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Main class
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class MockL2Adapter(L2AdapterInterface):
|
|
107
|
+
"""
|
|
108
|
+
A mock-up L2 adapter with a specific RAM size and mocked bandwidth
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self, config: MockL2AdapterConfig):
|
|
112
|
+
super().__init__()
|
|
113
|
+
self._config = config
|
|
114
|
+
self._max_capacity_bytes = int(config.max_size_gb * (1024**3))
|
|
115
|
+
self._bandwidth_byte_ps = int(config.mock_bandwidth_gb * (1024**3))
|
|
116
|
+
|
|
117
|
+
self._store_efd = os.eventfd(0, os.EFD_NONBLOCK | os.EFD_CLOEXEC)
|
|
118
|
+
self._lookup_efd = os.eventfd(0, os.EFD_NONBLOCK | os.EFD_CLOEXEC)
|
|
119
|
+
self._load_efd = os.eventfd(0, os.EFD_NONBLOCK | os.EFD_CLOEXEC)
|
|
120
|
+
|
|
121
|
+
self._memory_objects: dict[ObjectKey, MemoryObj] = {}
|
|
122
|
+
self._locked_keys: dict[ObjectKey, int] = defaultdict(int)
|
|
123
|
+
self._current_size_bytes: int = 0
|
|
124
|
+
|
|
125
|
+
# Task ID management
|
|
126
|
+
self._next_task_id: L2TaskId = 0
|
|
127
|
+
self._completed_store_tasks: dict[L2TaskId, bool] = {}
|
|
128
|
+
self._completed_lookup_tasks: dict[L2TaskId, Bitmap] = {}
|
|
129
|
+
self._completed_load_tasks: dict[L2TaskId, Bitmap] = {}
|
|
130
|
+
self._lock = threading.Lock() # lock for all shared state
|
|
131
|
+
|
|
132
|
+
# Asyncio event loop running in a background thread
|
|
133
|
+
self._loop = asyncio.new_event_loop()
|
|
134
|
+
self._loop_thread = threading.Thread(target=self._run_event_loop, daemon=True)
|
|
135
|
+
self._loop_thread.start()
|
|
136
|
+
|
|
137
|
+
# --------------------
|
|
138
|
+
# Event Fd Interface
|
|
139
|
+
# --------------------
|
|
140
|
+
|
|
141
|
+
def get_store_event_fd(self) -> int:
|
|
142
|
+
return self._store_efd
|
|
143
|
+
|
|
144
|
+
def get_lookup_and_lock_event_fd(self) -> int:
|
|
145
|
+
return self._lookup_efd
|
|
146
|
+
|
|
147
|
+
def get_load_event_fd(self) -> int:
|
|
148
|
+
return self._load_efd
|
|
149
|
+
|
|
150
|
+
# --------------------
|
|
151
|
+
# Store Interface
|
|
152
|
+
# --------------------
|
|
153
|
+
|
|
154
|
+
def submit_store_task(
|
|
155
|
+
self,
|
|
156
|
+
keys: list[ObjectKey],
|
|
157
|
+
objects: list[MemoryObj],
|
|
158
|
+
) -> L2TaskId:
|
|
159
|
+
"""
|
|
160
|
+
Submit a store task to store a batch of memory objects associated with
|
|
161
|
+
a batch of keys.
|
|
162
|
+
|
|
163
|
+
For the mock adapter, the store operation simulates bandwidth-limited
|
|
164
|
+
transfer by delaying completion based on object size and configured bandwidth.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
keys (list[ObjectKey]): the list of keys to be stored.
|
|
168
|
+
objects (list[MemoryObj]): the list of memory objects to be stored.
|
|
169
|
+
The length of the objects list should be the same as the length of
|
|
170
|
+
the keys list.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
L2TaskId: the task id of the submitted store task.
|
|
174
|
+
"""
|
|
175
|
+
with self._lock:
|
|
176
|
+
task_id = self._get_next_task_id()
|
|
177
|
+
|
|
178
|
+
asyncio.run_coroutine_threadsafe(
|
|
179
|
+
self._execute_store_in_the_loop(keys, objects, task_id), self._loop
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return task_id
|
|
183
|
+
|
|
184
|
+
def pop_completed_store_tasks(self) -> dict[L2TaskId, bool]:
|
|
185
|
+
"""
|
|
186
|
+
Pop all the completed store tasks with a flag indicating
|
|
187
|
+
whether the task is successful or not.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
dict[L2TaskId, bool]: a dictionary mapping the task id to a boolean flag
|
|
191
|
+
indicating whether the task is successful or not. True means
|
|
192
|
+
successful, and False means failed.
|
|
193
|
+
"""
|
|
194
|
+
with self._lock:
|
|
195
|
+
completed = self._completed_store_tasks
|
|
196
|
+
self._completed_store_tasks = {}
|
|
197
|
+
return completed
|
|
198
|
+
|
|
199
|
+
def submit_lookup_and_lock_task(self, keys: list[ObjectKey]) -> L2TaskId:
|
|
200
|
+
with self._lock:
|
|
201
|
+
task_id = self._get_next_task_id()
|
|
202
|
+
|
|
203
|
+
# Schedule the lookup operation in the event loop thread
|
|
204
|
+
self._loop.call_soon_threadsafe(self._execute_lookup_in_the_loop, keys, task_id)
|
|
205
|
+
return task_id
|
|
206
|
+
|
|
207
|
+
def query_lookup_and_lock_result(self, task_id: L2TaskId) -> Bitmap | None:
|
|
208
|
+
with self._lock:
|
|
209
|
+
return self._completed_lookup_tasks.pop(task_id, None)
|
|
210
|
+
|
|
211
|
+
def submit_unlock(self, keys: list[ObjectKey]) -> None:
|
|
212
|
+
def _unlock_keys(keys: list[ObjectKey]) -> None:
|
|
213
|
+
"""
|
|
214
|
+
Coroutine to unlock keys in the event loop thread.
|
|
215
|
+
This is a helper function to avoid blocking the main thread.
|
|
216
|
+
"""
|
|
217
|
+
for key in keys:
|
|
218
|
+
if key not in self._locked_keys:
|
|
219
|
+
continue
|
|
220
|
+
if self._locked_keys[key] <= 1:
|
|
221
|
+
del self._locked_keys[key]
|
|
222
|
+
else:
|
|
223
|
+
self._locked_keys[key] -= 1
|
|
224
|
+
|
|
225
|
+
# Schedule the unlock operation in the event loop thread
|
|
226
|
+
self._loop.call_soon_threadsafe(_unlock_keys, keys)
|
|
227
|
+
|
|
228
|
+
def submit_load_task(
|
|
229
|
+
self,
|
|
230
|
+
keys: list[ObjectKey],
|
|
231
|
+
objects: list[MemoryObj],
|
|
232
|
+
) -> L2TaskId:
|
|
233
|
+
with self._lock:
|
|
234
|
+
task_id = self._get_next_task_id()
|
|
235
|
+
|
|
236
|
+
# Schedule the load operation in the event loop thread
|
|
237
|
+
asyncio.run_coroutine_threadsafe(
|
|
238
|
+
self._execute_load_in_loop(keys, objects, task_id), self._loop
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return task_id
|
|
242
|
+
|
|
243
|
+
def query_load_result(self, task_id: L2TaskId) -> Bitmap | None:
|
|
244
|
+
with self._lock:
|
|
245
|
+
return self._completed_load_tasks.pop(task_id, None)
|
|
246
|
+
|
|
247
|
+
def close(self):
|
|
248
|
+
# Stop the event loop and wait for the thread to finish
|
|
249
|
+
async def _stop_tasks():
|
|
250
|
+
tasks = [
|
|
251
|
+
t
|
|
252
|
+
for t in asyncio.all_tasks(self._loop)
|
|
253
|
+
if t is not asyncio.current_task()
|
|
254
|
+
]
|
|
255
|
+
for task in tasks:
|
|
256
|
+
task.cancel()
|
|
257
|
+
if tasks:
|
|
258
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
259
|
+
|
|
260
|
+
if self._loop.is_running():
|
|
261
|
+
future = asyncio.run_coroutine_threadsafe(_stop_tasks(), self._loop)
|
|
262
|
+
try:
|
|
263
|
+
future.result(timeout=5) # Wait for tasks to be cancelled
|
|
264
|
+
except Exception:
|
|
265
|
+
pass # Ignore exceptions during shutdown
|
|
266
|
+
self._loop.call_soon_threadsafe(self._loop.stop)
|
|
267
|
+
|
|
268
|
+
self._loop_thread.join()
|
|
269
|
+
self._loop.close()
|
|
270
|
+
|
|
271
|
+
os.close(self._store_efd)
|
|
272
|
+
os.close(self._lookup_efd)
|
|
273
|
+
os.close(self._load_efd)
|
|
274
|
+
|
|
275
|
+
##################
|
|
276
|
+
# Debug / test-only functions
|
|
277
|
+
##################
|
|
278
|
+
|
|
279
|
+
def report_status(self) -> dict:
|
|
280
|
+
"""Return a status dict for the mock L2 adapter."""
|
|
281
|
+
with self._lock:
|
|
282
|
+
return {
|
|
283
|
+
"is_healthy": True,
|
|
284
|
+
"type": "MockL2Adapter",
|
|
285
|
+
"stored_object_count": len(self._memory_objects),
|
|
286
|
+
"locked_key_count": len(self._locked_keys),
|
|
287
|
+
"current_size_bytes": self._current_size_bytes,
|
|
288
|
+
"max_capacity_bytes": self._max_capacity_bytes,
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
def debug_get_stored_object_count(self) -> int:
|
|
292
|
+
"""
|
|
293
|
+
Return the number of objects currently stored in the mock adapter.
|
|
294
|
+
|
|
295
|
+
This method is intended for testing and debugging only.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
int: Number of stored objects.
|
|
299
|
+
"""
|
|
300
|
+
with self._lock:
|
|
301
|
+
return len(self._memory_objects)
|
|
302
|
+
|
|
303
|
+
def debug_get_locked_key_count(self) -> int:
|
|
304
|
+
"""
|
|
305
|
+
Return the number of currently locked keys.
|
|
306
|
+
|
|
307
|
+
This method is intended for testing and debugging only.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
int: Number of locked keys.
|
|
311
|
+
"""
|
|
312
|
+
with self._lock:
|
|
313
|
+
return len(self._locked_keys)
|
|
314
|
+
|
|
315
|
+
def debug_has_key(self, key: ObjectKey) -> bool:
|
|
316
|
+
"""
|
|
317
|
+
Check whether a specific key is stored in the mock adapter.
|
|
318
|
+
|
|
319
|
+
This method is intended for testing and debugging only.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
key: The object key to check.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
bool: True if the key is stored.
|
|
326
|
+
"""
|
|
327
|
+
with self._lock:
|
|
328
|
+
return key in self._memory_objects
|
|
329
|
+
|
|
330
|
+
##################
|
|
331
|
+
# Helper functions
|
|
332
|
+
##################
|
|
333
|
+
|
|
334
|
+
def _run_event_loop(self) -> None:
|
|
335
|
+
"""Run the asyncio event loop in a background thread."""
|
|
336
|
+
asyncio.set_event_loop(self._loop)
|
|
337
|
+
self._loop.run_forever()
|
|
338
|
+
|
|
339
|
+
def _get_next_task_id(self) -> L2TaskId:
|
|
340
|
+
"""Get the next task ID and increment the counter."""
|
|
341
|
+
task_id = self._next_task_id
|
|
342
|
+
self._next_task_id += 1
|
|
343
|
+
return task_id
|
|
344
|
+
|
|
345
|
+
#####################
|
|
346
|
+
# Eviction Interface
|
|
347
|
+
#####################
|
|
348
|
+
|
|
349
|
+
def delete(self, keys: list[ObjectKey]) -> None:
|
|
350
|
+
"""Delete a batch of objects from the mock adapter."""
|
|
351
|
+
deleted_keys: list[ObjectKey] = []
|
|
352
|
+
with self._lock:
|
|
353
|
+
for key in keys:
|
|
354
|
+
if key not in self._memory_objects:
|
|
355
|
+
continue
|
|
356
|
+
obj = self._memory_objects.pop(key)
|
|
357
|
+
self._current_size_bytes -= obj.get_size()
|
|
358
|
+
deleted_keys.append(key)
|
|
359
|
+
if deleted_keys:
|
|
360
|
+
self._notify_keys_deleted(deleted_keys)
|
|
361
|
+
|
|
362
|
+
def get_usage(self) -> tuple[float, float]:
|
|
363
|
+
"""Return (current_usage, usage_after_ongoing_eviction) in [0, 1]."""
|
|
364
|
+
with self._lock:
|
|
365
|
+
if self._max_capacity_bytes == 0:
|
|
366
|
+
return (0.0, 0.0)
|
|
367
|
+
usage = self._current_size_bytes / self._max_capacity_bytes
|
|
368
|
+
return (usage, usage)
|
|
369
|
+
|
|
370
|
+
def _signal_store_event(self) -> None:
|
|
371
|
+
"""Signal the store event fd to notify completion."""
|
|
372
|
+
os.eventfd_write(self._store_efd, 1)
|
|
373
|
+
|
|
374
|
+
async def _execute_store_in_the_loop(
|
|
375
|
+
self,
|
|
376
|
+
keys: list[ObjectKey],
|
|
377
|
+
objects: list[MemoryObj],
|
|
378
|
+
task_id: L2TaskId,
|
|
379
|
+
) -> None:
|
|
380
|
+
"""
|
|
381
|
+
Execute the store operation in the event loop thread.
|
|
382
|
+
This is a helper function to avoid blocking the main thread.
|
|
383
|
+
"""
|
|
384
|
+
total_bytes = 0
|
|
385
|
+
success = True
|
|
386
|
+
start = time.perf_counter()
|
|
387
|
+
|
|
388
|
+
stored_keys: list[ObjectKey] = []
|
|
389
|
+
try:
|
|
390
|
+
for key, obj in zip(keys, objects, strict=False):
|
|
391
|
+
obj_size = obj.get_size()
|
|
392
|
+
|
|
393
|
+
# If the object is larger than max capacity, skip it
|
|
394
|
+
if obj_size > self._max_capacity_bytes:
|
|
395
|
+
continue
|
|
396
|
+
|
|
397
|
+
# If key already exists, simply skip
|
|
398
|
+
if key in self._memory_objects:
|
|
399
|
+
continue
|
|
400
|
+
|
|
401
|
+
# Skip if there is not enough capacity
|
|
402
|
+
if self._current_size_bytes + obj_size > self._max_capacity_bytes:
|
|
403
|
+
logger.warning(
|
|
404
|
+
"MockL2Adapter: not enough capacity to store key %s "
|
|
405
|
+
"(used=%d, needed=%d, max=%d); skipping.",
|
|
406
|
+
key,
|
|
407
|
+
self._current_size_bytes,
|
|
408
|
+
obj_size,
|
|
409
|
+
self._max_capacity_bytes,
|
|
410
|
+
)
|
|
411
|
+
continue
|
|
412
|
+
|
|
413
|
+
# Store the object
|
|
414
|
+
new_obj = clone_tensor_memory_obj(obj)
|
|
415
|
+
self._memory_objects[key] = new_obj
|
|
416
|
+
self._current_size_bytes += obj_size
|
|
417
|
+
total_bytes += obj_size
|
|
418
|
+
stored_keys.append(key)
|
|
419
|
+
except Exception:
|
|
420
|
+
success = False
|
|
421
|
+
|
|
422
|
+
# Calculate delay based on bandwidth simulation
|
|
423
|
+
end = time.perf_counter()
|
|
424
|
+
delay_seconds = (
|
|
425
|
+
total_bytes / self._bandwidth_byte_ps if self._bandwidth_byte_ps > 0 else 0
|
|
426
|
+
)
|
|
427
|
+
delay_seconds -= end - start
|
|
428
|
+
delay_seconds = max(delay_seconds, 0) # Ensure non-negative delay
|
|
429
|
+
|
|
430
|
+
# Schedule completion coroutine on the event loop
|
|
431
|
+
await asyncio.sleep(delay_seconds)
|
|
432
|
+
with self._lock:
|
|
433
|
+
self._completed_store_tasks[task_id] = success
|
|
434
|
+
|
|
435
|
+
if stored_keys:
|
|
436
|
+
self._notify_keys_stored(stored_keys)
|
|
437
|
+
self._signal_store_event()
|
|
438
|
+
|
|
439
|
+
def _signal_lookup_event(self) -> None:
|
|
440
|
+
"""Signal the lookup event fd to notify completion."""
|
|
441
|
+
os.eventfd_write(self._lookup_efd, 1)
|
|
442
|
+
|
|
443
|
+
def _execute_lookup_in_the_loop(
|
|
444
|
+
self, keys: list[ObjectKey], task_id: L2TaskId
|
|
445
|
+
) -> None:
|
|
446
|
+
bitmap = Bitmap(len(keys))
|
|
447
|
+
for i, key in enumerate(keys):
|
|
448
|
+
if key not in self._memory_objects:
|
|
449
|
+
continue
|
|
450
|
+
bitmap.set(i)
|
|
451
|
+
self._locked_keys[key] += 1
|
|
452
|
+
with self._lock:
|
|
453
|
+
self._completed_lookup_tasks[task_id] = bitmap
|
|
454
|
+
self._signal_lookup_event()
|
|
455
|
+
|
|
456
|
+
def _signal_load_event(self) -> None:
|
|
457
|
+
"""Signal the load event fd to notify completion."""
|
|
458
|
+
os.eventfd_write(self._load_efd, 1)
|
|
459
|
+
|
|
460
|
+
async def _execute_load_in_loop(
|
|
461
|
+
self,
|
|
462
|
+
keys: list[ObjectKey],
|
|
463
|
+
objects: list[MemoryObj],
|
|
464
|
+
task_id: L2TaskId,
|
|
465
|
+
) -> None:
|
|
466
|
+
"""
|
|
467
|
+
Execute the load operation in the event loop thread.
|
|
468
|
+
This is a helper function to avoid blocking the main thread.
|
|
469
|
+
"""
|
|
470
|
+
bitmap = Bitmap(len(keys))
|
|
471
|
+
total_bytes = 0
|
|
472
|
+
accessed_keys: list[ObjectKey] = []
|
|
473
|
+
start = time.perf_counter()
|
|
474
|
+
|
|
475
|
+
for i, key in enumerate(keys):
|
|
476
|
+
if key not in self._memory_objects:
|
|
477
|
+
continue
|
|
478
|
+
# load data into the provided memory object
|
|
479
|
+
obj = self._memory_objects[key]
|
|
480
|
+
src_tensor = obj.tensor
|
|
481
|
+
dst_tensor = objects[i].tensor
|
|
482
|
+
assert src_tensor is not None
|
|
483
|
+
assert dst_tensor is not None
|
|
484
|
+
dst_tensor.copy_(src_tensor)
|
|
485
|
+
bitmap.set(i)
|
|
486
|
+
total_bytes += obj.get_size()
|
|
487
|
+
accessed_keys.append(key)
|
|
488
|
+
|
|
489
|
+
end = time.perf_counter()
|
|
490
|
+
delay_seconds = (
|
|
491
|
+
total_bytes / self._bandwidth_byte_ps if self._bandwidth_byte_ps > 0 else 0
|
|
492
|
+
)
|
|
493
|
+
delay_seconds -= end - start
|
|
494
|
+
delay_seconds = max(delay_seconds, 0) # Ensure non-negative delay
|
|
495
|
+
|
|
496
|
+
await asyncio.sleep(delay_seconds)
|
|
497
|
+
if accessed_keys:
|
|
498
|
+
self._notify_keys_accessed(accessed_keys)
|
|
499
|
+
with self._lock:
|
|
500
|
+
self._completed_load_tasks[task_id] = bitmap
|
|
501
|
+
self._signal_load_event()
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
# Self-register config type and adapter factory
|
|
505
|
+
register_l2_adapter_type("mock", MockL2AdapterConfig)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _create_mock_adapter(
|
|
509
|
+
config: L2AdapterConfigBase,
|
|
510
|
+
l1_memory_desc: "Optional[L1MemoryDesc]" = None,
|
|
511
|
+
) -> L2AdapterInterface:
|
|
512
|
+
"""Create a MockL2Adapter from config."""
|
|
513
|
+
return MockL2Adapter(config) # type: ignore[arg-type]
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
register_l2_adapter_factory("mock", _create_mock_adapter)
|