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,890 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import (
|
|
4
|
+
TYPE_CHECKING,
|
|
5
|
+
Any,
|
|
6
|
+
List,
|
|
7
|
+
Literal,
|
|
8
|
+
Optional,
|
|
9
|
+
Tuple,
|
|
10
|
+
TypedDict,
|
|
11
|
+
Union,
|
|
12
|
+
overload,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Third Party
|
|
16
|
+
import torch
|
|
17
|
+
|
|
18
|
+
# First Party
|
|
19
|
+
from lmcache.logging import init_logger
|
|
20
|
+
from lmcache.utils import EngineType
|
|
21
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
# First Party
|
|
25
|
+
from lmcache.v1.gpu_connector.gpu_connectors import GPUConnectorInterface
|
|
26
|
+
|
|
27
|
+
# First Party
|
|
28
|
+
import lmcache.c_ops as lmc_ops
|
|
29
|
+
|
|
30
|
+
logger = init_logger(__name__)
|
|
31
|
+
|
|
32
|
+
# Error message for accessing non-existent attributes in GPU KV Cache
|
|
33
|
+
_ATTRIBUTE_NOT_EXIST_ERROR = "trying to access an attribute of the GPU KV Cache "
|
|
34
|
+
"that does not exist for the format detected {format}. "
|
|
35
|
+
"A misalignment with the GPUKVFormat must be resolved"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LayoutHints(TypedDict, total=False):
|
|
39
|
+
"""Hints passed from a serving engine to LMCache during KV cache
|
|
40
|
+
registration (``REGISTER_KV_CACHE``).
|
|
41
|
+
|
|
42
|
+
Serving engines may pass a plain ``dict`` that satisfies this
|
|
43
|
+
schema — importing this type is optional.
|
|
44
|
+
|
|
45
|
+
Keys:
|
|
46
|
+
kv_layout: Physical ordering of the KV cache dimensions.
|
|
47
|
+
``"NHD"`` — heads after block-size (default for most
|
|
48
|
+
vLLM builds).
|
|
49
|
+
``"HND"`` — heads before block-size (``VLLM_KV_CACHE_LAYOUT=HND``).
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
kv_layout: Literal["NHD", "HND"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def permute_to_contiguous(tensor: torch.Tensor) -> torch.Tensor:
|
|
56
|
+
"""Permute a tensor back to contiguous state (metadata-only, no copy).
|
|
57
|
+
|
|
58
|
+
Assumption: the tensor is only non-contiguous because of a previous
|
|
59
|
+
permutation. Raises if this assumption is not met.
|
|
60
|
+
|
|
61
|
+
The only known case is HND: vLLM allocates physically as HND
|
|
62
|
+
(e.g. [2, NB, NH, BS, HS]) but exposes an NHD logical view via
|
|
63
|
+
permute/transpose. This function recovers the underlying HND
|
|
64
|
+
physical shape so that ``discover_gpu_kv_format`` (with the
|
|
65
|
+
``kv_layout="HND"`` hint) sees the true memory layout and selects
|
|
66
|
+
the correct HND format enum. The reverse — physically NHD but
|
|
67
|
+
logically permuted to HND — does not occur in practice.
|
|
68
|
+
|
|
69
|
+
Returns the tensor unchanged if already contiguous.
|
|
70
|
+
"""
|
|
71
|
+
if tensor.is_contiguous():
|
|
72
|
+
return tensor
|
|
73
|
+
|
|
74
|
+
strides = tensor.stride()
|
|
75
|
+
perm = sorted(range(tensor.ndim), key=lambda i: strides[i], reverse=True)
|
|
76
|
+
result = tensor.permute(perm)
|
|
77
|
+
|
|
78
|
+
if not result.is_contiguous():
|
|
79
|
+
raise ValueError(
|
|
80
|
+
"tensor is non-contiguous for reasons other than permutation "
|
|
81
|
+
"(e.g., slicing or as_strided). Cannot recover contiguous view."
|
|
82
|
+
)
|
|
83
|
+
return result
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def permute_kv_caches_to_contiguous(
|
|
87
|
+
kv_caches: List[torch.Tensor],
|
|
88
|
+
) -> List[torch.Tensor]:
|
|
89
|
+
"""Apply :func:`permute_to_contiguous` to each tensor in *kv_caches*.
|
|
90
|
+
|
|
91
|
+
The returned list shares the same underlying storage as the input.
|
|
92
|
+
"""
|
|
93
|
+
return [permute_to_contiguous(t) for t in kv_caches]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def assert_contiguous(tensor: torch.Tensor) -> None:
|
|
97
|
+
"""Assert that a tensor has a contiguous physical layout with zero offset.
|
|
98
|
+
|
|
99
|
+
LMCache transfer kernels assume logical and physical views match
|
|
100
|
+
for coalesced memory accesses. Do NOT blindly call ``.contiguous()``
|
|
101
|
+
or ``.permute()`` to fix failures here — identify the root cause.
|
|
102
|
+
"""
|
|
103
|
+
assert tensor.storage_offset() == 0, (
|
|
104
|
+
f"expected storage_offset 0, got {tensor.storage_offset()}"
|
|
105
|
+
)
|
|
106
|
+
assert tensor.is_contiguous(), "tensor is not contiguous"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def any_non_contiguous(kv_caches: dict[str, torch.Tensor] | List[torch.Tensor]) -> bool:
|
|
110
|
+
"""Return True if any tensor in *kv_caches* is non-contiguous."""
|
|
111
|
+
tensors = kv_caches.values() if isinstance(kv_caches, dict) else kv_caches
|
|
112
|
+
return not all(t.is_contiguous() for t in tensors)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@overload
|
|
116
|
+
def ensure_contiguous_kv_caches(
|
|
117
|
+
kv_caches: dict[str, torch.Tensor],
|
|
118
|
+
kv_layout: str | None = None,
|
|
119
|
+
) -> dict[str, torch.Tensor]: ...
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@overload
|
|
123
|
+
def ensure_contiguous_kv_caches(
|
|
124
|
+
kv_caches: List[torch.Tensor],
|
|
125
|
+
kv_layout: str | None = None,
|
|
126
|
+
) -> List[torch.Tensor]: ...
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def ensure_contiguous_kv_caches(
|
|
130
|
+
kv_caches: dict[str, torch.Tensor] | List[torch.Tensor],
|
|
131
|
+
kv_layout: str | None = None,
|
|
132
|
+
) -> dict[str, torch.Tensor] | List[torch.Tensor]:
|
|
133
|
+
"""Permute non-contiguous KV caches to contiguous physical shape.
|
|
134
|
+
|
|
135
|
+
LMCache assumes tensors have matching logical and physical views.
|
|
136
|
+
Known reasons for non-contiguity: HND format from vLLM.
|
|
137
|
+
|
|
138
|
+
Accepts both ``dict`` and ``list`` forms.
|
|
139
|
+
Returns *kv_caches* unchanged if already contiguous.
|
|
140
|
+
"""
|
|
141
|
+
if not any_non_contiguous(kv_caches):
|
|
142
|
+
return kv_caches
|
|
143
|
+
|
|
144
|
+
if isinstance(kv_caches, dict):
|
|
145
|
+
result: dict[str, torch.Tensor] | List[torch.Tensor] = dict(
|
|
146
|
+
zip(
|
|
147
|
+
kv_caches.keys(),
|
|
148
|
+
permute_kv_caches_to_contiguous(list(kv_caches.values())),
|
|
149
|
+
strict=False,
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
result = permute_kv_caches_to_contiguous(kv_caches)
|
|
154
|
+
|
|
155
|
+
if kv_layout == "HND":
|
|
156
|
+
logger.info("Permuted HND tensors to contiguous physical shape")
|
|
157
|
+
else:
|
|
158
|
+
logger.warning(
|
|
159
|
+
"Non-contiguous KV tensors detected with layout=%s; "
|
|
160
|
+
"permuted to contiguous. Please identify the underlying reason.",
|
|
161
|
+
kv_layout,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return result
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def need_gpu_interm_buffer(lmcache_config: LMCacheEngineConfig):
|
|
168
|
+
"""
|
|
169
|
+
Check if the GPU Connector needs to create an intermediate
|
|
170
|
+
buffer on the GPU
|
|
171
|
+
"""
|
|
172
|
+
if lmcache_config.enable_pd:
|
|
173
|
+
return False
|
|
174
|
+
else:
|
|
175
|
+
return True
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def assert_layerwise_gpu_connector(gpu_connector: "GPUConnectorInterface"):
|
|
179
|
+
"""
|
|
180
|
+
Assert that a GPU Connector is a layerwise connector.
|
|
181
|
+
"""
|
|
182
|
+
# Import at runtime to avoid circular dependency
|
|
183
|
+
# First Party
|
|
184
|
+
from lmcache.v1.gpu_connector.gpu_connectors import (
|
|
185
|
+
SGLangLayerwiseGPUConnector,
|
|
186
|
+
VLLMBufferLayerwiseGPUConnector,
|
|
187
|
+
VLLMPagedMemLayerwiseGPUConnector,
|
|
188
|
+
)
|
|
189
|
+
from lmcache.v1.gpu_connector.xpu_connectors import (
|
|
190
|
+
VLLMPagedMemLayerwiseXPUConnector,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
assert isinstance(
|
|
194
|
+
gpu_connector,
|
|
195
|
+
(
|
|
196
|
+
VLLMPagedMemLayerwiseGPUConnector,
|
|
197
|
+
VLLMBufferLayerwiseGPUConnector,
|
|
198
|
+
SGLangLayerwiseGPUConnector,
|
|
199
|
+
VLLMPagedMemLayerwiseXPUConnector,
|
|
200
|
+
),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_gpu_kv_shape_description(gpu_kv_format: "lmc_ops.GPUKVFormat") -> str:
|
|
205
|
+
"""Return a human-readable shape description for the GPU KV format.
|
|
206
|
+
|
|
207
|
+
Uses short names matching the ``GPUKVFormat`` enum convention:
|
|
208
|
+
NB=num_blocks, NL=num_layers, BS=block_size, NH=num_heads,
|
|
209
|
+
HS=head_size, PBS=page_buffer_size (NB*BS).
|
|
210
|
+
"""
|
|
211
|
+
_SHAPE_DESCRIPTIONS: dict["lmc_ops.GPUKVFormat", str] = {
|
|
212
|
+
lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS: "[NB, NL, 2, BS, NH, HS]",
|
|
213
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS: "NL x [2, NB, BS, NH, HS]",
|
|
214
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS: "NL x [NB, 2, BS, NH, HS]",
|
|
215
|
+
lmc_ops.GPUKVFormat.NL_X_NB_BS_HS: "NL x [NB, BS, HS]",
|
|
216
|
+
lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS: "2 x NL x [PBS, NH, HS]",
|
|
217
|
+
lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS: "NL x [PBS, 1, HS]",
|
|
218
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS: "NL x [2, NB, NH, BS, HS]",
|
|
219
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS: "NL x [NB, 2, NH, BS, HS]",
|
|
220
|
+
}
|
|
221
|
+
return _SHAPE_DESCRIPTIONS.get(gpu_kv_format, f"Unknown ({gpu_kv_format})")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def get_attention_backend(gpu_kv_format: "lmc_ops.GPUKVFormat") -> str:
|
|
225
|
+
"""Return the attention backend name for the GPU KV format."""
|
|
226
|
+
_ATTENTION_BACKENDS: dict["lmc_ops.GPUKVFormat", str] = {
|
|
227
|
+
lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS: "vLLM CROSS_LAYER",
|
|
228
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS: "vLLM non-MLA flash attention",
|
|
229
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS: "vLLM non-MLA flash infer",
|
|
230
|
+
lmc_ops.GPUKVFormat.NL_X_NB_BS_HS: "vLLM MLA",
|
|
231
|
+
lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS: (
|
|
232
|
+
"SGLang MHA (flash attention and flash infer)"
|
|
233
|
+
),
|
|
234
|
+
lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS: "SGLang MLA",
|
|
235
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS: (
|
|
236
|
+
"vLLM non-MLA flash attention (HND layout)"
|
|
237
|
+
),
|
|
238
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS: (
|
|
239
|
+
"vLLM non-MLA flash infer (HND layout)"
|
|
240
|
+
),
|
|
241
|
+
}
|
|
242
|
+
return _ATTENTION_BACKENDS.get(gpu_kv_format, f"Unknown ({gpu_kv_format})")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def get_concrete_gpu_kv_shape(
|
|
246
|
+
kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat"
|
|
247
|
+
) -> str:
|
|
248
|
+
"""Return the shape with actual numeric values substituted.
|
|
249
|
+
|
|
250
|
+
For example, instead of ``NL x [2, NB, BS, NH, HS]``
|
|
251
|
+
this returns ``80 x [2, 2048, 128, 8, 128]``.
|
|
252
|
+
"""
|
|
253
|
+
nl = get_num_layers(kv_caches, gpu_kv_format)
|
|
254
|
+
hs = get_head_size(kv_caches, gpu_kv_format)
|
|
255
|
+
|
|
256
|
+
fmt = gpu_kv_format
|
|
257
|
+
F = lmc_ops.GPUKVFormat
|
|
258
|
+
|
|
259
|
+
if fmt == F.NB_NL_TWO_BS_NH_HS:
|
|
260
|
+
nb = get_num_blocks(kv_caches, fmt)
|
|
261
|
+
bs = get_block_size(kv_caches, fmt)
|
|
262
|
+
nh = get_num_heads(kv_caches, fmt)
|
|
263
|
+
return f"[{nb}, {nl}, 2, {bs}, {nh}, {hs}]"
|
|
264
|
+
|
|
265
|
+
if fmt == F.NL_X_TWO_NB_BS_NH_HS:
|
|
266
|
+
nb = get_num_blocks(kv_caches, fmt)
|
|
267
|
+
bs = get_block_size(kv_caches, fmt)
|
|
268
|
+
nh = get_num_heads(kv_caches, fmt)
|
|
269
|
+
return f"{nl} x [2, {nb}, {bs}, {nh}, {hs}]"
|
|
270
|
+
|
|
271
|
+
if fmt == F.NL_X_NB_TWO_BS_NH_HS:
|
|
272
|
+
nb = get_num_blocks(kv_caches, fmt)
|
|
273
|
+
bs = get_block_size(kv_caches, fmt)
|
|
274
|
+
nh = get_num_heads(kv_caches, fmt)
|
|
275
|
+
return f"{nl} x [{nb}, 2, {bs}, {nh}, {hs}]"
|
|
276
|
+
|
|
277
|
+
if fmt == F.NL_X_NB_BS_HS:
|
|
278
|
+
nb = get_num_blocks(kv_caches, fmt)
|
|
279
|
+
bs = get_block_size(kv_caches, fmt)
|
|
280
|
+
return f"{nl} x [{nb}, {bs}, {hs}]"
|
|
281
|
+
|
|
282
|
+
if fmt == F.TWO_X_NL_X_NBBS_NH_HS:
|
|
283
|
+
pbs = get_page_buffer_size(kv_caches, fmt)
|
|
284
|
+
nh = get_num_heads(kv_caches, fmt)
|
|
285
|
+
return f"2 x {nl} x [{pbs}, {nh}, {hs}]"
|
|
286
|
+
|
|
287
|
+
if fmt == F.NL_X_NBBS_ONE_HS:
|
|
288
|
+
pbs = get_page_buffer_size(kv_caches, fmt)
|
|
289
|
+
return f"{nl} x [{pbs}, 1, {hs}]"
|
|
290
|
+
|
|
291
|
+
if fmt == F.NL_X_TWO_NB_NH_BS_HS:
|
|
292
|
+
nb = get_num_blocks(kv_caches, fmt)
|
|
293
|
+
nh = get_num_heads(kv_caches, fmt)
|
|
294
|
+
bs = get_block_size(kv_caches, fmt)
|
|
295
|
+
return f"{nl} x [2, {nb}, {nh}, {bs}, {hs}]"
|
|
296
|
+
|
|
297
|
+
if fmt == F.NL_X_NB_TWO_NH_BS_HS:
|
|
298
|
+
nb = get_num_blocks(kv_caches, fmt)
|
|
299
|
+
nh = get_num_heads(kv_caches, fmt)
|
|
300
|
+
bs = get_block_size(kv_caches, fmt)
|
|
301
|
+
return f"{nl} x [{nb}, 2, {nh}, {bs}, {hs}]"
|
|
302
|
+
|
|
303
|
+
return f"Unknown ({gpu_kv_format})"
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def legible_print_gpu_kv_format(gpu_kv_format: "lmc_ops.GPUKVFormat"):
|
|
307
|
+
"""
|
|
308
|
+
Print the GPU KV Format in a legible way
|
|
309
|
+
"""
|
|
310
|
+
shape = get_gpu_kv_shape_description(gpu_kv_format)
|
|
311
|
+
backend = get_attention_backend(gpu_kv_format)
|
|
312
|
+
if shape.startswith("Unknown"):
|
|
313
|
+
logger.warning(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
314
|
+
else:
|
|
315
|
+
logger.info("GPU KV Format: %s", shape)
|
|
316
|
+
logger.info("Currently used by:\n - %s", backend)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _list_depth_tensor_dim(kv_caches: Any) -> Tuple[int, int]:
|
|
320
|
+
"""
|
|
321
|
+
Get the number of external wrapping lists in the kv_caches.
|
|
322
|
+
|
|
323
|
+
Assumption: kv_caches is of the form
|
|
324
|
+
List[List[...List[torch.Tensor]]]
|
|
325
|
+
"""
|
|
326
|
+
depth = 0
|
|
327
|
+
while isinstance(kv_caches, list):
|
|
328
|
+
depth += 1
|
|
329
|
+
if not kv_caches:
|
|
330
|
+
raise ValueError("encountered an empty list")
|
|
331
|
+
kv_caches = kv_caches[0]
|
|
332
|
+
if not isinstance(kv_caches, torch.Tensor):
|
|
333
|
+
raise ValueError("encountered a non-tensor inside")
|
|
334
|
+
return depth, kv_caches.ndim
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def discover_gpu_kv_format(
|
|
338
|
+
kv_caches: Any,
|
|
339
|
+
serving_engine: EngineType,
|
|
340
|
+
layout_hints: "LayoutHints | None" = None,
|
|
341
|
+
) -> "lmc_ops.GPUKVFormat":
|
|
342
|
+
"""
|
|
343
|
+
Discover the GPU KV Cache Format from the kv_caches.
|
|
344
|
+
|
|
345
|
+
KEY: the logical view and physical views of the kv_caches should be made consistent
|
|
346
|
+
BEFORE format discovery
|
|
347
|
+
|
|
348
|
+
The logic is that "external" layers are lists and there is one tensor internally.
|
|
349
|
+
We "unwrap" layers until we find the tensor.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
kv_caches: The KV cache tensors (possibly nested lists of tensors).
|
|
353
|
+
serving_engine: Which serving engine produced the caches.
|
|
354
|
+
layout_hints: See :class:`~lmcache.v1.multiprocess.custom_types.LayoutHints`.
|
|
355
|
+
|
|
356
|
+
Please see csrc/mem_kernels.cuh for the naming schema of the GPUKVFormat.
|
|
357
|
+
"""
|
|
358
|
+
# list_depth: number of external wrapping lists
|
|
359
|
+
# tensor_dim: number of dimensions of the internal tensor
|
|
360
|
+
list_depth, tensor_dim = _list_depth_tensor_dim(kv_caches)
|
|
361
|
+
logger.info("list_depth: %d, tensor_dim: %d", list_depth, tensor_dim)
|
|
362
|
+
list_dims = []
|
|
363
|
+
ptr = kv_caches
|
|
364
|
+
for _ in range(list_depth):
|
|
365
|
+
list_dims.append(len(ptr))
|
|
366
|
+
ptr = ptr[0]
|
|
367
|
+
# ptr is now the tensor
|
|
368
|
+
assert_contiguous(ptr)
|
|
369
|
+
|
|
370
|
+
tensor_dims = list(ptr.shape)
|
|
371
|
+
dims_str = (
|
|
372
|
+
"".join(f"[{d}]" for d in list_dims) + f"[{', '.join(map(str, tensor_dims))}]"
|
|
373
|
+
)
|
|
374
|
+
logger.info("GPU KV Cache Dimensions: %s", dims_str)
|
|
375
|
+
|
|
376
|
+
if layout_hints is None:
|
|
377
|
+
layout_hints = {}
|
|
378
|
+
|
|
379
|
+
detected_format = None
|
|
380
|
+
|
|
381
|
+
if serving_engine == EngineType.VLLM:
|
|
382
|
+
kv_layout = layout_hints.get("kv_layout")
|
|
383
|
+
if kv_layout is None:
|
|
384
|
+
logger.warning(
|
|
385
|
+
"No KV Cache Layout hint provided when using vLLM, defaulting to NHD"
|
|
386
|
+
)
|
|
387
|
+
kv_layout = "NHD"
|
|
388
|
+
logger.info("vLLM KV cache layout: %s", kv_layout)
|
|
389
|
+
is_hnd = kv_layout == "HND"
|
|
390
|
+
if list_depth == 0:
|
|
391
|
+
# vllm cross layer
|
|
392
|
+
detected_format = lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS
|
|
393
|
+
elif list_depth == 1:
|
|
394
|
+
if tensor_dim == 5:
|
|
395
|
+
if kv_caches[0].shape[0] == 2:
|
|
396
|
+
# vllm non-MLA flash attention
|
|
397
|
+
if is_hnd:
|
|
398
|
+
detected_format = lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS
|
|
399
|
+
else:
|
|
400
|
+
detected_format = lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS
|
|
401
|
+
elif kv_caches[0].shape[1] == 2:
|
|
402
|
+
# vllm non-MLA flash infer
|
|
403
|
+
if is_hnd:
|
|
404
|
+
detected_format = lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS
|
|
405
|
+
else:
|
|
406
|
+
detected_format = lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS
|
|
407
|
+
elif tensor_dim == 3:
|
|
408
|
+
# vllm MLA
|
|
409
|
+
detected_format = lmc_ops.GPUKVFormat.NL_X_NB_BS_HS
|
|
410
|
+
elif serving_engine == EngineType.SGLANG:
|
|
411
|
+
if list_depth == 1:
|
|
412
|
+
if kv_caches[0].shape[1] == 1:
|
|
413
|
+
# sglang MLA
|
|
414
|
+
detected_format = lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS
|
|
415
|
+
elif list_depth == 2:
|
|
416
|
+
# sglang MHA (flash attention and flash infer)
|
|
417
|
+
detected_format = lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS
|
|
418
|
+
|
|
419
|
+
if detected_format is not None:
|
|
420
|
+
legible_print_gpu_kv_format(detected_format)
|
|
421
|
+
return detected_format
|
|
422
|
+
else:
|
|
423
|
+
raise ValueError(
|
|
424
|
+
"currently unsupported kv_caches format "
|
|
425
|
+
f"with list depth {list_depth} and tensor dimension {tensor_dim}"
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def get_num_layers(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
430
|
+
"""
|
|
431
|
+
Get the number of layers from the kv_caches
|
|
432
|
+
"""
|
|
433
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
434
|
+
return kv_caches.shape[1]
|
|
435
|
+
elif gpu_kv_format in (
|
|
436
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
437
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
438
|
+
lmc_ops.GPUKVFormat.NL_X_NB_BS_HS,
|
|
439
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
440
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
441
|
+
):
|
|
442
|
+
return len(kv_caches)
|
|
443
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
444
|
+
return len(kv_caches[0])
|
|
445
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
446
|
+
return len(kv_caches)
|
|
447
|
+
else:
|
|
448
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def get_num_blocks(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
452
|
+
"""
|
|
453
|
+
Get the number of blocks from the kv_caches
|
|
454
|
+
"""
|
|
455
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
456
|
+
return kv_caches.shape[0]
|
|
457
|
+
elif gpu_kv_format in (
|
|
458
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
459
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
460
|
+
):
|
|
461
|
+
# [2, num_blocks, ...] — shape[1] is num_blocks
|
|
462
|
+
return kv_caches[0].shape[1]
|
|
463
|
+
elif gpu_kv_format in (
|
|
464
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
465
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
466
|
+
):
|
|
467
|
+
# [num_blocks, 2, ...] — shape[0] is num_blocks
|
|
468
|
+
return kv_caches[0].shape[0]
|
|
469
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
470
|
+
return kv_caches[0].shape[0]
|
|
471
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
472
|
+
raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
|
|
473
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
474
|
+
raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
|
|
475
|
+
else:
|
|
476
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def get_block_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
480
|
+
"""
|
|
481
|
+
Get the block size from the kv_caches
|
|
482
|
+
"""
|
|
483
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
484
|
+
return kv_caches.shape[3]
|
|
485
|
+
elif gpu_kv_format in (
|
|
486
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
487
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
488
|
+
):
|
|
489
|
+
# NHD: [..., BS, NH, HS] — block_size at shape[2]
|
|
490
|
+
return kv_caches[0].shape[2]
|
|
491
|
+
elif gpu_kv_format in (
|
|
492
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
493
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
494
|
+
):
|
|
495
|
+
# HND: [..., NH, BS, HS] — block_size at shape[3]
|
|
496
|
+
return kv_caches[0].shape[3]
|
|
497
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
498
|
+
return kv_caches[0].shape[1]
|
|
499
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
500
|
+
raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
|
|
501
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
502
|
+
raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
|
|
503
|
+
else:
|
|
504
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def get_page_buffer_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
508
|
+
"""
|
|
509
|
+
Get page buffer size (num_blocks * block_size) from the kv_caches
|
|
510
|
+
"""
|
|
511
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
512
|
+
# [num_blocks, num_layers, 2, block_size, num_heads, head_size]
|
|
513
|
+
return kv_caches.shape[0] * kv_caches.shape[3]
|
|
514
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS:
|
|
515
|
+
# List[num_layers] of [2, num_blocks, block_size, num_heads, head_size]
|
|
516
|
+
return kv_caches[0].shape[1] * kv_caches[0].shape[2]
|
|
517
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS:
|
|
518
|
+
# List[num_layers] of [2, num_blocks, num_heads, block_size, head_size]
|
|
519
|
+
# num_blocks=shape[1], block_size=shape[3]
|
|
520
|
+
return kv_caches[0].shape[1] * kv_caches[0].shape[3]
|
|
521
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS:
|
|
522
|
+
# List[num_layers] of [num_blocks, 2, block_size, num_heads, head_size]
|
|
523
|
+
return kv_caches[0].shape[0] * kv_caches[0].shape[2]
|
|
524
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS:
|
|
525
|
+
# List[num_layers] of [num_blocks, 2, num_heads, block_size, head_size]
|
|
526
|
+
# num_blocks=shape[0], block_size=shape[3]
|
|
527
|
+
return kv_caches[0].shape[0] * kv_caches[0].shape[3]
|
|
528
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
529
|
+
# List[num_layers] of [num_blocks, block_size, head_size]
|
|
530
|
+
return kv_caches[0].shape[0] * kv_caches[0].shape[1]
|
|
531
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
532
|
+
# List[2] -> List[num_layers] of [page_buffer_size, num_heads, head_size]
|
|
533
|
+
return kv_caches[0][0].shape[0]
|
|
534
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
535
|
+
# List[num_layers] of [page_buffer_size, 1, head_size]
|
|
536
|
+
return kv_caches[0].shape[0]
|
|
537
|
+
else:
|
|
538
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def get_num_heads(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
542
|
+
"""
|
|
543
|
+
Get the number of heads from the kv_caches
|
|
544
|
+
"""
|
|
545
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
546
|
+
return kv_caches.shape[4]
|
|
547
|
+
elif gpu_kv_format in (
|
|
548
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
549
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
550
|
+
):
|
|
551
|
+
# NHD: [..., BS, NH, HS] — num_heads at shape[3]
|
|
552
|
+
return kv_caches[0].shape[3]
|
|
553
|
+
elif gpu_kv_format in (
|
|
554
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
555
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
556
|
+
):
|
|
557
|
+
# HND: [..., NH, BS, HS] — num_heads at shape[2]
|
|
558
|
+
return kv_caches[0].shape[2]
|
|
559
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
560
|
+
# MLA: heads are absorbed into hidden dim, so num_heads = 1
|
|
561
|
+
return 1
|
|
562
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
563
|
+
return kv_caches[0][0].shape[1]
|
|
564
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
565
|
+
return kv_caches[0].shape[1]
|
|
566
|
+
else:
|
|
567
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def get_hidden_dim_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
571
|
+
"""
|
|
572
|
+
Get the hidden dimension from the kv_caches
|
|
573
|
+
"""
|
|
574
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
575
|
+
return kv_caches.shape[4] * kv_caches.shape[5]
|
|
576
|
+
elif gpu_kv_format in (
|
|
577
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
578
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
579
|
+
):
|
|
580
|
+
# NHD: [..., NH, HS] — hidden_dim = shape[3] * shape[4]
|
|
581
|
+
return kv_caches[0].shape[3] * kv_caches[0].shape[4]
|
|
582
|
+
elif gpu_kv_format in (
|
|
583
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
584
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
585
|
+
):
|
|
586
|
+
# HND: [..., NH, BS, HS] — hidden_dim = NH * HS = shape[2] * shape[4]
|
|
587
|
+
return kv_caches[0].shape[2] * kv_caches[0].shape[4]
|
|
588
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
589
|
+
return kv_caches[0].shape[2]
|
|
590
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
591
|
+
return kv_caches[0][0].shape[1] * kv_caches[0][0].shape[2]
|
|
592
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
593
|
+
return kv_caches[0].shape[2]
|
|
594
|
+
else:
|
|
595
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def get_head_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
599
|
+
"""
|
|
600
|
+
Get the head size from the kv_caches
|
|
601
|
+
"""
|
|
602
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
603
|
+
return kv_caches.shape[5]
|
|
604
|
+
elif gpu_kv_format in (
|
|
605
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
606
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
607
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
608
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
609
|
+
):
|
|
610
|
+
# Both NHD [..., NH, HS] and HND [..., BS, HS] have head_size last
|
|
611
|
+
return kv_caches[0].shape[4]
|
|
612
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
613
|
+
return kv_caches[0].shape[2]
|
|
614
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
615
|
+
return kv_caches[0][0].shape[2]
|
|
616
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
617
|
+
return kv_caches[0].shape[2]
|
|
618
|
+
else:
|
|
619
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def get_tokens_per_layer(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
623
|
+
"""
|
|
624
|
+
Get the number of tokens per layer from the kv_caches
|
|
625
|
+
(num_blocks * block_size or page_buffer_size)
|
|
626
|
+
"""
|
|
627
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
628
|
+
# [num_blocks, num_layers, 2, block_size, num_heads, head_size]
|
|
629
|
+
return kv_caches.shape[0] * kv_caches.shape[3]
|
|
630
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS:
|
|
631
|
+
# List[num_layers] of [2, num_blocks, block_size, num_heads, head_size]
|
|
632
|
+
k_cache_shape = kv_caches[0][0].shape
|
|
633
|
+
return k_cache_shape[0] * k_cache_shape[1]
|
|
634
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS:
|
|
635
|
+
# List[num_layers] of [2, num_blocks, num_heads, block_size, head_size]
|
|
636
|
+
# k_cache = kv_caches[0][0] → (NB, NH, BS, HS); tokens = NB * BS
|
|
637
|
+
k_cache_shape = kv_caches[0][0].shape
|
|
638
|
+
return k_cache_shape[0] * k_cache_shape[2]
|
|
639
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS:
|
|
640
|
+
# List[num_layers] of [num_blocks, 2, block_size, num_heads, head_size]
|
|
641
|
+
k_cache_shape = kv_caches[0][:, 0].shape
|
|
642
|
+
return k_cache_shape[0] * k_cache_shape[1]
|
|
643
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS:
|
|
644
|
+
# List[num_layers] of [num_blocks, 2, num_heads, block_size, head_size]
|
|
645
|
+
# k_cache = kv_caches[0][:, 0] → (NB, NH, BS, HS); tokens = NB * BS
|
|
646
|
+
k_cache_shape = kv_caches[0][:, 0].shape
|
|
647
|
+
return k_cache_shape[0] * k_cache_shape[2]
|
|
648
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
649
|
+
# List[num_layers] of [num_blocks, block_size, head_size]
|
|
650
|
+
return kv_caches[0].shape[0] * kv_caches[0].shape[1]
|
|
651
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
652
|
+
# List[2] -> List[num_layers] of [page_buffer_size, num_heads, head_size]
|
|
653
|
+
return kv_caches[0][0].shape[0]
|
|
654
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
655
|
+
# List[num_layers] of [page_buffer_size, 1, head_size]
|
|
656
|
+
return kv_caches[0].shape[0]
|
|
657
|
+
else:
|
|
658
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def get_elements_per_layer(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
|
|
662
|
+
"""
|
|
663
|
+
Get the number of elements per layer from the kv_caches
|
|
664
|
+
(including both K and V for non-MLA)
|
|
665
|
+
"""
|
|
666
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
667
|
+
# [num_blocks, num_layers, 2, block_size, num_heads, head_size]
|
|
668
|
+
# For one layer: [num_blocks, 2, block_size, num_heads, head_size]
|
|
669
|
+
num_blocks = kv_caches.shape[0]
|
|
670
|
+
block_size = kv_caches.shape[3]
|
|
671
|
+
num_heads = kv_caches.shape[4]
|
|
672
|
+
head_size = kv_caches.shape[5]
|
|
673
|
+
return num_blocks * 2 * block_size * num_heads * head_size
|
|
674
|
+
elif gpu_kv_format in (
|
|
675
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
676
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
677
|
+
):
|
|
678
|
+
# [2, num_blocks, ...] — k_cache is kv_caches[0][0]
|
|
679
|
+
k_cache_shape = kv_caches[0][0].shape
|
|
680
|
+
return k_cache_shape.numel() * 2
|
|
681
|
+
elif gpu_kv_format in (
|
|
682
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
683
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
684
|
+
):
|
|
685
|
+
# [num_blocks, 2, ...] — k_cache is kv_caches[0][:, 0]
|
|
686
|
+
k_cache_shape = kv_caches[0][:, 0].shape
|
|
687
|
+
return k_cache_shape.numel() * 2
|
|
688
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
|
|
689
|
+
# List[num_layers] of [num_blocks, block_size, head_size] (MLA)
|
|
690
|
+
return kv_caches[0].numel()
|
|
691
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
692
|
+
# List[2] -> List[num_layers] of
|
|
693
|
+
# [page_buffer_size, num_heads, head_size] (separate K and V)
|
|
694
|
+
return kv_caches[0][0].numel() * 2
|
|
695
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
|
|
696
|
+
# List[num_layers] of [page_buffer_size, 1, head_size] (MLA)
|
|
697
|
+
return kv_caches[0].numel()
|
|
698
|
+
else:
|
|
699
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
def assert_is_vllm_flash_attn_or_flash_infer(gpu_kv_format: "lmc_ops.GPUKVFormat"):
|
|
703
|
+
"""
|
|
704
|
+
Ensure that we have a GPU KV Cache Format
|
|
705
|
+
that is either vLLM's flash attention or flash infer.
|
|
706
|
+
"""
|
|
707
|
+
assert gpu_kv_format in (
|
|
708
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
709
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
710
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
711
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def is_hnd(gpu_kv_format: "lmc_ops.GPUKVFormat") -> bool:
|
|
716
|
+
"""
|
|
717
|
+
Check if the GPU KV Format uses HND physical layout
|
|
718
|
+
"""
|
|
719
|
+
return gpu_kv_format in (
|
|
720
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
721
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def assert_is_vllm_mla_or_flash_attn_or_flash_infer(
|
|
726
|
+
gpu_kv_format: "lmc_ops.GPUKVFormat",
|
|
727
|
+
) -> None:
|
|
728
|
+
"""
|
|
729
|
+
Ensure that we have a GPU KV Cache Format that is either
|
|
730
|
+
vLLM's MLA, flash attention, or flash infer.
|
|
731
|
+
|
|
732
|
+
Accepted formats:
|
|
733
|
+
- ``NL_X_TWO_NB_BS_NH_HS`` (flash attention, NHD)
|
|
734
|
+
- ``NL_X_NB_TWO_BS_NH_HS`` (flash infer, NHD)
|
|
735
|
+
- ``NL_X_TWO_NB_NH_BS_HS`` (flash attention, HND)
|
|
736
|
+
- ``NL_X_NB_TWO_NH_BS_HS`` (flash infer, HND)
|
|
737
|
+
- ``NL_X_NB_BS_HS`` (MLA)
|
|
738
|
+
|
|
739
|
+
Raises:
|
|
740
|
+
AssertionError: If *gpu_kv_format* is not one of the accepted formats.
|
|
741
|
+
"""
|
|
742
|
+
assert gpu_kv_format in (
|
|
743
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
744
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
745
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
746
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
747
|
+
lmc_ops.GPUKVFormat.NL_X_NB_BS_HS,
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def is_mla(gpu_kv_format: "lmc_ops.GPUKVFormat") -> bool:
|
|
752
|
+
"""
|
|
753
|
+
Check if the GPU KV Format is MLA
|
|
754
|
+
"""
|
|
755
|
+
return (
|
|
756
|
+
gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS # vllm MLA
|
|
757
|
+
or gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS # sglang MLA
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def get_dtype(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> torch.dtype:
|
|
762
|
+
"""
|
|
763
|
+
Get the dtype from the kv_caches
|
|
764
|
+
"""
|
|
765
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
|
|
766
|
+
return kv_caches.dtype
|
|
767
|
+
elif gpu_kv_format in (
|
|
768
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
|
|
769
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
|
|
770
|
+
lmc_ops.GPUKVFormat.NL_X_NB_BS_HS,
|
|
771
|
+
lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
|
|
772
|
+
lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
|
|
773
|
+
lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS,
|
|
774
|
+
):
|
|
775
|
+
return kv_caches[0].dtype
|
|
776
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
|
|
777
|
+
return kv_caches[0][0].dtype
|
|
778
|
+
else:
|
|
779
|
+
raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
def _split_token2d_kv(token2d: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
783
|
+
"""
|
|
784
|
+
Accepts either:
|
|
785
|
+
- [2, T, D]
|
|
786
|
+
- [T, 2, D]
|
|
787
|
+
Returns:
|
|
788
|
+
- k_tok: [T, D]
|
|
789
|
+
- v_tok: [T, D]
|
|
790
|
+
"""
|
|
791
|
+
if token2d.dim() != 3:
|
|
792
|
+
raise ValueError(f"Expected token2d dim=3, got {token2d.shape}")
|
|
793
|
+
if token2d.shape[0] == 2: # [2, T, D]
|
|
794
|
+
return token2d[0], token2d[1]
|
|
795
|
+
if token2d.shape[1] == 2: # [T, 2, D]
|
|
796
|
+
return token2d[:, 0, :], token2d[:, 1, :]
|
|
797
|
+
raise ValueError(f"Unrecognized token2d layout: {token2d.shape}")
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def _get_head_size_view(
|
|
801
|
+
kv_cache_layer: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]],
|
|
802
|
+
*,
|
|
803
|
+
use_mla: bool,
|
|
804
|
+
gpu_kv_format: Optional["lmc_ops.GPUKVFormat"] = None,
|
|
805
|
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
|
806
|
+
"""
|
|
807
|
+
Returns flattened views for index_copy/index_select.
|
|
808
|
+
|
|
809
|
+
If gpu_kv_format is provided, use it to interpret tensor layout explicitly.
|
|
810
|
+
If not provided, fall back to current structural behavior:
|
|
811
|
+
- MLA: expects Tensor [P, B, HS]
|
|
812
|
+
- Non-MLA: expects either
|
|
813
|
+
* Tensor [2, P, B, NH, HS] OR
|
|
814
|
+
* (k, v) tuple each [P, B, NH, HS]
|
|
815
|
+
(and also supports [P, 2, B, NH, HS] as a safe extension)
|
|
816
|
+
"""
|
|
817
|
+
# -------------------------
|
|
818
|
+
# MLA
|
|
819
|
+
# -------------------------
|
|
820
|
+
if use_mla:
|
|
821
|
+
if not isinstance(kv_cache_layer, torch.Tensor):
|
|
822
|
+
raise ValueError("MLA expects kv_cache_layer as Tensor")
|
|
823
|
+
if kv_cache_layer.dim() != 3:
|
|
824
|
+
raise ValueError(f"MLA expects 3D [P,B,HS], got {kv_cache_layer.shape}")
|
|
825
|
+
p, b, hs = kv_cache_layer.shape
|
|
826
|
+
return kv_cache_layer.view(p * b, hs)
|
|
827
|
+
|
|
828
|
+
# -------------------------
|
|
829
|
+
# non-MLA (K/V)
|
|
830
|
+
# -------------------------
|
|
831
|
+
# If already provided (k, v) in canonical per-layer form, no format needed.
|
|
832
|
+
if not isinstance(kv_cache_layer, torch.Tensor):
|
|
833
|
+
k, v = kv_cache_layer
|
|
834
|
+
if k.dim() != 4 or v.dim() != 4:
|
|
835
|
+
raise ValueError(f"Expected (k,v) 4D [P,B,NH,HS], got {k.shape}, {v.shape}")
|
|
836
|
+
p, b, nh, hs = k.shape
|
|
837
|
+
if v.shape != (p, b, nh, hs):
|
|
838
|
+
raise ValueError(f"k/v shape mismatch: {k.shape} vs {v.shape}")
|
|
839
|
+
return k.view(p * b, nh * hs), v.view(p * b, nh * hs)
|
|
840
|
+
|
|
841
|
+
t = kv_cache_layer
|
|
842
|
+
if t.dim() != 5:
|
|
843
|
+
raise ValueError(f"Expected 5D tensor for non-MLA, got {t.shape}")
|
|
844
|
+
|
|
845
|
+
# If we have the format enum, decode explicitly.
|
|
846
|
+
if gpu_kv_format is not None:
|
|
847
|
+
if gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS:
|
|
848
|
+
# per-layer: [2, NB, BS, NH, HS]
|
|
849
|
+
if t.shape[0] != 2:
|
|
850
|
+
raise ValueError(
|
|
851
|
+
f"{gpu_kv_format} expects [2,NB,BS,NH,HS], got {t.shape}"
|
|
852
|
+
)
|
|
853
|
+
k, v = t[0], t[1] # [NB,BS,NH,HS]
|
|
854
|
+
|
|
855
|
+
elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS:
|
|
856
|
+
# per-layer: [NB, 2, BS, NH, HS]
|
|
857
|
+
if t.shape[1] != 2:
|
|
858
|
+
raise ValueError(
|
|
859
|
+
f"{gpu_kv_format} expects [NB,2,BS,NH,HS], got {t.shape}"
|
|
860
|
+
)
|
|
861
|
+
k, v = t[:, 0], t[:, 1] # [NB,BS,NH,HS]
|
|
862
|
+
|
|
863
|
+
else:
|
|
864
|
+
# Other formats are either MLA-only or require upstream normalization.
|
|
865
|
+
raise NotImplementedError(
|
|
866
|
+
f"gpu_kv_format={gpu_kv_format} not supported in non-MLA path here. "
|
|
867
|
+
"Normalize to (k,v) tuple [NB,BS,NH,HS] per-layer before calling."
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
else:
|
|
871
|
+
# No enum available: Assumed [2,P,B,H,D] (or [2,NB,BS,NH,HS] per-layer).
|
|
872
|
+
# Also accept [P,2,B,H,D] (or [NB,2,BS,NH,HS]) to be more robust.
|
|
873
|
+
if t.shape[0] == 2:
|
|
874
|
+
k, v = t[0], t[1]
|
|
875
|
+
elif t.shape[1] == 2:
|
|
876
|
+
k, v = t[:, 0], t[:, 1]
|
|
877
|
+
else:
|
|
878
|
+
raise ValueError(
|
|
879
|
+
f"gpu_kv_format is None and tensor does not look like stacked KV. "
|
|
880
|
+
f"Expected axis0==2 or axis1==2, got {t.shape}"
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
if k.dim() != 4 or v.dim() != 4:
|
|
884
|
+
raise ValueError(f"Expected k/v 4D [NB,BS,NH,HS], got {k.shape}, {v.shape}")
|
|
885
|
+
|
|
886
|
+
nb, bs, nh, hs = k.shape
|
|
887
|
+
if v.shape != (nb, bs, nh, hs):
|
|
888
|
+
raise ValueError(f"k/v shape mismatch after decode: {k.shape} vs {v.shape}")
|
|
889
|
+
|
|
890
|
+
return k.view(nb * bs, nh * hs), v.view(nb * bs, nh * hs)
|