ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LMCache 自定义 L2 存储插件(plugin / native_plugin)接入指南:零修改源码扩展 KV Cache 后端

LMCache 自定义 L2 存储插件(plugin / native_plugin)接入指南:零修改源码扩展 KV Cache 后端 LMCache 自定义 L2 存储插件plugin / native_plugin接入指南零修改源码扩展 KV Cache 后端【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache导读LMCache 多进程MP模式采用 L1/L2 两级缓存架构L1 为 CPU 内存等快速介质L2 为文件系统、NIXL、S3 等持久化后端。当现有内置后端无法满足需求时LMCache 提供了两条无需修改 LMCache 源码的插件化扩展路径——plugin加载纯 Python 实现的完整 L2 Adapter与native_plugin加载 pybind 封装的 C 原生连接器。本文以 docs/source/mp/l2_storage/plugin.rst 为骨架结合仓库源码与参考示例讲解两种插件类型的配置字段、加载机制、接口契约与完整接入流程读完即可动手编写并接入自己的外部存储后端。两种插件类型plugin 与 native_plugin 的定位L2 插件机制的核心设计是在启动时从用户提供的 Python 模块动态加载适配器类从而将自有存储后端指向 LMCache而无需改动 LMCache 任何源码。这对应 vLLM 中kv_connector_module_path的定位是 LMCache 的--l2-adapter模拟实现参见 plugin.rst。两种类型的分工如下类型加载内容适配层逻辑适用场景plugin实现L2AdapterInterface完整契约的纯 Python 类由插件自身实现全部抽象方法与 3 个 eventfd希望完全掌控 I/O 细节的 Python 后端native_plugin暴露六方法异步批量契约的 pybind 封装 C 连接器复用内置NativeConnectorL2Adapter桥接复用 C 高性能连接器Redis、RDMA、Mooncake 等后文分别展开两者的配置与原理。plugin的完整参考实现见 examples/lmc_external_l2_adapter/native_plugin的完整参考实现见 examples/lmc_external_native_connector/。plugin加载完整 L2AdapterInterface 实现加载机制与字段说明plugin类型通过importlib.import_module()动态导入module_path再用getattr()取出class_name并在加载时校验该类必须是L2AdapterInterface的子类否则抛出TypeError实现见 plugin_l2_adapter.py。字段定义见 PluginL2AdapterConfig必填字段字段类型说明module_pathstr包含适配器类的模块的点分 Python 导入路径。模块必须能被 LMCache 进程导入已安装或位于PYTHONPATHclass_namestrmodule_path内实现L2AdapterInterface的类名可选字段字段类型默认值说明adapter_paramsdict{}转发给适配器构造函数的任意字典config_class_namestr无module_path内继承L2AdapterConfigBase的配置类名。设置后或自动发现时工厂通过from_dict(adapter_params)构建配置对象并将配置对象而非原始adapter_params字典传给适配器构造函数与内置适配器约定保持一致值得强调的是配置类自动发现顺序源码见 _resolve_config_class显式指定的config.config_class_name字段约定俗成的class_nameConfig即在模块内查找与适配器类同名加Config后缀的类适配器类上的config_class_name类属性以上皆无则回退为原始字典模式直接传adapter_paramsdict。配置示例源自原文档# Raw-dict 模式adapter_params 直接传给构造函数 --l2-adapter {type: plugin, module_path: my_plugin.l2, class_name: MyL2Adapter, adapter_params: {host: localhost}} # Config-class 模式构建 my_plugin.l2.MyL2AdapterConfig.from_dict(adapter_params) 并传入 --l2-adapter {type: plugin, module_path: my_plugin.l2, class_name: MyL2Adapter, config_class_name: MyL2AdapterConfig, adapter_params: {host: localhost}}native_plugin加载 pybind 封装的 C 连接器加载机制与字段说明native_plugin动态导入module_path、实例化class_name以adapter_params作为构造函数关键字参数随后验证实例是否暴露所需异步批量方法event_fd、submit_batch_get、submit_batch_set、submit_batch_exists、drain_completions、close缺少任何一个都会抛出TypeError并将其包装进NativeConnectorL2Adapter。可选的submit_batch_delete用于启用 L2 淘汰删除若不提供删除操作退化为 no-op 并记录警告日志实现见 native_plugin_l2_adapter.py。字段定义见 NativePluginL2AdapterConfig必填字段字段类型说明module_pathstr包含连接器类的模块的点分 Python 导入路径class_namestrmodule_path内的连接器类名可选字段字段类型默认值说明adapter_paramsdict{}以关键字参数形式转发给连接器构造函数max_capacity_gbfloat0L2 聚合容量GB用于用量跟踪/淘汰。0表示禁用聚合淘汰配置示例源自原文档# Native pybind 连接器带构造函数 kwargs --l2-adapter {type: native_plugin, module_path: my_ext.connector, class_name: MyConnectorClient, adapter_params: {host: localhost, port: 1234}}NativeConnectorL2Adapter 桥接原理C 连接器只有 1 个 eventfd 且完成事件是混合的而 MP 模式的L2AdapterInterface要求 3 个独立的 eventfd 和类型化结果。NativeConnectorL2Adapternative_connector_l2_adapter.py在 Python 侧透明地完成了这座桥3 个 Python eventfdstore / lookup / load 1 个后台 demux 线程轮询原生 eventfd 并调用drain_completions()依据future_id → op_type映射将完成事件路由到正确类别序列化ObjectKey在提交边界序列化为字符串model_namekv_rank_hexobject_group_id_hexchunk_hash_hex带 cache_salt 时追加cache_saltMemoryObj通过byte_array属性提取为 memoryview见 _object_key_to_string客户端锁远程后端没有淘汰概念因此加锁在客户端以引用计数字典实现submit_unlock递减见 submit_unlock统计口径demux 线程在 store 完成时调用_notify_keys_storeddelete 完成时调用_notify_keys_deleted由基类统一维护聚合字节与按cache_salt分桶的用量见 base.py。L2AdapterInterface 契约编写 plugin 必须实现的接口plugin类型要求加载的类完整实现L2AdapterInterfacebase.py。该接口以非阻塞原语提供三类核心功能Store将一批内存对象与一批 key 关联存储Lookup and lock按 key 批量查询并锁定对象防止其在加载到 L1 前被淘汰Load按 key 批量加载加载不保证成功调用方需检查返回值通常锁定成功后成功概率很高。需要实现的抽象方法类别方法说明eventfdget_store_event_fd()/get_lookup_and_lock_event_fd()/get_load_event_fd()三个 fd必须各不相同store/lookup/load 控制器据此构建 fd→adapter 映射fd 冲突会导致 poll 分发静默错乱Storesubmit_store_task(keys, objects) - L2TaskId提交批量存储任务Storepop_completed_store_tasks() - dict[L2TaskId, L2StoreResult]弹出全部已完成存储任务Lookupsubmit_lookup_and_lock_task(keys, group_layout_descs) - L2TaskIdgroup_layout_descs是建议性提示多数适配器忽略P2P 适配器会转发给对端Lookupquery_lookup_and_lock_result(task_id) - Bitmap \| None非阻塞查询同一 task_id只返回一次非 None非幂等Bitmap 中 1成功 0失败Lookupsubmit_unlock(keys)无返回值的解锁实现必须保证最终成功含错误处理与重试Loadsubmit_load_task(keys, objects) - L2TaskId将加载数据写入调用方提供的 buffer调用方负责 MemoryObj 生命周期Loadquery_load_result(task_id) - Bitmap \| None同上非阻塞、非幂等清理close()释放全部资源关闭后不可再用接口还包含若干带默认实现的可选扩展点delete()默认 no-op支持淘汰的适配器应覆写、list_l2_keys()默认抛NotImplementedError、get_usage()返回AdapterUsage未声明容量时usage_fraction -1.0以及report_status()至少含is_healthy。另外需要注意L2 适配器会被 store controller 和 prefetch controller 两个控制器线程并发调用因此实现必须是线程安全的。端到端实操基于参考插件编写并接入自有后端Step 1搭建参考插件plugin 类型仓库提供了一个可直接安装的完整参考插件 examples/lmc_external_l2_adapter/其结构为examples/lmc_external_l2_adapter/ ├── pyproject.toml # 声明依赖 lmcachesetuptools 打包 ├── scripts/ │ └── install_and_test.sh # 一键安装验证脚本 ├── src/lmc_external_l2_adapter/ │ ├── __init__.py │ └── adapter.py # InMemoryL2Adapter InMemoryL2AdapterConfig └── tests/ └── test_plugin.py # 配置解析 / 导入 / 全链路 round-trip 测试参考插件InMemoryL2Adapter是一个把 KV Cache 对象存进 Python dict 的最小但功能完整的 L2 适配器adapter.py。它的构造签名同时兼容两种模式class InMemoryL2Adapter(L2AdapterInterface): def __init__( self, config: InMemoryL2AdapterConfig | dict[str, Any], **_kwargs: object, ): # 传入 dict 时自行 from_dict传入 config 对象时直接使用 if isinstance(config, dict): config InMemoryL2AdapterConfig.from_dict(config) ...配套的配置类InMemoryL2AdapterConfig继承L2AdapterConfigBase定义max_size_gb默认 0.5 GiB与mock_bandwidth_gb默认 10 GiB/s两个字段并实现from_dict()与help()。Step 2安装并验证参考 install_and_test.sh插件接入只需四步# 1. 以 editable 模式安装插件 pip install -e examples/lmc_external_l2_adapter # 2. 验证模块可导入 python -c from lmc_external_l2_adapter import InMemoryL2Adapter; print(InMemoryL2Adapter) # 3. 验证 LMCache 已注册 plugin 类型 python -c from lmcache.v1.distributed.l2_adapters.config import get_registered_l2_adapter_types print(get_registered_l2_adapter_types()) # 4. 运行插件的单元测试 python -m pytest examples/lmc_external_l2_adapter/tests/ -v测试 test_plugin.py 覆盖了关键行为可作为自研插件的行为基准配置解析PluginL2AdapterConfig.from_dict()解析两种 JSONadd_l2_adapters_argsparse_args_to_l2_adapters_config模拟--l2-adapterCLI 参数导入与继承issubclass(InMemoryL2Adapter, L2AdapterInterface)必须成立eventfd 互异三个 fd 组成集合长度必须为 3store/lookup/load round-trip用约 4 KB 的 tensor 对象执行存取并比对数据一致性混合 lookup已存在 不存在的 keyBitmap 位分别命中/未命中FIFO 淘汰容量约 10 KB 时存 3 个 4 KB 对象最老的 key 被淘汰bm.test(0) is False。Step 3通过 --l2-adapter 接入运行# 原始字典模式不指定 config_class_name走自动发现或 raw dict --l2-adapter {type:plugin,module_path:lmc_external_l2_adapter,class_name:InMemoryL2Adapter,adapter_params:{max_size_gb:1.0,mock_bandwidth_gb:20.0}} # 显式 config-class 模式 --l2-adapter {type:plugin,module_path:lmc_external_l2_adapter,class_name:InMemoryL2Adapter,config_class_name:InMemoryL2AdapterConfig,adapter_params:{max_size_gb:1.0,mock_bandwidth_gb:20.0}}--l2-adapter是一个可重复的参数每个 JSON 对象对应一个适配器实例且可以级联多个适配器store 时全部写入lookup 时按顺序查询详见 index.rst 的 Multiple Adapters (Cascade) 一节。CLI 解析逻辑见 config.py每个 JSON 必须含type字段未知类型、非法 JSON、from_dict()校验失败都会抛出带索引定位的ValueError并打印该类型的help()帮助文本。Step 4native_plugin 类型的外部原生连接器native_plugin参考示例见 examples/lmc_external_native_connector/其结构为examples/lmc_external_native_connector/ ├── csrc/ │ ├── connector.h / connector.cpp / pybind.cpp # C ConnectorBase 子类 pybind11 绑定 ├── src/lmc_external_native_connector/ │ ├── __init__.py │ └── connector.py # Python 工厂类 ExampleNativeConnector ├── pyproject.toml └── setup.py # 构建 C 扩展该示例提供两种后端文件系统ExampleFSConnector默认数据持久化为文件与内存ExampleMemoryConnectorCunordered_map重启即失。Python 侧的ExampleNativeConnector是工厂式包装通过backend/base_path/num_workers构造参数选择后端并返回原生连接器实例connector.py--l2-adapter { type: native_plugin, module_path: lmc_external_native_connector, class_name: ExampleNativeConnector, adapter_params: {backend: fs, base_path: /tmp/lmcache_ext, num_workers: 2} }若需在 C 层从零编写连接器可参考 native_connectors.rst 的完整教程继承csrc/storage_backends/connector_base.h中的ConnectorBaseT覆写create_connection、do_single_get、do_single_set、do_single_exists四个必需方法可选覆写do_single_delete以支持淘汰再用csrc/storage_backends/connector_pybind_utils.h中的LMCACHE_BIND_CONNECTOR_METHODS宏完成 pybind 绑定——该宏统一处理 GIL 释放、buffer 协议与完成事件转换绑定后即可同时获得非 MP 模式ConnectorClientBase与 MP 模式NativeConnectorL2Adapter两种接入方式。常见问题与排查建议ImportError: Could not import module ...module_path不在PYTHONPATH或未安装。先执行pip install -e plugin_dir或显式设置PYTHONPATH。TypeError: xxx is not a subclass of L2AdapterInterfaceplugin类型的class_name指向的类未继承L2AdapterInterface校验在加载时立即执行plugin_l2_adapter.py。TypeError: ... missing required methodnative_plugin的连接器实例缺少六方法契约中的某个方法工厂会先调用close()避免资源泄漏再抛出native_plugin_l2_adapter.py。删除不生效native_plugin未暴露submit_batch_delete时L2 淘汰删除为 no-op仅告警日志需要实现该方法。L2 无淘汰行为native_plugin未设置max_capacity_gb默认 0时禁用聚合淘汰plugin类型则需要在适配器中自行声明容量max_capacity_bytes并实现delete()与用量统计详见 base.py 的supports_global_eviction说明。调试设置LMCACHE_LOG_LEVELDEBUG可观察 store 任务提交/完成与 prefetch 请求明细见 index.rst 的 Verifying L2 Storage 一节。小结plugin与native_plugin为 LMCache 提供了对等的两条外部扩展通道前者面向希望以纯 Python 完整实现L2AdapterInterface的开发者后者面向已有或希望复用C pybind 连接器的场景由内置NativeConnectorL2Adapter自动补齐 eventfd 分发、键序列化与客户端锁。配合 examples/lmc_external_l2_adapter/ 与 examples/lmc_external_native_connector/ 两个可直接安装运行的参考实现以及 native_connectors.rst 的完整编写教程即可在不改动 LMCache 源码的前提下将任意自有存储后端接入多进程模式的 L2 缓存层。【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表