跳到主要内容
索引放在哪张内存上,决定这个方法到底值不值钱

索引放在哪张内存上,决定这个方法到底值不值钱

画唠
画唠

· 阅读约 5 分钟

百万 token 的会话,KV cache 早就不在显存里了。这不新鲜。

新鲜的是很少有人往下追一步:搬出去之后,瓶颈换成了什么。

画张图:

   GPU 显存                    主机内存
  ┌──────────┐              ┌───────────────────────┐
  │ 当前层的  │◄──── PCIe ───│  KV cache(百万 token)│
  │ 计算      │              │  + 排序索引            │
  └──────────┘              └───────────────────────┘

解码每一步的 top-k,不是"在显存里挑几个最大值"那么轻。键在主机那头,排序索引也在主机那头。你要挑,得先扫;要扫,读的是字节,不是 FLOPs。计算单元闲着,链路在冒烟。

这个前提我接受得很快,因为它顺手解释了一个我一直没绕明白的现象:长上下文的推理优化,越来越不像在讲"怎么算得更少",越来越像在讲"怎么少搬点东西"。token 稀疏、层稀疏、KV 量化——名字五花八门,干的是同一件事,给那条链路减压。

九月中旬挂上 arXiv 的 Fathom 就在这一堆里。v1 是 9 月 15 日,v3 是 9 月 18 日,三天里改到第三版,作者手速挺猛。

但它挑的角度跟我以为的不一样。这也是我第一版画错的地方。

我先入为主,以为它是"通道稀疏":每个查询挑一部分通道读,剩下的不管。

不是。

我回去把它的存储布局画了一遍,才发现自己看错了维度。

4-bit 的 K 缓存,按通道主序存成位平面。这句话我第一次读的时候直接滑过去了——"位平面"听着像个实现细节,其实它整篇的立命之本就在这三个字上。

  某个通道 c,四个位平面挨着放:
    plane3   plane2   plane1   plane0
      │        │        │        │
      ▼        ▼        ▼        ▼
    [ 1 ]    [ 0 ]    [ 1 ]    [ 1 ]   ← 键 1
    [ 0 ]    [ 1 ]    [ 1 ]    [ 0 ]   ← 键 2
    [ 1 ]    [ 1 ]    [ 0 ]    [ 0 ]   ← 键 3

通道主序的意思是,同一个通道下所有键的比特挨着排。于是"读 t 个平面的前缀",读出来的正好是这个通道的 t 比特量化器:1 位是符号,2 位粗一点,4 位最精细。前 t 个平面拼起来,本身就是一个合法的、只是更粗的量化器,不需要额外解码器去凑。

等等等等,先别急。

这意味着读出精度不是个全有全无的开关,是个能逐级往下拧的旋钮。✨

一旦它是旋钮,整个问题的形状就变了。

那我第一版错在哪?我以为它在"读哪些通道"上做稀疏,实际上通道它一个不少,它抠的是"每个通道读多深"。题目里那个 read depth 就是这个意思。扭的是旋钮,不是开关。

(位平面这套我第一次见是在图像压缩里。扯远了——但这里确实是一回事:把精度拆成可以分级读的层,要几分精度读几层。拉回来。)

那"读多深"谁定?每个查询自己算。反向注水法,按通道的方差加权分比特预算:方差大的通道多给几位,方差小的少给。逐查询走一遍。

打个比方:像一本按条目排的字典,每一条你自己决定读到第几层细节——生僻条目读满四层,常见条目读一层就够。这比喻抓的是"深度逐条可调"这个方向。

漏风的地方得标出来。我说"每个查询自己决定",这话有点飘。真实情况是预算由方差算出来的,是个确定性的分配算法——不是学出来的策略,更不是查询读懂了你在问什么然后灵机一动。所以"自己决定"这四个字,在"怎么决定"这一层是漏的。拿它抓大方向可以,别拿它当它懂你的证据。

说跑出来的数。Qwen3-8B,百万 token,跟 Double Sparsity、Loki、SparQ r=32 那条 136-bit 扫描比,GPU 时间上快 1.67 倍。

我觉得更能说明问题的是另一条:同样 GPU 时间下,跟 SparQ 的 68-bit 读(r=16)比,Fathom 少读 18% 的字节,七种模型加上下文设置里六种注意力误差更低。等时间的对照比等精度的对照诚实得多。RULER 风格任务上,它逐 token 的扫描跟精确 top-k 解码结果一致;真实编码 agent 会话里,92 bit 就打到了 136-bit 最精确扫描的步骤一致率。存储也没多要东西——用的就是量化服务栈里本来就有的那份 4-bit K 副本。

好,现在说这篇里我最想标出来的一条,也是最诚实的一条:

索引驻留在 GPU 内存时,Fathom 没有速度提升。

干脆,一行,不解释。但这一行比前面所有数字都更有信息量。它说明 Fathom 优化的那段读流量,只在键和索引都躺在主机内存那头的时候才成为瓶颈。索引一旦回到显存,扫描本来就不贵,你再怎么抠读取深度,省的是不存在的时间。

所以它不是一个"更好"的方法,是个"位置敏感"的方法。全部价值押在一个前提上:你的 KV cache 是被 offload 出去的。前提不成立,它归零。

我挺欣赏这种把适用边界直接写进结论的做法。比那些在哪儿都说自己快的方案舒服多了。

画开完了,照例自检:现在你能不能一句话说清 Fathom 在哪一维上做稀疏?如果脱口而出是"选通道",那还没扣上,回去再看一眼那张位平面的图。

我目前的判断是:这一轮长上下文的优化,重心正在从"让模型算得少"搬到"让数据搬得少",而后者比前者更看你把东西摆在哪。这话我说得有点满,但先这么信着。

下期想画开哪个?我候选清单上躺着两个:KV cache 的淘汰策略到底在淘汰什么,还有 paged attention 的分页到底在分什么。

本期画开:稀疏的维度搞错了,优化就跑偏了。

画唠
画唠

把被讲玄的概念用图 + 比喻 + 动手实验拆到咔哒扣明白,错的也保留。

查看主页 →