LMCache KV Cache Pin 接口实战:通过 Controller 持久化 KV Cache 防止被驱逐
2026/9/15 16:56:13 网站建设 项目流程

LMCache KV Cache Pin 接口实战:通过 Controller 持久化 KV Cache 防止被驱逐

【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache

导读

LMCache 的pin接口允许用户将指定 token 序列对应的 KV Cache 分块(chunk)"钉住"(persist)在指定实例(instance_id)的指定存储位置(location),从而防止这些缓存块被缓存策略驱逐。本文以 docs/source/kv_cache_management/pin.rst 为骨架,完整演示从 YAML 配置、vLLM 实例启动、Controller 启动到通过 HTTP 调用/pin接口的全流程,并结合 lmcache/v1/api_server/main.py、lmcache/v1/cache_controller/executor.py 等源码,剖析 pin 请求从 API Server 到 LMCache Worker 再到底层存储后端的完整调用链。读完本文,你将掌握 pin 接口的调用方式、返回语义,以及它在缓存生命周期管理中的定位。

注意:本文档描述的是 LMCache 的 in-process 模式(已弃用)下的行为。如需更完善的功能支持与性能,建议使用 LMCache MP 模式。

一、接口定义与语义

pin接口的定义如下:

pin(instance_id: str, location: str, tokens: List[int]) -> event_id: str, num_tokens: int

各参数含义:

  • instance_id:LMCache 实例的标识符。在分布式场景下,同一个模型服务实例内的所有 rank 会共享同一个instance_id,Controller 据此找到该实例下注册的所有 Worker;
  • location:目标存储位置名称,例如LocalCPUBackend(本地 CPU 内存后端)或LocalDiskBackend(本地磁盘后端)。只有被 pin 的后端需要实现pin/unpin语义;
  • tokens:要钉住的 token ID 列表。Controller 会依据 token 序列在 token 数据库中按 chunk 粒度进行匹配。

该函数将tokens指定的 KV Cache 分块持久化到instance_id实例的指定location中。Controller 会返回一个event_id(操作事件 ID)以及本次被调度去 pin 的 token 数量num_tokens

返回值的语义:

  • num_tokens:表示有多少个 token 的 KV Cache 被成功 pin 住。如果某些 token 对应的 chunk 在当前实例的指定位置中不存在,则不会计入该数字;
  • event_id:本次操作的唯一事件标识,可用于后续查询操作状态(例如配合check_finish接口确认异步操作是否完成)。

在 Controller 的 KV 缓存管理 API 家族中,pin 与clear(清空)、lookup(查询)、move(迁移)、compress(压缩)等接口并列,属于面向用户与编排器(orchestrator)的缓存管理能力之一,详见 docs/source/kv_cache_management/index.rst。

二、环境准备:编写 LMCache 实例配置

首先创建一个 YAML 文件example.yaml来配置 LMCache 实例:

chunk_size: 256 local_cpu: True max_local_cpu_size: 5 # cache controller configurations enable_controller: True lmcache_instance_id: "lmcache_default_instance" controller_pull_url: "localhost:9001" lmcache_worker_ports: 8001 # Peer identifiers p2p_host: "localhost" p2p_init_ports: 8200

各配置项的作用:

配置项说明
chunk_size256KV Cache 分块大小(以 token 数计)。pin 操作按 chunk 粒度匹配与处理 token 序列
local_cpuTrue启用本地 CPU 内存后端(LocalCPUBackend),使 KV Cache 可落盘到 CPU 内存
max_local_cpu_size5本地 CPU 缓存的最大容量(单位为 GB),超出部分由缓存策略按需驱逐;被 pin 的块不受驱逐影响
enable_controllerTrue开启 LMCache Controller,这是 pin 等管理接口生效的前提
lmcache_instance_id"lmcache_default_instance"本实例的 ID,Controller 依此注册与管理 Worker
controller_pull_url"localhost:9001"Worker 向 Controller Manager 主动拉取命令的地址(即 Controller 的 monitor 端口)
lmcache_worker_ports8001LMCache Worker 监听的端口,端口数量需与 rank 数量一致
p2p_host"localhost"P2P 传输的主机地址
p2p_init_ports8200P2P 初始化端口

注意:lmcache_worker_ports的端口数量必须等于实例的 rank 数量。若开启enable_p2p,则必须同时启用 Controller,由 Controller 作为中心节点保存每个 chunk 的元信息,P2PBackend通过查询 Controller 获取 chunk 信息并借助 NIXL 完成数据传输。

三、三步启动:vLLM 实例、Controller 与请求下发

3.1 启动 vLLM/LMCache 实例(端口 8000)

