~/yomxxx/posts/2026-09-17-justfit-200k-context-24gib-laptop-paper.mdx
Paper

$ cat 论文速读:JustFit 让 24GiB 笔记本扛住 200K 上下文,可用窗口扩大 6.93 倍

-- 5 min read ·

本地推理的讨论长期围绕一个数字转:模型有多大。量化到几比特、能不能塞进显存、值不值得换张卡。而 arXiv 上 9 月 15 日挂出的 JustFit,把注意力挪到了另一个数字上——执行状态有多大,并给出了一个相当激进的实测结果。

硬件是一台 24GiB 内存的 M4 Pro MacBook。模型是 Qwen3.8-27B 的 MXFP4 量化版。三次独立运行,每次都完整跑完 196608 个输入 token 与 16384 个输出 token。

单请求可用上下文容量从 mlx-vlm 基线的 30720 个位置,提升到 212992 个位置,倍率 6.93 倍。

一、把瓶颈重新定位

论文的核心判断是:在长上下文场景里,权重占用是常数,而 KV 缓存是线性增长的。

27B 模型量化到 MXFP4 之后,权重占用是确定的。但上下文推到 20 万 token 时,KV 状态本身就可能超过权重。如果并发两个请求,还要再乘一遍。于是在同一台 24GiB 的机器上,限制来自状态而不是参数。

这个视角切换带来一个直接结论:模型权重的量化方案和上下文容量的伸缩方案,应该被解耦处理。JustFit 明确声明它的机制独立于权重量化——这一点很重要,意味着你可以沿用任何已有的权重量化方案,只把状态管理这一层换掉。

它由三个机制组成:

机制负责的维度解决的问题
KVExec状态大小压缩 KV 的直接执行,规避解压峰值
PhaseSwap占用时机按执行阶段决定组件驻留与换出
StateTrans生命周期服务切换时保全状态,避免重建

二、KVExec:让压缩态直接参与计算

压缩 KV 缓存有个常被忽略的陷阱:解压本身的开销。

朴素流程是「压缩存储、取出时解压、再算注意力」。但解压会在一瞬间产生一个完整的、未压缩的 KV 张量——这个瞬时峰值可能比压缩省下来的空间还大。结果是内存账面上限没变好,只是把压力从「持续占用」变成了「峰值尖刺」。

KVExec 的做法是融合重构与计算:让压缩态直接参与运算路径,只在必要时即时物化(just-in-time materialization)所需的片段,并且同步即时释放不再需要的部分。这不是把压缩率推到极致,而是把「物化—使用—释放」这个生命周期管住。

💡 一句话理解:它不追求单个张量更小,而追求任何时刻同时存活的张量总量更小。

这个区别在长上下文下被放大:上下文越长,同一时刻理论上的存活的中间量越多,管住生命周期带来的收益就越大。

三、PhaseSwap:按阶段而不是按大小做调度

第二个机制处理的是时间维度的错峰。

推理过程不是一段均匀的旅程。预填充阶段主要消耗 KV 状态写入、注意力矩阵计算;解码阶段每个 token 只处理一行,但权重访问频繁;权重加载本身又是另一波内存需求。这些需求在时间上并不同步,但在朴素的实现里,所有组件都从头到尾保持在场。

PhaseSwap 按执行阶段划分:此刻必须有谁在场、谁可以换出。它和 KVExec 正交——KVExec 管「状态有多大」,PhaseSwap 管「什么时候真的需要它」。两层叠起来,峰值占用才被真正压下去。

论文报告的一个可验证指标是:在重复执行的 32768 加 6144 token 工作负载下,进程中位峰值占用为 16374 MiB。这个数字释放出的余量,正是上下文能继续往上推的空间。

四、StateTrans:切换时的状态保全

第三个机制解决的是服务切换。本地推理工具在长任务里经常需要换模型、换配置、或者在预填充与解码之间做运行时切换,而每次切换如果都要重建执行状态,代价就是几十秒甚至几分钟的等待,长上下文下尤其不能接受。

StateTrans 做的是保状态的切换:让状态跨切换存活,而不是丢弃重建。对交互式使用来说,这决定了「能不能在长会话中做调整」,而不只是「能不能跑起来」。

五、实测数字

论文在 13 页、4 图、9 表里给了相当完整的测量,整理如下:

硬件           24 GiB 统一内存 M4 Pro MacBook
模型           Qwen3.8-27B  (MXFP4)
─────────────────────────────────────────────
容量测试        3 次独立运行,均完成
               196,608 输入 + 16,384 输出 token
单请求上下文    30,720  (mlx-vlm 基线)
          →   212,992  (JustFit)
