LLM Infer Infra 学习资料库

LMCache:知识地图、架构与时序图

白话导读:用图把 LMCache 的架构、冷请求、热请求、对象状态和与 Mooncake 的组合画出来。

  • 冷请求:先算再存;热请求:先查、再预取到 L1、最后回填 GPU。
  • LMCache 的 L1/L2 是它自己的管理层级(常见为主机内存/外部存储),不是 CPU 缓存,也不是 GPU。
  • 图省略了错误分支和平台差异,失败路径看设计图册 D10。

图看不懂时,对照架构与源码主线中同名小节。

怎么看这些图:实线是主要的调用或数据路径,虚线是管理、元数据或可选的联系。架构图省略了平台差异和错误分支,不能据此认为任意两个节点之间都能直接传输。失败路径(超时、租约过期、主节点切换)见设计图册。图太宽时可以在图上横向滚动。

1. 全景知识地图

学 LMCache 要同时懂四块:推理语义(KV 为什么能复用)、LMCache 运行时(怎样查、取、存)、存储与传输(数据放哪、怎么搬)、性能与生产(值不值、出错怎么办)。

查看图源
flowchart LR
  A["KV Cache / 推理 infra"] --> B["推理语义"]
  A --> C["LMCache 运行时"]
  A --> D["存储与传输"]
  A --> E["性能与生产"]
  B --> B1["Prefill / Decode / Attention"]
  B --> B2["GQA / MLA / Hybrid / TP"]
  B --> B3["前缀依赖 / 位置 / key / layout"]
  C --> C1["Connector / Scheduler / Worker"]
  C --> C2["Lookup / Prefetch / Retrieve / Store"]
  C --> C3["L1 staging / 锁 / 事件 / 会话"]
  D --> D1["DRAM / SSD / S3 / 远端内存"]
  D --> D2["Mooncake Store / Transfer Engine"]
  D --> D3["RDMA / PCIe / NUMA / GDS"]
  E --> E1["TTFT / ITL / Goodput / 尾延迟"]
  E --> E2["准入 / 淘汰 / 背压 / 路由"]
  E --> E3["隔离 / 故障 / 可观测 / 正确性"]
  C2 --> F["高级复用"]
  F --> F1["CacheBlend / 压缩 / Layerwise"]
  F --> F2["P/D 分离 / 跨实例共享"]

对应阅读:推理语义看KV 原理;运行时看架构与源码主线;存储看后端契约;生产问题看优化与可观测。

2. MP 架构:谁决定、谁执行、谁存储

一句话:引擎决定算什么,LMCache 服务负责查、搬、存,后端负责保存字节。引擎里的调度器通过 adapter 发 LOOKUP、查状态;worker 通过 adapter 发 STORE、RETRIEVE。LMCache 服务内部由 StorageManager 统一管理 L1 和各个 controller,再通过 L2 adapter 连接外部存储。

查看图源
flowchart TB
  U["请求 / tokenizer"] --> S["vLLM Scheduler"]
  subgraph ENGINE["推理引擎进程"]
    S --> SA["LMCacheMPSchedulerAdapter"]
    W["vLLM Worker"] --> WA["LMCacheMPWorkerAdapter"]
    W <--> G["GPU paged KV"]
  end
  subgraph MP["独立 LMCache 服务"]
    R["RPC / Session / Modules"]
    L["LookupModule"]
    T["Transfer modules"]
    SM["StorageManager"]
    L1["L1Manager / allocator"]
    P["PrefetchController"]
    ST["StoreController"]
    AD["L2 adapters"]
    R --> L --> SM
    R --> T --> SM
    SM <--> L1
    SM --> P
    L1 --> ST
    P <--> AD
    ST --> AD
  end
  SA -->|"LOOKUP / 状态"| R
  WA -->|"STORE / RETRIEVE / 上下文"| R
  G <-->|"设备传输;不等于普通 RPC payload"| T
  AD <--> FS["文件 / 对象存储 / 远端 Store"]
  CO["可选 Coordinator"] -.-> R
  OBS["HTTP / Metrics / Traces"] -.-> SM

三点说明:

3. 进程内模式对照

早期教程多用进程内模式:LMCache 作为库跑在引擎进程里。了解它是为了看懂旧资料。

查看图源
flowchart LR
  V["vLLM / LMCacheConnectorV1"] --> E["inprocess LMCacheEngine"]
  E --> H["TokenDatabase"]
  E --> GC["GPU connector"]
  E --> BS["inprocess StorageManager"]
  BS --> B["CPU / Disk / Remote backend"]

它和 MP 模式共用一部分底层能力,但 ObjectKey、L2 adapter 和 MP server 的参数不能直接拿到进程内配置里用。

4. 冷请求:先算再存

缓存里没有可用数据时:先查一遍确认没命中,引擎照常做 Prefill,然后把新算出的 KV 存进 L1,再按配置异步写到 L2。

查看图源
sequenceDiagram
  participant S as Scheduler
  participant M as LMCache server
  participant W as Worker/GPU
  participant L as L1 manager
  participant B as L2 backend
  S->>M: LOOKUP
  M-->>S: ack
  S->>M: QUERY_PREFETCH_STATUS
  M-->>S: 0 个可复用 chunk
  S->>W: 调度 prefill
  W->>W: 计算 KV / 记录设备完成事件
  W->>M: STORE 元数据与传输依赖
  M->>L: reserve_write,创建 staging
  M->>W: 等待生产者事件并发起 GPU 到 L1 传输
  Note over M,L: 实际 copy 完成后才发布
  M->>L: finish_write,admit resident
  L->>B: StoreController 异步写 L2(若配置)
  B-->>L: 完成后释放所持读锁
  W-->>S: 继续生成;请求完成与存储完成分开处理