CUDA_VISIBLE_DEVICES=0 LMCACHE_CONFIG_FILE=example.yaml vllm serve meta-llama/Llama-3.1-8B-Instruct --max-model-len 4096 \ --gpu-memory-utilization 0.8 --port 8000 --kv-transfer-config '{"kv_connector":"LMCacheConnectorV1", "kv_role":"kv_both"}'

要点:

  • LMCACHE_CONFIG_FILE=example.yaml指定上一步编写的配置,vLLM 内部的 LMCache 集成会据此初始化缓存引擎与 Controller 通信;
  • --kv-transfer-config中的LMCacheConnectorV1是 in-process 模式下 vLLM 与 LMCache 之间的 KV 传输连接器,kv_rolekv_both表示该实例同时承担 KV 的生成(produce)与消费(consume)角色;
  • 建议保持--gpu-memory-utilization有一定余量,避免因显存不足导致缓存写入失败。

3.2 启动 LMCache Controller(端口 9000)与 Monitor(端口 9001)

lmcache_controller --host localhost --port 9000 --monitor-port 9001

Controller 由 Controller Manager 与 LMCache Worker 两部分构成(架构详见 docs/source/kv_cache_management/index.rst):

  • KV Controller:处理 LMCache Worker 上报的 chunk 信息,并响应 lookup 等查询请求;
  • Reg Controller:处理 Worker 的 register / deregister / heartbeat(注册、注销、心跳);
  • Cluster Executor:当 Controller Manager 收到用户的控制请求(如 pin、clear、move)时,通过它向 LMCache Worker 下发对应命令。

LMCache Worker 是 rank 进程内的一个线程,负责向 Reg Controller 发送注册/注销/心跳、向 KV Controller 上报 admit/evict 的 chunk 信息,并监听端口接收 Cluster Executor 下发的命令。

3.3 向 vLLM 发送一次推理请求

curl -X POST http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/Llama-3.1-8B-Instruct", "prompt": "Explain the significance of KV cache in language models.", "max_tokens": 10 }'

该请求的目的是让 vLLM 实际处理上述 prompt,从而在 LMCache 中生成并缓存对应的 KV Cache 分块,为后续 pin 操作提供数据基础。

3.4 获取 token ID 序列

pin 接口接收的是 token ID 列表,因此需要先将 prompt 分词:

curl -X POST http://localhost:8000/tokenize \ -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/Llama-3.1-8B-Instruct", "prompt": "Explain the significance of KV cache in language models." }'

从返回结果中取出 token ID 列表,用于下一步的 pin 请求。

四、调用 Pin 接口

4.1 发起 pin 请求

curl -X POST http://localhost:9000/pin \ -H "Content-Type: application/json" \ -d '{ "tokens": [128000, 849, 21435, 279, 26431, 315, 85748, 6636, 304, 4221, 4211, 13], "instance_id": "lmcache_default_instance", "location": "LocalCPUBackend" }'

Controller 将返回类似下面的响应:

{"event_id": "xxx", "num_tokens": 12}

其中num_tokens表示有多少个 token 的 KV Cache 被成功 pin,返回的event_id可用于查询该操作的状态。

4.2 请求字段与响应结构

从源码看,lmcache/v1/api_server/main.py 中定义了对应的 Pydantic 模型与处理逻辑:

  • PinRequestinstance_id(str)、location(str)、tokens(list[int]);
  • PinResponseevent_id(str)、num_tokens(int);
  • 服务端在收到请求后,会以"Pin" + uuid4()的形式生成一个全局唯一的event_id,并将其与请求字段一起封装为PinMsg,交给handle_orchestration_message分发到 KV Controller。

PinMsg的完整定义位于 lmcache/v1/cache_controller/message.py,包含event_idinstance_idlocationtokens四个字段,并提供了describe()方法用于日志描述("Pin tokens ... in instance ... and location ...")。

五、源码视角:pin 请求的完整调用链

理解 pin 在 Controller 内部的流转,有助于在实际部署中排查"pin 后num_tokens不符合预期"等问题。完整调用链如下:

5.1 KV Controller 分发

lmcache/v1/cache_controller/controllers/kv_controller.py 中的pin方法将消息委托给cluster_executor.execute("pin", msg)

5.2 Cluster Executor 向所有 Worker 广播

lmcache/v1/cache_controller/executor.py 中的pin执行逻辑如下:

  1. 通过reg_controller.get_workers(instance_id)获取该实例注册的所有 Worker ID,并对每个 Worker 获取其命令 Socket;
  2. 为每个 Worker 生成独立的worker_event_id(形如Worker{worker_id}{event_id}),将 tokens 与 location 封装成PinWorkerMsg,使用msgspec.msgpack序列化后通过execute_workers并发下发;
  3. 汇总所有 Worker 返回的num_tokens,并断言各 Worker 返回的数量一致(源码注释提到后续需要保证跨 Worker 的缓存一致性);
  4. 最终以第一个 Worker 的结果构造PinRetMsg(event_id, num_tokens)返回。

