AIBrix KVCache 卸载框架#
对大型语言模型日益增长的需求,使得对高效内存管理和缓存的需求也随之增加,以优化推理性能并降低成本。在聊天机器人和基于代理的系统等多轮用例中,重叠的 token 序列导致预填充阶段的冗余计算,浪费资源并限制吞吐量。
许多推理引擎,如 vLLM,使用内置的 KV 缓存来缓解这个问题,利用空闲的 HBM 和 DRAM。然而,单节点 KV 缓存面临关键限制:受限的内存容量、特定于引擎的存储(阻止跨实例共享)以及难以支持 KV 迁移和预填充-解码分离等场景。
在 AIBrix v0.3.0 中,我们引入了一个生产就绪的 KVCache 卸载框架,它能够实现高效的内存分层和低开销的跨引擎复用。默认情况下,该框架利用基于 L1 DRAM 的缓存,通过减轻 GPU 内存压力同时不带来高延迟,已经提供了显著的性能改进。对于需要多节点共享或更大规模复用的场景,AIBrix 允许用户选择性地启用 L2 远程缓存,从而释放分布式 KV 缓存层的优势。
图 1. AIBrix KVCache 卸载框架
如图 1 所示,在数据平面上,它通过 AIBrix 卸载连接器与推理引擎(例如 vLLM)紧密集成,该连接器采用优化的 CUDA 内核显著加速 GPU 和 CPU 之间的数据移动。为了提高内存可伸缩性,其多层缓存管理器动态地平衡存储层之间的工作负载,缓解 GPU 内存容量限制,同时最大程度地减少延迟损失。该框架支持可插拔的逐出策略(例如 LRU,S3FIFO)和多样化的后端存储选项(例如 InfiniStore),从而能够选择性地卸载 KV 缓存以减少网络和 PCIe 争用。至关重要的是,其缓存放置模块可以与集中式分布式 KV 缓存集群管理器协调,以最大限度地提高全局 KV 缓存利用率。这使得跨引擎 KV 复用成为可能,并确保集群范围内的资源效率,将孤立的 KV 缓存实例转变为可扩展的共享 KV 缓存基础设施。
L1 引擎 DRAM 缓存管理#
现代模型日益增长的需求和 LLM 推理中上下文长度的增加,导致 KV 缓存消耗越来越多的 GPU 内存,甚至超出了最先进 GPU 的硬件限制。最近的系统,如 Dynamo、LMCache 和 MoonCake,已经开发出将 KV 缓存卸载到外部内存层次结构(从 CPU 内存到 SSD)的解决方案。KVCache 卸载 也支持通过仅启用其 DRAM 支持的 L1Cache 将 KV 缓存卸载到 CPU 内存。虽然这种方法不能实现多个引擎之间的 KV 缓存共享,但它消除了分布式 KV 缓存设置和配置的复杂性。更重要的是,通过利用 CPU 内存显著更大的容量,这种方法提供了实质性的性能提升——使其成为优先考虑可伸缩 KV 缓存容量而非跨引擎 KV 复用的用例的理想解决方案。
L2 分布式 KVCache 和跨引擎 KV 复用#
大型语言模型日益增长的需求显著增加了对大容量 KV 缓存的需求。虽然 CPU 内存卸载有效地解决了适度的扩展需求,但处理大规模、动态工作负载的生产环境需要更大的可扩展性——尤其是在内存需求超出单节点容量时。为了解决这个问题,AIBrix 启用分布式 KV 缓存服务作为其 L2Cache 后端,可以跨多个节点水平扩展以满足容量需求。
与此同时,随着 LLM 部署在集群中跨多个引擎扩展,引擎之间 KV 缓存的冗余会带来巨大的低效率。重复计算常见提示前缀会浪费 GPU 周期和 HBM 带宽。AIBrix 通过高性能、共享的分布式 KV 缓存实现高效的跨引擎 KV 复用来解决这一挑战,从而大规模优化资源利用率。
添加新的 KVCache 后端#
可以通过实现 Connector 接口轻松添加新的 KVCache 后端
1@dataclass
2class ConnectorFeature:
3 """The features of the kv cache connector.
4 Args:
5 mput_mget: Whether the kv cache connector supports mput/mget
6 prefetch: Whether the kv cache connector supports prefetch.
7 rdma: Whether the kv cache connector supports RDMA.
8 gdr_put: Whether the kv cache connector supports GDR put.
9 gdr_get: Whether the kv cache connector supports GDR get.
10 """
11
12 mput_mget: bool = False
13 prefetch: bool = False
14 rdma: bool = False
15 gdr_put: bool = False
16 gdr_get: bool = False
17
18
19@dataclass
20class ConnectorConfig:
21 """The config of the kv cache connector."""
22
23 backend_name: str
24 namespace: str
25 partition_id: str
26 executor: Executor
27 block_spec_signature: str = ""
28 key_builder_signature: str = ""
29 layout_signature: str = ""
30
31
32@dataclass
33class ConnectorRegisterDescriptor:
34 """The register descriptor"""
35
36 pass
37
38
39class Connector(Generic[K, V]):
40 """Connector interface."""
41
42 @classmethod
43 @abstractmethod
44 def from_envs(cls, conn_id: str, executor: Executor, **kwargs):
45 """Create a connector from environment variables."""
46 raise NotImplementedError
47
48 @property
49 @abstractmethod
50 def name(self) -> str:
51 raise NotImplementedError
52
53 @property
54 @abstractmethod
55 def feature(self) -> ConnectorFeature:
56 """Get the feature of the connector.
57 Returns:
58 The feature of the kv cache service.
59 """
60 raise NotImplementedError
61
62 @abstractmethod
63 def open(self) -> Status:
64 """Open a connection."""
65 raise NotImplementedError
66
67 @abstractmethod
68 def close(self) -> Status:
69 """Close a connection."""
70 raise NotImplementedError
71
72 async def prefetch(self, keys: Sequence[K]) -> None:
73 """Prefetch a list of keys.
74 Args:
75 keys: The keys of the kv tensors.
76 """
77 pass
78
79 @abstractmethod
80 async def exists(self, key: K) -> Status:
81 """Check if key is in the store."""
82 raise NotImplementedError
83
84 @abstractmethod
85 async def get(
86 self, key: K, mr: MemoryRegion | Sequence[MemoryRegion]
87 ) -> Status:
88 """Get a value.
89 Args:
90 key: The key of the kv tensor.
91 mr: The memory region or MR list to place the fetched kv
92 tensor. It is an MR list only if using GDR.
93 Returns:
94 The status of the get operation.
95 """
96 raise NotImplementedError
97
98 @abstractmethod
99 async def put(
100 self, key: K, mr: MemoryRegion | Sequence[MemoryRegion]
101 ) -> Status:
102 """Put a key value pair.
103 Args:
104 key: The key of the kv cache.
105 mr: The memory region or MR list holding the kv tensors. It is an
106 MR list only if using GDR.
107 Returns:
108 The status of the put operation.
109 """
110 raise NotImplementedError
111
112 def register_slabs(self, slabs: List[torch.Tensor]) -> Status:
113 """Register slabs with backend-specific register function.
114 Args:
115 slabs: slabs to be registered.
116 Returns:
117 Status of the register operation.
118 """
119 raise NotImplementedError
120
121 def get_batches(
122 self,
123 keys: Sequence[Any],
124 mrs: Sequence[MemoryRegion | Sequence[MemoryRegion]],
125 batch_size: int,
126 ) -> Sequence[Sequence[Tuple[K, MemoryRegion | Sequence[MemoryRegion]]]]:
127 """Get a list of key MR batches that is used for mput and mget
128 operations.
129
130 Args:
131 keys: The keys of the kv tensors.
132 mrs: Memory regions or lists of MRs holding the kv tensors.
133 batch_size: The maximum number of key MR pairs in a batch.
134 Returns:
135 List of key MR/MR List batches.
136 """
137 raise NotImplementedError
138
139 async def mget(
140 self,
141 keys: Sequence[K],
142 mrs: Sequence[MemoryRegion | Sequence[MemoryRegion]],
143 ) -> Sequence[Status]:
144 """MGet a list of values. This function is optional and only connectors
145 have mput_mget feature enabled can implement this function.
146 Args:
147 keys: The keys of the kv tensors.
148 mrs: Memory regions or lists of MRs to hold the fetched kv
149 tensors. It is an MR list only if using GDR.
150 Returns:
151 List of statuses.
152 """
153 raise NotImplementedError
154
155 async def mput(
156 self,
157 keys: Sequence[K],
158 mrs: Sequence[MemoryRegion | Sequence[MemoryRegion]],
159 ) -> Sequence[Status]:
160 """MPut a list of key value pairs. This function is optional and only
161 connectors have mput_mget feature enabled can implement this function.
162 Args:
163 keys: The keys of the kv tensors.
164 mrs: Memory regions or lists of MRs holding the kv tensors. It is
165 an MR list only if using GDR.
166 Returns:
167 List of statuses.
168 """
169 raise NotImplementedError
170
171 @abstractmethod
172 async def delete(self, key: K) -> Status:
173 """Delete a key.
174 Args:
175 key: The key of the kv cache.
176 Returns:
177 The status of the delete operation.
178 """
179 raise NotImplementedError
请参考现有连接器了解更多详情。
环境变量参考#
本节描述了 AIBrix KVCache 卸载框架所有可用的环境变量。
核心配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_CHUNK_SIZE |
“512” |
操作的块大小。 |
AIBRIX_KV_CACHE_OL_BLOCK_SIZE |
“-1” |
kvcache 块中的 token 数量(最细的 IO 粒度)。默认为 -1,表示使用引擎的块大小。 |
AIBRIX_KV_CACHE_OL_MAX_SEQ_LEN |
“-1” |
最大序列长度。默认为 -1,表示没有限制。如果设置,超过此长度的 token 将被忽略。 |
AIBRIX_KV_CACHE_OL_TIME_MEASUREMENT_ENABLED |
“1” |
启用时间测量。 |
AIBRIX_KV_CACHE_OL_BREAKDOWN_MEASUREMENT_ENABLED |
“1” |
启用分解测量。 |
AIBRIX_KV_CACHE_OL_DOUBLE_GET_THRESHOLD |
“4,0.1” |
控制何时向 L2 缓存发出第二次获取请求。第一个值是最小缺失块数,第二个是比例阈值。 |
AIBRIX_KV_CACHE_OL_TOKEN_VALIDATION_ENABLED |
“0” |
是否验证 L2 缓存中的 token。禁用则使用更紧凑的内存布局。 |
AIBRIX_KV_CACHE_OL_TRANSPORT_RDMA_ADDR_RANGE |
“::/0” |
有效的 GID 范围(CIDR 格式)。类似于 NVSHMEM 的 NVSHMEM_IB_ADDR_RANGE。 |
AIBRIX_KV_CACHE_OL_PROFILING_ENABLED |
“0” |
启用性能分析。 |
AIBRIX_KV_CACHE_OL_PROFILING_SERVER_ADDRESS |
性能分析服务器地址。性能分析服务器负责收集性能分析数据并在 Web UI 中显示。 |
L1 缓存配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_L1_CACHE_ENABLED |
“1” |
启用 L1 缓存。 |
AIBRIX_KV_CACHE_OL_L1_CACHE_EVICTION_POLICY |
“S3FIFO” |
L1 缓存的逐出策略(“S3FIFO”、“LRU”或“FIFO”) |
AIBRIX_KV_CACHE_OL_L1_CACHE_CAPACITY_GB |
“10” |
L1 缓存容量(GB)。 |
AIBRIX_KV_CACHE_OL_DEVICE |
“cpu” |
用于缓存操作的设备(“cpu”或“cuda”) |
AIBRIX_KV_CACHE_OL_S3FIFO_SMALL_TO_MAIN_PROMO_THRESHOLD |
“1” |
S3FIFO 逐出策略:从小队列到主队列的提升阈值。 |
AIBRIX_KV_CACHE_OL_S3FIFO_SMALL_FIFO_CAPACITY_RATIO |
0.3 |
S3FIFO 逐出策略:小 FIFO 的容量比。 |
L2 缓存配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_L2_CACHE_BACKEND |
“” |
L2 缓存的后端。 |
AIBRIX_KV_CACHE_OL_L2_CACHE_NAMESPACE |
“aibrix” |
L2 缓存的命名空间。 |
AIBRIX_KV_CACHE_OL_L2_CACHE_OP_BATCH |
“32” |
操作批次大小。 |
AIBRIX_KV_CACHE_OL_L2_CACHE_PER_TOKEN_TIMEOUT_MS |
“20” |
每 token 超时时间(毫秒)。 |
AIBRIX_KV_CACHE_OL_L2_CACHE_KEY_BUILDER |
“ROLLING_HASH” |
L2 缓存的键生成器(“RAW”、“ROLLING_HASH”或“SIMPLE_HASH”) |
AIBRIX_KV_CACHE_OL_L2_CACHE_INGESTION_TYPE |
“HOT” |
摄取类型(“ALL”、“HOT”或“EVICTED”)。 |
AIBRIX_KV_CACHE_OL_L2_CACHE_INGESTION_MAX_INFLIGHT_TOKENS |
“0” |
最大未完成写入数量(0 表示同步)。 |
AIBRIX_KV_CACHE_OL_L2_CACHE_NUM_ASYNC_WORKERS |
“8” |
异步工作器数量。 |
AIBRIX_KV_CACHE_OL_L2_CACHE_PLACEMENT_POLICY |
“SIMPLE” |
放置策略(仅在使用元数据服务时适用)。 |
元数据服务配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_META_SERVICE_BACKEND |
“” |
元数据服务的后端。如果未设置元数据服务后端,L2 缓存后端将使用直接模式访问给定的缓存服务器。否则,我们将从元数据服务获取成员信息并构建 L2 缓存集群。 |
AIBRIX_KV_CACHE_OL_META_SERVICE_REFRESH_INTERVAL_S |
“30” |
刷新间隔(秒)。 |
AIBRIX_KV_CACHE_OL_META_SERVICE_URL |
“” |
元数据服务的 URL。 |
AIBRIX_KV_CACHE_OL_META_SERVICE_CLUSTER_META_KEY |
“” |
集群元数据键。 |
连接器配置#
InfiniStore 连接器配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_INFINISTORE_HOST_ADDR |
“127.0.0.1” |
主机地址。 |
AIBRIX_KV_CACHE_OL_INFINISTORE_SERVICE_PORT |
“12345” |
服务端口。 |
AIBRIX_KV_CACHE_OL_INFINISTORE_CONNECTION_TYPE |
“RDMA” |
连接类型。 |
AIBRIX_KV_CACHE_OL_INFINISTORE_IB_PORT |
“1” |
IB 端口。 |
AIBRIX_KV_CACHE_OL_INFINISTORE_LINK_TYPE |
“Ethernet” |
链路类型。 |
AIBRIX_KV_CACHE_OL_INFINISTORE_VISIBLE_DEV_LIST |
“” |
可见设备列表。自 0.2.42 版本起,InfiniStore 支持客户端配置中的 RDMA GID 索引,用户可以按此格式指定每个设备的 GID 索引:“mlx5_0:gid0,mlx5_1:gid1,mlx5_2:gid2” |
HPKV 连接器配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_HPKV_REMOTE_ADDR |
“127.0.0.1” |
远程地址。 |
AIBRIX_KV_CACHE_OL_HPKV_REMOTE_PORT |
“12346” |
远程端口。 |
AIBRIX_KV_CACHE_OL_HPKV_LOCAL_ADDR |
“127.0.0.1” |
本地地址。 |
AIBRIX_KV_CACHE_OL_HPKV_LOCAL_PORT |
“12345” |
本地端口。 |
PrisKV 连接器配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_PRISKV_REMOTE_ADDR |
“127.0.0.1” |
远程地址。 |
AIBRIX_KV_CACHE_OL_PRISKV_REMOTE_PORT |
“6379” |
远程端口。 |
AIBRIX_KV_CACHE_OL_PRISKV_USE_MPUT_MGET |
“0” |
启用 MPUT/MGET。 |
AIBRIX_KV_CACHE_OL_PRISKV_PASSWORD |
“” |
密码。 |
EIC 连接器配置#
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_EIC_CONFIG_FILE |
“” |
EIC 配置文件。 |
Mock 连接器配置#
Mock 连接器用于测试和性能分析。
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_MOCK_USE_RDMA |
“0” |
在模拟连接器中使用 RDMA。 |
AIBRIX_KV_CACHE_OL_MOCK_USE_MPUT_MGET |
“0” |
在模拟连接器中使用 MPUT/MGET。 |
AIBRIX_KV_CACHE_OL_MOCK_USE_NOOP |
“0” |
所有操作都使用 NOOP。有助于分析框架开销。 |
RocksDB 连接器配置#
RocksDB 连接器用于测试目的。
变量 |
默认 |
描述 |
|---|---|---|
AIBRIX_KV_CACHE_OL_ROCKSDB_ROOT |
“~/.kv_cache_ol/rocksdb” |
RocksDB 的根目录。 |
AIBRIX_KV_CACHE_OL_ROCKSDB_TTL_S |
“600” |
TTL(秒)。 |
AIBRIX_KV_CACHE_OL_ROCKSDB_WRITE_BUFFER_SIZE |
“67108864” |
写入缓冲区大小。默认为 64MB。 |
AIBRIX_KV_CACHE_OL_ROCKSDB_TARGET_FILE_SIZE_BASE |
“67108864” |
目标文件大小基数。默认为 64MB。 |
AIBRIX_KV_CACHE_OL_ROCKSDB_MAX_WRITE_BUFFER_NUMBER |
“3” |
最大写入缓冲区数量。 |
AIBRIX_KV_CACHE_OL_ROCKSDB_MAX_TOTAL_WAL_SIZE |
“134217728” |
最大 WAL 总大小。默认为 128MB。 |
AIBRIX_KV_CACHE_OL_ROCKSDB_MAX_BACKGROUND_JOBS |
“8” |
最大后台作业数量。 |