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.
Files changed (399) hide show
  1. lmcache/__init__.py +84 -0
  2. lmcache/_version.py +24 -0
  3. lmcache/cli/__init__.py +1 -0
  4. lmcache/cli/commands/__init__.py +34 -0
  5. lmcache/cli/commands/base.py +157 -0
  6. lmcache/cli/commands/bench/__init__.py +557 -0
  7. lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
  8. lmcache/cli/commands/bench/engine_bench/config.py +245 -0
  9. lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
  10. lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
  11. lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
  12. lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
  13. lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
  14. lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
  15. lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
  16. lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
  17. lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
  18. lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
  19. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
  20. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
  21. lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
  22. lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
  23. lmcache/cli/commands/describe.py +310 -0
  24. lmcache/cli/commands/kvcache.py +133 -0
  25. lmcache/cli/commands/mock.py +75 -0
  26. lmcache/cli/commands/ping.py +113 -0
  27. lmcache/cli/commands/query/__init__.py +155 -0
  28. lmcache/cli/commands/query/prompt.py +134 -0
  29. lmcache/cli/commands/query/request.py +357 -0
  30. lmcache/cli/commands/server.py +99 -0
  31. lmcache/cli/commands/tool/__init__.py +63 -0
  32. lmcache/cli/commands/tool/cache_simulator.py +113 -0
  33. lmcache/cli/commands/trace/__init__.py +505 -0
  34. lmcache/cli/commands/trace/dispatch.py +249 -0
  35. lmcache/cli/commands/trace/driver.py +372 -0
  36. lmcache/cli/commands/trace/stats.py +289 -0
  37. lmcache/cli/documents/lmcache.txt +11 -0
  38. lmcache/cli/main.py +42 -0
  39. lmcache/cli/metrics/__init__.py +29 -0
  40. lmcache/cli/metrics/formatter.py +171 -0
  41. lmcache/cli/metrics/handler.py +94 -0
  42. lmcache/cli/metrics/metrics.py +161 -0
  43. lmcache/cli/metrics/section.py +77 -0
  44. lmcache/connections.py +173 -0
  45. lmcache/integration/__init__.py +2 -0
  46. lmcache/integration/base_service_factory.py +165 -0
  47. lmcache/integration/request_telemetry/__init__.py +1 -0
  48. lmcache/integration/request_telemetry/base.py +51 -0
  49. lmcache/integration/request_telemetry/factory.py +113 -0
  50. lmcache/integration/request_telemetry/fastapi.py +109 -0
  51. lmcache/integration/request_telemetry/noop.py +35 -0
  52. lmcache/integration/sglang/__init__.py +2 -0
  53. lmcache/integration/sglang/sglang_adapter.py +326 -0
  54. lmcache/integration/sglang/utils.py +39 -0
  55. lmcache/integration/vllm/__init__.py +1 -0
  56. lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
  57. lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
  58. lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
  59. lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
  60. lmcache/integration/vllm/utils.py +433 -0
  61. lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
  62. lmcache/integration/vllm/vllm_service_factory.py +339 -0
  63. lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
  64. lmcache/logging.py +107 -0
  65. lmcache/native_storage_ops.pyi +230 -0
  66. lmcache/non_cuda_equivalents.py +1424 -0
  67. lmcache/observability.py +1958 -0
  68. lmcache/storage_backend/serde/__init__.py +1 -0
  69. lmcache/storage_backend/serde/cachegen_basics.py +210 -0
  70. lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
  71. lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
  72. lmcache/storage_backend/serde/serde.py +75 -0
  73. lmcache/tools/__init__.py +1 -0
  74. lmcache/tools/cache_simulator/README.md +392 -0
  75. lmcache/tools/cache_simulator/__init__.py +1 -0
  76. lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
  77. lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
  78. lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
  79. lmcache/tools/cache_simulator/lru_cache.py +124 -0
  80. lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
  81. lmcache/tools/cache_simulator/simulator.py +795 -0
  82. lmcache/tools/controller_benchmark/README.md +161 -0
  83. lmcache/tools/controller_benchmark/__init__.py +1 -0
  84. lmcache/tools/controller_benchmark/__main__.py +331 -0
  85. lmcache/tools/controller_benchmark/benchmark.py +660 -0
  86. lmcache/tools/controller_benchmark/config.py +44 -0
  87. lmcache/tools/controller_benchmark/constants.py +10 -0
  88. lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
  89. lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
  90. lmcache/tools/controller_benchmark/handlers/base.py +47 -0
  91. lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
  92. lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
  93. lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
  94. lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
  95. lmcache/tools/controller_benchmark/handlers/register.py +56 -0
  96. lmcache/tools/mp_status_viewer/__init__.py +1 -0
  97. lmcache/tools/mp_status_viewer/__main__.py +95 -0
  98. lmcache/usage_context.py +417 -0
  99. lmcache/utils.py +665 -0
  100. lmcache/v1/__init__.py +2 -0
  101. lmcache/v1/api_server/__init__.py +2 -0
  102. lmcache/v1/api_server/__main__.py +537 -0
  103. lmcache/v1/basic_check.py +112 -0
  104. lmcache/v1/cache_controller/__init__.py +9 -0
  105. lmcache/v1/cache_controller/commands/__init__.py +15 -0
  106. lmcache/v1/cache_controller/commands/base.py +35 -0
  107. lmcache/v1/cache_controller/commands/full_sync.py +49 -0
  108. lmcache/v1/cache_controller/config.py +176 -0
  109. lmcache/v1/cache_controller/controller_manager.py +535 -0
  110. lmcache/v1/cache_controller/controllers/__init__.py +11 -0
  111. lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
  112. lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
  113. lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
  114. lmcache/v1/cache_controller/executor.py +463 -0
  115. lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
  116. lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
  117. lmcache/v1/cache_controller/frontend/static/index.html +234 -0
  118. lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
  119. lmcache/v1/cache_controller/full_sync_sender.py +475 -0
  120. lmcache/v1/cache_controller/locks.py +149 -0
  121. lmcache/v1/cache_controller/message.py +828 -0
  122. lmcache/v1/cache_controller/observability.py +208 -0
  123. lmcache/v1/cache_controller/utils.py +679 -0
  124. lmcache/v1/cache_controller/worker.py +665 -0
  125. lmcache/v1/cache_engine.py +2058 -0
  126. lmcache/v1/cache_interface.py +19 -0
  127. lmcache/v1/check/__init__.py +74 -0
  128. lmcache/v1/check/check_mode_gen.py +86 -0
  129. lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
  130. lmcache/v1/check/check_mode_test_remote.py +155 -0
  131. lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
  132. lmcache/v1/check/utils.py +571 -0
  133. lmcache/v1/compute/__init__.py +2 -0
  134. lmcache/v1/compute/attention/__init__.py +0 -0
  135. lmcache/v1/compute/attention/abstract.py +39 -0
  136. lmcache/v1/compute/attention/flash_attn.py +129 -0
  137. lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
  138. lmcache/v1/compute/attention/metadata.py +85 -0
  139. lmcache/v1/compute/attention/utils.py +14 -0
  140. lmcache/v1/compute/blend/__init__.py +7 -0
  141. lmcache/v1/compute/blend/blender.py +168 -0
  142. lmcache/v1/compute/blend/metadata.py +34 -0
  143. lmcache/v1/compute/blend/utils.py +63 -0
  144. lmcache/v1/compute/models/__init__.py +0 -0
  145. lmcache/v1/compute/models/base.py +141 -0
  146. lmcache/v1/compute/models/llama.py +9 -0
  147. lmcache/v1/compute/models/qwen3.py +24 -0
  148. lmcache/v1/compute/models/utils.py +68 -0
  149. lmcache/v1/compute/positional_encoding.py +199 -0
  150. lmcache/v1/config.py +848 -0
  151. lmcache/v1/config_base.py +848 -0
  152. lmcache/v1/distributed/api.py +248 -0
  153. lmcache/v1/distributed/config.py +321 -0
  154. lmcache/v1/distributed/error.py +64 -0
  155. lmcache/v1/distributed/eviction.py +192 -0
  156. lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
  157. lmcache/v1/distributed/eviction_policy/factory.py +27 -0
  158. lmcache/v1/distributed/eviction_policy/lru.py +244 -0
  159. lmcache/v1/distributed/eviction_policy/noop.py +50 -0
  160. lmcache/v1/distributed/internal_api.py +170 -0
  161. lmcache/v1/distributed/l1_manager.py +835 -0
  162. lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
  163. lmcache/v1/distributed/l2_adapters/base.py +360 -0
  164. lmcache/v1/distributed/l2_adapters/config.py +385 -0
  165. lmcache/v1/distributed/l2_adapters/factory.py +205 -0
  166. lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
  167. lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
  168. lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
  169. lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
  170. lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
  171. lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
  172. lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
  173. lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
  174. lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
  175. lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
  176. lmcache/v1/distributed/memory_manager.py +179 -0
  177. lmcache/v1/distributed/storage_controller.py +39 -0
  178. lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
  179. lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
  180. lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
  181. lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
  182. lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
  183. lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
  184. lmcache/v1/distributed/storage_manager.py +532 -0
  185. lmcache/v1/event_manager.py +145 -0
  186. lmcache/v1/exceptions/__init__.py +16 -0
  187. lmcache/v1/gpu_connector/__init__.py +126 -0
  188. lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
  189. lmcache/v1/gpu_connector/gpu_ops.py +85 -0
  190. lmcache/v1/gpu_connector/hpu_connector.py +326 -0
  191. lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
  192. lmcache/v1/gpu_connector/utils.py +890 -0
  193. lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
  194. lmcache/v1/health_monitor/__init__.py +1 -0
  195. lmcache/v1/health_monitor/base.py +587 -0
  196. lmcache/v1/health_monitor/checks/__init__.py +1 -0
  197. lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
  198. lmcache/v1/health_monitor/constants.py +36 -0
  199. lmcache/v1/internal_api_server/__init__.py +0 -0
  200. lmcache/v1/internal_api_server/api_registry.py +59 -0
  201. lmcache/v1/internal_api_server/api_server.py +120 -0
  202. lmcache/v1/internal_api_server/common/__init__.py +1 -0
  203. lmcache/v1/internal_api_server/common/env_api.py +22 -0
  204. lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
  205. lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
  206. lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
  207. lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
  208. lmcache/v1/internal_api_server/common/thread_api.py +63 -0
  209. lmcache/v1/internal_api_server/controller/__init__.py +1 -0
  210. lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
  211. lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
  212. lmcache/v1/internal_api_server/utils.py +43 -0
  213. lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
  214. lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
  215. lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
  216. lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
  217. lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
  218. lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
  219. lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
  220. lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
  221. lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
  222. lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
  223. lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
  224. lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
  225. lmcache/v1/kv_layer_groups.py +267 -0
  226. lmcache/v1/lazy_memory_allocator.py +284 -0
  227. lmcache/v1/lookup_client/__init__.py +25 -0
  228. lmcache/v1/lookup_client/abstract_client.py +77 -0
  229. lmcache/v1/lookup_client/async_lookup_message.py +50 -0
  230. lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
  231. lmcache/v1/lookup_client/factory.py +251 -0
  232. lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
  233. lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
  234. lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
  235. lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
  236. lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
  237. lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
  238. lmcache/v1/lookup_client/record_strategies/base.py +327 -0
  239. lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
  240. lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
  241. lmcache/v1/manager.py +539 -0
  242. lmcache/v1/memory_management.py +2619 -0
  243. lmcache/v1/metadata.py +114 -0
  244. lmcache/v1/mp_observability/AGENTS.override.md +21 -0
  245. lmcache/v1/mp_observability/README.md +204 -0
  246. lmcache/v1/mp_observability/config.py +340 -0
  247. lmcache/v1/mp_observability/event.py +100 -0
  248. lmcache/v1/mp_observability/event_bus.py +313 -0
  249. lmcache/v1/mp_observability/otel_init.py +145 -0
  250. lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
  251. lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
  252. lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
  253. lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
  254. lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
  255. lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
  256. lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
  257. lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
  258. lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
  259. lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
  260. lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
  261. lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
  262. lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
  263. lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
  264. lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
  265. lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
  266. lmcache/v1/mp_observability/trace/__init__.py +50 -0
  267. lmcache/v1/mp_observability/trace/codecs.py +255 -0
  268. lmcache/v1/mp_observability/trace/decorator.py +147 -0
  269. lmcache/v1/mp_observability/trace/format.py +132 -0
  270. lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
  271. lmcache/v1/mp_observability/trace/reader.py +167 -0
  272. lmcache/v1/mp_observability/trace/recorder.py +300 -0
  273. lmcache/v1/multiprocess/__init__.py +0 -0
  274. lmcache/v1/multiprocess/affinity_pool.py +102 -0
  275. lmcache/v1/multiprocess/blend_server_v2.py +891 -0
  276. lmcache/v1/multiprocess/config.py +253 -0
  277. lmcache/v1/multiprocess/custom_types.py +281 -0
  278. lmcache/v1/multiprocess/futures.py +194 -0
  279. lmcache/v1/multiprocess/gpu_context.py +511 -0
  280. lmcache/v1/multiprocess/http_server.py +235 -0
  281. lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
  282. lmcache/v1/multiprocess/mq.py +732 -0
  283. lmcache/v1/multiprocess/protocol.py +86 -0
  284. lmcache/v1/multiprocess/protocols/README.md +213 -0
  285. lmcache/v1/multiprocess/protocols/__init__.py +127 -0
  286. lmcache/v1/multiprocess/protocols/base.py +89 -0
  287. lmcache/v1/multiprocess/protocols/blend.py +109 -0
  288. lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
  289. lmcache/v1/multiprocess/protocols/controller.py +53 -0
  290. lmcache/v1/multiprocess/protocols/debug.py +34 -0
  291. lmcache/v1/multiprocess/protocols/engine.py +146 -0
  292. lmcache/v1/multiprocess/protocols/observability.py +39 -0
  293. lmcache/v1/multiprocess/server.py +1134 -0
  294. lmcache/v1/multiprocess/session.py +190 -0
  295. lmcache/v1/multiprocess/token_hasher.py +441 -0
  296. lmcache/v1/offload_server/__init__.py +17 -0
  297. lmcache/v1/offload_server/abstract_server.py +37 -0
  298. lmcache/v1/offload_server/message.py +30 -0
  299. lmcache/v1/offload_server/zmq_server.py +122 -0
  300. lmcache/v1/periodic_thread.py +579 -0
  301. lmcache/v1/pin_monitor.py +246 -0
  302. lmcache/v1/plugin/__init__.py +0 -0
  303. lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
  304. lmcache/v1/protocol.py +317 -0
  305. lmcache/v1/rpc/__init__.py +17 -0
  306. lmcache/v1/rpc/transport.py +105 -0
  307. lmcache/v1/rpc/zmq_transport.py +213 -0
  308. lmcache/v1/rpc_utils.py +165 -0
  309. lmcache/v1/server/__init__.py +2 -0
  310. lmcache/v1/server/__main__.py +170 -0
  311. lmcache/v1/server/storage_backend/__init__.py +21 -0
  312. lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
  313. lmcache/v1/server/storage_backend/local_backend.py +75 -0
  314. lmcache/v1/server/utils.py +21 -0
  315. lmcache/v1/standalone/__init__.py +1 -0
  316. lmcache/v1/standalone/__main__.py +583 -0
  317. lmcache/v1/standalone/manager.py +80 -0
  318. lmcache/v1/standalone/standalone_service_factory.py +86 -0
  319. lmcache/v1/storage_backend/__init__.py +313 -0
  320. lmcache/v1/storage_backend/abstract_backend.py +445 -0
  321. lmcache/v1/storage_backend/audit_backend.py +233 -0
  322. lmcache/v1/storage_backend/batched_message_sender.py +222 -0
  323. lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
  324. lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
  325. lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
  326. lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
  327. lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
  328. lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
  329. lmcache/v1/storage_backend/connector/__init__.py +443 -0
  330. lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
  331. lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
  332. lmcache/v1/storage_backend/connector/base_connector.py +379 -0
  333. lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
  334. lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
  335. lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
  336. lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
  337. lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
  338. lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
  339. lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
  340. lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
  341. lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
  342. lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
  343. lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
  344. lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
  345. lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
  346. lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
  347. lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
  348. lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
  349. lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
  350. lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
  351. lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
  352. lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
  353. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
  354. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
  355. lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
  356. lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
  357. lmcache/v1/storage_backend/gds_backend.py +1199 -0
  358. lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
  359. lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
  360. lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
  361. lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
  362. lmcache/v1/storage_backend/local_disk_backend.py +656 -0
  363. lmcache/v1/storage_backend/maru_backend.py +734 -0
  364. lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
  365. lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
  366. lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
  367. lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
  368. lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
  369. lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
  370. lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
  371. lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
  372. lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
  373. lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
  374. lmcache/v1/storage_backend/p2p_backend.py +788 -0
  375. lmcache/v1/storage_backend/path_sharder.py +117 -0
  376. lmcache/v1/storage_backend/pd_backend.py +646 -0
  377. lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
  378. lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
  379. lmcache/v1/storage_backend/remote_backend.py +624 -0
  380. lmcache/v1/storage_backend/resp_client.py +227 -0
  381. lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
  382. lmcache/v1/storage_backend/storage_manager.py +1352 -0
  383. lmcache/v1/system_detection.py +110 -0
  384. lmcache/v1/token_database.py +551 -0
  385. lmcache/v1/transfer_channel/__init__.py +83 -0
  386. lmcache/v1/transfer_channel/abstract.py +285 -0
  387. lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
  388. lmcache/v1/transfer_channel/nixl_channel.py +639 -0
  389. lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
  390. lmcache/v1/transfer_channel/transfer_utils.py +63 -0
  391. lmcache/v1/utils/__init__.py +1 -0
  392. lmcache/v1/utils/bloom_filter.py +109 -0
  393. lmcache/v1/utils/cache_utils.py +125 -0
  394. lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
  395. lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
  396. lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
  397. lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
  398. lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
  399. lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,360 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """
3
+ Convert LMCache lookup-hash JSONL logs into a ``vllm bench serve`` custom
4
+ dataset (JSONL with ``"prompt"`` and ``"output_tokens"`` fields).
5
+
6
+ The conversion preserves the **prefix-sharing structure** of the original
7
+ requests: requests that shared a chunk hash in the lookup logs will share the
8
+ same token prefix in the generated prompts. This allows the synthetic
9
+ benchmark to exercise LMCache prefix caching in the same pattern as the
10
+ original production workload.
11
+
12
+ Algorithm
13
+ ---------
14
+ 1. Build a *safe vocabulary*: token IDs from the tokenizer whose single-token
15
+ round-trip is stable (``decode([id])`` re-encodes back to exactly ``[id]``).
16
+ Tokens that start with a leading space are preferred to avoid BPE merges at
17
+ chunk boundaries.
18
+ 2. For each unique chunk hash, deterministically seed an RNG from
19
+ ``SHA-256(hash)`` and sample ``chunk_size`` token IDs from the safe vocab.
20
+ The same hash always produces the same token sequence.
21
+ 3. Per request: concatenate the token sequences for each full chunk, add
22
+ ``tail_len = seq_len mod chunk_size`` tail tokens (unique per request so
23
+ they are never accidentally cached), decode the whole list to text, and
24
+ write ``{"prompt": <text>, "output_tokens": <N>}``.
25
+
26
+ Usage (module mode)::
27
+
28
+ python3 -m lmcache.tools.cache_simulator.gen_bench_dataset \\
29
+ -i /path/to/lookup_hashes/ \\
30
+ --tokenizer /models/DeepSeek-V3 \\
31
+ --output-len 128 \\
32
+ -o bench_dataset.jsonl
33
+
34
+ Usage (CLI)::
35
+
36
+ lmcache tool cache-simulator gen-dataset \\
37
+ -i /path/to/lookup_hashes/ \\
38
+ --tokenizer /models/DeepSeek-V3 \\
39
+ --output-len 128 \\
40
+ -o bench_dataset.jsonl
41
+ """
42
+
43
+ # Standard
44
+ from pathlib import Path
45
+ from typing import Any
46
+ import argparse
47
+ import hashlib
48
+ import json
49
+ import random
50
+ import sys
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # Safe-vocabulary helpers
54
+ # ---------------------------------------------------------------------------
55
+
56
+
57
+ def build_safe_vocab(tokenizer: Any) -> list[int]:
58
+ """Return token IDs that round-trip stably through the tokenizer.
59
+
60
+ A *safe* token is one where::
61
+
62
+ tokenizer.encode(tokenizer.decode([id]), add_special_tokens=False) == [id]
63
+
64
+ These tokens can be concatenated safely — decoding a list of safe tokens
65
+ and re-tokenizing it will reproduce the same token IDs as long as each
66
+ token starts with a leading space (preventing BPE merges at boundaries).
67
+
68
+ Tokens that begin with a space (``Ġ``, ``▁``, or ``" "``) are placed
69
+ first so that ``hash_to_tokens`` preferentially picks space-prefixed
70
+ tokens, making BPE boundary merges much less likely.
71
+
72
+ Args:
73
+ tokenizer: A HuggingFace ``PreTrainedTokenizer`` or
74
+ ``PreTrainedTokenizerFast`` instance.
75
+
76
+ Returns:
77
+ A non-empty list of safe token IDs. Raises ``RuntimeError`` if
78
+ fewer than 10 safe tokens are found (the tokenizer is probably
79
+ unsupported).
80
+ """
81
+ vocab_size = tokenizer.vocab_size
82
+ safe_with_space: list[int] = []
83
+ safe_other: list[int] = []
84
+
85
+ for token_id in range(vocab_size):
86
+ try:
87
+ decoded = tokenizer.decode(
88
+ [token_id], skip_special_tokens=True, clean_up_tokenization_spaces=False
89
+ )
90
+ except Exception:
91
+ continue
92
+ if not decoded or not decoded.isprintable():
93
+ continue
94
+ # Re-encode and check round-trip
95
+ try:
96
+ re_encoded = tokenizer.encode(decoded, add_special_tokens=False)
97
+ except Exception:
98
+ continue
99
+ if re_encoded != [token_id]:
100
+ continue
101
+ # Prefer tokens that start with whitespace (prevents inter-chunk merges)
102
+ if decoded[0] in (" ", "\u0120", "\u2581"):
103
+ safe_with_space.append(token_id)
104
+ else:
105
+ safe_other.append(token_id)
106
+
107
+ result = safe_with_space + safe_other
108
+ if len(result) < 10:
109
+ raise RuntimeError(
110
+ f"Only {len(result)} safe tokens found in the vocabulary. "
111
+ "This tokenizer may not be supported."
112
+ )
113
+ return result
114
+
115
+
116
+ def hash_to_tokens(chunk_hash: str, n_tokens: int, vocab_ids: list[int]) -> list[int]:
117
+ """Deterministically map *chunk_hash* to *n_tokens* token IDs.
118
+
119
+ The mapping is a pure function of ``chunk_hash`` and ``n_tokens`` — the
120
+ same inputs always produce the same output regardless of call order.
121
+
122
+ Args:
123
+ chunk_hash: Hex string identifying the chunk (e.g. ``"0xabcd1234"``).
124
+ n_tokens: Number of token IDs to generate.
125
+ vocab_ids: Pool of safe token IDs to sample from.
126
+
127
+ Returns:
128
+ A list of ``n_tokens`` token IDs drawn from ``vocab_ids``.
129
+ """
130
+ seed_bytes = hashlib.sha256(chunk_hash.encode()).digest()
131
+ seed = int.from_bytes(seed_bytes, "big") % (2**31)
132
+ rng = random.Random(seed)
133
+ return [rng.choice(vocab_ids) for _ in range(n_tokens)]
134
+
135
+
136
+ # ---------------------------------------------------------------------------
137
+ # Main conversion
138
+ # ---------------------------------------------------------------------------
139
+
140
+
141
+ def gen_bench_dataset(
142
+ events: list[dict[str, Any]],
143
+ tokenizer: Any,
144
+ output_len: int,
145
+ output_path: Path,
146
+ ) -> int:
147
+ """Convert lookup-hash events into a vllm bench serve custom dataset.
148
+
149
+ Each event becomes one JSONL line with fields ``"prompt"`` (text) and
150
+ ``"output_tokens"`` (int). Chunk hashes are deterministically mapped to
151
+ token sequences so that prefix-sharing in the original workload is
152
+ reproduced in the synthetic prompts.
153
+
154
+ Args:
155
+ events: Lookup-hash events as returned by
156
+ :func:`~lmcache.tools.cache_simulator.simulator.load_lookup_events`.
157
+ tokenizer: A HuggingFace tokenizer loaded for the target model.
158
+ output_len: Number of output tokens for every request in the dataset.
159
+ output_path: Where to write the output JSONL file.
160
+
161
+ Returns:
162
+ Number of records written.
163
+ """
164
+ print("Building safe vocabulary …", flush=True)
165
+ vocab_ids = build_safe_vocab(tokenizer)
166
+ space_chars = (" ", "\u0120", "\u2581")
167
+ n_space = sum(1 for t in vocab_ids if tokenizer.decode([t])[0] in space_chars)
168
+ print(
169
+ f" Safe vocab size: {len(vocab_ids)} tokens ({n_space} space-prefixed)",
170
+ flush=True,
171
+ )
172
+
173
+ # Cache: chunk_hash -> token_id list (memoized across requests)
174
+ hash_cache: dict[str, list[int]] = {}
175
+ n_written = 0
176
+
177
+ output_path.parent.mkdir(parents=True, exist_ok=True)
178
+ with open(output_path, "w", encoding="utf-8") as fout:
179
+ for event in events:
180
+ chunk_hashes: list[str] = event.get("chunk_hashes", [])
181
+ chunk_size: int = event["chunk_size"]
182
+ seq_len: int = event["seq_len"]
183
+
184
+ # --- Full-chunk tokens ----------------------------------------
185
+ all_token_ids: list[int] = []
186
+ for h in chunk_hashes:
187
+ if h not in hash_cache:
188
+ hash_cache[h] = hash_to_tokens(h, chunk_size, vocab_ids)
189
+ all_token_ids.extend(hash_cache[h])
190
+
191
+ # --- Tail tokens ----------------------------------------------
192
+ # seq_len mod chunk_size tail tokens; always a miss in LMCache.
193
+ # Use a per-request unique seed so tails are never accidentally
194
+ # shared across requests.
195
+ tail_len = seq_len - len(chunk_hashes) * chunk_size
196
+ if tail_len > 0:
197
+ # Build a per-request identifier: prefer request_id, fall back
198
+ # to timestamp+index to ensure uniqueness.
199
+ req_id = event.get(
200
+ "request_id", f"{event.get('timestamp', n_written)}_{n_written}"
201
+ )
202
+ tail_hash = f"__tail__{req_id}"
203
+ all_token_ids.extend(hash_to_tokens(tail_hash, tail_len, vocab_ids))
204
+
205
+ # --- Decode and write -----------------------------------------
206
+ prompt_text = tokenizer.decode(
207
+ all_token_ids,
208
+ skip_special_tokens=True,
209
+ clean_up_tokenization_spaces=False,
210
+ )
211
+ record = {"prompt": prompt_text, "output_tokens": output_len}
212
+ fout.write(json.dumps(record, ensure_ascii=False) + "\n")
213
+ n_written += 1
214
+
215
+ if n_written % 1000 == 0:
216
+ print(f" {n_written} / {len(events)} records written …", flush=True)
217
+
218
+ return n_written
219
+
220
+
221
+ # ---------------------------------------------------------------------------
222
+ # CLI helpers (shared by main() and lmcache tool cache-simulator gen-dataset)
223
+ # ---------------------------------------------------------------------------
224
+
225
+
226
+ def add_gen_dataset_arguments(parser: argparse.ArgumentParser) -> None:
227
+ """Register all gen-dataset CLI flags onto *parser*.
228
+
229
+ Called by both :func:`main` (``python -m`` entry point) and the
230
+ ``lmcache tool cache-simulator gen-dataset`` sub-command in
231
+ ``lmcache/cli/commands/tool/cache_simulator.py``.
232
+
233
+ **When adding or removing a flag**, edit only this function — the
234
+ ``lmcache tool`` command picks up the change automatically.
235
+
236
+ Args:
237
+ parser: The :class:`argparse.ArgumentParser` (or sub-parser) to
238
+ populate.
239
+ """
240
+ parser.add_argument(
241
+ "-i",
242
+ "--input",
243
+ nargs="+",
244
+ required=True,
245
+ metavar="PATH",
246
+ type=Path,
247
+ help=(
248
+ "One or more JSONL files or directories containing "
249
+ "``lookup_hashes_*.jsonl`` files."
250
+ ),
251
+ )
252
+ parser.add_argument(
253
+ "--tokenizer",
254
+ required=True,
255
+ metavar="PATH",
256
+ help=(
257
+ "Path or HuggingFace model name for the tokenizer used to decode "
258
+ "synthetic token IDs into text. Should match the model that will "
259
+ "be benchmarked."
260
+ ),
261
+ )
262
+ parser.add_argument(
263
+ "--output-len",
264
+ type=int,
265
+ default=128,
266
+ metavar="N",
267
+ help="Number of output tokens for every request (default: %(default)s).",
268
+ )
269
+ parser.add_argument(
270
+ "-o",
271
+ "--output",
272
+ default="bench_dataset.jsonl",
273
+ metavar="FILE",
274
+ help="Output JSONL file path (default: %(default)s).",
275
+ )
276
+ parser.add_argument(
277
+ "-n",
278
+ "--max-samples",
279
+ type=int,
280
+ default=None,
281
+ metavar="N",
282
+ help="Truncate to the first N events after sorting by timestamp.",
283
+ )
284
+ parser.add_argument(
285
+ "--model",
286
+ default=None,
287
+ metavar="NAME",
288
+ help=(
289
+ "Filter events by ``model_name`` (exact match). Useful when logs "
290
+ "contain traffic for multiple models."
291
+ ),
292
+ )
293
+
294
+
295
+ def run_gen_dataset(args: argparse.Namespace) -> None:
296
+ """Execute the gen-dataset workflow from a parsed argument namespace.
297
+
298
+ Loads events via
299
+ :func:`~lmcache.tools.cache_simulator.simulator.load_lookup_events`,
300
+ loads the tokenizer, calls :func:`gen_bench_dataset`, and prints a
301
+ summary.
302
+
303
+ Args:
304
+ args: Parsed CLI namespace produced by a parser that was populated
305
+ with :func:`add_gen_dataset_arguments`.
306
+ """
307
+ # Lazy imports — keeps CLI startup fast
308
+ # Third Party
309
+ from transformers import AutoTokenizer
310
+
311
+ # First Party
312
+ from lmcache.tools.cache_simulator.simulator import load_lookup_events
313
+
314
+ # ---- Load events -------------------------------------------------------
315
+ print(f"Loading events from: {[str(p) for p in args.input]}", flush=True)
316
+ events = load_lookup_events(
317
+ args.input,
318
+ model=args.model,
319
+ max_samples=args.max_samples,
320
+ )
321
+ if not events:
322
+ print("No events found — nothing to write.", file=sys.stderr)
323
+ sys.exit(1)
324
+ print(f"Loaded {len(events):,} events.", flush=True)
325
+
326
+ # ---- Load tokenizer ----------------------------------------------------
327
+ print(f"Loading tokenizer: {args.tokenizer}", flush=True)
328
+ tokenizer = AutoTokenizer.from_pretrained(args.tokenizer, trust_remote_code=True)
329
+
330
+ # ---- Convert -----------------------------------------------------------
331
+ output_path = Path(args.output)
332
+ n = gen_bench_dataset(
333
+ events=events,
334
+ tokenizer=tokenizer,
335
+ output_len=args.output_len,
336
+ output_path=output_path,
337
+ )
338
+ print(f"\nWrote {n:,} records to {output_path}")
339
+
340
+
341
+ # ---------------------------------------------------------------------------
342
+ # Module entry point
343
+ # ---------------------------------------------------------------------------
344
+
345
+
346
+ def main() -> None:
347
+ """CLI entry point for ``python -m``."""
348
+ parser = argparse.ArgumentParser(
349
+ description=(
350
+ "Convert LMCache lookup-hash JSONL logs into a "
351
+ "vllm bench serve custom dataset."
352
+ )
353
+ )
354
+ add_gen_dataset_arguments(parser)
355
+ args = parser.parse_args()
356
+ run_gen_dataset(args)
357
+
358
+
359
+ if __name__ == "__main__":
360
+ main()
@@ -0,0 +1,124 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """
3
+ LRU cache implementations for the cache simulator.
4
+
5
+ Two variants are provided:
6
+
7
+ * ``LRUCacheFast`` — O(1) OrderedDict-backed. Supports only hit/miss queries.
8
+ Use this for capacity sweeps where per-chunk statistics are not needed.
9
+
10
+ * ``LRUCache`` — O(log n) dict + SortedList. Adds ``position(key)`` for
11
+ computing cache-position statistics (0 = MRU, capacity-1 = LRU).
12
+ Use this for the detailed single-run report.
13
+ """
14
+
15
+ # Standard
16
+ from collections import OrderedDict
17
+ from typing import Any
18
+
19
+ # Third Party
20
+ from sortedcontainers import SortedList
21
+
22
+
23
+ class LRUCacheFast:
24
+ """
25
+ Lightweight LRU cache using a single :class:`OrderedDict`.
26
+
27
+ All operations are O(1). No per-key position tracking.
28
+
29
+ Parameters
30
+ ----------
31
+ capacity:
32
+ Maximum number of entries before LRU eviction kicks in.
33
+ """
34
+
35
+ def __init__(self, capacity: int) -> None:
36
+ if capacity < 1:
37
+ raise ValueError(f"LRUCacheFast capacity must be >= 1, got {capacity}")
38
+ self.capacity = capacity
39
+ self._cache: OrderedDict[Any, None] = OrderedDict()
40
+ self.eviction_count: int = 0
41
+
42
+ def contains(self, key: Any) -> bool:
43
+ return key in self._cache
44
+
45
+ def access(self, key: Any) -> None:
46
+ """Mark an existing entry as most-recently used. O(1)."""
47
+ self._cache.move_to_end(key)
48
+
49
+ def insert(self, key: Any) -> None:
50
+ """Insert a new entry, or refresh if already present. O(1)."""
51
+ if key in self._cache:
52
+ self._cache.move_to_end(key)
53
+ return
54
+ if len(self._cache) >= self.capacity:
55
+ self._cache.popitem(last=False)
56
+ self.eviction_count += 1
57
+ self._cache[key] = None
58
+
59
+ def __len__(self) -> int:
60
+ return len(self._cache)
61
+
62
+
63
+ class LRUCache:
64
+ """
65
+ LRU cache backed by a dict (O(1) lookup) and a
66
+ :class:`~sortedcontainers.SortedList` (O(log n) rank queries).
67
+
68
+ Each entry carries a strictly-increasing clock value so that the SortedList
69
+ order is unambiguous.
70
+
71
+ * LRU end = smallest clock value = ``SortedList[0]``
72
+ * MRU end = largest clock value = ``SortedList[-1]``
73
+
74
+ Parameters
75
+ ----------
76
+ capacity:
77
+ Maximum number of entries before LRU eviction kicks in.
78
+ """
79
+
80
+ def __init__(self, capacity: int) -> None:
81
+ if capacity < 1:
82
+ raise ValueError(f"LRUCache capacity must be >= 1, got {capacity}")
83
+ self.capacity = capacity
84
+ self._clock: int = 0
85
+ self._map: dict[Any, int] = {} # key -> clock value at last access
86
+ self._sl: SortedList = SortedList(key=lambda x: x[0])
87
+ self.eviction_count: int = 0
88
+
89
+ def contains(self, key: Any) -> bool:
90
+ return key in self._map
91
+
92
+ def position(self, key: Any) -> int:
93
+ """
94
+ LRU rank of *key*: 0 = most-recently used, ``len-1`` = least-recently used.
95
+ O(log n).
96
+ """
97
+ clock = self._map[key]
98
+ idx = self._sl.index((clock, key))
99
+ return len(self._sl) - 1 - idx
100
+
101
+ def access(self, key: Any) -> None:
102
+ """Mark an existing entry as most-recently used. O(log n)."""
103
+ old_clock = self._map[key]
104
+ self._sl.remove((old_clock, key))
105
+ self._clock += 1
106
+ self._map[key] = self._clock
107
+ self._sl.add((self._clock, key))
108
+
109
+ def insert(self, key: Any) -> None:
110
+ """Insert a new entry, or refresh if already present. O(log n)."""
111
+ if key in self._map:
112
+ self.access(key)
113
+ return
114
+ if len(self._map) >= self.capacity:
115
+ lru_clock, lru_key = self._sl[0]
116
+ self._sl.remove((lru_clock, lru_key))
117
+ del self._map[lru_key]
118
+ self.eviction_count += 1
119
+ self._clock += 1
120
+ self._map[key] = self._clock
121
+ self._sl.add((self._clock, key))
122
+
123
+ def __len__(self) -> int:
124
+ return len(self._map)