5.3 Worker 侧执行:复用 lookup + pin 语义

LMCache Worker 在收到PinWorkerMsg后(见 lmcache/v1/cache_controller/worker.py),并非调用独立的 pin 函数,而是调用缓存引擎的lookup,并携带pin=True标志:

num_pinned_tokens = self.lmcache_engine.lookup( tokens=request.tokens, search_range=[request.location], lookup_id=request.worker_event_id, pin=True, )

也就是说:pin 本质上是"限定location的查找 + 命中即钉住"的组合操作——只有那些在指定位置真实存在的 chunk 才会被 pin,这也是num_tokens可能小于请求 token 总数的原因。

5.4 存储后端:pin 计数与驱逐保护

以文档示例中的LocalCPUBackend为例,lmcache/v1/storage_backend/local_cpu_backend.py 在cpu_lock保护下对hot_cache中命中的键调用memory_obj.pin()unpin()

底层内存对象的 pin 语义在 lmcache/v1/memory_management.py 中实现:

  • pin():首次 pin(pin_count从 0 变为 1)时更新监控计数,随后pin_count += 1,并注册到PinMonitor以便做超时跟踪;
  • unpin()pin_count -= 1,当pin_count == 0时更新监控计数并注销PinMonitor;仅当pin_count <= 0ref_count <= 0时才会真正把内存归还给父级分配器(释放)。

因此,被 pin 的内存对象不会因缓存策略(如 LRU)而被驱逐,pin_count相当于一把"驱逐保护锁",且支持多次 pin / 多次 unpin 的引用式计数。

5.5 不同后端的 pin 支持情况(可推断)

从 lmcache/v1/storage_backend/abstract_backend.py 的抽象定义看,pin(key)是后端接口的组成部分,但各后端实现存在差异:

  • LocalCPUBackend:真实支持,见上文;
  • LocalDiskBackend:实现了 pin/unpin(lmcache/v1/storage_backend/local_disk_backend.py);
  • RemoteBackend:pin 为 no-op 并直接返回 True(lmcache/v1/storage_backend/remote_backend.py);
  • P2PBackend:源码注释明确"pin is useless for P2P backend now"(lmcache/v1/storage_backend/p2p_backend.py);
  • GDSBackend:由于 GDS 当前没有驱逐机制,pin 返回 False(lmcache/v1/storage_backend/gds_backend.py)。

因此在实际使用时,应根据location选择支持 pin 语义的后端(例如LocalCPUBackend),否则可能出现"请求成功但实际并未持久化"的情况。

六、与其他缓存管理接口的关系

pin 通常与以下接口配合使用,共同构成 Controller 的 KV 缓存管理能力(详见 docs/source/kv_cache_management/index.rst):

  • lookup:查询 token 序列的缓存布局;pin 的内部实现即"带 pin 标志的 lookup";
  • clear:清空指定实例、指定位置的 KV Cache;被 pin 的块不受普通驱逐影响,但clear是显式操作;
  • move:将 KV Cache 迁移到不同位置;
  • compress/decompress:对缓存进行压缩与解压;
  • check_finish:查询某个(非阻塞)控制事件是否已完成——可用于轮询 pin 这类异步操作的最终状态;
  • query_worker_info:查询 Worker 信息。

从架构上看,pin 是 Controller 面向"需要长期保留的 KV Cache"(例如高频复用的系统提示词、常用文档前缀、稳定知识库片段)提供的关键能力:既可以把宝贵的 KV Cache 从驱逐风险中保护起来,又可以在多实例间通过instance_id精确定位目标实例与目标后端。

七、常见问题排查建议

  1. num_tokens远小于请求的 token 数:说明指定instance_id+location下没有完整的 chunk 命中。请确认已先向 vLLM 发送过包含该 prompt 的推理请求、local_cpu: True已开启,且chunk_size与 token 序列匹配(pin 按 chunk 粒度对齐)。
  2. Controller 返回错误:检查lmcache_controller是否已启动且端口一致(--port 9000对应/pin接口,--monitor-port 9001对应controller_pull_url);检查 YAML 中的lmcache_instance_id与请求中的instance_id是否一致。
  3. 多 rank 场景lmcache_worker_ports需配置与 rank 数相同的端口,否则 Worker 无法全部注册,executor 会因找不到部分 Worker 的 Socket 而返回错误。
  4. in-process 模式弃用:如需更完善的功能与性能支持,请迁移到 LMCache MP 模式(docs/source/mp/index.rst),其中驱逐控制器通过维护_pin_counts将 pin 键排除在驱逐之外(见 lmcache/v1/mp_coordinator/controllers/eviction_controller.py),实现了同样"防驱逐"的语义。

【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询