ZipFusion: Fusing Lossless KV Decompression with Attention for Efficient LLM Decoding

Yuetao Chen, Peng Wang, Hong Xu · arXiv 2610.33719 · 发表 2026-10-24
11 分钟阅读时长 4301字 4.25综合分 第 5 名10-28 Infra 日报
目录 · 9 节1它到底在解决什么问题?2整体流程3压缩格式:只动指数,不动尾数4在寄存器里还原:一次加法还原四个数5显存管理:省下的空间要真的用起来6实验结果7我认为它的局限8一个推理服务团队可以怎么试9一句话结论
相关 4新颖 4实用 5证据 4时效 4

无损压缩 KV cache 以前主要用来省传输:搬之前压缩,搬到显卡上再解压成原样给注意力读。ZipFusion 的判断是,解压这一步把好处吃掉了一大半,所以干脆让注意力直接读压缩后的数据,在寄存器里用几条整数位运算还原出原值。数据格式和注意力算子一起设计,再把省下的显存交给调度器多收请求。在 H100 上,注意力算子比 FlashInfer 快约 1.25 倍;8K 上下文下,同样显存里整机解码吞吐比不压缩的 SGLang 高 26.3%,是此前最好的无损压缩方案的 2.76 倍,而且结果逐位一致。

做大模型推理服务、尤其是对精度零容忍的团队值得读,原因是它说明了一件事:无损压缩不只能省带宽,也能直接让解码变快,前提是不要另起一步解压。核心思想一句话:只压缩指数位、保留尾数原样,让还原便宜到能塞进注意力算子里。不过它的压缩上限只有 25%(一块最多缩到原来的 75%),只在一张 H100、两个 8B 模型、8K 固定提示词上测过,更适合当一个可叠加的优化。

它到底在解决什么问题?

先看为什么"先解压再算"不划算(示意场景,数字取自论文图 1 的测量条件)。用 Llama 3.1 8B、每批 32 条、每条 2048 个输入 token 跑解码,把三种现成的无损压缩方案接进 SGLang,每一步都先把一层的缓存解压到缓冲区,再交给注意力算子。

示意场景每步解压耗时其余耗时解压占比
三种现成无损方案5.3–30.8 毫秒约 10.9 毫秒32.7%–73.8%
不压缩0约 10.9 毫秒0
ZipFusion(目标)并入注意力少读字节不再单独存在

这说明解压不是一笔小账:最好的情况也占解码时间的三分之一。更麻烦的是,解压完注意力读的仍是原尺寸数据,每一步重复读取的字节一点没少,还要额外占一块显存放解压结果。而 KV cache 写一次、之后每一步解码都要读,所以如果能一直保持压缩状态去读,一次压缩就能在后面很多步里反复受益。

作者选无损而不是量化,是因为已有研究发现,在长上下文检索和问答任务上,多数模型对 KV cache 量化比对权重量化更敏感。对要求结果与原模型完全一致的部署,无损是唯一选择。

整体流程

论文图 2:压缩、注意力和显存分配三部分。(a) 满足压缩条件的块缩到原来的 75%,不满足的保持原样;(b) 压缩块从显存经片上共享内存一路保持压缩,直到寄存器里才还原成 BF16;(c) 每个方框是一块可以放多页的显存,第 0 层空出来的一块被重新分给第 1 层。
论文图 2:压缩、注意力和显存分配三部分。(a) 满足压缩条件的块缩到原来的 75%,不满足的保持原样;(b) 压缩块从显存经片上共享内存一路保持压缩,直到寄存器里才还原成 BF16;(c) 每个方框是一块可以放多页的显存,第 0 层空出来的一块被重新分给第 1 层。

