推理服务商向你隐瞒了什么:KV 缓存、批处理与延迟底线
你正在运行一个由 LLM 驱动的应用,你的 p99 延迟为 4 秒。你已经优化了提示词,减少了输出长度,并切换到了流式传输。但这个数字几乎没变。问题不在于你的代码——而是在你无法控制的黑盒内部运作的物理学和排队论。
每个推理服务商在你的第一次 API 调用之前,就已经通过数十项架构决策决定了你应用的性能上限。KV 缓存淘汰策略、连续批处理(continuous batching)调度、分块预填充(chunked prefill)块大小——文档中没有提到这些,你也无法配置,但它们决定了你不得不面对的延迟和成本曲线。
这篇文章将解释推理基础设施内部究竟发生了什么,为什么它会产生不可避免的延迟底线,以及你真正能做的少数几件事。
什么是 KV 缓存,以及为什么它在消耗你的预算
当 Transformer 模型生成下一个 token 时,它会对上下文中的每个先前 token 计算注意力(attention)。如果没有缓存,在 10,000 个 token 的提示词基础上生成 2,000 个 token 的回复,将需要为这 2,000 个解码步骤中的每一个重新计算这 10,000 个 token 的注意力——这种二次方级的计算膨胀会使 LLM 实际上无法使用。
KV 缓存通过在处理每个 token 后存储注意力层的键(key)和值(value)张量来解决这个问题。在随后的解码步骤中,模型从缓存中读取,而不是重新计算。这就是为什么生成一旦开始就会很快:每个新 token 只需要计算自身与缓存序列之间的注意力。
难点在于内存。一个具有 128K 上下文的 70B 参数模型的单次缓存序列大约消耗 40GB 的高带宽显存(HBM)。HBM 是 GPU 上最快且最有限的内存——它不仅稀缺,而且由所有并发请求共享。当缓存填满时,必须淘汰(evict)一些内容。淘汰什么以及何时淘汰,决定了你的下一个请求是从热缓存还是冷缓存开始。
服务商如何决定淘汰什么是事情变得有趣的地方。最基础的是 LRU(最近最少使用)算法——淘汰最近最少访问的内容。但生产系统会在其上叠加优先级分数、估计的重用概率和尾部延迟优化。关于尾部优化淘汰策略的研究表明,与标准 LRU 相比,SLO 违规减少了高达 38.9%。你服务商使用的算法直接影响你的 p95 和 p99 指标。
前缀缓存:你可能没有得到的 90% 成本削减
KV 缓存重用的产品化版本被称为前缀缓存(prefix caching)或提示词缓存(prompt caching)。服务商不再仅限于在单个请求内进行缓存,而是可以跨多个请求缓存共享前缀的 KV 状态。如果你的应用在每次 API 调用时都发送相同的 5,000 token 系统提示词,智能服务商只需处理一次这些 token——后续共享该前缀的请求将重用已缓存的状态。
经济效益是巨大的。在 Anthropic 的 API 上,缓存读取的成本为每百万 token 0.30 美元,而全新的输入处理成本为每百万 token 3.00 美元——相当于 90% 的折扣。OpenAI 为超过 1,024 token 的提示词提供自动缓存,可节省 50% 的成本。一个具有稳定系统提示词和每个会话文档上下文的聊天应用可以缓存 70% 以上的输入 token,从而在一夜之间将成本降低一半以上。
生产数据证实了这一点。Google Vertex AI 报告称,通过将缓存感知作为首要约束的调度优化,首 token 时间(TTFT)减少了 40%,p50 延迟提高了 43%。Anthropic 的客户报告称,在发生缓存命中时,长提示词的延迟减少了 85%。
但前缀缓存有一个大多数开发者在不知情的情况下会违反的结构要求:前缀必须从提示词的开头开始,字节级完全一致。 序列早期的任何变化都会破坏后续所有内容的缓存。这就是为什么在系统提示词的开头放置时间戳、用户 ID 或会话令牌,可能会让你在无声无息中比结构良好的提示词多付出 10 到 50 倍的成本。
