桃子桃子快讯
返回首页
工具

EdgeSync-LLM:面向端侧 LLM 推理的 KV 缓存分片引擎

一个引擎无关的 KV 缓存分片系统,通过 HNSW 语义检索复用注意力张量,将 Android 端 LLM 首 toke…

2026.06.30 · 周二6 分钟阅读

EdgeSync-LLM 是一个引擎无关的 KV 缓存分片系统,专为 ARM64 Android 设备(Cortex-A55/A78)上的端侧大模型推理设计,可移植到任何运行 llama.cpp、MLC-LLM 或 ONNX Runtime 的平台。该项目由个人开发者发布在 Hacker News,附带完整开源实现与基准测试脚本。

项目定位与核心思路

EdgeSync-LLM 并非传统意义上的「语义缓存」——后者通常只缓存响应文本字符串。该系统在应用与 LLM 引擎之间引入一层可复用的 KV 缓存层,将注意力机制中的 Key 与 Value 张量按 token 区间和层区间切分为「片段」进行持久化。下一次请求到达时,系统先由 MiniLM-L6-v2(384 维,CPU 编码约 8 ms)生成嵌入向量,经 HNSW 索引(纯 Go 实现,M=16、efSearch=50)检索出语义相近的历史片段,再将其直接注入引擎的 KV 缓存,从而跳过推理中最耗时的 prefill 阶段。

三级路由架构

根据语义相似度阈值,请求被分为三类处理:

  • 精确命中(sim ≥ 0.92):直接注入 KV 片段,首 token 时间(TTFT)约 8 ms
  • 部分命中(0.75 ≤ sim < 0.92):注入前缀并由 LLM 生成 delta,TTFT 约 280 ms
  • 完全未命中(sim < 0.75):执行完整 prefill,并将新片段写入 HNSW 索引

整体数据流为:Prompt → 嵌入模型 → HNSW 检索 → 三路路由 → KVAdapter 层 → llamacpp / mlc-llm / onnx runtime。

KVFragment 数据结构

缓存的原子单元 KVFragment 在 cache/fragment.go 中正式定义,核心字段包括:

  • TokenStart / TokenEnd:覆盖的 token 区间 [start, end)
  • LayerStart / LayerEnd / LayerStride:Transformer 层采样范围与步长
  • Keys / Values:原始注意力张量(引擎序列化格式)
  • TokenIDs:用于前缀一致性校验
  • ContentHash:基于 TokenIDs 的 SHA-256 摘要
  • EmbeddingVector:384 维语义向量,供 HNSW 查询
  • ExpiresAt / HitCount:TTL 与自动晋升机制(命中 ≥ 5 次晋升为长期片段,TTL 由 30 分钟延长至 7 天)

构造时强制校验以下不变量:Token 跨度 ∈ [64, 2048]、LayerEnd ≤ 模型总层数、TokenIDs 长度等于跨度、张量非空、LayerStride ≥ 1。

跨引擎适配层

KVAdapter 接口定义了 6 个方法(ExtractFragment / InjectFragment / Generate / Tokenize / ClearKVCache / Close),三个引擎各自实现:

  • llamacpp:基于 GGML tensor API,通过 CGO 调用
  • mlc-llm:基于 TVM 分页 KV,使用 mlc4j
  • onnx runtime:基于 past_key_values

跨引擎复用受 CompatibleWith() 矩阵约束。最新版已加入 adapter/reshape.go,通过张量维度转置([seq, heads, dim] ↔ [heads, seq, dim])实现 llama.cpp 与 ONNX Runtime 之间的片段复用,并由 CanInjectWithReshape() 自动检测与回退。

基准测试结果

基准测试 benchmark/runner.go 在 Snapdragon 685(Cortex-A55)上以 8 个语义提示簇、64 个唯一 prompt 各 4 种变体、共 1000 次请求进行评估,关键参数源自实测而非随机假设:

  • 无缓存基线:TTFT 约 1800 ms,内存带宽 100%,能耗 253 mAh
  • 朴素字符串缓存:TTFT 约 1600 ms,命中率约 12%,内存带宽约 88%,能耗 222 mAh
  • EdgeSync-LLM 片段缓存:TTFT 约 350 ms,命中率约 70%,内存带宽约 35%,能耗 88 mAh

底层常数:prefill 6.8 ms/token、生成 18.4 ms/token、HNSW 搜索 3.2 ms、片段注入 0.029 ms/MB、单片段约 6 MB(128 token、12 层、Q4_K_M 量化)。

构建与当前实现

主机端可直接 go run ./benchmark/;Android ARM64 构建需启用 CGO 并链接 llama.cpp;NEON fp16 余弦相似度模块由 aarch64-linux-gnu-gcc -O3 -march=armv8.2-a+fp16 编译。项目还实现了片段去重与层配置合并(cache/compactor.go,按 ContentHash 去重并按轴 0 或按头轴 1 拼接张量)、N-gram 预取预测器(top-3 候选)以及 Android /sys/class/power_supply/ 电源监测探针(monitor/energy_android.go)。

信源