跳转到内容
Yuuki学习手记
GitHub

缓存为什么也有成本

学习入口 · 上一篇 key 与 Map · 下一篇 KV 为什么这么大

缓存通过保留结果,减少以后重复执行的工作。它同时增加了查找、维护和存储。先把这笔交换讲清楚,才能理解模型为什么用内存换计算。

保存引用和复制内容分别发生了什么

Section titled “保存引用和复制内容分别发生了什么”
const message = { id: 1, text: '一段很长的回答' };
const byId = new Map([[message.id, message]]);
console.log(byId.get(1) === message); // true

这个索引保存同一个消息对象的引用,文字没有因为这行代码而被深复制。Map 仍需保存索引结构与引用;只要它继续强引用这个对象,该对象就仍可能被保留。新增解析树、图片像素或数值数组,则会增加新的结果数据。

不要用普通 JavaScript 对象的条目数直接推算精确堆内存;运行时还要处理对象布局、字符串表示和分配。数值缓冲区可以先检查明确的有效数据大小:

const vector = new Float32Array(128);
console.log(vector.byteLength); // 512

Float32Array 每个元素 4 字节;128 个元素的缓冲区有效数据是 512 字节。这仍未包含视图对象等运行时开销。MDN Float32Array

把下面这些量放进同一张账里:

缓存路径耗时 ≈ 查找与校验 + 未命中时的计算 + 写入和淘汰
保留空间 ≈ 结果数据 + 索引和元数据 + 因引用而仍被保留的对象

这是一种分析方法,具体数值要测量。如果计算本来很便宜,或者输入几乎每次都变化,维护缓存可能比重新计算更麻烦。若同一份昂贵结果反复使用,收益就更值得观察。

问题机制例子
有没有保存过兼容结果命中判断输入和计算版本是否对应
保存的结果还正确吗失效规则原文、字号或模型配置改变
正确但暂时不用的结果还要留多少淘汰策略容量上限、最近使用顺序或到期时间

有缓存键仍可能遗漏依赖;有 TTL 仍可能在到期前读到旧数据。需要先明确结果对什么输入有效,再决定保留多久。

  • 重复计算。 每次增量都重新解析整段历史。
  • 数据保留。 会话已经关闭,外部 Map 仍持有解析结果或大对象。
  • 同步维护。 数组更新了,按 ID 建立的索引还指向旧版本。
  • 数据搬运。 计算放进 Worker 后,每次却复制整份历史数据。

前两项连接到前缀增量计算,最后一项连接到Worker 与内存。

模型权重决定怎样计算,某段输入的 K/V 是这些计算产生的中间状态。为了继续生成,推理引擎保留历史状态;保存的对象从前端的一棵解析树,变成按层与位置组织的数值向量。

接下来用同样的方法问:一个位置有多少数据,保存了多少位置,多少条序列同时存在?沿为什么 KV 会这么大把每个乘数算出来。