注意:

5. 热请求:查找、预取、回填 GPU

缓存里有数据时:查找触发预取,数据从 L2 先搬进 L1,再在 RETRIEVE 时拷贝到 GPU,最后引擎只计算没覆盖的部分。

查看图源
sequenceDiagram
  participant S as Scheduler
  participant M as LookupModule
  participant P as PrefetchController
  participant L as L1
  participant B as L2 adapters
  participant W as Worker/GPU
  S->>M: LOOKUP
  M->>P: 提交 grouped keys
  M-->>S: ack(并非预取完成)
  P->>L: reserve_read
  P->>B: lookup_and_lock
  B-->>P: 命中 bitmap
  P->>P: 汇总层级结果,规划不重复的数据来源
  P->>L: 为需要的 L2 数据 reserve_write
  P->>B: load 到 L1 staging
  B-->>P: 完成 bitmap
  P->>L: admission 并保留读锁
  S->>M: QUERY_PREFETCH_STATUS
  M-->>S: 完成后的可复用前缀(未完成时为 None)
  S->>W: 分配物理块并传递加载计划
  W->>M: RETRIEVE
  M->>W: L1 到 GPU;发出完成事件
  W->>W: 等待数据依赖后执行剩余计算
  M->>L: 读完成后释放;清理未使用的尾锁

要分清三个时刻:lookup 回执(只是登记)、预取完成(数据已在 L1 且被保护)、GPU 可读(拷贝事件完成)。有多个 server 时,学习版本取大家共同可用的前缀,而不是“最快那个 server 的最长结果”。

6. L1 对象的状态变化

一个 L1 对象从无到有、被读、被回收的过程:

查看图源
stateDiagram-v2
  [*] --> Absent
  Absent --> Staging: reserve_write(key, tag)
  Staging --> Resident: finish_write / admission
  Staging --> Discarded: 另一 writer 已发布同 key
  Staging --> Reclaimable: 写锁过期
  Reclaimable --> Absent: 回收
  Resident --> Reading: reserve_read
  Reading --> Resident: finish_read(仍有副本)
  Reading --> Absent: 最后读者结束且对象为 temporary
  Resident --> Absent: 无锁时淘汰或删除
  Discarded --> Absent: 释放此 staging,其他 resident 不受影响

读图时注意三点:

7. LMCache 与 Mooncake 组合

LMCache 通过 native L2 adapter 内嵌一个 Mooncake Client:控制信息找 Master(分配、副本位置、租约),数据通过 Transfer Engine 直接和远端 segment 传输。

查看图源
flowchart LR
  G["推理 GPU"] <-->|"D2H / H2D"| L["LMCache L1 buffer"]
  L <--> A["Mooncake native L2 adapter"]
  A --> C["嵌入式 Mooncake Client"]
  C -.->|"分配 / 副本位置 / 租约"| M["Mooncake Master"]
  C --> T["本地 Transfer Engine"]
  T <-->|"数据面:TCP / RDMA"| RT["远端 Transfer Engine"]
  RT <--> R["已注册的远端 segment"]
  T -.-> MS["segment / endpoint metadata"]
  RT -.-> MS
  M -.-> R

两类元数据别混:Master 管“对象在哪、能不能读、何时回收”;Transfer Engine 的元数据管“segment、buffer 和网络端点怎么访问”。这张图画的是常见的主机 L1 路径,并不表示这个 adapter 会自动做 GPU 直连远端 GPU 的 GPUDirect RDMA,控制 RPC 也不传大块数据。完整的保存与恢复步骤见联合链路。

8. PD 分离与跨请求复用

这是两件不同的事:PD 分离让 Prefill 和 Decode 分开扩缩容、互不干扰;跨请求复用让后来的请求少算重复的 Prefill。两者可以组合。

查看图源
flowchart TB
  R["Router"] --> P["Prefill engine"]
  R --> D["Decode engine"]
  P -->|"本次请求 KV 交接:例如 NIXL"| D
  P <--> LP["Prefill 的 LMCache server"]
  D <--> LD["Decode 的 LMCache server"]
  LP <--> SH["可选共享存储 / P2P"]
  LD <--> SH

学习版本仓库里的 MP PD 示例用 MultiConnector 同时接 NIXL(负责本次请求的 KV 交接)和 LMCache(负责跨请求复用),并且两个引擎各配一个 LMCache server。它不是“打开 Mooncake Store 开关就自动有了 PD 分离”。

9. 性能分析因果链

命中更多,一方面少做了 Prefill,另一方面多了查询、网络和 H2D,还可能带来排队、内存和锁的竞争。最终 TTFT、goodput 和 Decode 的 ITL 是变好还是变差,取决于这两方面谁更大。

查看图源
flowchart LR
  H["更多缓存命中"] --> S["少做重复 prefill"]
  H --> IO["更多查询 / 网络 / H2D"]
  S --> T["TTFT 与 goodput"]
  IO --> T
  IO --> Q["队列 / 内存 / 锁竞争"]
  Q --> T
  Q --> D["Decode ITL"]
  POL["准入 / 路由 / chunk / 背压"] --> H
  POL --> Q

所以命中率只说明“有多少复用机会”,不能单独证明性能变好。可以用互动盈亏计算器体会这个取舍。

输入主题、术语或问题