异步加载#

概述#

本文解释了 LMCache async_loading 功能的原理、优点、与 vLLM PR 19330 的区别以及局限性。它重点介绍了 LMCache v1 与 vLLM 的集成以及内部存储管道。

此功能中组件的主要变化包括:

  • LMCache 异步查找客户端/服务器(基于 ZMQ)

  • 存储管理器协调后端和并发性

  • 缓存引擎异步 API 入口点

  • vLLM 适配器集成点

原理与理论#

从高层次来看,async_loading 将调度器端的查找与工作端的预取/检索解耦,允许 I/O 和计算之间的重叠,同时保持基于前缀的正确性。

  • 调度器发送带有令牌块哈希和偏移量的查找请求。

  • 工作端服务器在可用后端上执行分层的 batched_async_contains,并积极启动非阻塞的批量获取操作以处理命中前缀。

  • 完成情况通过 EventManager 进行跟踪,以安全地将加载的内存对象返回给请求路径。

  • 带权信号量与 AsyncSerializer 结合使用,通过根据块预算调整并发性来防止分配器死锁。

以下的 Mermaid 序列图展示了端到端的流程:

        sequenceDiagram
  autonumber
  participant S as Scheduler (vLLM)
  participant LC as LMCacheAsyncLookupClient
  participant WS as LMCacheAsyncLookupServer (Worker)
  participant SM as StorageManager
  participant BE as Backends (LocalCPU/LocalDisk/FSConnector)
  participant EM as EventManager

  S->>LC: lookup(token_ids, lookup_id, request_configs)
  note right of LC: Hashes + offsets via TokenDatabase
  LC->>WS: ZMQ PUSH multipart [lookup_id, hashes, offsets, configs]
  WS->>SM: async_lookup_and_prefetch(lookup_id, keys, cum_chunk_lengths)
  SM->>BE: batched_async_contains(lookup_id, keys, pin=True)
  alt prefix hit across tiers
    BE-->>SM: num_hit_chunks (per tier)
    SM->>BE: batched_get_non_blocking(lookup_id, hit_prefix)
    BE-->>SM: Future[List[MemoryObj]]
    SM->>EM: add_event(EventType.LOADING, lookup_id, gather_all)
    SM-->>WS: send_response_to_scheduler(lookup_id, retrieved_length)
    WS-->>LC: ZMQ PUSH [lookup_id, num_hit_tokens]
  else cache miss
    SM-->>WS: send_response_to_scheduler(lookup_id, 0)
    WS-->>LC: ZMQ PUSH [lookup_id, 0]
  end
    

架构(工作端)#

        flowchart LR
    subgraph Worker
      direction TB
      A["LMCacheAsyncLookupServer<br/>ZMQ PULL/PUSH"]
      B["StorageManager<br/>Async loop (thread)"]
      C["AsyncSerializer<br/>WeightedSemaphore"]
      D["EventManager<br/>EventType.LOADING"]
    end

    subgraph Backends
      E["LocalCPUBackend<br/>contains/get"]
      F["LocalDiskBackend<br/>async contains/get"]
      G["FSConnector<br/>remote FS"]
    end

    A --> B
    B --> C
    B --> D
    B -.contains/get.-> E
    B -.contains/get.-> F
    B -.contains/get.-> G

    style E fill:#dff,stroke:#333,stroke-width:1px
    style F fill:#ffd,stroke:#333,stroke-width:1px
    style G fill:#dfd,stroke:#333,stroke-width:1px
    

好处#

  • 性能重叠
    • I/O–计算重叠:将查找/预取与加载解耦,使得在 vLLM 继续调度/计算的同时可以获取 KV 块。

  • 鲁棒性和错误处理
    • 事件驱动同步EventManager 确保未来对象的安全交接,并避免线程与异步循环之间的竞争条件。

    • 背压与死锁避免AsyncSerializer 使用加权信号量限制并发块检索,基于分配器预算,防止饥饿或分配器锁定。

    • 优雅未命中路径:当无法检索到任何内容时,立即响应 None 命中令牌;工作线程快速返回,不会阻塞调度器。

与 vLLM 加载失败恢复功能的比较#

VLLM_PR_19330 引入了一个故障恢复机制,用于 vLLM 的 KV 连接器基础设施,能够优雅地处理 KV 缓存加载失败,通过自动检测失败的块加载并仅重新调度受影响的请求,从有效前缀中进行重计算。相比之下,LMCache 的 async_loading 是一个外部缓存层,具有自己的客户端/服务器、存储后端和并发控制。

限制#

  • 仅适用于合并了 VLLM_PR_23620 的 vllm

  • 后端支持限制:此功能当前需要实现 batched_async_contains 的后端;仅限于少数后端,例如:
    • LocalCpuBackend

    • LocalDiskBackend

    • S3Connector

    • FSConnector

    • RedisConnector/RedisClusterConnector

未来工作#

  • 引入默认的 batched_async_contains 实现,以便所有后端都可以支持 async_loading

  • 添加指标和可观察性,以跟踪异步查找请求的数量和占用的 MemoryObj 实例的数量。

  • 通过传递 vLLM 前缀缓存命中令牌来改进查找框架,以便异步查找可以跳过已在 vLLM 中命中的部分加载。