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,646 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
# Standard
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Callable, List, Optional, Sequence, Union
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
# Third Party
|
|
10
|
+
import msgspec
|
|
11
|
+
import torch
|
|
12
|
+
import zmq
|
|
13
|
+
|
|
14
|
+
# First Party
|
|
15
|
+
from lmcache.integration.vllm.utils import get_size_bytes
|
|
16
|
+
from lmcache.logging import init_logger
|
|
17
|
+
from lmcache.utils import (
|
|
18
|
+
STR_DTYPE_TO_TORCH_DTYPE,
|
|
19
|
+
TORCH_DTYPE_TO_STR_DTYPE,
|
|
20
|
+
CacheEngineKey,
|
|
21
|
+
)
|
|
22
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
23
|
+
from lmcache.v1.memory_management import (
|
|
24
|
+
MemoryFormat,
|
|
25
|
+
MemoryObj,
|
|
26
|
+
PagedCpuGpuMemoryAllocator,
|
|
27
|
+
)
|
|
28
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
29
|
+
from lmcache.v1.rpc_utils import get_zmq_context, get_zmq_socket
|
|
30
|
+
from lmcache.v1.storage_backend.abstract_backend import AllocatorBackendInterface
|
|
31
|
+
from lmcache.v1.transfer_channel import CreateTransferChannel
|
|
32
|
+
from lmcache.v1.transfer_channel.transfer_utils import get_correct_device
|
|
33
|
+
|
|
34
|
+
logger = init_logger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PDMsgBase(msgspec.Struct, tag=True):
|
|
38
|
+
"""Base class for all PD-related messages"""
|
|
39
|
+
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AllocRequest(PDMsgBase):
|
|
44
|
+
"""Allocation request message"""
|
|
45
|
+
|
|
46
|
+
keys: list[str] # len(keys) indicates num_chunks
|
|
47
|
+
fmt: int
|
|
48
|
+
shape: list[int] # The shape of the memory objects
|
|
49
|
+
dtype: str
|
|
50
|
+
last_chunk_toks: int
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AllocResponse(PDMsgBase):
|
|
54
|
+
"""Allocation response message"""
|
|
55
|
+
|
|
56
|
+
# Indexes (local) of already sent memory objects
|
|
57
|
+
already_sent_indexes: list[int]
|
|
58
|
+
|
|
59
|
+
# Indexes (remote) of allocated memory objects (to be written)
|
|
60
|
+
remote_indexes: list[int]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ProxyNotif(PDMsgBase):
|
|
64
|
+
req_id: str # The request UUID to notify the proxy
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
PDMsg = Union[AllocRequest, AllocResponse, ProxyNotif]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class PDConfig:
|
|
72
|
+
role: str
|
|
73
|
+
|
|
74
|
+
peer_host: str
|
|
75
|
+
peer_init_port: int
|
|
76
|
+
peer_alloc_port: int
|
|
77
|
+
|
|
78
|
+
buffer_size: int
|
|
79
|
+
buffer_device: str
|
|
80
|
+
|
|
81
|
+
proxy_host: Optional[str] = None
|
|
82
|
+
proxy_port: Optional[int] = None
|
|
83
|
+
skip_proxy_notification: bool = False
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def from_cache_engine_config(
|
|
87
|
+
config: LMCacheEngineConfig,
|
|
88
|
+
metadata: LMCacheMetadata,
|
|
89
|
+
tp_rank: int,
|
|
90
|
+
) -> "PDConfig":
|
|
91
|
+
"""Convert the LMCacheEngineConfig to PDConfig"""
|
|
92
|
+
|
|
93
|
+
role = config.pd_role
|
|
94
|
+
|
|
95
|
+
# TODO(Jiayi): Could be both if we want to do dynamic role switch.
|
|
96
|
+
assert role in ["sender", "receiver"], (
|
|
97
|
+
f"Invalid role: {config.pd_role}, must be either sender or receiver"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
assert config.pd_buffer_size is not None
|
|
101
|
+
assert config.pd_buffer_device is not None
|
|
102
|
+
|
|
103
|
+
if role == "receiver":
|
|
104
|
+
assert config.pd_peer_host is not None
|
|
105
|
+
assert config.pd_peer_init_port is not None
|
|
106
|
+
assert config.pd_peer_alloc_port is not None
|
|
107
|
+
elif role == "sender":
|
|
108
|
+
if not config.pd_skip_proxy_notification:
|
|
109
|
+
assert config.pd_proxy_host is not None
|
|
110
|
+
assert config.pd_proxy_port is not None
|
|
111
|
+
|
|
112
|
+
corrected_device = get_correct_device(
|
|
113
|
+
config.pd_buffer_device, metadata.worker_id
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if config.pd_peer_alloc_port is not None:
|
|
117
|
+
pd_peer_alloc_port = config.pd_peer_alloc_port[tp_rank]
|
|
118
|
+
else:
|
|
119
|
+
pd_peer_alloc_port = None
|
|
120
|
+
|
|
121
|
+
if config.pd_peer_init_port is not None:
|
|
122
|
+
pd_peer_init_port = config.pd_peer_init_port[tp_rank]
|
|
123
|
+
else:
|
|
124
|
+
pd_peer_init_port = None
|
|
125
|
+
|
|
126
|
+
return PDConfig(
|
|
127
|
+
role=role,
|
|
128
|
+
peer_host=config.pd_peer_host,
|
|
129
|
+
peer_init_port=pd_peer_init_port,
|
|
130
|
+
peer_alloc_port=pd_peer_alloc_port,
|
|
131
|
+
proxy_host=config.pd_proxy_host,
|
|
132
|
+
proxy_port=config.pd_proxy_port,
|
|
133
|
+
buffer_size=config.pd_buffer_size,
|
|
134
|
+
buffer_device=corrected_device,
|
|
135
|
+
skip_proxy_notification=config.pd_skip_proxy_notification,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class PDBackend(AllocatorBackendInterface):
|
|
140
|
+
"""
|
|
141
|
+
Implementation of the StorageBackendInterface for PD Disaggregation.
|
|
142
|
+
|
|
143
|
+
At the sender side, it will never save anything but directly write the data
|
|
144
|
+
to the receiver side.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def __init__(
|
|
148
|
+
self,
|
|
149
|
+
config: LMCacheEngineConfig,
|
|
150
|
+
metadata: LMCacheMetadata,
|
|
151
|
+
):
|
|
152
|
+
self.running = True
|
|
153
|
+
|
|
154
|
+
self.tp_rank = metadata.worker_id
|
|
155
|
+
|
|
156
|
+
self.pd_config = PDConfig.from_cache_engine_config(
|
|
157
|
+
config, metadata, self.tp_rank
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
self.corrected_device = get_correct_device(
|
|
161
|
+
config.pd_buffer_device,
|
|
162
|
+
metadata.worker_id,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# NOTE(Jiayi): sender/prefiller will not use this pool;
|
|
166
|
+
# only receiver/decoder will.
|
|
167
|
+
self.data: dict[CacheEngineKey, MemoryObj] = {}
|
|
168
|
+
self.data_lock = threading.Lock()
|
|
169
|
+
|
|
170
|
+
self.memory_allocator = self.initialize_allocator(config, metadata)
|
|
171
|
+
assert isinstance(self.memory_allocator, PagedCpuGpuMemoryAllocator)
|
|
172
|
+
|
|
173
|
+
# TODO(Jiayi): add async zmq context if we want better asynchrony.
|
|
174
|
+
self.zmq_context = get_zmq_context(use_asyncio=False)
|
|
175
|
+
self.running_threads: list[threading.Thread] = []
|
|
176
|
+
self.side_channels: list[zmq.Socket] = []
|
|
177
|
+
|
|
178
|
+
# Initialize transfer channel
|
|
179
|
+
peer_init_url = None
|
|
180
|
+
self.local_id = ""
|
|
181
|
+
# TODO(Jiayi): both sender and receiver have to have
|
|
182
|
+
# peer_init_url if they want to do instance flip.
|
|
183
|
+
if self.pd_config.peer_init_port is not None:
|
|
184
|
+
peer_init_url = (
|
|
185
|
+
f"{self.pd_config.peer_host}:{self.pd_config.peer_init_port}"
|
|
186
|
+
)
|
|
187
|
+
self.local_id = self.pd_config.peer_host + str(
|
|
188
|
+
self.pd_config.peer_init_port
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
allocator = (
|
|
192
|
+
self.memory_allocator.cpu_allocator
|
|
193
|
+
if self.corrected_device == "cpu"
|
|
194
|
+
else self.memory_allocator.gpu_allocator
|
|
195
|
+
)
|
|
196
|
+
self.transfer_channel = CreateTransferChannel(
|
|
197
|
+
async_mode=False,
|
|
198
|
+
channel_type=config.transfer_channel,
|
|
199
|
+
role=self.pd_config.role,
|
|
200
|
+
buffer_ptr=allocator.buffer_ptr,
|
|
201
|
+
buffer_size=allocator.buffer_size,
|
|
202
|
+
align_bytes=allocator.align_bytes,
|
|
203
|
+
tp_rank=self.tp_rank,
|
|
204
|
+
peer_init_url=peer_init_url,
|
|
205
|
+
backends=config.nixl_backends,
|
|
206
|
+
device=self.corrected_device,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if self.pd_config.role == "sender":
|
|
210
|
+
self._init_sender()
|
|
211
|
+
self.initialized_peers: set[str] = set()
|
|
212
|
+
self.mem_alloc_sockets: dict[str, zmq.Socket] = {}
|
|
213
|
+
elif self.pd_config.role == "receiver":
|
|
214
|
+
self._init_receiver()
|
|
215
|
+
else:
|
|
216
|
+
raise ValueError("Invalid PD role.")
|
|
217
|
+
|
|
218
|
+
self.full_chunk_size_bytes = config.chunk_size
|
|
219
|
+
|
|
220
|
+
def __str__(self):
|
|
221
|
+
return self.__class__.__name__
|
|
222
|
+
|
|
223
|
+
def initialize_allocator(
|
|
224
|
+
self, config: LMCacheEngineConfig, metadata: LMCacheMetadata
|
|
225
|
+
) -> PagedCpuGpuMemoryAllocator:
|
|
226
|
+
if self.corrected_device != "cpu":
|
|
227
|
+
logger.info(f"Setting cuda device to {self.corrected_device} ")
|
|
228
|
+
torch.cuda.set_device(self.corrected_device)
|
|
229
|
+
|
|
230
|
+
paged_mem_allocator = PagedCpuGpuMemoryAllocator()
|
|
231
|
+
|
|
232
|
+
init_func = (
|
|
233
|
+
paged_mem_allocator.init_cpu_memory_allocator
|
|
234
|
+
if self.corrected_device == "cpu"
|
|
235
|
+
else paged_mem_allocator.init_gpu_memory_allocator
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Calculate the chunk size (align_bytes) and align buffer size
|
|
239
|
+
shapes = [torch.Size(metadata.kv_shape)]
|
|
240
|
+
dtypes = [metadata.kv_dtype]
|
|
241
|
+
chunk_size_bytes = get_size_bytes(shapes, dtypes)
|
|
242
|
+
origin_buffer_size = config.pd_buffer_size
|
|
243
|
+
aligned_buffer_size = origin_buffer_size // chunk_size_bytes * chunk_size_bytes
|
|
244
|
+
|
|
245
|
+
if aligned_buffer_size == 0 and origin_buffer_size > 0:
|
|
246
|
+
raise ValueError(
|
|
247
|
+
f"pd_buffer_size ({origin_buffer_size}) is smaller than a "
|
|
248
|
+
f"single chunk ({chunk_size_bytes}), resulting in an aligned "
|
|
249
|
+
f"buffer of size 0. Please increase pd_buffer_size to be at "
|
|
250
|
+
f"least {chunk_size_bytes}."
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if aligned_buffer_size != origin_buffer_size:
|
|
254
|
+
logger.info(
|
|
255
|
+
f"Auto align pd_buffer_size, origin: {origin_buffer_size}, "
|
|
256
|
+
f"aligned: {aligned_buffer_size}, chunk size: {chunk_size_bytes}. "
|
|
257
|
+
f"The remaining {origin_buffer_size - aligned_buffer_size} bytes "
|
|
258
|
+
f"will not be allocated."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
init_func(
|
|
262
|
+
aligned_buffer_size,
|
|
263
|
+
shapes,
|
|
264
|
+
dtypes,
|
|
265
|
+
MemoryFormat.KV_2LTD, # TODO: remove this hardcode
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return paged_mem_allocator
|
|
269
|
+
|
|
270
|
+
def get_memory_allocator(self) -> PagedCpuGpuMemoryAllocator:
|
|
271
|
+
return self.memory_allocator
|
|
272
|
+
|
|
273
|
+
def get_allocator_backend(self):
|
|
274
|
+
return self
|
|
275
|
+
|
|
276
|
+
def allocate(
|
|
277
|
+
self,
|
|
278
|
+
shapes: Union[torch.Size, list[torch.Size]],
|
|
279
|
+
dtypes: Union[torch.dtype, list[torch.dtype]],
|
|
280
|
+
fmt: MemoryFormat = MemoryFormat.KV_2LTD,
|
|
281
|
+
eviction: bool = True,
|
|
282
|
+
busy_loop: bool = True,
|
|
283
|
+
) -> Optional[MemoryObj]:
|
|
284
|
+
if fmt is None:
|
|
285
|
+
fmt = MemoryFormat.KV_2LTD
|
|
286
|
+
# NOTE: no eviction and busy_loop in PD
|
|
287
|
+
alloc_type = "cpu" if self.corrected_device == "cpu" else "gpu"
|
|
288
|
+
return self.memory_allocator.allocate(
|
|
289
|
+
shapes, dtypes, fmt=fmt, allocator_type=alloc_type
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# TODO(Jiayi): Please implement batched allocate to reduce memory
|
|
293
|
+
# allocation overhead.
|
|
294
|
+
def batched_allocate(
|
|
295
|
+
self,
|
|
296
|
+
shapes: Union[torch.Size, list[torch.Size]],
|
|
297
|
+
dtypes: Union[torch.dtype, list[torch.dtype]],
|
|
298
|
+
batch_size: int,
|
|
299
|
+
fmt: MemoryFormat = MemoryFormat.KV_2LTD,
|
|
300
|
+
eviction: bool = True,
|
|
301
|
+
busy_loop: bool = True,
|
|
302
|
+
):
|
|
303
|
+
if fmt is None:
|
|
304
|
+
fmt = MemoryFormat.KV_2LTD
|
|
305
|
+
alloc_type = "cpu" if self.corrected_device == "cpu" else "gpu"
|
|
306
|
+
return self.memory_allocator.batched_allocate(
|
|
307
|
+
shapes, dtypes, batch_size, fmt, allocator_type=alloc_type
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# NOTE(Jiayi): If two requests have overlapped keys, will
|
|
311
|
+
# the later one cause any problems here?
|
|
312
|
+
def contains(self, key: CacheEngineKey, pin: bool = False) -> bool:
|
|
313
|
+
assert isinstance(key, CacheEngineKey)
|
|
314
|
+
with self.data_lock:
|
|
315
|
+
if mem_obj := self.data.get(key, None):
|
|
316
|
+
if pin:
|
|
317
|
+
mem_obj.ref_count_up()
|
|
318
|
+
return True
|
|
319
|
+
return False
|
|
320
|
+
|
|
321
|
+
def exists_in_put_tasks(self, key: CacheEngineKey) -> bool:
|
|
322
|
+
return False
|
|
323
|
+
|
|
324
|
+
############################################################
|
|
325
|
+
# Prefiller functions
|
|
326
|
+
############################################################
|
|
327
|
+
def _init_sender(self):
|
|
328
|
+
if self.pd_config.skip_proxy_notification:
|
|
329
|
+
logger.info(
|
|
330
|
+
"pd_skip_proxy_notification=True, "
|
|
331
|
+
"skipping ZMQ PUSH proxy notification setup. "
|
|
332
|
+
"This mode is for external orchestrators only "
|
|
333
|
+
"(e.g., vLLM Production Stack router). "
|
|
334
|
+
"Do not use with LMCache's built-in disagg proxy."
|
|
335
|
+
)
|
|
336
|
+
self.proxy_side_channel = None
|
|
337
|
+
else:
|
|
338
|
+
proxy_url = f"{self.pd_config.proxy_host}:{self.pd_config.proxy_port}"
|
|
339
|
+
self.proxy_side_channel = get_zmq_socket(
|
|
340
|
+
self.zmq_context,
|
|
341
|
+
proxy_url,
|
|
342
|
+
"tcp",
|
|
343
|
+
zmq.PUSH,
|
|
344
|
+
"connect",
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def _ensure_peer_connection(
|
|
348
|
+
self,
|
|
349
|
+
receiver_id: str,
|
|
350
|
+
receiver_host: str,
|
|
351
|
+
receiver_init_port: int,
|
|
352
|
+
receiver_alloc_port: int,
|
|
353
|
+
) -> None:
|
|
354
|
+
if receiver_id in self.initialized_peers:
|
|
355
|
+
return
|
|
356
|
+
|
|
357
|
+
receiver_init_url = f"{receiver_host}:{receiver_init_port}"
|
|
358
|
+
receiver_mem_alloc_url = f"{receiver_host}:{receiver_alloc_port}"
|
|
359
|
+
|
|
360
|
+
# Establish the connection with the receiver/decoder
|
|
361
|
+
self.transfer_channel.lazy_init_peer_connection(
|
|
362
|
+
local_id=self.local_id, peer_id=receiver_id, peer_init_url=receiver_init_url
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Set up the memory allocation socket
|
|
366
|
+
mem_alloc_socket = get_zmq_socket(
|
|
367
|
+
self.zmq_context,
|
|
368
|
+
receiver_mem_alloc_url,
|
|
369
|
+
"tcp",
|
|
370
|
+
zmq.REQ,
|
|
371
|
+
"connect",
|
|
372
|
+
)
|
|
373
|
+
self.mem_alloc_sockets[receiver_id] = mem_alloc_socket
|
|
374
|
+
|
|
375
|
+
self.initialized_peers.add(receiver_id)
|
|
376
|
+
|
|
377
|
+
def _remote_allocate(
|
|
378
|
+
self, receiver_id: str, alloc_request: AllocRequest
|
|
379
|
+
) -> AllocResponse:
|
|
380
|
+
side_channel = self.mem_alloc_sockets[receiver_id]
|
|
381
|
+
side_channel.send(msgspec.msgpack.encode(alloc_request))
|
|
382
|
+
msg = side_channel.recv()
|
|
383
|
+
alloc_response = msgspec.msgpack.decode(msg, type=PDMsg)
|
|
384
|
+
|
|
385
|
+
return alloc_response
|
|
386
|
+
|
|
387
|
+
def _get_remote_alloc_request(
|
|
388
|
+
self, keys: Sequence[CacheEngineKey], mem_objs: List[MemoryObj]
|
|
389
|
+
) -> AllocRequest:
|
|
390
|
+
"""
|
|
391
|
+
Get the allocation request given the keys and memory objects.
|
|
392
|
+
|
|
393
|
+
Let's say there are N memory objects in total.
|
|
394
|
+
We have the following assumptions:
|
|
395
|
+
- The first N-1 memory objects are full chunks, each with
|
|
396
|
+
`full_chunk_size_bytes` tokens.
|
|
397
|
+
- The last memory object can be a partial chunk, which has
|
|
398
|
+
`last_chunk_toks` tokens.
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
fmt = mem_objs[0].meta.fmt
|
|
402
|
+
shape = mem_objs[0].meta.shape
|
|
403
|
+
dtype = TORCH_DTYPE_TO_STR_DTYPE[mem_objs[0].meta.dtype]
|
|
404
|
+
token_dim = fmt.token_dim()
|
|
405
|
+
last_chunk_toks = mem_objs[-1].meta.shape[token_dim]
|
|
406
|
+
|
|
407
|
+
str_keys = [key.to_string() for key in keys]
|
|
408
|
+
|
|
409
|
+
return AllocRequest(
|
|
410
|
+
keys=str_keys,
|
|
411
|
+
fmt=fmt.value,
|
|
412
|
+
shape=list(shape),
|
|
413
|
+
dtype=dtype,
|
|
414
|
+
last_chunk_toks=last_chunk_toks,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
# TODO(Jiayi): make this async in the future
|
|
418
|
+
def batched_submit_put_task(
|
|
419
|
+
self,
|
|
420
|
+
keys: Sequence[CacheEngineKey],
|
|
421
|
+
memory_objs: List[MemoryObj],
|
|
422
|
+
transfer_spec: Any = None,
|
|
423
|
+
on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
|
|
424
|
+
) -> None:
|
|
425
|
+
"""
|
|
426
|
+
Submit batched put tasks to transfer KV caches to peer.
|
|
427
|
+
|
|
428
|
+
:param on_complete_callback: Optional callback invoked once per key
|
|
429
|
+
after the transfer completes. Callback exceptions are caught and logged.
|
|
430
|
+
"""
|
|
431
|
+
for mem_obj in memory_objs:
|
|
432
|
+
mem_obj.ref_count_up()
|
|
433
|
+
|
|
434
|
+
receiver_init_port = transfer_spec.receiver_init_port[self.tp_rank]
|
|
435
|
+
receiver_alloc_port = transfer_spec.receiver_alloc_port[self.tp_rank]
|
|
436
|
+
receiver_id = transfer_spec.receiver_host + str(receiver_init_port)
|
|
437
|
+
receiver_host = transfer_spec.receiver_host
|
|
438
|
+
|
|
439
|
+
self._ensure_peer_connection(
|
|
440
|
+
receiver_id=receiver_id,
|
|
441
|
+
receiver_host=receiver_host,
|
|
442
|
+
receiver_init_port=receiver_init_port,
|
|
443
|
+
receiver_alloc_port=receiver_alloc_port,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
# Allocate remote memory objects
|
|
447
|
+
alloc_request = self._get_remote_alloc_request(keys, memory_objs)
|
|
448
|
+
alloc_response = self._remote_allocate(receiver_id, alloc_request)
|
|
449
|
+
already_sent_indexes = alloc_response.already_sent_indexes
|
|
450
|
+
remote_indexes = alloc_response.remote_indexes
|
|
451
|
+
|
|
452
|
+
# Filter out already sent memory objects and free them
|
|
453
|
+
mem_objs_to_send = []
|
|
454
|
+
for idx, mem_obj in enumerate(memory_objs):
|
|
455
|
+
if idx in already_sent_indexes:
|
|
456
|
+
mem_obj.ref_count_down()
|
|
457
|
+
else:
|
|
458
|
+
mem_objs_to_send.append(mem_obj)
|
|
459
|
+
|
|
460
|
+
if mem_objs_to_send:
|
|
461
|
+
# TODO(Jiayi): make this decoupled with transfer channel
|
|
462
|
+
# Construct transfer spec
|
|
463
|
+
channel_transfer_spec = {
|
|
464
|
+
"receiver_id": receiver_id,
|
|
465
|
+
"remote_indexes": remote_indexes,
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
# TODO(Jiayi): Consider making this real async
|
|
469
|
+
# Perform the actual transfer
|
|
470
|
+
self.transfer_channel.batched_write(
|
|
471
|
+
objects=mem_objs_to_send,
|
|
472
|
+
transfer_spec=channel_transfer_spec,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
# TODO(Jiayi): consider moving this to the transfer channel
|
|
476
|
+
# since we might want the transfer to be async.
|
|
477
|
+
for mem_obj in mem_objs_to_send:
|
|
478
|
+
mem_obj.ref_count_down()
|
|
479
|
+
else:
|
|
480
|
+
logger.debug(
|
|
481
|
+
"All memory objects have been already sent to the remote peer."
|
|
482
|
+
" Skipping transfer."
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
if transfer_spec.is_last_prefill:
|
|
486
|
+
# Notify the proxy that the transfer is done
|
|
487
|
+
if self.proxy_side_channel is not None:
|
|
488
|
+
notif_msg = ProxyNotif(req_id=transfer_spec.req_id)
|
|
489
|
+
notif_msg_bytes = msgspec.msgpack.encode(notif_msg)
|
|
490
|
+
self.proxy_side_channel.send(notif_msg_bytes)
|
|
491
|
+
|
|
492
|
+
# Call completion callback for all keys after transfer completes
|
|
493
|
+
if on_complete_callback is not None:
|
|
494
|
+
for key in keys:
|
|
495
|
+
try:
|
|
496
|
+
on_complete_callback(key)
|
|
497
|
+
except Exception as e:
|
|
498
|
+
logger.warning(f"on_complete_callback failed for key {key}: {e}")
|
|
499
|
+
|
|
500
|
+
############################################################
|
|
501
|
+
# Prefiller functions end
|
|
502
|
+
############################################################
|
|
503
|
+
|
|
504
|
+
############################################################
|
|
505
|
+
# Decoder functions
|
|
506
|
+
############################################################
|
|
507
|
+
def _init_receiver(self):
|
|
508
|
+
# Initialize initialization side channels
|
|
509
|
+
receiver_alloc_url = (
|
|
510
|
+
f"{self.pd_config.peer_host}:{self.pd_config.peer_alloc_port}"
|
|
511
|
+
)
|
|
512
|
+
self.alloc_side_channel = get_zmq_socket(
|
|
513
|
+
self.zmq_context, receiver_alloc_url, "tcp", zmq.REP, "bind"
|
|
514
|
+
)
|
|
515
|
+
self.side_channels.append(self.alloc_side_channel)
|
|
516
|
+
|
|
517
|
+
# Start the memory allocation thread
|
|
518
|
+
self.mem_alloc_thread = threading.Thread(
|
|
519
|
+
target=self._mem_alloc_loop, daemon=True
|
|
520
|
+
)
|
|
521
|
+
self.mem_alloc_thread.start()
|
|
522
|
+
self.running_threads.append(self.mem_alloc_thread)
|
|
523
|
+
|
|
524
|
+
def _allocate_and_put(self, alloc_request: AllocRequest) -> AllocResponse:
|
|
525
|
+
total_allocs = len(alloc_request.keys)
|
|
526
|
+
fmt = MemoryFormat(alloc_request.fmt)
|
|
527
|
+
dtype = STR_DTYPE_TO_TORCH_DTYPE[alloc_request.dtype]
|
|
528
|
+
shape = alloc_request.shape
|
|
529
|
+
|
|
530
|
+
alloc_indexes = []
|
|
531
|
+
already_send_indexes = []
|
|
532
|
+
|
|
533
|
+
for idx, key_str in enumerate(alloc_request.keys):
|
|
534
|
+
key = CacheEngineKey.from_string(key_str)
|
|
535
|
+
if self.contains(key, pin=False):
|
|
536
|
+
already_send_indexes.append(idx)
|
|
537
|
+
continue
|
|
538
|
+
|
|
539
|
+
if idx == total_allocs - 1:
|
|
540
|
+
num_alloc_tokens = alloc_request.last_chunk_toks
|
|
541
|
+
token_dim = fmt.token_dim()
|
|
542
|
+
shape[token_dim] = num_alloc_tokens
|
|
543
|
+
else:
|
|
544
|
+
num_alloc_tokens = self.full_chunk_size_bytes
|
|
545
|
+
|
|
546
|
+
mem_obj = self.allocate(torch.Size(shape), dtype, fmt)
|
|
547
|
+
|
|
548
|
+
# TODO(Jiayi): make busy loop allocation part of
|
|
549
|
+
# memory allocator instead of backend as both PD
|
|
550
|
+
# and CPU offloading might need this.
|
|
551
|
+
wait_time = 0.01
|
|
552
|
+
while mem_obj is None:
|
|
553
|
+
logger.warning(
|
|
554
|
+
"Failed to allocate memory object, retrying...",
|
|
555
|
+
)
|
|
556
|
+
time.sleep(wait_time)
|
|
557
|
+
mem_obj = self.allocate(torch.Size(shape), dtype, fmt)
|
|
558
|
+
|
|
559
|
+
alloc_indexes.append(mem_obj.meta.address)
|
|
560
|
+
|
|
561
|
+
self.put(key, mem_obj)
|
|
562
|
+
|
|
563
|
+
return AllocResponse(
|
|
564
|
+
already_sent_indexes=already_send_indexes, remote_indexes=alloc_indexes
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
def _mem_alloc_loop(self):
|
|
568
|
+
"""
|
|
569
|
+
Running the memory allocation loop.
|
|
570
|
+
"""
|
|
571
|
+
while self.running:
|
|
572
|
+
try:
|
|
573
|
+
# receive alloc request
|
|
574
|
+
alloc_req_bytes = self.alloc_side_channel.recv()
|
|
575
|
+
alloc_req = msgspec.msgpack.decode(alloc_req_bytes, type=PDMsg)
|
|
576
|
+
assert isinstance(alloc_req, AllocRequest), (
|
|
577
|
+
"The request from the remote peer is not a AllocRequest"
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
# NOTE: it's okay to put the memory objs into the storage backend
|
|
581
|
+
# first because decode vllm will not be able to see the decode
|
|
582
|
+
# request until proxy receives the ack.
|
|
583
|
+
alloc_resp = self._allocate_and_put(alloc_req)
|
|
584
|
+
|
|
585
|
+
# send back response
|
|
586
|
+
self.alloc_side_channel.send(msgspec.msgpack.encode(alloc_resp))
|
|
587
|
+
|
|
588
|
+
except Exception as e:
|
|
589
|
+
logger.error("Failed to process mem alloc loop: %s", str(e))
|
|
590
|
+
if self.running:
|
|
591
|
+
time.sleep(0.01)
|
|
592
|
+
|
|
593
|
+
def put(
|
|
594
|
+
self,
|
|
595
|
+
key: CacheEngineKey,
|
|
596
|
+
mem_obj: MemoryObj,
|
|
597
|
+
):
|
|
598
|
+
with self.data_lock:
|
|
599
|
+
self.data[key] = mem_obj
|
|
600
|
+
|
|
601
|
+
def get_blocking(self, key: CacheEngineKey) -> Optional[MemoryObj]:
|
|
602
|
+
with self.data_lock:
|
|
603
|
+
# NOTE(Jiayi): we assume that the key must be in local data
|
|
604
|
+
# because we are using a push-based transfer
|
|
605
|
+
mem_obj = self.data.get(key, None)
|
|
606
|
+
assert mem_obj is not None, f"Key {key} not found in local data."
|
|
607
|
+
return mem_obj
|
|
608
|
+
|
|
609
|
+
def remove(
|
|
610
|
+
self,
|
|
611
|
+
key: CacheEngineKey,
|
|
612
|
+
force: bool = True,
|
|
613
|
+
) -> bool:
|
|
614
|
+
"""
|
|
615
|
+
Remove the key from the storage backend.
|
|
616
|
+
|
|
617
|
+
:param key: The key to remove.
|
|
618
|
+
"""
|
|
619
|
+
# TODO(Jiayi): The logic here is confusing. Ref count down
|
|
620
|
+
# will be done after this function call in cache engine.
|
|
621
|
+
with self.data_lock:
|
|
622
|
+
if mem_obj := self.data.get(key, None):
|
|
623
|
+
if mem_obj.get_ref_count() == 1:
|
|
624
|
+
del self.data[key]
|
|
625
|
+
return True
|
|
626
|
+
return False
|
|
627
|
+
|
|
628
|
+
############################################################
|
|
629
|
+
# Decoder functions end
|
|
630
|
+
############################################################
|
|
631
|
+
|
|
632
|
+
def close(self) -> None:
|
|
633
|
+
"""
|
|
634
|
+
Close the storage backend.
|
|
635
|
+
"""
|
|
636
|
+
self.running = False
|
|
637
|
+
for thread in self.running_threads:
|
|
638
|
+
thread.join()
|
|
639
|
+
self.transfer_channel.close()
|
|
640
|
+
self.zmq_context.term()
|
|
641
|
+
|
|
642
|
+
def pin(self, key: CacheEngineKey) -> bool:
|
|
643
|
+
return True
|
|
644
|
+
|
|
645
|
+
def unpin(self, key: CacheEngineKey) -> bool:
|
|
646
|
+
return True
|