Agent 工程实践 / 03 · 共 12 篇

Agent 工程实践

KV Cache:计算复用与历史读取的成本

推理计算 / 缓存复用与历史读取

KV Cache 保留历史位置的键和值,让新位置少做重复投影;当前查询仍需读取可见历史,完成注意力计算。

缓存复用与历史读取,是两项不同的工作。这里用单层、单头的二维示意区分它们,不把示意计数换算成真实加速倍数。

查看交互演示 ↓

机制分析

生成一步,包含两类不同的工作

先处理四个已知位置 A—D。每个位置产生相应的键和值;最后位置 D 的查询 Q 与可见的 K 匹配,经过缩放点积、softmax 和对 V 的加权,得到用于后续预测的表示。本页把生成结果标为 E,强调处理位置与预测下一个位置之间的顺序。

当 E 成为下一次输入位置,因果约束使先前位置不需要因为它而重新计算自己的 K/V。保留缓存时,A—D 的四组可以直接复用,只新增 E 的一组;不保留缓存的对照则重新计算 A—E 的五组。

本例计数保留缓存不保留缓存
先处理 A—D计算4组 K/V计算4组 K/V
再处理 E新增1组重新计算5组
累计 K/V 投影组数5组9组
E 的查询读取可见的5组 K/V可见的5组 K/V

5与9只统计这个示意层的 K/V 计算组数。模型还包含其他投影、注意力操作和网络层,真实服务还有调度与数据传输,因此不能据此声称延迟下降到5/9。缓存滚筒表达保存与追加,实际数据位于推理引擎管理的内存中。

缓存也不是模型的长期记忆

K/V 是一次推理过程中的中间状态,模型参数没有因此变化。它与把用户偏好保存到外部数据库不是同一种持久化,也不能因为页面保留了几组向量,就认为下一个独立请求会自动复用这些状态。

工程取舍

少做重算之后,瓶颈可能转移

计算节省与内存占用怎样权衡?

保存更长的可见历史,需要更多 K/V 存储;同时服务多个序列时,这些状态会竞争内存容量。即使每步只新增少量位置,读取历史的工作也仍存在。是否值得保留、能同时接纳多少请求,需要结合上下文长度、模型结构与设备资源测量,不能只看缓存命中数量。

减少历史,是缓存优化还是上下文策略?

直接丢弃缓存中的旧位置,会改变可用的注意力范围,不能当成对语义完全透明的内存回收。滑动窗口或局部注意力可以限制读取范围,但那是相应模型结构和运行策略的条件;本例展示的是全历史因果注意力,不能外推为所有架构的统一行为。

同一次生成的复用,与跨请求复用有什么区别?

本页只讨论一次自回归生成内的缓存。跨请求前缀复用还要判断前缀及运行条件是否兼容,并由服务实现管理生命周期与隔离。应用日志里出现相同文字,不足以证明底层复用了同一份缓存;相关收益需要在对应服务与配置下单独验证。

落到实现

把性能问题拆成可测量的问题

比较方案时,应固定模型、输入和输出条件,分别观察首次处理输入的时间、逐步生成的耗时以及缓存内存占用。真实请求的吞吐、等待和尾延迟还受批处理与并发影响,单条示意计算不能代替服务测量。

页面实际计算二维投影后的注意力运算,A—E 代表位置,不代表一个汉字就是一个 token;E/F 用于说明生成顺序,没有运行完整语言模型。各层通常各自维护相应的缓存,图中只展示一层的一头。

缓存让旧结果可以复用,却没有免除使用这些结果的工作。理解这一区别,才能解释为什么少了重算,长上下文仍然可能很贵。

交互演示 · KV Cache 计算装置

用同一输入对照缓存与重算,展开向量计算
待机 · 任务等待操作
00:00
模型推理引擎内部Harness 提交输入;这里计算、缓存和读取 K/V。
一次回复内的缓存复用新计算复用读取
已就位的 K/V0 组
累计 K/V 计算0 组
当前查询读过尚未读取
下一 token尚未生成

操作

从四个已知 token 开始,观察第一次计算怎样建立缓存。

查看组件职责、向量计算与适用边界

K 是供查询匹配的键,V 是按匹配权重提取的值;Q 是当前处理位置的查询。这里展示单层、单头的因果注意力,各层实际有自己的 K/V。

当前查询与实际计算

本例的二维投影结果

这是本例的计算数据,当前缓存是否已保存,以主图槽位为准。

A—E 表示 token 位置,不代表一个汉字就是一个 token。二维投影、缩放点积、softmax 和加权和在页面内计算;E/F 仅演示生成顺序,未运行完整语言模型。缓存滚筒是机制比喻,实际 K/V 存储在推理引擎的内存中。

本页讨论一次自回归生成内的 KV Cache,不讨论跨 API 请求的前缀缓存。计算组数不等于整个模型的计算量、实际延迟或费用;模型参数全程不变。

登录后收藏评论 0

登录后发表评论

暂无评论。