系统分三部分,从下往上看:

  1. 压缩格式。 KV cache 按页组织,每页 16 个 token。页写满后,按每 512 个数一块独立压缩;满足条件的块缩到 75%,不满足的原样保留。还没写满的页不压缩,新生成的 token 可以直接追加。
  2. 注意力算子。 算子从显存把块搬进片上共享内存,搬的过程中保持压缩;每个线程取出自己负责的 8 个数,在寄存器里还原后马上送进矩阵乘法单元,结果不写回显存。
  3. 显存管理。 压缩后空出的显存块可以在层与层之间重新分配;分配器实时算出"还能放多少新页",交给调度器决定能多收几个请求,同时给正在生成的请求留出增长空间。

压缩格式:只动指数,不动尾数

为什么只压指数?BF16(一种 16 位浮点格式)每个数由 1 位符号、8 位指数、7 位尾数组成。同一块里的数大小往往差不多,所以指数集中在一个小范围里,有压缩空间;尾数则接近随机,很难压。

具体做法是:先找出块内最小的指数,向下取到偶数,作为基准 b。如果块内所有指数都落在 b 到 b+15 之间,这块就压缩,否则不压。压缩时,每个数的低 8 位原样保留,这 8 位正好包含 7 位尾数和指数的最低位。剩下要存的只有符号位和指数偏移的高 3 位(记作 d,等于偏移除以 2 取整),凑成 4 位编码。这样每个数从 16 位变成 8+4=12 位,正好是 75%。

用一个算例走一遍。某块的指数范围是 120 到 133,基准取 b=120。块里有一个数指数是 127,偏移是 7,二进制 0111:最低位 1 已经在保留的低 8 位里,只需额外存高 3 位 011,也就是 3。这里 d=3。还原时把 d 乘 2 加上 b,再加上低 8 位里那个最低位 1,得到 120+6+1=127,和原值完全一样。整个过程只有整数加法和移位,不涉及浮点运算,所以不会有舍入误差。

为了让注意力算子找到每块数据,每页还要存一点元数据,开销很小。以一页 16 个 token、头维度 128 为例,每个头的键和值各切成 4 块;4 个键块合用 8 字节,4 个值块另用 8 字节。这 8 字节里,一半记第一块的起始位置,另一半每块占 1 字节,要么标记"未压缩",要么记下基准的一半 b/2。所以算子能知道每块是原尺寸还是 75%,把前面各块大小累加就能定位下一块。

在寄存器里还原:一次加法还原四个数

论文图 3:无损编码与寄存器内还原。(a) 跟踪一个线程负责的 8 个数中的两个,看它们的符号和指数偏移如何打包进同一个字节;(b) 线程把 4 个编码字节当成一个 32 位整数,分两路处理:一路用掩码取出前 4 个数的编码,另一路移位取出后 4 个数的编码,各自加上一个每字节都是 b/2 的常数,一次加法就还原出 4 个数的高字节,再和保留的低字节拼回 BF16。
论文图 3:无损编码与寄存器内还原。(a) 跟踪一个线程负责的 8 个数中的两个,看它们的符号和指数偏移如何打包进同一个字节;(b) 线程把 4 个编码字节当成一个 32 位整数,分两路处理:一路用掩码取出前 4 个数的编码,另一路移位取出后 4 个数的编码,各自加上一个每字节都是 b/2 的常数,一次加法就还原出 4 个数的高字节,再和保留的低字节拼回 BF16。

还原必须足够便宜,否则省下的读取时间会被计算抵掉。作者的办法是让数据排布直接迎合硬件。GPU 的矩阵乘法指令要求每个线程手里拿着特定的 8 个元素,于是写缓存时就把同一线程要用的 8 个元素放在一起;键和值在两次矩阵乘法里角色不同,所以排布也各自设计。