倍率            6.93x
双请求总量      229,376 positions
─────────────────────────────────────────────
性能探针        32K 输入 + 64 输出 → 19.11 tokens/s
峰值占用        重复 32K+6K 负载,中位 16,374 MiB
─────────────────────────────────────────────
AIME 2026       29 / 30 正确

最后一行值得单独看:AIME 2026 三十道题答对二十九道。这不是一个容量指标,而是「容量扩大之后模型还能不能正确思考」的指标。论文的表述是「支持扩展的生成式推理」——意思是长思考链不会被内存压力截断,能力的兑现没有被基础设施掐掉。

另外要注意双请求那一行:229376 个位置是总量,不是每个请求的容量。两个请求共享这个额度,这与单请求场景的账不一样。

六、该有的怀疑

第一,19.11 tokens/s 是探针数字,不是生产数字。 它对应 32K 输入加 64 输出的短解码场景。上下文真正拉到 20 万时,解码速度会显著下降,论文没有把这一档的吞吐作为卖点,读者也不应该替它假设。

第二,平台绑定。 整个运行时基于 MLX,面向 Apple Silicon 的统一内存架构。这个架构的特殊性(CPU 与 GPU 共享物理内存,无需拷贝)是这套方案能成立的前提之一。搬到独立显存的平台上,PhaseSwap 的很多设计假设会失效。

第三,容量测试是单请求口径。 高并发服务场景关注的是「多少请求共享多少 KV 预算」,而论文的 6.93 倍是单请求容量。把它当作并发容量的提升会严重误判。

第四,基线选择的敏感性。 6.93 倍是与 mlx-vlm 基线对比得到的。换一个不同的基线实现,倍率会变。看这类论文时,倍率永远要连着基线一起读。

结语

JustFit 的价值主要在提问方式上。它把「本地能跑多长的上下文」从一个关于显存的硬件问题,改写成了一个关于执行状态生命周期管理的系统问题。

这个改写带来的实际好处是:你不需要换更贵的硬件,也不需要接受更激进的权重量化损失,而是通过让「物化、使用、释放」这个循环更紧凑来换容量。

对于做本地长文档处理、离线代码库审查、或者单纯想在笔记本上跑完整推理链的人来说,这是一个值得跟进的工程方向——只要记住 6.93 倍是单请求容量口径,而 19.11 tokens/s 是短探针口径,两者不能混着用。

Frequently asked questions

为什么长上下文的关键瓶颈不是权重而是执行状态?
权重是固定大小,量化后可以精确算出占用;而 KV 缓存随上下文长度线性增长,还会随并发请求数成倍叠加。在同一台机器上,27B 模型的 MXFP4 权重占用是常数,但 20 万 token 的 KV 状态可以轻松超过权重本身。所以真正决定你能开多长窗口的,是执行状态能不能被有效压缩与换出。
压缩 KV 之后不是还要解压吗,这不白费吗?
这正是 KVExec 要解决的问题。朴素做法是先解压再算注意力,解压产生的中间张量峰值可能比省下的空间还大。KVExec 的做法是把重构与计算融合在一起,直接以压缩态参与运算、在需要时才即时物化,并同步释放不再需要的部分。所以它换来的是峰值内存下降,而不是把压力从存储挪到计算峰值上。
PhaseSwap 的组件驻留是什么意思?
推理过程中的各个组件对内存的需求在时间上并不同步:预填充阶段吃 KV 状态,解码阶段吃注意力中间量,权重加载又是另一波。PhaseSwap 按阶段决定哪些组件此刻必须在场、哪些可以换出,把「同时在场」的集合压到最小。它和 KV 压缩是正交的两层,一个管状态大小,一个管占用时机。
6.93 倍这个数字应该怎么理解?
它对比的是 mlx-vlm 基线在同样硬件上能完成的单请求上下文:30720 个位置提升到 212992 个位置。要注意这是「完整跑完」的容量测试,不是理论最大值;论文中还有一次双请求运行在总量上保留了 229376 个位置。另外这个倍率是在特定模型与量化格式下测得的,换模型后需要重新验证。
这套方案适合什么场景,不适合什么?
适合需要在单机、离线或隐私敏感环境下处理超长文本的场景,比如整库代码审查、长文档推理、以及需要长时间思考链的题目求解。不适合高并发在线服务:它优化的是单请求容量,论文的性能测试也只有 19.11 tokens 每秒这个量级。另外它绑定 MLX 与 Apple Silicon,换平台无法直接复用。
// next.txt ›

Some outbound links in this post are affiliate links — see disclosure.