Llama-Server 正在丢弃您完美的KV缓存,以及如何修复

Reddit r/LocalLLaMA 工具

摘要

本文详细介绍了一个关键bug:在llama-server中,由于缺少检查点元数据,恢复的KV缓存会立即被丢弃。文章提供了一个117行的修复方法,使用sidecar文件跨重启持久化检查点,实现了约720倍的预填充加速。

引言 我目睹了2.49 GB的状态在1.23秒内从磁盘恢复——然后被丢弃。llama-server的槽保存/恢复功能正是预算硬件上进行长上下文工作所需:将会话暂存到磁盘,之后无需再次支付预填充开销即可恢复。恢复本身运行完美。但在进程重启后,该功能实际上无用:恢复后的第一个查询丢弃了整个重新水化的状态,并从头开始重新预填充。原因竟然是一个仅存在于进程内存中的元数据片段。测试平台:Qwopus3.6-27B Q6_K(混合架构),2× Tesla P100 层拆分,turbo4 KV量化。 发现过程 在高上下文基准测试中,恢复在机制上看起来完美——2.49 GB在1.23秒内回到VRAM——但下一个/completion请求却记录了因缺少缓存数据而强制重新处理整个提示,并运行了完整的重新预填充(在100K上下文中约17.5分钟)。保存快、恢复快,然后全部丢弃。(那次发现运行使用的配置与验证运行略有不同;以下所有关键数据均来自补丁构建上的受控验证运行。) 根本原因 llama_state_seq_save_file 序列化了token和物理KV单元——但不包括 slot.prompt.checkpoints,即检查点元数据列表,该列表仅存在于进程内存中。冷重启后,第一个请求总是需要一个小回滚:重新将提示尾部标记化可能会改变BPE边界,并且必须至少重新处理一个token以产生logits。重用路径只能通过覆盖性检查点(即处于或早于回滚目标的检查点)进行回滚。由于检查点列表为空,服务器找不到任何内容,放弃并重新计算一切。 不起作用的修复(v1) 显而易见的修复:在恢复时从恢复状态合成一个检查点。它失败了,而且这个失败很有启发性。合成的检查点位于尖端——最终位置(100K运行中为100,042)——而回滚目标是一个更早的位置(100,034)。检查点只能服务于处于或在其自身位置之后的回滚,因此尖端检查点对于总是发生的回滚毫无用处。不变条件:你需要来自尖端之前的检查点,而这些仅在保存时存在。 有效的修复(v2:sidecar) 在 tools/server/server-context.cpp 中添加117行:在 SLOT_SAVE 时,将检查点列表持久化到版本化的 sidecar 文件(<state>.ckpt);在 SLOT_RESTORE 时重新加载它。现在回滚会落在一个真实持久化的检查点上,只有少数尾部token被重新处理。如果缺少 sidecar——例如由旧版本保存的状态文件——服务器会回退到 v1 风格的尖端合成,这仍然能覆盖完全追加的恢复,否则会优雅地降级到补丁前的行为:完整重新预填充,正确输出,无崩溃。 关键结果 指标 性能数据 100K 基线完整预填充 722.4 秒(挂钟时间)/ 719,975 毫秒 (API) (138.9 t/s) 保存 1,777 毫秒(2.56 GB 状态 + 299.3 MB sidecar) 冷重启 → 恢复 100,043 个token 耗时 1,592 毫秒 恢复后的增量查询 1,000 毫秒 (API) / 2.72 秒(挂钟时间)——在 95% 深度处召回金丝雀事实 相同构建 A/B(隐藏 sidecar)720.1 秒完整重新预填充;正确输出,无崩溃 摘要 ~720倍增量预填充 / ~167倍端到端恢复 A/B 行是关键的证据:相同的二进制文件,相同的状态文件,仅移除了 sidecar——没有它时 720.1 秒,有它时 1.0 秒。sidecar 是整个效果。实时回归测试也全面通过:实时前缀重用未受影响(除非显式调用 action=save,否则不会写入 sidecar 文件),恢复的会话持有完全缓存的多轮对话且召回正确,并且具有分叉延续的暂存-恢复仍然从 sidecar 检查点提供服务。 结论与细微差别 该修复已作为 PR 提交到 llama-cpp-turboquant 分支(commit eaf98e612,+117 行):TheTom/llama-cpp-turboquant#206。潜在 bug 也存在于上游 llama.cpp master 中——在密集模型和 SWA 模型上均验证——且尚未修复。在此混合架构上,sidecar 大小与上下文无关:每个循环状态检查点约 149.6 MiB,两个检查点 → 在 1K 和 100K 上下文中均为 299.3 MB。纯 SWA 检查点负载可能按不同方式缩放,尚未测量。另外:在混合模型上使用 -np 2 --kv-unified 时出现的双槽缓存丢弃是已存在的行为,在未打补丁的构建上重现,与此补丁无关。https://gist.github.com/apollo-mg/6defe7c0e3aba47727c758df03360b3e
查看原文

相似文章

llama.cpp 有一个加速 KV 缓存解码的巧妙技巧

Reddit r/LocalLLaMA

llama.cpp 的 webUI 中有一个设置,它会将生成的 token 重新发送到 KV 缓存,从而显著减少提示处理延迟,提高长生成或工具调用的响应速度,且没有明显的权衡。

KV Packet: 免重计算的上下文无关KV缓存用于大语言模型

Hugging Face Daily Papers

KV Packet 提出了一种免重计算的缓存复用框架,用于大语言模型。该框架使用可训练的软标记适配器来弥合上下文不连续性,消除了开销,同时在 Llama-3.1 和 Qwen2.5 上的性能与完全重计算基线相当。