每个线程读 8 个低字节和 4 个编码字节。关键观察是:BF16 的高字节等于"符号位 ×128 + 指数的高 7 位",而指数的高 7 位正好等于 b/2 + d。所以只要把符号放在第 7 位、偏移 d 放在第 0–2 位,再加上 b/2,就直接得到高字节。4 位编码因此两两打包:一个字节里,第 1 个数的符号放第 7 位、偏移放第 0–2 位;第 5 个数的符号放第 6 位、偏移放第 3–5 位。线程把 4 个编码字节当成一个 32 位整数,一路用掩码只保留每字节的第 7 位和第 0–2 位,得到前 4 个数的编码;另一路用移位和掩码把中间 4 位挪到同样的位置,得到后 4 个数的编码。两路各加上一个每字节都是 b/2 的常数,一次加法还原 4 个高字节。以论文的例子说明:编码字节是 0xD0,b/2=63,第 1 个数符号为 1、偏移 0,高字节是 128+63+0=0xBF;第 5 个数符号为 1、偏移 2,高字节是 128+63+2=0xC1。因为 b/2+d 不超过 127,加法不会进位到符号位或相邻字节。8 个数只需两次加法,加上几条拼接指令,就能直接送进矩阵乘法单元。

显存管理:省下的空间要真的用起来

压缩省出的显存如果不交给调度器,就只是闲着。难点在于这块空间是动态的:新页写满后被压缩会释放空间,正在生成的请求又会不断申请新页。

作者的处理有三点。第一,新页总是按未压缩大小申请,因为它得先能接受写入,写满才压缩。第二,压缩后的页可能搬到新位置,用一张查找表把页编号映射到当前位置,页编号本身不变。第三,每层有自己的私有空间,另有一个共享池;某层的一块空间完全空出来、且没有正在进行的读取时,就还给共享池,可以分给别的层。分配器据此算出所有层都还能容纳多少新页,调度器按这个数字收新请求。

举个例子说明为什么要跨层共享:一个新请求的每一页,在每一层都要占一份空间,所以能收多少请求取决于最紧张的那一层。如果第 0 层因为数据好压省出了一大块,第 1 层却几乎压不动,没有共享池的话,第 0 层省下的空间就只能闲着;有了共享池,这块空间可以挪给第 1 层,整体能放下的页数才真正增加。 Hoot 说

Hoot 说两层显存,各自 10 格:有没有共享池,能收多少新页收起 ✕
能收多少新页由最紧张的那一层决定:第 0 层压缩得好、空出一大块,没有共享池就只能闲着;有了共享池,它空出来的块能分给第 1 层,同样的显存多收一半的页。
没有共享池:收 10 页
第 0 层 2 格旧页、4 格新页、4 格空闲且被虚线框出;第 1 层 6 格旧页、4 格新页,已满第 0 层第 1 层
  • 闲着的 40 单位
有共享池:收 15 页
第 0 层 2 格旧页、6 格新页、2 格分给第 1 层;第 1 层 6 格旧页、4 格新页第 0 层第 1 层
  • 已压缩的旧页
  • 本层新页(按 4 单位申请)
  • 经共享池分给第 1 层的新页
  • 空闲
  1. 没有共享池:第 0 层空 100 − 20 = 80,第 1 层空 100 − 60 = 40。每个新页每层都要 4,最多 min(80, 40) ÷ 4 = 10 页,第 0 层剩 40 闲着。
  2. 有共享池:两层一共空 80 + 40 = 120,每个新页两层合计 8,能收 120 ÷ 8 = 15 页;第 1 层要 15 × 4 = 60,比私有的 40 多出的 20 来自第 0 层还给共享池的空块(一块要整块空出、且没有正在进行的读取,才能还回去)。
  3. 如果按压缩后大小申请:第 0 层会以为每页只要 1,能收 80 页;可新页写满前不能压缩,80 页同时写入要 80 × 4 = 320 单位,比 80 单位空闲多出 240,根本放不下。这就是第一条“按未压缩大小申请”的原因。

全部数字是示意:每格 10 个单位,每层私有 100 单位;已有 20 页,第 0 层压缩后每页 1 单位、第 1 层 3 单位;新页一律按未压缩大小 4 单位申请。 出处: 论文 §5:新页按未压缩大小申请;压缩页可搬迁,查找表映射页编号到当前位置;每层私有空间 + 共享池,整块空出且无读取时还给共享池;分配器算出所有层还能容纳的新页数交给调度器

