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,394 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Dict, Tuple
4
+
5
+ # Third Party
6
+ import torch
7
+
8
+ # First Party
9
+ from lmcache.logging import init_logger
10
+ from lmcache.storage_backend.serde.cachegen_basics import (
11
+ CacheGenConfig,
12
+ CacheGenGPUBytestream,
13
+ CacheGenGPUEncoderOutput,
14
+ )
15
+ from lmcache.storage_backend.serde.serde import Serializer
16
+ from lmcache.utils import _lmcache_nvtx_annotate
17
+ from lmcache.v1.config import LMCacheEngineConfig
18
+ from lmcache.v1.metadata import LMCacheMetadata
19
+ import lmcache.c_ops as lmc_ops
20
+ import lmcache.storage_backend.serde.cachegen_basics as CGBasics
21
+
22
+ logger = init_logger(__name__)
23
+
24
+
25
+ @_lmcache_nvtx_annotate
26
+ def torch_quant(bins: int, qA: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
27
+ """
28
+ Quantize a float tensor to fixed number of bins
29
+
30
+ Input:
31
+ bins: number of bins
32
+ qA: the input tensor
33
+
34
+ Returns:
35
+ xq: the quantized tensor, in float32
36
+ max1: the maximum value of the tensor
37
+ """
38
+ MAX = bins // 2 - 1
39
+ C = MAX
40
+ max1 = torch.amax(torch.abs(qA), dim=-1, keepdim=True)
41
+ xq = torch.round(qA * (C / max1)).to(torch.int8)
42
+
43
+ return xq, max1
44
+
45
+
46
+ @_lmcache_nvtx_annotate
47
+ def torch_quant_vectorized(
48
+ bins: torch.Tensor, input_groups: torch.Tensor
49
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
50
+ """
51
+ Quantize each group of a tensor to fixed number of bins
52
+
53
+ Input:
54
+ bins: number of bins for different layers, with shape [nlayer]
55
+ input_groups: with shape [nlayers, ntokens, nchannels]
56
+
57
+ Returns:
58
+ quantized groups: [nlayers, ntokens, nchannels]
59
+ maxes: [nlayers, ntokens, 1]
60
+ """
61
+ MAX = (bins // 2 - 1)[:, None, None] # shape [nlayers, 1, 1]
62
+ max1 = torch.amax(
63
+ torch.abs(input_groups), dim=-1, keepdim=True
64
+ ) # shape [nlayers, ntokens, 1]
65
+ factor = MAX / max1 # shape [nlayers, ntokens, 1]
66
+ xq = torch.round(input_groups * factor + MAX).to(
67
+ torch.int8
68
+ ) # shape [nlayers, ntokens, nchannels]
69
+
70
+ return xq, max1
71
+
72
+
73
+ @_lmcache_nvtx_annotate
74
+ def concat_max(max1):
75
+ """
76
+ Given a dict of max tensors, concatenate them into a single tensor
77
+ """
78
+ # TODO: this function can be optimized, we don't really need this
79
+ maxes = []
80
+ for i in range(len(max1)):
81
+ maxes.append(max1[i].unsqueeze(0))
82
+ return torch.cat(maxes, dim=0)
83
+
84
+
85
+ def _split_kv(tensor: torch.Tensor) -> Tuple[torch.Tensor, ...]:
86
+ """
87
+ Split a blob KV tensor to K and V tensors with the merged heads
88
+
89
+ Input:
90
+ tensor: the KV tensor with shape
91
+ [num_layers, 2, num_tokens, num_heads, head_size]
92
+
93
+ Returns:
94
+ K and V tensors with shape
95
+ [num_layers, num_tokens, num_channels]
96
+ """
97
+ num_layers, _, num_tokens, num_heads, head_size = tensor.shape
98
+ return torch.unbind(
99
+ tensor.reshape(num_layers, 2, num_tokens, num_heads * head_size), dim=1
100
+ )
101
+
102
+
103
+ @_lmcache_nvtx_annotate
104
+ def _convert_to_int_and_normalize(cdf_float, needs_normalization):
105
+ """
106
+ Convert floatingpoint CDF to integers. See README for more info.
107
+
108
+ The idea is the following:
109
+ When we get the cdf here, it is (assumed to be) between 0 and 1, i.e,
110
+ cdf in [0, 1)
111
+ (note that 1 should not be included.)
112
+ We now want to convert this to int16 but make sure we do not get
113
+ the same value twice, as this would break the arithmetic coder
114
+ (you need a strictly monotonically increasing function).
115
+ So, if needs_normalization==True, we multiply the input CDF
116
+ with 2**16 - (Lp - 1). This means that now,
117
+ cdf in [0, 2**16 - (Lp - 1)].
118
+ Then, in a final step, we add an arange(Lp), which is just a line with
119
+ slope one. This ensure that for sure, we will get unique, strictly
120
+ monotonically increasing CDFs, which are in [0, 2**16)
121
+ """
122
+ PRECISION = 16
123
+ Lp = cdf_float.shape[-1]
124
+ factor = torch.tensor(2, dtype=torch.float32, device=cdf_float.device).pow_(
125
+ PRECISION
126
+ )
127
+ new_max_value = factor
128
+ if needs_normalization:
129
+ new_max_value = new_max_value - (Lp - 1)
130
+ cdf_float = cdf_float.mul(new_max_value)
131
+ cdf_float = cdf_float.round()
132
+ cdf = cdf_float.to(dtype=torch.int16, non_blocking=True)
133
+ if needs_normalization:
134
+ r = torch.arange(Lp, dtype=torch.int16, device=cdf.device)
135
+ cdf.add_(r)
136
+ return cdf
137
+
138
+
139
+ class CacheGenEncoderImpl:
140
+ def __init__(self, **kwargs) -> None:
141
+ """
142
+ Fields:
143
+ - fp_kv:
144
+ should be a tensor of shape (num_layers, num_tokens, num_channels)
145
+ - fp_v:
146
+ should be a tensor of shape (num_layers, num_tokens, num_channels)
147
+ """
148
+ self.fp_k = kwargs["fp_k"]
149
+ self.fp_v = kwargs["fp_v"]
150
+
151
+ self.quantized_key: Dict[int, torch.Tensor] = {}
152
+ self.max_tensors_key: Dict[int, torch.Tensor] = {}
153
+ self.quantized_value: Dict[int, torch.Tensor] = {}
154
+ self.max_tensors_value: Dict[int, torch.Tensor] = {}
155
+ self.config = kwargs["config"]
156
+
157
+ @_lmcache_nvtx_annotate
158
+ def quantize(self):
159
+ """Quantize the key and value tensors
160
+ (self.fp_k and self.fp_v)
161
+ """
162
+ for layer in range(len(self.fp_k)):
163
+ if layer < self.config["key_first_layers"]:
164
+ bins = self.config["key_first_bins"]
165
+ elif layer < self.config["key_second_layers"]:
166
+ bins = self.config["key_second_bins"]
167
+ else:
168
+ bins = self.config["key_third_bins"]
169
+
170
+ tmp = torch_quant(bins, self.fp_k[layer].float())
171
+ self.quantized_key[layer] = tmp[0] + bins // 2 - 1
172
+ self.max_tensors_key[layer] = tmp[1]
173
+
174
+ for layer in range(len(self.fp_v)):
175
+ if layer < self.config["value_first_layers"]:
176
+ bins = self.config["value_first_bins"]
177
+ else:
178
+ bins = self.config["value_second_bins"]
179
+ tmp = torch_quant(bins, self.fp_v[layer].float())
180
+ self.quantized_value[layer] = tmp[0] + bins // 2 - 1
181
+ self.max_tensors_value[layer] = tmp[1]
182
+
183
+ @_lmcache_nvtx_annotate
184
+ def compute_cdf(self, is_key):
185
+ """
186
+ Compute the CDF based on the quantized tensors
187
+ Field:
188
+ - start_layer: the start layer to compute the CDF
189
+ - end_layer: the end layer to compute the CDF
190
+ """
191
+ # TODO: Add start_index here
192
+ channels = self.fp_k[0].shape[-1]
193
+
194
+ def process_batch(X, max_val):
195
+ """
196
+ input shape should be [channels, tokens]
197
+ """
198
+ nchannels, ntokens = X.shape
199
+ one_hot = torch.nn.functional.one_hot(X.long(), num_classes=max_val + 1).to(
200
+ torch.float32
201
+ ) # Use float32 to avoid integer overflow
202
+ counts = one_hot.sum(dim=1) / ntokens
203
+ ret = torch.cumsum(counts, dim=1).roll(1)
204
+ ret[:, 0] = 0
205
+ return ret
206
+
207
+ def process_layers(X, max_val):
208
+ """
209
+ x is a iterator of dict values
210
+ each element's shape is [tokens, channels]
211
+ """
212
+ results = []
213
+ for x in X:
214
+ """do permute here"""
215
+ batch_counts = process_batch(x.cuda().permute(1, 0), max_val)
216
+ results.append(batch_counts)
217
+
218
+ final_counts = torch.cat(results, dim=0)
219
+
220
+ return final_counts
221
+
222
+ if is_key:
223
+ X = self.quantized_key.values()
224
+ else:
225
+ X = self.quantized_value.values()
226
+ value_range = 32
227
+ cdfs = process_layers(X, value_range) # 4096 is batch size, ==> 18GB GPU memory
228
+ final_cdf = cdfs.reshape((len(self.fp_k), channels, value_range + 1))
229
+
230
+ return final_cdf
231
+
232
+
233
+ @_lmcache_nvtx_annotate
234
+ def collect_bytes(output_buffer, output_lengths) -> torch.Tensor:
235
+ """
236
+ Collect a byte tensor from the output_buffer + output_lengths
237
+ """
238
+ output_buffer_size = output_buffer.shape[-1]
239
+ flattened_lengths = output_lengths.flatten()
240
+ flattened_buffer = output_buffer.flatten()
241
+ summed_length = (output_buffer_size - flattened_lengths).cumsum(0)
242
+ summed_length = summed_length.roll(1)
243
+ summed_length[0] = 0
244
+ indexes = summed_length.repeat_interleave(flattened_lengths)
245
+ indexes = indexes + torch.arange(len(indexes), device=indexes.device)
246
+ return flattened_buffer[indexes]
247
+
248
+
249
+ @_lmcache_nvtx_annotate
250
+ def encode_ntokens(
251
+ cdf_int, encode_input, output_buffer, output_lengths
252
+ ) -> torch.Tensor:
253
+ """Encode a batch of ntokens.
254
+
255
+ :param cdf_int: int16 tensor on GPU with shape [nlayers, nchannels, Lp]
256
+ :param encode_input: int8 tensor on GPU with shape
257
+ :param [nlayers, ntokens, nchannels]
258
+ :param output_buffer: uint8 tensor on GPU with shape
259
+ [nlayers, nchannels, BUFFER_SIZE]
260
+ :param output_lengths: int32 tensor on GPU with shape [nlayers, nchannels]
261
+
262
+ :return byte_tensor: the byte tensor
263
+ """
264
+ lmc_ops.encode_fast_new(
265
+ cdf_int,
266
+ encode_input,
267
+ output_buffer,
268
+ output_lengths,
269
+ )
270
+ byte_tensor = collect_bytes(output_buffer, output_lengths)
271
+ return byte_tensor
272
+ # return byte_tensor.cpu().numpy().tobytes()
273
+
274
+
275
+ @_lmcache_nvtx_annotate
276
+ def encode_function(
277
+ kv: torch.Tensor,
278
+ config: CacheGenConfig,
279
+ key_bins: torch.Tensor,
280
+ value_bins: torch.Tensor,
281
+ chunk_size: int,
282
+ ) -> CacheGenGPUEncoderOutput:
283
+ """
284
+ Given the path to the original key value cache, encode the KV cache
285
+ """
286
+ num_heads, head_size = kv.shape[-2:]
287
+ fp_k, fp_v = _split_kv(kv)
288
+ nchannels = num_heads * head_size
289
+ nlayers = fp_k.shape[0] + fp_v.shape[0]
290
+
291
+ new_key, max_tensors_key = torch_quant_vectorized(key_bins, fp_k)
292
+ new_value, max_tensors_value = torch_quant_vectorized(value_bins, fp_v)
293
+ encode_input = torch.cat((new_key, new_value), dim=0).reshape(
294
+ nlayers, chunk_size, nchannels
295
+ )
296
+
297
+ new_cdf_key = lmc_ops.calculate_cdf(new_key, int(key_bins.max()))
298
+ new_cdf_value = lmc_ops.calculate_cdf(new_value, int(value_bins.max()))
299
+ cdf_int = torch.cat([new_cdf_key, new_cdf_value])
300
+
301
+ output_buffer = torch.zeros(
302
+ (nlayers, nchannels, CGBasics.CACHEGEN_GPU_MAX_TOKENS_PER_CHUNK),
303
+ dtype=torch.uint8,
304
+ device=encode_input.device,
305
+ )
306
+ output_lengths = torch.zeros(
307
+ (nlayers, nchannels), dtype=torch.int32, device=encode_input.device
308
+ )
309
+
310
+ data_chunks = []
311
+ for i in range(0, chunk_size, CGBasics.CACHEGEN_GPU_MAX_TOKENS_PER_CHUNK):
312
+ start = i
313
+ end = min(i + CGBasics.CACHEGEN_GPU_MAX_TOKENS_PER_CHUNK, chunk_size)
314
+ bytestream = encode_ntokens(
315
+ cdf_int,
316
+ encode_input[:, start:end, :],
317
+ output_buffer,
318
+ output_lengths,
319
+ )
320
+ data_chunks.append(
321
+ CacheGenGPUBytestream(
322
+ bytestream=bytestream,
323
+ bytestream_lengths=output_lengths.clone(),
324
+ ntokens=end - start,
325
+ )
326
+ )
327
+
328
+ return CacheGenGPUEncoderOutput(
329
+ data_chunks,
330
+ cdf_int,
331
+ max_tensors_key=max_tensors_key,
332
+ max_tensors_value=max_tensors_value,
333
+ num_heads=num_heads,
334
+ head_size=head_size,
335
+ )
336
+
337
+
338
+ class CacheGenSerializer(Serializer):
339
+ def __init__(self, config: LMCacheEngineConfig, metadata: LMCacheMetadata):
340
+ self.cachegen_config = CacheGenConfig.from_model_name(metadata.model_name)
341
+ self.chunk_size = config.chunk_size
342
+ self.key_bins = self.make_key_bins(self.cachegen_config)
343
+ self.value_bins = self.make_value_bins(self.cachegen_config)
344
+
345
+ def make_key_bins(self, config: CacheGenConfig) -> torch.Tensor:
346
+ ret = torch.zeros(config.nlayers)
347
+ for spec in config.kspecs:
348
+ ret[spec.start_layer : spec.end_layer] = spec.bins
349
+ return ret.cuda()
350
+
351
+ def make_value_bins(self, config: CacheGenConfig) -> torch.Tensor:
352
+ ret = torch.zeros(config.nlayers)
353
+ for spec in config.vspecs:
354
+ ret[spec.start_layer : spec.end_layer] = spec.bins
355
+ return ret.cuda()
356
+
357
+ @_lmcache_nvtx_annotate
358
+ def to_bytes(self, tensor: torch.Tensor) -> bytes:
359
+ """
360
+ Serialize a pytorch tensor to bytes. The serialized bytes should contain
361
+ both the data and the metadata (shape, dtype, etc.) of the tensor.
362
+
363
+ Input:
364
+ t: the input pytorch tensor, can be on any device, in any shape,
365
+ with any dtype
366
+
367
+ Returns:
368
+ bytes: the serialized bytes
369
+ """
370
+ # Temporary fix for issue #83: encoder will have the default device 0
371
+ # on all the ray workers. Need to set it to the correct device.
372
+ # Also need to figure out why this happens.
373
+ if torch.cuda.current_device != tensor.device:
374
+ torch.cuda.set_device(tensor.device)
375
+ if tensor.device != self.key_bins.device:
376
+ self.key_bins = self.key_bins.to(tensor.device)
377
+ if tensor.device != self.value_bins.device:
378
+ self.value_bins = self.value_bins.to(tensor.device)
379
+
380
+ # TODO: permute is expensive here, need a better way to do it at lower
381
+ # level
382
+ # huggingface:
383
+ # tensor = tensor.permute(0, 1, 3, 2, 4)
384
+ """ expecting a tensor of shape
385
+ [num_layers, 2, num_tokens, num_heads, head_size] """
386
+ ntokens = tensor.shape[2]
387
+ output_dict = encode_function(
388
+ tensor.cuda(),
389
+ self.cachegen_config,
390
+ self.key_bins,
391
+ self.value_bins,
392
+ ntokens,
393
+ )
394
+ return output_dict.to_bytes()
@@ -0,0 +1,75 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ import abc
4
+ import time
5
+
6
+ # Third Party
7
+ import torch
8
+
9
+ # First Party
10
+ from lmcache.logging import init_logger
11
+ from lmcache.utils import _lmcache_nvtx_annotate
12
+
13
+ logger = init_logger(__name__)
14
+
15
+
16
+ class Serializer(metaclass=abc.ABCMeta):
17
+ @abc.abstractmethod
18
+ def to_bytes(self, t: torch.Tensor) -> bytes:
19
+ """
20
+ Serialize a pytorch tensor to bytes. The serialized bytes should contain
21
+ both the data and the metadata (shape, dtype, etc.) of the tensor.
22
+
23
+ Input:
24
+ t: the input pytorch tensor, can be on any device, in any shape,
25
+ with any dtype
26
+
27
+ Returns:
28
+ bytes: the serialized bytes
29
+ """
30
+ raise NotImplementedError
31
+
32
+
33
+ class SerializerDebugWrapper(Serializer):
34
+ def __init__(self, s: Serializer):
35
+ self.s = s
36
+
37
+ def to_bytes(self, t: torch.Tensor) -> bytes:
38
+ start = time.perf_counter()
39
+ bs = self.s.to_bytes(t)
40
+ end = time.perf_counter()
41
+
42
+ logger.debug(f"Serialization took {end - start:.2f} seconds")
43
+ return bs
44
+
45
+
46
+ class Deserializer(metaclass=abc.ABCMeta):
47
+ def __init__(self, dtype):
48
+ self.dtype = dtype
49
+
50
+ @abc.abstractmethod
51
+ def from_bytes(self, bs: bytes) -> torch.Tensor:
52
+ """
53
+ Deserialize a pytorch tensor from bytes.
54
+
55
+ Input:
56
+ bytes: a stream of bytes
57
+
58
+ Output:
59
+ torch.Tensor: the deserialized pytorch tensor
60
+ """
61
+ raise NotImplementedError
62
+
63
+
64
+ class DeserializerDebugWrapper(Deserializer):
65
+ def __init__(self, d: Deserializer):
66
+ self.d = d
67
+
68
+ @_lmcache_nvtx_annotate
69
+ def from_bytes(self, t: bytes) -> torch.Tensor:
70
+ start = time.perf_counter()
71
+ ret = self.d.from_bytes(t)
72
+ end = time.perf_counter()
73
+
74
+ logger.debug(f"Deserialization took {(end - start) * 1000:.2f} ms")
75
+ return ret
@@ -0,0 +1 @@
1
+ # SPDX-License-Identifier: Apache-2.0