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,267 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
# Third Party
|
|
8
|
+
import torch
|
|
9
|
+
|
|
10
|
+
# First Party
|
|
11
|
+
from lmcache.logging import init_logger
|
|
12
|
+
|
|
13
|
+
logger = init_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class KVLayerGroupInfo:
|
|
18
|
+
"""Information about a group of layers with the same KV cache structure.
|
|
19
|
+
|
|
20
|
+
Layers within the same group have identical shape and dtype for their KV cache.
|
|
21
|
+
Different groups may have different shapes (especially head_size) and/or dtypes.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
""" List of layer names belonging to this group """
|
|
25
|
+
layer_names: list[str]
|
|
26
|
+
""" List of layer indices (0-based) belonging to this group """
|
|
27
|
+
layer_indices: list[int]
|
|
28
|
+
""" Shape of the KV cache tensor for layers in this group """
|
|
29
|
+
""" For MHA: typically [2, num_blocks, block_size, num_heads, head_size] """
|
|
30
|
+
""" For MLA: typically [num_blocks, block_size, head_size] """
|
|
31
|
+
shape: torch.Size
|
|
32
|
+
""" Data type of the KV cache tensor for layers in this group """
|
|
33
|
+
dtype: torch.dtype
|
|
34
|
+
|
|
35
|
+
# Internal sets for fast membership checking
|
|
36
|
+
_layer_indices_set: set[int] = field(init=False, repr=False)
|
|
37
|
+
_layer_names_set: set[str] = field(init=False, repr=False)
|
|
38
|
+
|
|
39
|
+
def __post_init__(self):
|
|
40
|
+
"""Initialize sets for fast membership checking."""
|
|
41
|
+
self._layer_indices_set = set(self.layer_indices)
|
|
42
|
+
self._layer_names_set = set(self.layer_names)
|
|
43
|
+
|
|
44
|
+
def __repr__(self) -> str:
|
|
45
|
+
if not self.layer_indices:
|
|
46
|
+
indices_repr = "[]"
|
|
47
|
+
else:
|
|
48
|
+
indices_repr = f"{self.layer_indices[0]}-{self.layer_indices[-1]}"
|
|
49
|
+
return (
|
|
50
|
+
f"KVLayerGroupInfo(layers={len(self.layer_names)}, "
|
|
51
|
+
f"indices={indices_repr}, "
|
|
52
|
+
f"shape={self.shape}, dtype={self.dtype})"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def num_layers(self) -> int:
|
|
57
|
+
"""Return the number of layers in this group."""
|
|
58
|
+
return len(self.layer_names)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def hidden_dim_size(self) -> int:
|
|
62
|
+
"""Return the size of the hidden dimension in this group."""
|
|
63
|
+
# hidden_dim_size = num_heads * head_size
|
|
64
|
+
if len(self.shape) == 5:
|
|
65
|
+
# MHA
|
|
66
|
+
return self.shape[3] * self.shape[4]
|
|
67
|
+
elif len(self.shape) == 3:
|
|
68
|
+
# MLA
|
|
69
|
+
return self.shape[2]
|
|
70
|
+
else:
|
|
71
|
+
raise ValueError(f"Invalid shape: {self.shape}")
|
|
72
|
+
|
|
73
|
+
def contains_layer(self, layer_idx: int) -> bool:
|
|
74
|
+
"""Check if a layer index belongs to this group."""
|
|
75
|
+
return layer_idx in self._layer_indices_set
|
|
76
|
+
|
|
77
|
+
def contains_layer_name(self, layer_name: str) -> bool:
|
|
78
|
+
"""Check if a layer name belongs to this group."""
|
|
79
|
+
return layer_name in self._layer_names_set
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class KVLayerGroupsManager:
|
|
84
|
+
"""Manager for KV layer groups with the same structure.
|
|
85
|
+
|
|
86
|
+
This class encapsulates the functionality for managing groups of layers
|
|
87
|
+
that have identical KV cache structure (shape and dtype).
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
kv_layer_groups: list[KVLayerGroupInfo] = field(default_factory=list)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def num_groups(self) -> int:
|
|
94
|
+
"""Return the number of KV layer groups."""
|
|
95
|
+
return len(self.kv_layer_groups)
|
|
96
|
+
|
|
97
|
+
def get_group_by_layer_idx(self, layer_idx: int) -> Optional[KVLayerGroupInfo]:
|
|
98
|
+
"""Get the KVLayerGroupInfo for a given layer index.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
layer_idx: The 0-based index of the layer.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
The KVLayerGroupInfo containing this layer, or None if not found.
|
|
105
|
+
"""
|
|
106
|
+
for group in self.kv_layer_groups:
|
|
107
|
+
if group.contains_layer(layer_idx):
|
|
108
|
+
return group
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
def get_group_by_layer_name(self, layer_name: str) -> Optional[KVLayerGroupInfo]:
|
|
112
|
+
"""Get the KVLayerGroupInfo for a given layer name.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
layer_name: The name of the layer.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
The KVLayerGroupInfo containing this layer, or None if not found.
|
|
119
|
+
"""
|
|
120
|
+
for group in self.kv_layer_groups:
|
|
121
|
+
if group.contains_layer_name(layer_name):
|
|
122
|
+
return group
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
def get_layer_shape(self, layer_idx: int) -> Optional[torch.Size]:
|
|
126
|
+
"""Get the shape of the KV cache for a given layer index.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
layer_idx: The 0-based index of the layer.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
The shape, or None if layer not found.
|
|
133
|
+
"""
|
|
134
|
+
group = self.get_group_by_layer_idx(layer_idx)
|
|
135
|
+
return group.shape if group else None
|
|
136
|
+
|
|
137
|
+
def get_layer_dtype(self, layer_idx: int) -> Optional[torch.dtype]:
|
|
138
|
+
"""Get the dtype of the KV cache for a given layer index.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
layer_idx: The 0-based index of the layer.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
The dtype, or None if layer not found.
|
|
145
|
+
"""
|
|
146
|
+
group = self.get_group_by_layer_idx(layer_idx)
|
|
147
|
+
return group.dtype if group else None
|
|
148
|
+
|
|
149
|
+
def build_kv_layer_groups(self, kv_caches: dict[str, torch.Tensor]) -> None:
|
|
150
|
+
"""Build KV layer groups structure by analyzing each layer's shape and dtype.
|
|
151
|
+
|
|
152
|
+
Layers with the same shape and dtype are grouped together. This is useful
|
|
153
|
+
because different layers may have different structures (especially the
|
|
154
|
+
last dimension head_size may differ between groups), and different groups
|
|
155
|
+
may have different dtypes.
|
|
156
|
+
|
|
157
|
+
If layer groups are already built (non-empty list), this method does nothing.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
kv_caches: Dictionary mapping layer names to KV cache tensors.
|
|
161
|
+
"""
|
|
162
|
+
# Skip if already built (non-empty list)
|
|
163
|
+
if len(self.kv_layer_groups) > 0:
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
if len(kv_caches) == 0:
|
|
167
|
+
logger.debug("No KV caches available, skipping KV layer groups building")
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# Group layers by (shape, dtype) in a single loop
|
|
171
|
+
groups_dict: dict[tuple[torch.Size, torch.dtype], list[tuple[str, int]]] = (
|
|
172
|
+
defaultdict(list)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
for idx, (layer_name, kv_cache) in enumerate(kv_caches.items()):
|
|
176
|
+
# Supports two KV cache formats:
|
|
177
|
+
# - Single-tensor format: a single tensor with shape
|
|
178
|
+
# [2, num_blocks, block_size, num_heads, head_size].
|
|
179
|
+
# - List/tuple format (e.g., TPU/HPU): [k_tensor, v_tensor],
|
|
180
|
+
# where each tensor has shape
|
|
181
|
+
# [num_blocks, block_size, num_heads, head_size].
|
|
182
|
+
if isinstance(kv_cache, (tuple, list)):
|
|
183
|
+
if len(kv_cache) != 2:
|
|
184
|
+
raise ValueError(
|
|
185
|
+
f"Expected 2 tensors (k, v) for layer {layer_name}, "
|
|
186
|
+
f"got {len(kv_cache)}"
|
|
187
|
+
)
|
|
188
|
+
# Prepend the count as a leading dimension to produce the
|
|
189
|
+
# same canonical shape as the single-tensor format
|
|
190
|
+
# (e.g., [2, num_blocks, ...] for k+v), so downstream
|
|
191
|
+
# indexing (e.g., hidden_dim_size) is unaffected.
|
|
192
|
+
shape = torch.Size([len(kv_cache)] + list(kv_cache[0].shape))
|
|
193
|
+
dtype = kv_cache[0].dtype
|
|
194
|
+
else:
|
|
195
|
+
shape = kv_cache.shape
|
|
196
|
+
dtype = kv_cache.dtype
|
|
197
|
+
key = (shape, dtype)
|
|
198
|
+
groups_dict[key].append((layer_name, idx))
|
|
199
|
+
|
|
200
|
+
# Build KVLayerGroupInfo list
|
|
201
|
+
# Sort groups by the first layer index to maintain order
|
|
202
|
+
def _get_first_layer_index(shape_dtype_key):
|
|
203
|
+
"""Get the index of the first layer in a layer group."""
|
|
204
|
+
layer_group = groups_dict[
|
|
205
|
+
shape_dtype_key
|
|
206
|
+
] # list of (layer_name, layer_index) tuples
|
|
207
|
+
first_layer_info = layer_group[0] # first (layer_name, layer_index) tuple
|
|
208
|
+
layer_index = first_layer_info[1] # extract the layer index
|
|
209
|
+
return layer_index
|
|
210
|
+
|
|
211
|
+
sorted_keys = sorted(groups_dict.keys(), key=_get_first_layer_index)
|
|
212
|
+
|
|
213
|
+
kv_layer_groups: list[KVLayerGroupInfo] = []
|
|
214
|
+
for shape, dtype in sorted_keys:
|
|
215
|
+
layers = groups_dict[(shape, dtype)]
|
|
216
|
+
layer_names, layer_indices = zip(*layers, strict=False)
|
|
217
|
+
|
|
218
|
+
group_info = KVLayerGroupInfo(
|
|
219
|
+
layer_names=list(layer_names),
|
|
220
|
+
layer_indices=list(layer_indices),
|
|
221
|
+
shape=shape,
|
|
222
|
+
dtype=dtype,
|
|
223
|
+
)
|
|
224
|
+
kv_layer_groups.append(group_info)
|
|
225
|
+
|
|
226
|
+
# Store the built groups
|
|
227
|
+
self.kv_layer_groups = kv_layer_groups
|
|
228
|
+
|
|
229
|
+
# Print the group structure
|
|
230
|
+
logger.info("KV layer groups: %s", kv_layer_groups)
|
|
231
|
+
|
|
232
|
+
def build_kv_layer_groups_from_list(self, kv_caches: list[torch.Tensor]) -> None:
|
|
233
|
+
"""Build KV layer groups from a plain list of tensors (one per layer).
|
|
234
|
+
|
|
235
|
+
Layers with the same shape and dtype are grouped together.
|
|
236
|
+
If layer groups are already built (non-empty list), this method does nothing.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
kv_caches: List of KV cache tensors, one per layer.
|
|
240
|
+
"""
|
|
241
|
+
if len(self.kv_layer_groups) > 0:
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
if not kv_caches:
|
|
245
|
+
logger.debug("No KV caches available, skipping KV layer groups building")
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
groups_dict: dict[tuple[torch.Size, torch.dtype], list[int]] = defaultdict(list)
|
|
249
|
+
for idx, tensor in enumerate(kv_caches):
|
|
250
|
+
key = (tensor.shape, tensor.dtype)
|
|
251
|
+
groups_dict[key].append(idx)
|
|
252
|
+
|
|
253
|
+
sorted_keys = sorted(groups_dict.keys(), key=lambda k: groups_dict[k][0])
|
|
254
|
+
|
|
255
|
+
kv_layer_groups: list[KVLayerGroupInfo] = []
|
|
256
|
+
for shape, dtype in sorted_keys:
|
|
257
|
+
indices = groups_dict[(shape, dtype)]
|
|
258
|
+
group_info = KVLayerGroupInfo(
|
|
259
|
+
layer_names=[str(i) for i in indices],
|
|
260
|
+
layer_indices=indices,
|
|
261
|
+
shape=shape,
|
|
262
|
+
dtype=dtype,
|
|
263
|
+
)
|
|
264
|
+
kv_layer_groups.append(group_info)
|
|
265
|
+
|
|
266
|
+
self.kv_layer_groups = kv_layer_groups
|
|
267
|
+
logger.info("KV layer groups (from list): %s", kv_layer_groups)
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import List, Optional, Union
|
|
4
|
+
import ctypes
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
# Third Party
|
|
8
|
+
import torch
|
|
9
|
+
|
|
10
|
+
# First Party
|
|
11
|
+
from lmcache.logging import init_logger
|
|
12
|
+
from lmcache.v1.memory_management import (
|
|
13
|
+
AddressManager,
|
|
14
|
+
MemoryAllocatorInterface,
|
|
15
|
+
MemoryFormat,
|
|
16
|
+
MemoryObj,
|
|
17
|
+
TensorMemoryAllocator,
|
|
18
|
+
)
|
|
19
|
+
from lmcache.v1.system_detection import NUMAMapping
|
|
20
|
+
import lmcache.c_ops as lmc_ops
|
|
21
|
+
|
|
22
|
+
logger = init_logger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Helper functions
|
|
26
|
+
def get_numa_id(numa_mapping: NUMAMapping) -> int:
|
|
27
|
+
"""
|
|
28
|
+
Get the NUMA ID for the current GPU
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
numa_mapping (NUMAMapping): The NUMA mapping object.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
int: The NUMA ID for the current GPU.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
KeyError: If GPU id is not detected in the numa mapping.
|
|
38
|
+
"""
|
|
39
|
+
gpu_id = torch.cuda.current_device() if torch.cuda.is_available() else 0
|
|
40
|
+
return numa_mapping.gpu_to_numa_mapping[gpu_id]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def align_to(size: int, align_size: int) -> int:
|
|
44
|
+
"""
|
|
45
|
+
Align the given size to the nearest multiple of align_size.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
size (int): The size to align.
|
|
49
|
+
align_size (int): The alignment size, MUST BE a power of two.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
int: The aligned size.
|
|
53
|
+
"""
|
|
54
|
+
return (size + align_size - 1) & (~(align_size - 1))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Main class
|
|
58
|
+
class LazyMemoryAllocator(MemoryAllocatorInterface):
|
|
59
|
+
"""
|
|
60
|
+
Allocates CPU (numa) pinned memory with a initial size and expand
|
|
61
|
+
the size to the required size in the background.
|
|
62
|
+
|
|
63
|
+
Background expansion logic:
|
|
64
|
+
- After registering X GB memory, we call sbrk and updates _curr_size
|
|
65
|
+
- Once everything is registered, the background thread stops
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
PIN_CHUNK_SIZE = 1 << 26 # 64 MB pin chunk
|
|
69
|
+
COMMIT_SIZE = 1 << 30 # Do a commit every 1 GB
|
|
70
|
+
LOG_INTERVAL = 10 << 30 # Log expansion progress every 10 GB
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
init_size: int,
|
|
75
|
+
final_size: int,
|
|
76
|
+
align_bytes: int = AddressManager.ALIGN_BYTES,
|
|
77
|
+
numa_mapping: NUMAMapping | None = None,
|
|
78
|
+
):
|
|
79
|
+
"""
|
|
80
|
+
Args:
|
|
81
|
+
init_size (int): Initial size of the memory allocation in bytes.
|
|
82
|
+
final_size (int): Final size of the memory allocation in bytes.
|
|
83
|
+
align_bytes (int, optional): Alignment in for the underlying allocations
|
|
84
|
+
"""
|
|
85
|
+
# Whether using NUMA allocation
|
|
86
|
+
self._use_numa = numa_mapping is not None
|
|
87
|
+
# Currently pinned size, only accessed by the expansion thread
|
|
88
|
+
self._curr_size = align_to(init_size, self.PIN_CHUNK_SIZE)
|
|
89
|
+
# Final size of the allocation, only accessed by the expansion thread
|
|
90
|
+
self._final_size = align_to(final_size, self.PIN_CHUNK_SIZE)
|
|
91
|
+
# Underlying buffer for the memory allocation
|
|
92
|
+
self._buffer: torch.Tensor
|
|
93
|
+
# CUDA runtime API
|
|
94
|
+
self._cudart = torch.cuda.cudart()
|
|
95
|
+
|
|
96
|
+
# List of (ptr, size) for pinned memory chunks
|
|
97
|
+
self._pin_record: list[tuple[int, int]] = []
|
|
98
|
+
|
|
99
|
+
# Detect numa mapping
|
|
100
|
+
if numa_mapping is not None:
|
|
101
|
+
numa_id = get_numa_id(numa_mapping)
|
|
102
|
+
ptr = lmc_ops.alloc_numa_ptr(self._final_size, numa_id)
|
|
103
|
+
arr_type = ctypes.c_uint8 * self._final_size
|
|
104
|
+
buf = arr_type.from_address(ptr)
|
|
105
|
+
self._buffer = torch.frombuffer(buf, dtype=torch.uint8)
|
|
106
|
+
else:
|
|
107
|
+
self._buffer = torch.empty(
|
|
108
|
+
self._final_size, dtype=torch.uint8, device="cpu", pin_memory=False
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Pin the first `curr_size` bytes (aligned to the internal chunk size)
|
|
112
|
+
self._pin_memory_chunk(0, self._curr_size)
|
|
113
|
+
|
|
114
|
+
# Create the tensor memory allocator
|
|
115
|
+
self._allocator = TensorMemoryAllocator(
|
|
116
|
+
tensor=self._buffer,
|
|
117
|
+
align_bytes=align_bytes,
|
|
118
|
+
init_address_space=self._curr_size,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Get the address manager
|
|
122
|
+
# NOTE(ApostaC): this assumes the tensor memory allocator owns the address
|
|
123
|
+
# manager, which creates extra coupling in the code.
|
|
124
|
+
# NOTE(ApostaC): this also assumes that the behavior of the allocation is
|
|
125
|
+
# completely determined by the address manager.
|
|
126
|
+
self._address_manager = self._allocator.address_manager
|
|
127
|
+
|
|
128
|
+
# Launch the background expansion thread
|
|
129
|
+
self._stop_expand = threading.Event()
|
|
130
|
+
self._expand_thread = threading.Thread(
|
|
131
|
+
target=self._expand_worker, daemon=True, name="lazy-mem-expand-thread"
|
|
132
|
+
)
|
|
133
|
+
self._expand_thread.start()
|
|
134
|
+
|
|
135
|
+
# Public methods
|
|
136
|
+
def allocate(
|
|
137
|
+
self,
|
|
138
|
+
shapes: Union[torch.Size, list[torch.Size]],
|
|
139
|
+
dtypes: Union[torch.dtype, list[torch.dtype]],
|
|
140
|
+
fmt: MemoryFormat = MemoryFormat.UNDEFINED,
|
|
141
|
+
allocator_type: Optional[str] = None,
|
|
142
|
+
) -> Optional[MemoryObj]:
|
|
143
|
+
obj = self._allocator.allocate(shapes, dtypes, fmt, allocator_type)
|
|
144
|
+
# HACK(ApostaC): reset the parent allocator to this lazy allocator
|
|
145
|
+
# There should be a cleaner way to decouple lazy allocator and
|
|
146
|
+
# tensor memory allocator
|
|
147
|
+
if obj is not None:
|
|
148
|
+
obj.parent_allocator = self
|
|
149
|
+
return obj
|
|
150
|
+
|
|
151
|
+
def batched_allocate(
|
|
152
|
+
self,
|
|
153
|
+
shapes: Union[torch.Size, list[torch.Size]],
|
|
154
|
+
dtypes: Union[torch.dtype, list[torch.dtype]],
|
|
155
|
+
batch_size: int,
|
|
156
|
+
fmt: MemoryFormat = MemoryFormat.UNDEFINED,
|
|
157
|
+
allocator_type: Optional[str] = None,
|
|
158
|
+
) -> Optional[List[MemoryObj]]:
|
|
159
|
+
# HACK(ApostaC): reset the parent allocator to this lazy allocator
|
|
160
|
+
# There should be a cleaner way to decouple lazy allocator and
|
|
161
|
+
# tensor memory allocator
|
|
162
|
+
ret = self._allocator.batched_allocate(
|
|
163
|
+
shapes, dtypes, batch_size, fmt, allocator_type
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if ret is None:
|
|
167
|
+
return ret
|
|
168
|
+
|
|
169
|
+
for obj in ret:
|
|
170
|
+
obj.parent_allocator = self
|
|
171
|
+
return ret
|
|
172
|
+
|
|
173
|
+
def free(
|
|
174
|
+
self,
|
|
175
|
+
memory_obj: MemoryObj,
|
|
176
|
+
allocator_type: Optional[str] = None,
|
|
177
|
+
):
|
|
178
|
+
self._allocator.free(memory_obj, allocator_type)
|
|
179
|
+
|
|
180
|
+
def batched_free(
|
|
181
|
+
self,
|
|
182
|
+
memory_objs: List[MemoryObj],
|
|
183
|
+
allocator_type: Optional[str] = None,
|
|
184
|
+
update_stats: bool = True,
|
|
185
|
+
):
|
|
186
|
+
self._allocator.batched_free(memory_objs, allocator_type, update_stats)
|
|
187
|
+
|
|
188
|
+
def close(self):
|
|
189
|
+
# Stop the background expansion thread
|
|
190
|
+
self._stop_expand.set()
|
|
191
|
+
self._expand_thread.join()
|
|
192
|
+
|
|
193
|
+
# Unpin all pinned memory chunks
|
|
194
|
+
for ptr, size in self._pin_record:
|
|
195
|
+
self._cudart.cudaHostUnregister(ptr)
|
|
196
|
+
self._pin_record.clear()
|
|
197
|
+
|
|
198
|
+
# Free the underlying buffer if using NUMA allocation
|
|
199
|
+
if self._use_numa:
|
|
200
|
+
lmc_ops.free_numa_ptr(self._buffer.data_ptr(), self._final_size)
|
|
201
|
+
|
|
202
|
+
def memcheck(self) -> bool:
|
|
203
|
+
return self._allocator.memcheck()
|
|
204
|
+
|
|
205
|
+
def get_underlying_buffer(self) -> torch.Tensor:
|
|
206
|
+
"""
|
|
207
|
+
Get the underlying buffer tensor. Will be used by RDMA registrations.
|
|
208
|
+
"""
|
|
209
|
+
return self._buffer
|
|
210
|
+
|
|
211
|
+
def get_address_manager(self) -> AddressManager:
|
|
212
|
+
"""
|
|
213
|
+
Get the address manager used by this allocator.
|
|
214
|
+
"""
|
|
215
|
+
return self._address_manager
|
|
216
|
+
|
|
217
|
+
# Helper functions
|
|
218
|
+
def _pin_memory_chunk(self, offset: int, size: int):
|
|
219
|
+
"""
|
|
220
|
+
Pin a chunk of memory.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
offset (int): Offset in the buffer to pin.
|
|
224
|
+
size (int): Size of the memory chunk in bytes.
|
|
225
|
+
"""
|
|
226
|
+
assert offset & (self.PIN_CHUNK_SIZE - 1) == 0, (
|
|
227
|
+
"Offset must be aligned to PIN_CHUNK_SIZE"
|
|
228
|
+
)
|
|
229
|
+
assert size & (self.PIN_CHUNK_SIZE - 1) == 0, (
|
|
230
|
+
"Size must be aligned to PIN_CHUNK_SIZE"
|
|
231
|
+
)
|
|
232
|
+
assert offset + size <= self._final_size, "Pinning exceeds buffer size"
|
|
233
|
+
|
|
234
|
+
ptr = self._buffer.data_ptr() + offset
|
|
235
|
+
# Use flag: cudaHostRegisterMapped (0x02)
|
|
236
|
+
self._cudart.cudaHostRegister(ptr, size, 2)
|
|
237
|
+
self._pin_record.append((ptr, size))
|
|
238
|
+
|
|
239
|
+
def _commit_expansion(self, expand_size: int):
|
|
240
|
+
"""
|
|
241
|
+
Call sbrk in the address manager to commit the expansion.
|
|
242
|
+
"""
|
|
243
|
+
self._address_manager.sbrk(expand_size)
|
|
244
|
+
|
|
245
|
+
def _log_expansion_progress(self, expanded_since_last_log: int):
|
|
246
|
+
"""
|
|
247
|
+
Log the cumulative expansion progress since the last log.
|
|
248
|
+
"""
|
|
249
|
+
percent = 100.0 * self._curr_size / self._final_size
|
|
250
|
+
logger.info(
|
|
251
|
+
"LazyMemoryAllocator: Expanded %s MB pinned memory, "
|
|
252
|
+
"now total is %s MB / %s MB (%.1f%%)",
|
|
253
|
+
expanded_since_last_log >> 20,
|
|
254
|
+
self._curr_size >> 20,
|
|
255
|
+
self._final_size >> 20,
|
|
256
|
+
percent,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def _expand_worker(self):
|
|
260
|
+
"""
|
|
261
|
+
Background worker to expand the pinned memory.
|
|
262
|
+
"""
|
|
263
|
+
last_commit_size = self._curr_size
|
|
264
|
+
last_log_size = self._curr_size
|
|
265
|
+
while self._curr_size < self._final_size and not self._stop_expand.is_set():
|
|
266
|
+
# Expand chunk by chunk and commit
|
|
267
|
+
for i in range(self.COMMIT_SIZE // self.PIN_CHUNK_SIZE):
|
|
268
|
+
if self._curr_size >= self._final_size:
|
|
269
|
+
break
|
|
270
|
+
self._pin_memory_chunk(self._curr_size, self.PIN_CHUNK_SIZE)
|
|
271
|
+
self._curr_size += self.PIN_CHUNK_SIZE
|
|
272
|
+
|
|
273
|
+
expand_size = self._curr_size - last_commit_size
|
|
274
|
+
self._commit_expansion(expand_size)
|
|
275
|
+
last_commit_size = self._curr_size
|
|
276
|
+
|
|
277
|
+
# Log every LOG_INTERVAL bytes, and always on the final commit.
|
|
278
|
+
expanded_since_last_log = self._curr_size - last_log_size
|
|
279
|
+
if (
|
|
280
|
+
expanded_since_last_log >= self.LOG_INTERVAL
|
|
281
|
+
or self._curr_size >= self._final_size
|
|
282
|
+
):
|
|
283
|
+
self._log_expansion_progress(expanded_since_last_log)
|
|
284
|
+
last_log_size = self._curr_size
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# First Party
|
|
3
|
+
from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
|
|
4
|
+
from lmcache.v1.lookup_client.chunk_statistics_lookup_client import (
|
|
5
|
+
ChunkStatisticsLookupClient,
|
|
6
|
+
)
|
|
7
|
+
from lmcache.v1.lookup_client.factory import LookupClientFactory
|
|
8
|
+
from lmcache.v1.lookup_client.lmcache_lookup_client import (
|
|
9
|
+
LMCacheLookupClient,
|
|
10
|
+
LMCacheLookupServer,
|
|
11
|
+
)
|
|
12
|
+
from lmcache.v1.lookup_client.lmcache_lookup_client_bypass import (
|
|
13
|
+
LMCacheBypassLookupClient,
|
|
14
|
+
)
|
|
15
|
+
from lmcache.v1.lookup_client.mooncake_lookup_client import MooncakeLookupClient
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"LookupClientInterface",
|
|
19
|
+
"LookupClientFactory",
|
|
20
|
+
"MooncakeLookupClient",
|
|
21
|
+
"LMCacheBypassLookupClient",
|
|
22
|
+
"LMCacheLookupClient",
|
|
23
|
+
"LMCacheLookupServer",
|
|
24
|
+
"ChunkStatisticsLookupClient",
|
|
25
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
import abc
|
|
5
|
+
|
|
6
|
+
# Third Party
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LookupClientInterface(metaclass=abc.ABCMeta):
|
|
11
|
+
"""Abstract interface for lookup clients."""
|
|
12
|
+
|
|
13
|
+
def lookup_cache(self, lookup_id: str) -> Optional[int]:
|
|
14
|
+
"""
|
|
15
|
+
Lookup the cache for the given lookup ID.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
lookup_id: The lookup ID to lookup
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
-1 means not found;
|
|
22
|
+
None means ongoing; (this semantic is not supported in sync lookup clients)
|
|
23
|
+
int >= 0 means number of hit tokens
|
|
24
|
+
"""
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
@abc.abstractmethod
|
|
28
|
+
def lookup(
|
|
29
|
+
self,
|
|
30
|
+
token_ids: Union[torch.Tensor, list[int]],
|
|
31
|
+
lookup_id: str,
|
|
32
|
+
request_configs: Optional[dict] = None,
|
|
33
|
+
) -> Optional[int]:
|
|
34
|
+
"""
|
|
35
|
+
Perform lookup for the given token IDs.
|
|
36
|
+
Should be called for first lookup and pinning. Subsequent lookups for the same
|
|
37
|
+
request should call lookup_cache instead.
|
|
38
|
+
|
|
39
|
+
Caller should handle overlaps between tokens that exist in LMCache
|
|
40
|
+
and tokens that are already computed by the caller.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
token_ids: The token IDs to lookup
|
|
44
|
+
|
|
45
|
+
lookup_id: The lookup ID to associate with the lookup
|
|
46
|
+
|
|
47
|
+
request_configs: The configs of the request,
|
|
48
|
+
includes tags and the other configs
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
The number of tokens that exist inside LMCache.
|
|
52
|
+
None indicates the lookup/prefetch is in progress.
|
|
53
|
+
"""
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
@abc.abstractmethod
|
|
57
|
+
def close(self) -> None:
|
|
58
|
+
"""Close the lookup client and clean up resources."""
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
def supports_producer_reuse(self) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Return whether this lookup client supports producer KV cache reuse.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
True if producer reuse is supported, False otherwise
|
|
67
|
+
"""
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
def clear_lookup_status(self, lookup_id: str) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Clear temporary lookup status for a given lookup ID.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
lookup_id: The lookup ID whose status needs to be cleared.
|
|
76
|
+
"""
|
|
77
|
+
return
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import Dict, Optional
|
|
4
|
+
|
|
5
|
+
# Third Party
|
|
6
|
+
import msgspec
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AsyncLookupMsg(msgspec.Struct, tag=True): # type: ignore
|
|
10
|
+
"""Base class for async lookup messages"""
|
|
11
|
+
|
|
12
|
+
def describe(self) -> str:
|
|
13
|
+
return ""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LookupRequestMsg(AsyncLookupMsg):
|
|
17
|
+
"""Async lookup request message from scheduler to worker"""
|
|
18
|
+
|
|
19
|
+
lookup_id: str
|
|
20
|
+
hashes: list[int]
|
|
21
|
+
offsets: list[int]
|
|
22
|
+
request_configs: Optional[Dict[str, str]] = None
|
|
23
|
+
|
|
24
|
+
def describe(self) -> str:
|
|
25
|
+
return (
|
|
26
|
+
f"Async lookup request for lookup_id={self.lookup_id} "
|
|
27
|
+
f"with {len(self.hashes)} hashes"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LookupResponseMsg(AsyncLookupMsg):
|
|
32
|
+
"""Async lookup response message from worker to scheduler"""
|
|
33
|
+
|
|
34
|
+
lookup_id: str
|
|
35
|
+
num_hit_tokens: int
|
|
36
|
+
|
|
37
|
+
def describe(self) -> str:
|
|
38
|
+
return (
|
|
39
|
+
f"Async lookup response for lookup_id={self.lookup_id} "
|
|
40
|
+
f"with {self.num_hit_tokens} hit tokens"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class LookupCleanupMsg(AsyncLookupMsg):
|
|
45
|
+
"""Cleanup message from scheduler to worker to release memory objects"""
|
|
46
|
+
|
|
47
|
+
lookup_id: str
|
|
48
|
+
|
|
49
|
+
def describe(self) -> str:
|
|
50
|
+
return f"Cleanup memory for lookup_id={self.lookup_id}"
|