LMCache ValkeyConnector 基准测试深度解析:单键存储、GLIDE 同步客户端与 TLS 集群模式的性能实测

发布时间:2026/9/16 20:16:00
LMCache ValkeyConnector 基准测试深度解析:单键存储、GLIDE 同步客户端与 TLS 集群模式的性能实测 LMCache ValkeyConnector 基准测试深度解析单键存储、GLIDE 同步客户端与 TLS 集群模式的性能实测【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache导读本文基于 examples/kv_cache_reuse/remote_backends/valkey/VALKEY_CONNECTOR_BENCHMARKING.md完整还原 LMCache 新一代ValkeyConnector基于 valkey-glide 同步客户端相对旧版RedisClusterConnector的性能基准测试包含 70B/8B 模型、8k–64k 上下文、Provisioned 与 ServerlessTLS两种集群形态的完整 TTFT 对比矩阵以及一套可复现的 零前缀重叠 flood 提示词 keyspace_hits 校验 的 L2 基准测试方法论。读者将掌握ValkeyConnector 的三大设计改动零拷贝大值处理、TLS、单配置切换集群/单机模式、如何科学测量 L2 缓存命中时的 TTFT、以及配套的 LMCache 配置与 vLLM 启动参数。一、背景ValkeyConnector 的三项关键改动ValkeyConnector是 LMCache 面向 ValkeyRedis 的 BSD 开源分支的远程 KV Cache 存储连接器其底层客户端为valkey-glide。该连接器在加入集群模式、TLS 支持与优化的大值处理能力后与RedisClusterConnector在同一批 Valkey 集群上完成了对照基准测试。驱动本次测试的三个关键改动如下GLIDE 同步客户端 优化的大值处理——利用两个上游 GLIDE 贡献点减少大块 KV Cache 传输时的内存拷贝一是通过bytearray/memoryview参数实现零拷贝 SET二是通过 buffer GET 将数据直接读入预分配内存。这两项改动消除了多 MB 级 chunk 传输时的中间分配开销。TLS 支持——允许连接启用了 TLS 的集群包括强制要求 TLS 的 ElastiCache Serverless。这是RedisClusterConnector无法做到的且实测在 64k 上下文下 TLS 仅带来约 7–8% 的额外开销。集群与单机双模式——通过一个valkey_mode配置项即可在GlideClusterClient从种子节点自动发现拓扑与GlideClient单节点可选database_id之间切换。关于 零拷贝 需要准确理解这是 glide 官方语境下的术语实际是copy-reduced减拷贝而非完全免拷贝——GET 时值仍会在 glide-core 内部物化后再写入调用方缓冲区SET 时载荷仍会进入命令缓冲区。但相比 async 客户端因 Protobuf IPC 层导致的每次bytes复制同步路径已经消除了中间分配。glide 上游明确文档化zero-copy is sync-only。源码层面的印证新连接器的实现在 valkey_connector.py 中其模块文档说明了几个核心设计决策glide 客户端生命周期与工作线程池统一收敛在共享的ValkeyWorkerPool类worker_pool.py非 MP 模式的ValkeyConnector与 MP 模式的ValkeyL2Adapter复用同一套零拷贝 GET/SET/EXISTS/DELETE 实现通过memoryview直接访问 pinned CPU 内存——线程共享父进程地址空间无需共享内存 arena 或跨进程拷贝单键存储类似RESPConnector相比旧版 2 键metadata kv_bytes拆分将每次 chunk 的 Valkey 往返次数减半通过AsyncPQExecutor实现优先级调度PEEK PREFETCH GET PUT见 valkey_connector.py保证延迟敏感的查询不被批量写阻塞与RESPConnector的优先级方案一致。依赖方面该连接器需要valkey-glide-sync包 2.3.0提供的glide_sync模块普通的valkey-glide包仅含 async 客户端、不满足要求。安装命令为pip install valkey-glide-sync2.3.0若未安装会在 worker 启动时报出明确的引导错误Valkey support requires the glide_sync module. Install: pip install valkey-glide-sync2.3.0 (note: the plain valkey-glide package is async-only)二、硬件与软件测试环境运行环境组件详情实例p4de.24xlarge— 8× A100-SXM4-80GB96 vCPUs1.1 TB RAM模型meta-llama/Llama-3.1-70B-Instructbf16TP8与Llama-3.1-8B-Instructbf16TP1vLLM0.17.0配合LMCacheConnectorV1LMCache0.1.dev1240valkey-glide由valkey-io/valkey-glidemain 分支构建哈希算法sha256_cbor_64bitTP1 时必须——Python 的hash()在 vLLM 的 subprocess 边界上不可确定集群后端两组测试使用完全相同的 Valkey 集群后端——两个连接器面向同一套基础设施类型节点TLS实例类型ElastiCache Provisioned10 个 primary否cache.r7g.16xlargeElastiCache Serverless自动扩缩容是托管被测连接器连接器客户端库存储格式每 chunk 的 GET 数ValkeyConnectorGLIDE 同步Rust FFI单键原始字节1RedisClusterConnectorredis-pyasync2 键metadata kv_bytes2这里 每 chunk 的 GET 数 的差异正是后面keyspace_hits校验与性能差异的核心单键存储意味着一个 chunk 只需要一次 GET 往返而 2 键存储需要两次。Chunk 大小KV Cache 的 chunk 字节数取决于模型架构。chunk_size 默认按 token 计如 256 tokens模型层数chunk_size (tokens)Chunk 字节计算公式70B (TP8)80256~10 MB2 × 80 × 256 × 1 head × 128 dim × 2 bytes (bf16)8B (TP1)32256~4 MB2 × 32 × 256 × 8 heads × 128 dim × 2 bytes (bf16)注意两个 chunk 字节数公式中的 head 数不同是因为 TP8 时每 rank 只持有 1 个 head 的 KV而 TP1 时单进程持有全部 8 个 head。这正是 KV Cache 张量并行切分的结果。三、TTFT 测量方法TTFTTime To First Token从客户端侧端到端测量通过curl请求 vLLM 的/v1/completions接口START$(date %s%N) curl -s -X POST http://localhost:8000/v1/completions \ -H Content-Type: application/json \ -d prompt_64k_70b.json /dev/null END$(date %s%N) echo TTFT: $(( (END-START)/1000000 ))ms该测量捕获了完整请求延迟包括网络、tokenization、KV Cache 检索或计算以及首个 token 生成。测试报告三种 TTFT 变体Cold TTFT— 任何地方都没有缓存数据。vLLM 从零计算 KV Cache然后存入 L1CPU L2Valkey。这是基线。L1 TTFT— 数据在 CPU pinned 内存中约 5 GB/s per rank。这不是本次测试的目标仅作参考。L2 TTFT— 数据已从 L1 逐出从 Valkey 检索。这是目标指标。Speedup Cold TTFT / L2 TTFT。每 rank 吞吐量vLLM 会为每个请求记录 per-rank 检索统计Retrieved 65024 out of 65024 required tokens (from 65024 total tokens). size: 2.4805 gb, cost 2558.0752 ms, throughput: 0.9697 GB/s这是 LMCache 内部测量——从batched_get开始到所有 chunk 接收完毕的时间按 TP rank 统计。聚合吞吐量 per-rank × rank 数。从源码看batched_get的实现位于 valkey_connector.py它会先通过local_cpu_backend.allocate为每个 key 预分配固定大小的MemoryObj再并行向线程池提交submit_get_into最后用asyncio.gather汇总结果。这正是 直接读入预分配内存 的代码路径——每个memoryview缓冲区在请求到达前就已就位。四、为什么 L2 基准测试很难做LMCache 有 L1CPU与 L2远端两级缓存。命中时优先查 L1。要测量 L2 的性能必须先强制 L1 miss——即在检索请求发出前把测试数据从 L1 逐出。前缀哈希问题LMCache 使用滚动前缀哈希生成 chunk 键chunk_hash[i] hash(chunk_hash[i-1], tokens[i*256 : (i1)*256])如果两个 prompt 共享 token 前缀那么该前缀内的所有 chunk 会产生完全相同的哈希。LRU 会把这些条目视为同一项而永远不会逐出。也就是说发送那些恰好与测试 prompt 共享前缀的 不同 prompt根本无法逐出测试数据——这是 L2 基准测试中最隐蔽的坑。解法零重叠 Flood Prompts仓库提供了 benchmark_l2.py该脚本使用不同随机种子生成完全不相交的随机文本作为 flood prompts从第一个 chunk 起就保证每个 chunk 哈希唯一python3 benchmark_l2.py generate \ --model meta-llama/Llama-3.1-70B-Instruct \ --context-tokens 65024 \ --num-floods 3 \ --output-dir /home/ubuntu/bench_prompts脚本会验证零重叠Flood 1: 65024 tokens Token prefix overlap with test: 0 ( chunk_size256 → OK, different chunk hashes)从源码看benchmark_l2.py 的_random_text为每个种子使用独立的词汇空间3–10 个随机小写字母组成的单词 空格并通过seed42测试与seed1000 i*1000flood拉开距离生成后还会逐 token 计算与测试 prompt 的前缀重叠长度若第一个 chunk前 256 tokens完全一致则打印 WARNING 提示更换种子。L1 容量设置max_local_cpu_size必须足够大以容纳检索缓冲区否则报No eviction candidates found in local cpu backend但又必须足够小使 floods 能逐出测试数据模型上下文每 rank 数据量推荐 L1需要的 floods70B TP88k320 MB1 GiB370B TP864k2.48 GB5 GiB38B TP18k128 MB1 GiB38B TP164k1.02 GB10 GiB5注意 8B 64k 场景推荐 10 GiB L1 与 5 个 floods是因为该场景下每 rank 数据量1.02 GB较大、而单进程 L1 又需要留足检索缓冲区空间因此需要更多 flood 才能完成逐出。五、基准测试工作流每次基准测试严格遵循以下序列重启 vLLM— 清空 L1CPU pinned 内存是进程级的FLUSHALL— 清空所有 Valkey 集群节点Cold 请求— 发送测试 promptvLLM 从头计算 KV Cache存入 L1 L2Flood L1— 发送 3–5 个不相交的 prompts通过 LRU 填满 L1 并逐出测试数据记录keyspace_hits所有集群 primaryL2 请求之前L2 请求— 再次发送同一测试 promptL1 miss 强制从 Valkey 进行 L2 检索记录keyspace_hits所有集群 primaryL2 请求之后验证—keyspace_hits差值确认发生了真实的集群 GETL2 验证双重校验每条 L2 结果都通过两种独立方法验证方法一keyspace_hits差值Provisioned 集群for node in $NODES; do redis-cli -h $node INFO stats | grep keyspace_hits doneValkeyConnector期望差值 chunks × ranks每 chunk 1 次 GETRedisClusterConnector期望差值 chunks × ranks × 1.5每 chunk 2 次 GET差值为 0 意味着 L1 命中——测试无效方法二vLLM 日志Retrieved 65024 out of 65024 required tokens ... throughput: 0.95 GB/sRetrieved 亚 1 GB/s 吞吐量 L2 命中Retrieved 约 5 GB/s 吞吐量 L1 命中不是 L2 测试只有 Stored 未发生检索benchmark_l2.py的run子命令benchmark_l2.py将上述步骤自动化依次执行 cold 请求、发送所有 flood、记录前后keyspace_hits并计算差值最后输出明确的裁决——差值 0 则输出 ✓ L2 RETRIEVAL CONFIRMED 及 Speedup否则输出 ✗ L2 RETRIEVAL NOT DETECTED并列出可能原因max_local_cpu_size过大、flood 数量不足、flood 与测试 prompt 共享前缀。六、测试结果70B 模型完整对比矩阵TP810 MB chunks连接器后端TLS上下文Cold TTFTL2 TTFTSpeedupPer-rank聚合ValkeyConnector32 workersProvisioned否64k15,555ms3,216ms4.8×0.89–0.98 GB/s~7.5 GB/sValkeyConnector32 workersServerless是64k15,987ms3,425ms4.7×0.85–0.89 GB/s~6.9 GB/sValkeyConnector32 workersProvisioned否8k2,224ms505ms4.4×——ValkeyConnector32 workersServerless是8k2,274ms656ms3.5×——RedisClusterConnectorProvisioned否64k15,612ms5,794ms2.7×0.47–0.52 GB/s~4.0 GB/sRedisClusterConnectorProvisioned否8k2,361ms796ms3.0×——70B 模型 — 4 MB chunkschunk_size96连接器后端TLS上下文L2 TTFTSpeedupPer-rank聚合ValkeyConnector32 workersProvisioned否64k3,644ms4.5×0.87–0.93 GB/s~7.1 GB/sValkeyConnector32 workersServerless是64k3,884ms4.3×0.87–0.93 GB/s~6.9 GB/sValkeyConnector64 workersProvisioned否64k4,134ms3.9×0.74–0.92 GB/s~6.6 GB/sRedisClusterConnectorProvisioned否64k6,392ms2.3×0.46–0.58 GB/s~4.1 GB/s8B 模型TP1Provisioned4 MB chunks连接器上下文Cold TTFTL2 TTFTSpeedupkeyspace_hits ΔValkeyConnector32 workers8k803ms421ms1.9×64ValkeyConnector32 workers64k11,487ms2,527ms4.5×508RedisClusterConnector8k1,789ms1,859ms1.0×96RedisClusterConnector64k13,189ms15,600ms0.8×❌762汇总在所有配置下ValkeyConnector的 L2 检索速度相比RedisClusterConnector快1.6–1.8×在 64k 上下文下相对冷计算最多获得4.8× 加速3.2s vs 15.6s。七、性能差异分析为什么 ValkeyConnector 比 RedisClusterConnector 快每个 chunk 1 次 GET vs 2 次 GET。ValkeyConnector 将每个 chunk 存为单键RedisClusterConnector 拆成metadatakv_bytes两个键需要两次往返。keyspace_hits证实了这一点8B 64k 场景下 ValkeyConnector 为 508、RedisClusterConnector 为 762——恰好是 1.5× 的 GET 数差。32 个并行 worker 线程 独立客户端。每个 worker 线程拥有自己的 GLIDE 客户端和连接池。Rust FFI 调用期间 GIL 被释放可实现真正的并行 I/O。RedisClusterConnector 使用redis-py的 async 客户端并受 asyncio semaphore 约束。零拷贝 buffer GET。GLIDE 通过buffermemoryview直接写入 pinned CPU 内存避免中间分配 拷贝。RedisClusterConnector 从redis-py收到 bytes 后再拷贝进 memory 对象。集群原生的槽位路由。GLIDE 的 cluster 客户端对所有集群节点维持持久连接内部按槽位路由命令无需客户端自行计算哈希槽或处理重定向。源码佐证worker 线程池的并发模型见 worker_pool.py——ValkeyWorkerPool维护一个ThreadPoolExecutor每个 worker 通过threading.local()懒构建并缓存自己的GlideClientstandalone或GlideClusterClientcluster池只讲str键与字节缓冲区不感知任何 LMCache 类型因此连接器与 L2 adapter 可以共享。零拷贝 GET 的具体实现是_do_get_intoworker_pool.py它把目标缓冲区 cast 成无符号字节B格式的 memoryviewglide 的 buffer 协议拒绝非字节格式视图写入线程私有 scratch 缓冲区后在同一 GIL 保护下拷入目标内存并严格校验读取字节数必须等于缓冲区长度——任何短读、截断或超长值都被当作 miss 拒绝GET_MISS -1杜绝脏尾字节流入上层。buffer GET 能力通过buffer in inspect.signature(client.get).parameters探测一次并缓存。TLS 开销上下文Provisioned无 TLSServerlessTLS开销64k3,216ms3,425ms6.5%8k505ms656ms30%64k 时 TLS 开销可忽略因为数据传输占主导8k 时固定的 TLS 握手/加密成本在较小的传输中占比更大。综合两组数据文档将其总结为 TLS 在 64k 上下文下约 7–8% 的开销。Chunk 大小的影响Chunk 大小L2 TTFTProvisioned64kSpeedup10 MBchunk_size2563,216ms4.8×4 MBchunk_size963,644ms4.5×10 MB chunks 只比 4 MB 快13%。更少的 chunk 意味着更少的往返但在单请求开销本来就低的情况下差异有限。Worker 数量WorkersL2 TTFT4 MB chunksProvisioned64kPer-rank323,644ms0.87–0.93 GB/s644,134ms0.74–0.92 GB/s32 workers 是 70B TP8 的甜点值。64 workers 反而因线程竞争降低了吞吐量。这也与源码一致并发上限被num_workers卡住默认 8valkey_adapter.py 从extra_config读取同时兼容旧的valkey_sync_num_workers键在大型集群上线程池而非集群本身可能成为瓶颈——提高num_workers才能饱和更多节点。8B 模型RedisClusterConnector 的开销反转在 8B 64k 场景下RedisClusterConnector 的 L2 TTFT15,600ms超过了冷计算时间13,189ms意味着该模型规模下 2 键存储的额外开销完全抵消了缓存收益。keyspace_hits差值证实了原因同一份数据 762 次 GETRedisClusterConnectorvs 508 次 GETValkeyConnector——1.5× 的往返数。ValkeyConnector 的单键存储避免了这一点在相同负载下实现了 4.5× 加速。八、关键结论ValkeyConnector 比 RedisClusterConnector 快 1.6–1.8×——在所有模型与上下文长度下成立源于单键存储与并行 worker 线程。64k 下 TLS 开销为 7–8%——Serverless ElastiCache 可以用于生产。Chunk 大小的影响低于预期——10 MB 只比 4 MB 快 13%。70B TP8 下 32 workers 最优——更多线程只会增加竞争。RedisClusterConnector 的 2 键存储在较小模型上成为瓶颈——每 chunk 多出的往返数可以完全抵消缓存收益。九、使用的 LMCache 配置与启动命令ValkeyConnector — Provisioned 集群# ValkeyConnector — provisioned cluster chunk_size: 256 local_cpu: true max_local_cpu_size: 5.0 remote_url: valkey://cluster-endpoint:6379 remote_serde: naive blocking_timeout_secs: 120 pre_caching_hash_algorithm: sha256_cbor_64bit extra_config: valkey_num_workers: 32 valkey_mode: clusterValkeyConnector — Serverless TLS# ValkeyConnector — serverless TLS chunk_size: 256 local_cpu: true max_local_cpu_size: 5.0 remote_url: valkey://serverless-endpoint:6379 remote_serde: naive blocking_timeout_secs: 120 pre_caching_hash_algorithm: sha256_cbor_64bit extra_config: valkey_num_workers: 32 valkey_mode: cluster tls_enable: truevLLM 启动命令export LMCACHE_CONFIG_FILE/home/ubuntu/valkey_cluster.yaml vllm serve meta-llama/Llama-3.1-70B-Instruct \ --tensor-parallel-size 8 \ --kv-transfer-config {kv_connector:LMCacheConnectorV1,kv_role:kv_both} \ --no-enable-log-requests \ --no-enable-prefix-caching \ --gpu-memory-utilization 0.90 \ --max-model-len 65536配置项参考extra_config仓库的 Valkey 后端文档 给出了完整的extra_config键表与 valkey_adapter.py 的参数解析一一对应Key默认值说明valkey_num_workers8工作线程数每线程一个独立的 GLIDE 客户端连接valkey_modestandalonestandalone或cluster。集群模式从种子节点自动发现拓扑tls_enablefalse启用 TLS。ElastiCache Serverless 必须valkey_username认证用户名valkey_password认证密码valkey_databaseNone数据库 ID仅 standalone 模式集群模式忽略并告警valkey_enable_ttlfalse功能开关。为true时每个键写入过期时间见valkey_ttl_sec使 Valkey/Redis 的volatile-*逐出策略能在节点达到maxmemory后回收 L2 缓存键false默认则键无 TTL 永久保留valkey_ttl_sec86400键 TTL 秒数仅当valkey_enable_ttl为true时生效。必须是正整数request_timeout5.0GLIDE 请求超时秒同时用作 Python 侧 Future 超时connection_timeout10.0GLIDE 初始连接超时秒其他值得注意的约束均可在 valkey_adapter.py 中看到强制校验ValkeyConnector 采用单键、定长存储不携带逐 chunk 元数据因此save_chunk_meta必须为false、save_unfull_chunk必须为false否则连接器创建时直接抛ValueError。此外TLS 目前是 glideuse_tls的开关式支持仅适用于证书可由系统 OS 信任库验证的场景如 ElastiCache Serverless、Lets Encrypt 公网证书自签名证书、私有/内部 CA 与 mTLS 属于后续规划详见 Valkey L2 Adapter 设计文档。十、复现与进一步探索完整基准测试命令仓库在 benchmark_l2.py 中提供了端到端 L2 基准脚本依赖 vLLM LMCacheConnectorV1、transformers分词器、redis-py用于keyspace_hits校验# 生成测试 flood 提示词一次性 python3 benchmark_l2.py generate \ --model meta-llama/Llama-3.1-70B-Instruct \ --context-tokens 65536 \ --num-floods 3 \ --output-dir /home/ubuntu/bench_prompts # 运行完整基准cold → flood → L2 python3 benchmark_l2.py run \ --prompt-dir /home/ubuntu/bench_prompts \ --vllm-url http://localhost:8000 \ --valkey-nodes node1,node2,node3 \ --valkey-port 6379相关仓库资源连接器实现valkey_connector.py、valkey_adapter.py共享 worker 线程池零拷贝 GET/SET 核心worker_pool.pyMP 模式 L2 adaptervalkey_l2_adapter.py配置示例valkey.yaml非 MP 模式配置文档docs/source/kv_cache/storage_backends/valkey.rstMP 模式 L2 存储文档docs/source/mp/l2_storage/valkey.rst设计文档线程模型、槽位路由、MOVED/ASK 重定向、容量与逐出策略docs/design/v1/distributed/l2_adapters/valkey.md测试用例tests/v1/storage_backend/test_valkey_connector.py、tests/v1/distributed/test_valkey_l2_adapter.py适用前提提醒文中所有性能数字均来自仓库基准文档所记录的特定环境p4de.24xlarge、Llama 3.1 70B/8B、特定 chunk_size 与 worker 数不同硬件、模型与集群配置下的绝对数值会变化但单键 vs 2 键的往返数差异、32 workers 的甜点值、TLS 在长上下文下开销趋近于零等结构性结论在复测中具有较高的可迁移参考价值。【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询