实验结果

测试在一张 80 GB 显存的 H100 上进行,模型是 Llama 3.1 8B 和 Qwen3-8B,每条请求 8192 个输入 token、生成 256 个 token,所有方法在相同显存预算下比较,并逐位验证还原结果。

指标对照ZipFusion说明
注意力延迟(8K,每批 64,Llama)FlashInfer 704.6 微秒562.7 微秒快约 1.25 倍,已含还原
吞吐(每批 32,Llama)不压缩 SGLang每秒 2044 个 token高 15.3%
同批次对比最快的无损方案SplitZip2.51 倍省掉独立解压
最大批次(Llama)46 条57 条省下的显存多放请求
最大批次下吞吐不压缩 SGLang每秒 2453 个 token高 26.3%

这说明收益来自两处叠加。同样批次下,和不压缩的 SGLang 比,注意力每步少读了字节,所以快了约 15%;和其他无损方案比,又省掉了单独解压那一步,所以差距拉到 2.5 倍以上。显存省下来后能多放约 24% 的请求,两者合起来在最大批次下提升 26.3%。Qwen3-8B 上的数字几乎一样(批次从 40 增到 50,吞吐高 25.8%),说明效果不依赖某个特定模型。

与另一些方案对比也有启发:一项把权重解压和矩阵乘法融合的工作曾报告,在 A100 和 H800 上还原开销会抵消带宽节省;ZipFusion 在 H100 上取得了净收益,说明还原指令设计得够轻。

我认为它的局限

  1. 压缩上限低。 一块最多缩到 75%,指数分布散的块还压不了,因此单靠它省不了太多显存;最大批次只从 46 增到 57,就是这个上限的体现。它更像是可以和其他优化叠加的一层,而不是主力压缩手段。
  2. 测试条件窄。 只有一张 H100、两个 8B 模型、BF16 格式,输入是固定的 8192 token 文章,而且提示词部分是预先压好的,计时也不含预填充和首次压缩。换成更长上下文、多卡部署或真实请求分布,收益可能不同。
  3. 生成阶段的压缩没测。 设计上页写满就能压缩,但实验里所有方法新生成的缓存都保持不压缩,只有预先压好的 8192 token 提示词部分是压缩的。对输出很长的推理型请求,生成部分会占缓存的大头,在线压缩的额外开销和收益都还需要自己测。

一个推理服务团队可以怎么试

  1. 先测自家缓存的指数分布。 抽样导出线上模型的 KV cache,按 512 个数一块统计有多少块的指数跨度不超过 16,这决定了实际能省多少。
  2. 在单卡上复现算子收益。 用固定上下文长度和批次跑注意力延迟对比,确认在自己的显卡型号上还原开销确实小于读取节省。
  3. 接入调度看并发。 让分配器把压缩省出的空间报给调度器,测同样显存下的最大批次和吞吐,并检查长输出请求下的收益衰减。
  4. 与量化方案并行评估。 如果业务能接受轻微精度损失,同时测一组量化方案,看两者是否可以叠加,再决定上线组合。

一句话结论

维度评价
问题价值中高,对精度零容忍的推理服务很实用
方法新意中,格式与算子联合设计,位运算还原很巧
证据强度中,测量细致但只有单卡、两个 8B 模型
落地难度中,要改注意力算子和分配器,代码基于主流框架
适合谁运行 SGLang 等框架、受显存和带宽双重限制的团队

ZipFusion 证明了无损压缩也能让解码变快,关键是别单独解压;上限只有 25%,适合作为叠加在其他优化之上的一层。

读完这篇,你觉得

点一下即可,无需登录;再点一次撤回。只统计匿名次数,不记录你是谁。

讨论

欢迎补充实验细节、复现经验或不同看法。需 GitHub 登录。

正在加载讨论…