深度解读 📅 2026-10-26 ✍️ MagicNetWorld 编辑部

19.5 GB 的模型装进 24 GB 的机器,为什么论文实测反而快了五倍:Edge0 把 MoE 专家权重放回 SSD,用「预测即路由」抢出一个 token 的时间

论文实测(arXiv:2609.18063,Mac mini M4 Pro 24 GB,单流解码、短上下文)报告了一组反直觉的数字:一个 35B 级 MoE(基座 Qwen3.6-35B-A3B,发布档路由宽度 K=4)的 4-bit 权重文件是 19.5 GB——这是磁盘上的 int4 checkpoint,不是内存占用;把这 19.5 GB 全驻留内存时解码 3.9 tok/s、全驻留权重内存 18.2 GiB;而 Edge0 让专家权重留在 SSD 上按需流入,解码 20.4 tok/s,峰值活跃内存(MLX 分配器峰值,不等于进程 RSS)2.9 GiB。它用的不是纯搬运:预测路由与恢复适配器把模型的计算路径也换掉了。代价是每步都要等磁盘,它靠「预测即路由」抢出一个 token——部分层设置预测头,用第 N 层在 token t 的隐藏态预测第 N+1 层在 token t+1 的专家集合。本文把参数账、时间账、量化账和它没解决的三件事逐项算清:论文实测值可追溯,本文推导值列出假设与中间值。

MoE推理优化显存占用SSD 流式加载路由预测端侧推理

除另行标注外,本文引用的外部事实与页面核验均以 2026-10-26(北京时间)为时点。论文里的实验数字一律标「论文实测」,本文自己按公式算出来的数字一律标「本文自行推导」并写清假设与中间值。本文没有做端到端实测:所有 tok/s 与耗时都是论文报告值或按其报告的常量复算的结果,不是我在本机跑出来的。论文实测值只能追溯、不能由文内公式复算;凡标「本文自行推导」的才是可照着按一遍的。

一、先把三个数字摆在一起

2026 年 9 月 16 日挂到 arXiv 的这篇论文(arXiv:2609.18063 v1,AutoArk,五位作者,CC BY 4.0;v2 于 2026-09-17 更新)只有一件事要说,但它把这件事说得非常具体。一台机器、两种方案、三组关键指标:

  • 一台 24 GB 的 Mac mini M4 Pro,跑一个 35B 级的 MoE(基座 Qwen3.6-35B-A3B,论文发布档路由宽度 K=4K{=}4)。权重文件本身 19.5 GB——这是磁盘上的 int4 checkpoint,不是内存占用。
  • 用最直白的办法——全部权重常驻内存(现成的 mlx-lm 服务)——解码 3.9 tok/s,全驻留权重内存 18.2 GiB。
  • 用论文的引擎 Edge0——权重留在 SSD 上按需流入——解码 20.4 tok/s,峰值活跃内存 2.9 GiB(MLX 分配器峰值,不等于进程 RSS,见 §十二)。

同一台机器、同一套解码协议、同一份基座 int4 权重,后者快了约 5.2 倍——但注意基座权重相同,不代表计算路径相同:Edge0 还换了路由(预测即路由)并加了一个恢复适配器(见 §六、§九),这两件事本身会掉质量,所以「快」不能理解成免费。(本文自行推导:20.4/3.9≈5.2320.4 / 3.9 \approx 5.23),同时把内存占用压到前者的六分之一左右(2.9/18.2≈0.162.9 / 18.2 \approx 0.16)。

这件事的反直觉之处在于:变快的那一版,每一步都要去读硬盘。 常驻内存的版本一个字节都不用读盘,却被「装不下」这件事拖住了——18.2 GiB 不可回收的权重放在 24 GB 的机器上,KV 缓存和操作系统几乎无处安放,系统只能不停换页。于是问题从「怎么算得快」变成了「怎么让权重不住在内存里,还不在读盘上卡死」。

先把单位钉死,不然后面每个数都会打架:GB 是 10 亿字节、GiB 是 2 的 30 次方字节,19.5 GB 约合 18.2 GiB。本文里 19.5 GB 一律指「磁盘上的 checkpoint 文件大小」,18.2 GiB 指「全部权重常驻内存时的占用」,2.9 GiB 指「Edge0 的峰值活跃内存」——三个不同对象、三次不同测量。

这篇论文把全部力气花在第二个问题上,而它的答案不是工程技巧,是一个可训练的小模型:用预测出来的路由,直接当路由用。

二、术语与符号表(先把记号约定好)

先统一符号,后文所有公式都用这套。

符号含义在这篇论文里的取值
LL解码器层数35B 档 40 层,8B 档 24 层
EE每层专家数35B 档 256,8B 档 128
KK每 token 每层选中的专家数(路由宽度)基座 K=8K{=}8,发布档 35B 用 K=4K{=}4,8B 用 K=8K{=}8
bexpb_{\text{exp}}单个专家在 int4 下的字节数35B 档 1.77 MB
BlayerB_{\text{layer}}单层全部专家的字节数453 MB
BtokB_{\text{tok}}单个 token 必须就位的专家字节数本文自行推导,见 §三
hh页缓存命中率论文未单独报告,本文只按「1 − 冷页占比」间接引用,不当作独立测量
coldcold 的分母与采集方法:论文按「一次加载里有多少字节是冷页」统计,不是按加载次数;该统计定义未独立核实按需臂 17%–20%,预取臂 84%–98%
tloadt_{\text{load}}单次加载的耗时论文拟合 1.17+1.33×cold1.17 + 1.33 \times \text{cold} 毫秒
预测头 / prerouter用上一层在上一 token 的隐藏态预测下一层本轮路由的小网络35B 档 33 个,8B 档 16 个
学生路径 / student path解码时按「预测出的路由」而不是原路由器走一遍前向部署形态
教师 / teacher原始 fp16 基座,只用来蒸馏与打分不参与部署
recovery LoRA修复 int4 量化 + 路由替换两处损失的适配器秩 16、α=32\alpha{=}32、42 MB
冷读 cold该次加载读到的页不在页缓存里,必须等 SSD论文按每次加载统计

表里几个高负担的系统词,先就地给一句话释义(不然后面要回论文找定义):臂(arm) = A/B 实验里的一条对照分支,本文的「按需臂」与「预取臂」是同一 session 里交替跑的两条;flush = 触发一次待求值计算(MLX 是惰性求值,图建好不等于算过);sticky-slot = 常驻槽位的张量原地更新,配合 incr_stack 只重写变化的那几个专家;feature drift = 预测头输入里「上一层实际选了哪些专家」这一项,在训练时来自真路由器、在解码时来自预测本身,两边分布不同;尾部项 = 蒸馏时把 top-kk 之外的 token 概率质量用一个附加项承载,而不是直接丢掉。

三个容易混的口径先分清,不然后面会一直打架:

  1. 19.5 GB 是磁盘上的 int4 checkpoint;18.2 GiB 是全驻留时的内存占用;2.9 GiB 是流式方案的峰值活跃内存。这三个数指的不是同一件事,前两个单位也不同(GB 与 GiB)。
  2. 稀疏性省的是算力,不是字节。 论文原文说得很直:一个 35B MoE 每 token 大约激活 3B 参数,这缩小的是「要算多少」,不是「要存多少」。
  3. 磁盘读得少不等于不需要那些字节。 论文实测里每次解码步只从磁盘读了几十 MiB,而每步「必须就位」的专家字节是几百 MiB 量级——差的那部分是页缓存兜住的(§五会把这笔账算清)。

三、第一本账:稀疏为什么救不了内存

把一个 40 层、每层 256 个专家的 MoE 拆开看。int4 量化(affine、group size 64)之后,单个专家的三个投影(gate、up、down)连同分组缩放一起是 1.77 MB。于是单层的专家权重是:

Blayer=E×bexp=256×1.77 MB≈453 MBB_{\text{layer}} = E \times b_{\text{exp}} = 256 \times 1.77\ \text{MB} \approx 453\ \text{MB}

(本文自行推导,与论文 §3.1 报告值一致。)

40 层加起来:

Bexpert,total=L×Blayer=40×453 MB≈18.1 GBB_{\text{expert,total}} = L \times B_{\text{layer}} = 40 \times 453\ \text{MB} \approx 18.1\ \text{GB}

论文写的是「18 GB(16.9 GiB)的路由专家,装在一个 19.5 GB 的 checkpoint 里」,剩下的部分给注意力、词表嵌入、共享专家和 int4 的分组缩放。也就是说,专家占了整个模型的九成以上,而且一个都不能少——路由器可能在任何一步选中任何一个专家。

这里是最容易被误读的一步。有人看到「每 token 只激活 3B 参数」,就以为「那是不是只需要 3B × 0.5 字节 ≈ 1.5 GB」。不对。那 3B 是这一 token 用到的参数,模型必须有能力在下一个 token 用到另外 3B。要从「稀疏激活」里拿到「字节变少」,唯一的路是这些字节不在内存里也在别处——这正是 Edge0 把专家权重放回 SSD 的全部动机。

那么一个 token 到底要就位多少专家字节?答案并不小:

Btok=L×K×bexp=40×4×1.77 MB≈283 MBB_{\text{tok}} = L \times K \times b_{\text{exp}} = 40 \times 4 \times 1.77\ \text{MB} \approx 283\ \text{MB}

(本文自行推导,按发布档 K=4K{=}4 计。)

如果这 283 MB 每一步都得从 SSD 拿,20 tok/s 需要的持续读带宽是:

Rreq=Btok×r=283 MB×20.4 s−1≈5.8 GB/sR_{\text{req}} = B_{\text{tok}} \times r = 283\ \text{MB} \times 20.4\ \text{s}^{-1} \approx 5.8\ \text{GB/s}

(本文自行推导。rr 为解码速度。)

5.8 GB/s 已经吃满一条消费级 NVMe 的顺序读带宽了。

但这里必须加一道闸:283 MB 是「专家访问量」,不等于本步新增的 SSD 读取量。 40 层里只有 32 层按预测流式执行(§五),暂存槽的 sticky 复用还会省掉一部分重复读(§十四),同一份字节也可能已经在页缓存里。论文实测的每步读盘量是另一个量级:K=4K{=}4 时 58.9 MiB、K=8K{=}8 时 125.1 MiB(§十 的表,注意那是另一台机器上的测量)。两组数不能互相代入,也不能拿差值直接当成命中率。这也是为什么论文里「几十到一百余 MiB/步」的读数不是因为需求小,而是因为页缓存把大部分字节变成了命中:热专家留在内存里,只有冷的那一小部分真的落到盘上。论文给出的实测冷页占比在 17%–98% 之间,变化跟路由宽度无关、跟是哪条臂有关:按需臂 17%–20%,预取臂 84%–98%(论文 Table 4,本文 §十 的表;口径是「一次加载里的冷页字节占比」)。也就是说,是「预取」这个动作本身把读变冷了——提前发出的读,等不到被谁顺手带热。

一句话记住这一节:MoE 的稀疏性缩小的是每 token 的计算量,不是每 token 必须可达的权重集合;只要路由可以选到任意一个专家,那部分字节就必须「存在」,问题只是「存在哪、怎么搬」。

四、第二本账:串行的罚金

把权重放到 SSD 上,第一版实现一定很慢,而且慢得很有节奏——每层停一次。

原因是一条硬依赖:第 N+1N+1 层选哪些专家,取决于第 NN 层的输出。而第 N+1N+1 层的专家权重,要在这个选择出来之后才知道该去读哪几块。读盘必须等选择,选择必须等算完。 于是每一步解码变成一串「算一层 → 停 → 读专家 → 算一层 → 停 → ……」的锯齿:

sequenceDiagram
    autonumber
    participant G as GPU(层 N 计算)
    participant S as SSD / 页缓存
    participant R as 路由器
    Note over G,R: 按需流式加载(on-demand):读盘串在关键路径上
    G->>R: 层 N 输出完成
    R->>R: 选出层 N+1 的 K 个专家
    R->>S: 请求这 K 个专家的字节
    S-->>R: 冷读等待(ms 级,每层一次)
    R->>G: 权重就位,层 N+1 才能开算
    Note over G,S: 40 层乘一个 token,就是 40 次串行等待

这张图说明什么:慢的不是总字节数,是等待的次数。每一次等待都把一层计算和一次磁盘往返串成一条链,40 层就是 40 次。论文在 16 GB 的 MacBook M2 上(checkpoint 18.4 GiB,装不下,每步都得把专家换进来)实测了这条链的代价:主线程花在等专家加载上的时间,K=2K{=}2 时是每步 154.9 ms,K=4K{=}4 时 244.0 ms,K=8K{=}8 时 575.0 ms。

注意最后那个数:K=8K{=}8 的 575.0 ms 比它所在的那一整步(559.1 ms)还长——这两个数口径不同:575.0 ms 是把各层等待相加得到的和(跨层求和),559.1 ms 是那一步的墙钟时间;和能超过墙钟,正因为不同层的加载彼此重叠。原因是「每步阻塞时间」是把各层的等待相加得到的,而不同层的加载彼此有重叠,所以相加之和可以超过墙钟时间。这个细节只能说明等待存在、且层间有重叠,不能反过来当「串行度」的判据(§十 会专门说清)。它的用处是:既然层间加载能重叠,把读盘再往前挪就有希望收回一部分罚金。

收回它的钥匙,是一个关于「谁能预知未来」的工程妥协。

五、把「预知」做成一个可以训练的小网络

问题可以写成一句话:在第 N+1N+1 层的专家还没被选出来之前,能不能先知道它们是谁?

已有工作里最接近的答案是 Pre-gated MoE(arXiv:2308.12066,2023 年 8 月):它在同一个 token 内、在第 NN 层注意力算完之后,立刻预测第 N+1N+1 层的专家,然后用这个预测去提前取权重。这个设计的「提前量」是一层。

Edge0 说这条路在流式引擎里走不通,并给了两条理由:

  1. 一层的时间窗太窄。 论文实测:在同一 token 内做逐层预测,需要一次逐层的同步与预测头求值,会把 GPU 流水线排空,代价是每步 30–100 ms——比它能隐藏的加载时间还长。他们试过的每一个「同 token」变体,最终都跑不过一个朴素的 LRU 缓存基线。
  2. 下一层的注意力还没算。 预测第 N+1N+1 层专家时,第 N+1N+1 层的注意力输出根本不存在,输入特征先天不足。

Edge0 的答案是把提前量从一层改成一整个 token:第 NN 层挂在 token tt 上,用它自己的后注意力归一化输出,去预测第 N+1N+1 层在 token t+1t+1 的路由。于是有两个效果同时发生:

  • 一个 token 的提前量把预测头的求值搬出了逐层关键路径。每步只需要一次统一的冲刷(flush),一次性把全部待流式加载的层都预测出来(35B 档 32 层,8B 档 16 层)。
  • 这个窗口里的磁盘读取,正好与当前这一步的计算重叠。论文的 16 GB 机器上,K=4K{=}4 时一层专家大约 7 MB,而预测版每步仍要花 101.9 ms 等冷加载——它没有消灭等待:部分加载等待被重叠,仍有未隐藏等待(101.9 ms 是主线程等待的跨层求和,不是墙钟罚金)。

预测头的形状很朴素,值得抄下来(下文 u\mathbf{u} 是预测出的路由 logits,ℓ(x)\ell(\mathbf{x}) 是线性残差路径,注意它与路由器输出 logits 的符号 ℓ\boldsymbol{\ell} 不是同一个东西):

u=fc2(erf-gelu(fc1(z)))+ℓ(x),z=[x; onehotK(At); onehotK(At−1)]\mathbf{u} = \mathrm{fc}_2\big(\mathrm{erf\text{-}gelu}(\mathrm{fc}_1(\mathbf{z}))\big) + \ell(\mathbf{x}),\qquad \mathbf{z} = [\mathbf{x};\ \mathrm{onehot}_K(\mathcal{A}_t);\ \mathrm{onehot}_K(\mathcal{A}_{t-1})]

(论文 §3.2。x\mathbf{x} 是该层后注意力的归一化隐藏态,At\mathcal{A}_t、At−1\mathcal{A}_{t-1} 是本层本 token 与上一 token 实际选中的专家集合,ℓ\ell 是一条线性残差路径,训练脚本用「下一层的路由器权重」热启动它,默认值为零——等于让训练从「直接把下一层路由器套在当前的隐藏态上」这个起点开始,MLP 只学修正量。)

输入维度可以当场核对:

dim⁡(z)=dmodel+2E=2048+2×256=2560\dim(\mathbf{z}) = d_{\text{model}} + 2E = 2048 + 2 \times 256 = 2560

(35B 档;预测头 MLP 中间宽度 512——别和模型隐藏维度 2048 混成一个数;fp16。8B 档是 1536+2×1281536 + 2 \times 128。)

这里的 dmodel=2048d_{\text{model}} = 2048 不是论文自说自话——我去拉了基座模型 Qwen/Qwen3.6-35B-A3B 的 config.json(2026-10-26 读取),text_config.hidden_size 就是 2048,且 layer_types 是线性注意力与全注意力交错的混合结构(full_attention_interval: 4)。这个数对得上,是本文少数能独立核验的常量之一。

先说层编号:本文与论文一致,解码层从 0 开始编号。层数账也对得上:35B 档有 33 个预测头(所属层 6–38),预测被消费在 7–38 层,所以 40 层里有 32 层是从预测而不是从自己的路由器取路由的;第 38 层那个头预测的是第 39 层,而第 39 层不做流式加载,于是这个头出厂却不带消费者。8B 档 16 个头(所属层 7–22),消费在 8–23 层,24 层里 16 层走预测。

六、「预测即路由」:这一步决定了整套设计的性质

有了预测还不够。真正让 Edge0 与众不同的是它怎么用这个预测:解码时,MoE 层直接用预测头的 logits 当路由,而不是拿预测去当提示、再回退到真路由器。

这一句话把系统的性质换掉了:

  • 暂存的专家集合与被路由的专家集合,按构造完全相同。 不丢专家、不需要在推理时补加载、不需要回退。
  • Pre-gated MoE 那类方案在推理时要处理「预测偏了怎么办」——补加载要时间,丢掉会掉质量,于是变成一个「覆盖率 vs 质量」的运行时权衡。Edge0 把这个权衡整个搬到了训练阶段,一次性付掉。
  • 代价是模型确实在「换过的路由」下工作。这不是免费的:预测头能看到的信息有限(上一层、上一个 token 的隐藏态),不可能恢复到原路由器的全部信息。

论文把这个现象叫 feature drift,而且很诚实地指出它没有专门去修:预测头的输入里包含了「本层实际执行的专家集合」,训练时那是基座路由器的选择,解码时却变成「前一层所拥有的预测头在上一 token 给本层的预测」(因为预测已经取代了路由器),而这些头并没有为这种位移重新训练。他们靠 recovery LoRA 在「学生路径」(即带着预测路由跑)上训练来吸收这个位移,并用最终的质量测量把 int4、路由近似、位移三者的损失一起定价。

这一节想让你记住的一件事:在这套设计里,「预测得准不准」不是目标函数,「在预测路由下还能不能写出好文本」才是。 论文原话是:追更高的路由器一致率是错误的目标,跨 token 预测从上一个 token 的隐藏态出发,本身就是信息受限的。

七、路由数学:两种主流家族,三条不能弄错的顺序

预测头输出的不是「专家编号」,而是 logits;把它变成专家集合要走和原路由器同一套函数。论文实现两种主流家族,且直接把它们从模型实现里抽出来共用(resident、streaming、prerouter 三条路径共用同一份,并用对齐测试钉死一致性)。

softmax-top-k(Qwen 系):

g=softmax(ℓ),S=top-k(g,K),wi=gi∑j∈Sgj\mathbf{g} = \mathrm{softmax}(\boldsymbol{\ell}),\qquad \mathcal{S} = \mathrm{top\text{-}k}(\mathbf{g}, K),\qquad w_i = \frac{g_i}{\sum_{j \in \mathcal{S}} g_j}

sigmoid-group(DeepSeek 系、Ling 系):

σ=sigmoid(ℓ),scorei=∑j∈group itop2(σj)\boldsymbol{\sigma} = \mathrm{sigmoid}(\boldsymbol{\ell}),\qquad \text{score}_i = \sum_{j \in \text{group } i} \mathrm{top2}(\sigma_j)

S=top-k(σ among surviving groups,K),wi=s⋅σi∑j∈Sσj+10−20\mathcal{S} = \mathrm{top\text{-}k}\big(\sigma \text{ among surviving groups}, K\big),\qquad w_i = s \cdot \frac{\sigma_i}{\sum_{j \in \mathcal{S}} \sigma_j + 10^{-20}}

关键差别在顺序,共有三条不能弄错:① 选择分数:DeepSeek 系给每个专家一个可学习的偏置 bib_i,选择用带偏置的分数 si=σi+bis_i = \sigma_i + b_i,而权重仍用原始 σi\sigma_i(偏置只影响选谁,不影响权重,这是 DeepSeek 风格路由的惯例);② 分组保留:按组内 top-2 之和排名、保留 GG 个组,其余组整体掩到 −∞-\infty,然后才在存活组里取 top-KK;③ 归一化:softmax-top-k 要重新归一化到和 1,sigmoid-group 用原始 sigmoid 乘以缩放 ss 再归一化。8B 档用的是 n=8n{=}8 组、保留 G=4G{=}4 组、缩放 s=2.5s{=}2.5、K=8K{=}8。

把这三步的顺序对齐不是洁癖。这两段公式是示意写法(论文附录附录 A 亦为简写),落地时要按上面三条补全偏置与分组细节;本文不宣称给出了可逐位复现的实现。预测即路由意味着路由函数本身就是推理路径的一部分:这里差一个 10−2010^{-20}、那里把 top-k 放在分组掩码之前还是之后,出来的是不同的专家集合,而专家集合一不同,权重就要重新读盘——错误会被放大成性能与质量的双重偏差。

八、训练三阶段:顺序不是可调项

这套方案的全部赌注在下半句:近似的路由可以靠训练补回来。 论文的配方有三个阶段,全部在「4-bit 部署 checkpoint 的反量化 bf16 重建」上训练(即训练它将要服务的东西),基座全程冻结。

  1. 阶段一:把预测头蒸馏出来。 唯一被训练的参数就是这些头。损失函数去模仿下一层真实路由器的输出,所以头学到的是「预测路由」,不是「拟合文本」。
  2. 阶段二:在学生路径上做 SFT。 LoRA 挂在注意力、线性注意力和共享专家的投影上,不挂路由专家(那些权重要流式替换,必须保持可替换)。训练时整个前向都带着预测路由跑,数据是约两百万行教师生成的文本。论文说这一步才是让近似可用的关键:只做蒸馏、没有 SFT 的 checkpoint 在预测路由下会输出重复、坍缩的文本;同一批头加上 SFT 之后,在同样的速度和内存下输出连贯。
  3. 阶段三:on-policy 蒸馏。 阶段二用的是教师写的文本;阶段三用学生自己生成的文本,让原始 fp16 基座当教师打分,梯度仍然只走学生路径上那层可训练参数。目标函数是反向 KL(mode-seeking,不要求学生覆盖教师的全部支撑集),作用在教师的 top-kk token 上,top-kk 之外的 mass 用一个尾部项承载而不是直接丢掉。阶段三只用 SFT 语料的十分之一(约 20 万行 vs 200 万行)就收敛。

论文明确说这个顺序在那几轮里不可谈判:先头后 SFT(否则 SFT 的信号会把极小的头梯度淹掉),最后 on-policy。

这一节想让你记住的一件事:「预测即路由」不是一个推理技巧,它是一个训练契约。 想让推理时不回退、不补加载,就得在训练时把模型按部署形态再教一遍——这也是为什么这套方案不能直接套到「别人的权重 + 一个外挂脚本」上。

九、LoRA 为什么必须「不合并」

这是全文我觉得最值得单独拎出来讲的一段,因为它推翻了一个很常见的直觉:把适配器合并进 4-bit 基座,再重新量化回 4-bit,行不行? 论文的回答是:算术上不行。

部署形态是:

y=W4bit(x)+αrBAxy = W_{4\text{bit}}(x) + \frac{\alpha}{r} B A x

(int4 基座冻死,LoRA 作为并行 delta 参与计算,基座的 4-bit 字节一个都不动。)

而「合并再量化」这条路是这样塌掉的:LoRA 的 delta 量级(RMS ∼10−3\sim 10^{-3})落在 4-bit 分组量化的台阶以下,重新量化会把权重层面的 delta 大部分抹平。论文给了三个保留率数字(以未合并版为 1):

  • 注意力投影上,保留 34%;
  • 一个稠密投影上,保留 2%;
  • 在 logits 层面,保留 18%。

logits 层面的保留率定义是:

retention=1−∥U−M∥∥U−B∥\text{retention} = 1 - \frac{\lVert U - M \rVert}{\lVert U - B \rVert}

其中 UU 是未合并模型的 logits、MM 是合并后重新量化模型的 logits、BB 是基座 logits,都取固定 prompt 的首 token;论文报告该值为 0.18。权重层面的保留率按目标分别算:

retentionW=∑(actual⋅δ)∑δ2\text{retention}_{W} = \frac{\sum (\text{actual} \cdot \delta)}{\sum \delta^2}

代价一侧:不合并要多花 42 MB 适配器权重,解码时间无可测量的增加。于是在该论文所测的适配器与重新量化配置下它更优——一个只读基座可以服务所有代的适配器,给某个档位换路由宽度只需要换两个文件。

顺带一句量级感,但要说清它的边界:BF16 没有固定绝对单位,它的相对精度随指数变化;int4 affine 的台阶也要看组内范围与量化定义。所以不能只用「delta 的 RMS 小于台阶」推出普遍的不可能性。能说的是:在这篇论文所测的这组适配器与这套重新量化配置下,重新量化把 delta 的大部分抹掉了(34% / 2% / 18%),换基座或换重量化方案必须重测。

这一节想让你记住的一件事:低比特基座 + 适配器时,「合并」是能力损失而不是打包便利;如果你的部署链路默认合并,那你可能一直在白扔适配器的大部分效果。

十、实验账:收益主要来自「把读盘挪出关键路径」

论文的实验设计有一处值得学:它不跟你比「谁更快」的绝对值,而是同一个 session 内交替 A/B、每轮换顺序、两条腿在同一份采样 token 序列上重放、两边缓存预算对齐,报的是多轮中位数。它甚至明说了理由——这台硬件上单次基准的轮间波动有 ±40%,跨 session 可达 2.3 倍,所以「跨 session 的数字不当作证据」。

下面这张是它的核心结果(论文 Table 3,16 GB MacBook M2,18.4 GiB checkpoint 装不下,所有臂都在真实地从 SSD 换入专家):

路由宽度 KK按需流式(tok/s)预测预取(tok/s)增益
24.88.6+80%
43.56.4+82%
81.83.3+84%

增益几乎与路由宽度无关(+80% 到 +84%),而绝对速度随宽度急剧下降。这组数字本身就是论点:预测带来的不是「更聪明的缓存」,而是「同样的读盘,早一步发出」。

再看代价(论文 Table 4,同一 session、同一对臂):

KK方案加载次数/步MiB/步MiB/次冷读比例ms/次
2按需99.330.40.3120%1.56
2预测19.329.51.5398%2.41
4按需189.450.90.2717%1.29
4预测44.758.91.3284%2.28
8按需398.6126.90.3220%1.44
8预测89.5125.11.4090%2.36

(列名沿用论文口径:MiB/步 是每解码步从存储层实际读出的字节数,MiB/次 是平均每次加载的大小,冷读比例是这些字节里必须等 SSD 的那部分占比。)

三件事在这张表里同时出现:

  1. 字节数几乎不变(K=8K{=}8:125.1 vs 126.9 MiB;K=2K{=}2:29.5 vs 30.4 MiB),只有 K=4K{=}4 时预测臂多读了 16%(58.9 vs 50.9 MiB)。论文的定性是「守恒,不是预测得更准」——它把读盘提前,但不能让读盘消失。
  2. 次数缩到约四分之一(189.4 → 44.7 次/步,4.2 倍),每次大到约五倍(0.27 → 1.32 MiB,4.9 倍;其余两臂是 4.4–5.1 倍,都不到一个数量级)。这是预取的自然形态:把许多小的、随机的读,合并成更少的、更大的读。
  3. 每次变贵了(1.29 → 2.28 ms),因为冷读比例从 17% 涨到 84%。论文拟合了单次加载成本:

tload≈1.17 ms+1.33 ms×coldt_{\text{load}} \approx 1.17\ \text{ms} + 1.33\ \text{ms} \times \text{cold}

(论文 Figure 3 拟合式,拟合于 K=8K{=}8 那一对臂,对全部实测单次成本复现误差在 0.13 ms 内,预取点内 0.07 ms。本文自行代入:K=4K{=}4 预测臂 cold 0.84 → 1.17+1.33×0.84≈2.291.17 + 1.33 \times 0.84 \approx 2.29 ms,与表中 2.28 ms 一致;按需臂 cold 0.17 → 1.17+0.226≈1.401.17 + 0.226 \approx 1.40 ms,与 1.29 ms 差 0.11 ms。)

于是真正的收益可以直接读出来——主线程等加载的阻塞时间:

  • K=2K{=}2:154.9 → 46.5 ms/步
  • K=4K{=}4:244.0 → 101.9 ms/步
  • K=8K{=}8:575.0 → 211.6 ms/步

(论文 §5.3 实测。)

想自己核一遍量级的话,可以拿 K=4K{=}4 那行做粗算:按需臂每步 189.4 次加载、每次 1.29 ms,乘积约 244 ms,与实测等待之和 244.0 ms 等同量级;预测臂 44.7 × 2.28 ≈ 102 ms,对上 101.9 ms。这两组相等只说明统计相容(乘积与和是同一量级),不能据此断定没有重叠——论文明确说不同层的加载会重叠,K=8K{=}8 那组「等待之和 575.0 ms > 墙钟 559.1 ms」就是反例;判串行度要靠时间线或关键路径测量。能下的结论是窄的:同样的总字节、同样的单次成本表,仅仅因为「发出时机」不同,等待之和从 244 掉到 102;剩下的 102 ms 是仍未隐藏的等待(论文口径:主线程等待加载的跨层求和),预测把它压到零。

同一步里,三个资源都没被吃满:磁盘读最多占整步的 12%,进程只用约 1 个核(共 8 个),GPU 占用 35–41%。瓶颈不是任何一个资源,而是等待的顺序。 这句话是整篇论文最值钱的一句工程判断。

十一、质量账:损失集中在长链推理

质量用 OpenCompass 在算力服务器上跑,Edge0(int4 + 适配器 + 预测路由)与原始 fp16 基座同设置对比,不涉计时(论文 Table 2):

基准edge0-35b (int4)Qwen3.6 (fp16)edge0-8b (int4)Ling 3.0 tiny (fp16)
AIME 202686.692.763.373.3
HumanEval90.995.191.592.7
GPQA-Diamond79.881.870.771.2
MMLU-Pro81.084.670.165.8
IFBench57.961.753.960.6
平均79.283.269.972.7

(每格满分 100。)

两个档位的平均值差是 35B 档 4.0 分(论文报「逐基准平均偏差 3.9」)、8B 档 2.8 分。但这个平均掩盖了一个非均匀结构,论文自己在局限一节里点了出来:损失集中在长链推理——35B 档 AIME 掉 6.1 分,8B 档掉 10.0 分,而 8B 档其他基准都在 6.7 分以内,MMLU-Pro 甚至比基座高 4.3 分。

这个结构值得工程读者停下来想一想:它不是「整体稍微差一点」,而是「在需要长链条、每步都不能错的任务上差得多」。如果你的场景是单轮检索问答或格式抽取,3–4 分的平均差可能感觉不到——但这只是推测,五个基准的均分并不能证明某类任务无感,要到你自己的任务上测;如果是数学推理、多步工具调用、长程代码修复,就该按最差的那个分项(而不是平均值)来评估能不能用。

十二、内存账:2.9 GiB 到底买了什么

回到开头那三个数字。为什么「装不下」的版本反而快?论文的解释很直接:全驻留时那 18.2 GiB 是不可回收的权重,24 GB 里几乎什么都没给 KV 缓存和操作系统留,于是系统只能换页;Edge0 的峰值活跃内存是 2.9 GiB,其余字节留在磁盘上。但要注意:磁盘上有文件 ≠ 那些字节不在内存——页缓存与暂存槽都在真实占用内存,只是页缓存可回收、暂存槽已计入分配峰值。

口径全驻留(mlx-lm)Edge0 流式(35B 档)
解码3.9 tok/s20.4 tok/s
全量路由专家是否常驻内存是(含在 18.2 GiB 里)否(字节在磁盘,按需流入)
全驻留权重内存18.2 GiB不计(改由页缓存与暂存槽承担,二者都占真实内存)
峰值活跃内存18.2 GiB2.9 GiB
checkpoint 在盘上19.5 GB19.5 GB

预取的价钱也在这里付出:预测需要那些专家在被用到之前就在内存里,而 MLX 的已分配内存不可回收。论文实测(16 GB 机器):

  • 不可回收分配:按需 1.72 / 1.73 / 1.79 GiB(K=2,4,8K{=}2,4,8)→ 预测 2.33 / 2.60 / 3.22 GiB;
  • 进程 RSS(含文件页):按需 5.29 / 6.12 / 6.18 GiB → 预测 4.91 / 5.61 / 6.39 GiB;
  • 代价的来处:K=8K{=}8 时预测臂多占 1.43 GiB 不可回收内存,页缓存相应少了 1.15 GiB——约五分之四的驻留成本是从页缓存里抢来的。

注意 RSS 那三个数的方向并不一致:K=2K{=}2(5.29 → 4.91)与 K=4K{=}4(6.12 → 5.61)预测臂更低,而 K=8K{=}8(6.18 → 6.39)预测臂反而更高。原因大概是预取把「分散的小冷读」变成「集中的大冷读」,文件页的驻留形态跟着变,在驻留成本最高的那一档翻了向。结论不变、但口径要收紧:判断一个流式方案好不好,不能只看 RSS,要看不可回收分配和页缓存的差额——RSS 的变化方向可能随档位翻转。

一个 24 GB 机器的分配先验,可以用本文的算式自己摆开:

Mpeak≈Mdecoder+Mslots+MKV+MadaptersM_{\text{peak}} \approx M_{\text{decoder}} + M_{\text{slots}} + M_{\text{KV}} + M_{\text{adapters}}

(本文自行推导的记账式;论文没有给这个式子,它给的是各分项实测:解码器图与执行器分配、每层暂存槽、KV 缓存、适配器与预测头 0.2 GB。)按论文的 35B 档数字,等号左边是实测的 2.9 GiB,右边各分项的具体拆分论文没有逐项列出,这里不替它编。

prefill 是镜像的另一半:35B 档冷启动首次 prefill 113 tok/s、热 prefill 140 tok/s(3.1k token 的 prompt),8B 档 500 / 1102;prefill 走的是「整层加载」路径(一层所有专家一次性读进来,因为 prefill 会命中一层里的全部专家),此时没有预测可做,页缓存热起来之后 prefill 变成算力受限。这套机制的全部靶子是 decode,不是 prefill。

十三、它与你可能已见过的四种「专家卸载」有什么不同

专家卸载不是新题目。论文的相关工作把已有的四条路摆得挺清楚,但真正有价值的区分不在「谁把权重放哪」,而在谁知道下一步需要哪些专家:

方案权重放在哪谁决定下一批专家预测偏了怎么办
llama.cpp 的 --cpu-moeMoE 专家留在 CPU/系统内存,注意力在 GPU不预测:按当步真实路由从系统内存取不存在「偏」,但每条 token 都要过一遍内存带宽
PowerInfer神经元分热/冷,热驻 GPU、冷留 CPU部署前离线 profiling 定热/冷放置,运行时用 adaptive predictor 预测本层激活;不做跨层/跨 token 的提前取数冷神经元按原样回落到 CPU 路径
Mixtral-offloading / MoE-Infinity专家按热度或复用距离做缓存历史统计(热度 / 复用距离),没有跨 token 的可训练预测头未命中就从上一级取,等待暴露在关键路径上
FlexGen权重与 KV 一起做 GPU/CPU/磁盘三级不做跨层预测,按调度策略批量搬未命中即等待
Edge0专家权重留在 SSD,按需 mmap 流入部分层设置预测头(35B 档 33 个头、32 层消费),提前一个 token不存在「偏」:预测就是路由,暂存集恒等于路由集

这张表想让你注意一件事:前四行都在移动同一个字节集合,它们的差别只是在哪一级、缓存策略多聪明;只有最后一行改变了这个集合的性质——它让「需要的集合」由预测决定,于是「加载的集合」不可能错配。代价是模型的输出被永久地换了一条路由,而不只是搬运方式变了。

这也解释了论文里那句略带挑衅的话:「预测选择,暂存加载」——暂存(staging)本身不是可部署的配置,没有正确的集合来源,它要么丢专家导致输出退化,要么钉住一个热集合来回抖动。

十四、系统怎么搭:一张图看懂数据流

flowchart LR
    subgraph DISK["SSD:19.5 GB int4 checkpoint(按层堆叠的 safetensors)"]
        L1["层 N 专家<br/>256 × 1.77 MB"]
        L2["层 N+1 专家<br/>256 × 1.77 MB"]
    end
    subgraph MEM["内存:可回收页缓存 + 有限暂存槽"]
        PC["OS page cache<br/>(LRU,可回收)"]
        SLOT["暂存槽<br/>sticky slot 双缓冲"]
    end
    subgraph GPU["GPU:解码前向(跨两个 token)"]
        ATTN["注意力 + 归一化<br/>token t"]
        PR["预测头(层 N 拥有)<br/>在 token t 预测层 N+1 在 token t+1 的路由"]
        MOE["MoE 层 N+1<br/>token t+1,按预测集合执行"]
    end
    L1 --> PC
    L2 --> PC
    PC --> SLOT
    SLOT --> MOE
    ATTN --> PR
    PR -- "预测即路由:直接决定要预取的专家" --> PC
    PR -- "跨步消费:t+1 用 t 做出的预测" --> MOE
    MOE --> ATTN
    LORA["recovery LoRA(42 MB,永不合并)<br/>只挂注意力与共享专家投影,不作用于路由专家"] --> ATTN

这张图说明什么:预测头不在推理的关键路径上做决策,它只是提前把「该搬哪些字节」告诉装载器。 真正的路由决定由预测直接给出(图中从预测头到 MoE 层的那条线),装载器则靠页缓存与暂存槽把字节提前放好。LoRA 是一条常年挂着的并行支路,永远不并进 4-bit 基座。

论文的引擎还有四个执行器路径,按用途切换、但共用同一个量化 gather 内核,且每条路径都要与反量化参考实现做逐元素比对(相对 L2 误差只到内核自身的 bf16 内部精度):

  1. exact:去重后按需构建、堆叠、量化 gather。其他三条路径的正确性基线。
  2. staged:固定槽位双缓冲,解码的主力。路由索引通过槽表映射,索引不出 GPU;专家集合重复时复用已建的图节点;用常驻的 sticky-slot 张量原地更新(incr_stack),一步只重写变化的那几个专家,而不是重建一层的九个堆叠张量(gate/up/down 各自带缩放与偏置)。
  3. hot:每层一组 LRU 常驻的热专家,命中走堆叠 gather,未命中回落 exact——和 PowerInfer、Mixtral-offloading、MoE-Infinity 的热/冷切分是同一个思路。
  4. whole-layer:prefill 时一次把一层全部专家读进来。因为 checkpoint 是「按层堆叠」布局,这一次读是九个直接读(三个量化投影),而不是 256×9256 \times 9 次构建;CPU 侧的加载与前一层 GPU 执行重叠。

「exact 作为正确性基线、其他路径与之逐元素对齐」这个契约,是它能按层、按阶段自由切换路径而不改变输出的前提。工程上这是个便宜但经常被忽略的纪律:任何加速路径都要有一个可以随时回退的参考实现,否则「快」和「对」就只能二选一。

十五、论文没解决的三件事(也是你抄之前要知道的三件事)

论文在局限一节里自己交代了四条,我认为其中三条对「我要不要用」影响最大:

  1. 一次只服务一个请求,FIFO 串行。 并发属于服务层,不属于这个引擎;而批处理会改变专家的活跃集合,论文的按请求 profile 并没有建模这件事。这意味着这套数字是单流交互场景的数字,不能直接拿来推吞吐型服务的成本。
  2. 44 ms/步的图构建地板。 论文说得很直:流式引擎的解码瓶颈是 CPU 侧而不是存储侧,40 层前向里 44 ms/步的图构建是下限,任何存储侧优化都推不动它;要关掉这个差距需要内核级的图摊薄,或者换更小的模型。K=8 时主线程上同步做的填充就要 62.5 ms/步,把它挪到别的线程是论文列出的「还没拉的第二个杠杆」。
  3. 收益被复用率限制住。 相邻 token 在一层上只有约四分之一的专家集合重合,所以大部分预取来的专家在后续 token 里未再次复用(注意:这不是「预测白读」——预测集合就是当步路由集合,当步一定被消费,浪费的是后续 token 的命中)。论文归因得很干脆:存储层决定「有多少可赢」,复用率决定「预测能收到多少」。

另外两条值得知道:实现范围要带日期:论文 v1(2026-09-16)里只有 MLX 一条实现路径,CUDA 那一格是架构不是代码;但按官方模型卡,Edge0 已在 2026-09-30 放出 iOS·macOS·Android·Windows 四个平台的原生引擎(2026-10-26 读取)——所以「只有 MLX」是论文当时的实现状态,不是今天的事实;质量的损失集中在长链推理(§十一)。

一个不那么显眼但很关键的点:这套设计的收益会随硬件变薄。 论文自己说,预取收益来自「它移除的等待」——页缓存热、存储快的时候收益自然缩小。换句话说,如果你的机器是 128 GB 内存 + 能把 19.5 GB 全放下,那这套方案最大的卖点(内存)消失,剩下的是「省内存」而不是「更快」;如果你的盘是 PCIe 5.0 且冷读便宜,那么每次加载的成本项 1.33×cold1.33 \times \text{cold} 变小,预取的价值也跟着缩水。它更适合「内存紧、存储慢」这个区间;论文没有把它与全部替代方案做对比,所以不能说它是该区间的全局最优。

十五·补:仓库口径「约 2.5 GB」与论文实测 2.9 GiB 的关系

读这篇论文最容易撞上的第二个口径矛盾在这里。论文仓库的自我介绍写的是「在约 2.5 GB 内存里跑 35B MoE」,而论文正文实测的 35B 档峰值活跃内存是 2.9 GiB。两个数来自不同来源、覆盖的测量对象未必相同:

  • 约 2.5 GB 是仓库 About 的约数(它是否对应某一组具体分项,本轮没有核到)。它到底对应哪几个内存分项,本轮没有核到定义(README 里另有一组带 MLX allocator 说明的数字,也不是同一个口径)——所以这里只能说「两个数是不同来源的不同口径」,不能替仓库补一个测量定义;
  • 2.9 GiB 是论文 §5.4 报告的实验口径,MLX 分配器峰值、短上下文、按发布档 K=4K{=}4 配置实测。

本文一律引用 2.9 GiB,并在 §十二 的内存表里注明这是「峰值活跃内存」而不是「进程 RSS」或「磁盘占用」。同类数字并置时,先出现的那个位置就必须把口径写清楚——否则读者会把 19.5 GB(磁盘)、18.2 GiB(全驻留)、2.9 GiB(流式峰值)三个数当成同一件事的不同说法。

15.1 如果换成 32 路并发,这套账会变成什么样(本文自行推导)

论文的局限一节承认它一次只服务一个请求、FIFO 串行,而批处理会改变专家的活跃集合。这一步值得自己推,因为它是「这套方案能不能上生产」的分水岭。

单请求 K=4K{=}4 时,每层只碰 4 个专家,4imes1.77=7.084 imes 1.77 = 7.08 MB;40 层约 283 MB(前文 §三 的 BexttokB_{ ext{tok}})。

如果 32 个请求一起做一步解码,最坏情况下每层被选中的不同专家数是:

Eextunion=min⁡(32imes4, 256)=128E_{ ext{union}} = \min(32 imes 4,\ 256) = 128

对应每层字节数 128imes1.77≈227128 imes 1.77 \approx 227 MB,40 层约 9.19.1 GB——在这个「无缓存命中、无跨步复用、所需专家均从 SSD 读取」的示意上界下,一步的访问量约 9.1 GB,按 5 GB/s 的盘算约 1.8 秒一步。实际 I/O 会小得多(页缓存、批内共享、跨步复用都未建模)。

但注意这一步同时产出了 32 个 token,所以摊到每个 token 的字节仍是 283 MB,单请求与批处理一样。批处理改变的不是总字节效率,而是三件事:每步延迟从几十毫秒涨到秒级、交互体验(首 token 延迟、打字机感)直接崩掉、以及「预测预取」要覆盖的窗口从几十毫秒变成一个秒级区间(能不能藏住要看时间线,不能默认窗口变宽就更划算)。

结论要说得克制:流式 + 预取在单流交互下是正收益,在吞吐型批处理下必须先重算延迟预算(首 token 延迟与每步解码延迟要分开算),而论文没有测这一格(它的 profile 是单请求的)。这也解释了为什么它把并发明确划给「服务层」:那是另一套账。

15.2 冷启动那一次特别难看,稳态才是这套方案的主场

论文分别报了 prefill 的冷、热两个数(3.1k token 的 prompt):35B 档冷 113 tok/s、热 140 tok/s;8B 档冷 500 tok/s、热 1102 tok/s。

本文自行推导:热的/冷的比值,35B 档是 140/113≈1.24140/113 \approx 1.24,8B 档是 1102/500≈2.201102/500 \approx 2.20。小模型在冷启动时被 IO 拖得更狠,一个自然的解释是它的每层专家更少、每步算力更小,加载时间在整步里的占比更高(本文推断,论文未给这项归因)。

这条对验收方式有直接影响,也是很多「端侧大模型 demo」翻车的地方:你不能用第一次跑的感受评价这类方案,也不能只看稳态曲线。 但要分清这里的举证是冷热 prefill(同一阶段、缓存状态不同),而稳定 decode 是另一项测量(§十 那组),两者机制不同、不能互相解释。验收指标应按三段分开列:模型加载时间、首次 prefill / 首 token 延迟、稳定 decode 的 tok/s。要评它,就得分两个数报:冷启动的首次可用时间,与稳态的 tok/s。

15.3 预测头本身有多大:一笔可以自己核的账

论文说 35B 档的「适配器 + 预测头」一共多占 0.2 GB。这句话可以自己验一遍。

单个头按 §五 的形状算(35B 档:输入 2560、隐藏 512、输出 256,另加一条 2048→256 的线性残差):

Pexthead≈2560imes512+512imes256+2048imes256≈1.97 extMP_{ ext{head}} \approx 2560 imes 512 + 512 imes 256 + 2048 imes 256 \approx 1.97\ ext{M}

33 个头合计 ≈65 extM\approx 65\ ext{M} 参数,fp16 存储约 130 MB;加上论文报告的 42 MB LoRA 适配器,合计约 172 MB——与「0.2 GB」这个口径对得上(本文自行推导)。

算力侧同样可以估:每 token 每头约 2imes1.97 extM≈3.92 imes 1.97\ ext{M} \approx 3.9 MFLOPs,33 个头约 0.13 GFLOPs;而一个每 token 激活 3B 参数的 MoE 本身约 6 GFLOPs(2imes3extB2 imes 3 ext{B},本文按乘加两次运算估算)。也就是说预测头只占约 2% 的计算量——它便宜的真正原因不是「小」,而是它把几十毫秒的磁盘等待换成了几毫秒的计算。

十六、给工程读者的六条可迁移结论

这六条我认为即使你一辈子不部署 Edge0 也用得上。

  1. 先分清你卡的是「字节」还是「等待」。 判断方法很土但有效:看每步的阻塞时间之和是否近似等于「加载次数 × 单次耗时」。论文的 K=4K{=}4 那行是 189.4 × 1.29 ≈ 244 ms,与实测 244.0 ms 对得上——这说明它是串行的。如果对不上(显著小于),说明已经在重叠,你该去挖别的地方。
  2. 把「次数」和「字节」分开做优化。 同一份字节,189 次小读和 45 次大读在论文那台机器上差了约四到五倍(Table 4);别的硬件上要实测,不能默认同倍数。合并读的收益通常比减少总字节来得快。
  3. 低比特基座上的适配器不要合并。 保留率 34%/2%/18% 这三个数字说明:合并后再量化会把 delta 抹到 4-bit 台阶以下。要合并就得先提高基座精度,等于把省下来的内存还回去——是否值得取决于你的基座与重量化配置,需要实测(论文只测了它自己那一组)。
  4. 「预测即路由」是一条设计取舍,不是唯一解。 保留「预测失败就回退真路由」的路径也完全可行,代价是每次回退都在关键路径上等一次加载——论文实测同 token 的预门控变体跑不过 LRU 基线,这是它选「不回退」的理由,而不是「回退不可用」的证明。
  5. 估算任何「卸载」方案的收益,先算每 token 必须就位的字节数(L×K×bexpL \times K \times b_{\text{exp}}),再乘上你的目标解码速度,得到所需的持续带宽。这个数字如果超过你的存储层,就必须依赖缓存命中率;而命中率是可测的、不是可以假设的。
  6. 别用 RSS 判断内存方案。 用「不可回收分配」和「页缓存被挤掉多少」。论文的 K=8K{=}8 那组里,预测臂多占 1.43 GiB 不可回收内存、页缓存少了 1.15 GiB,而 RSS 只从 6.18 涨到 6.39 GiB(约 3%)——只看 RSS 会严重低估代价,而且它在低档位还会反过来下降,方向都不可靠。

十七、四步自测:你的场景到底吃不吃得到这套收益

  1. 量「每 token 必须就位字节」与你的盘带宽的比值。 用 L×K×bexpL \times K \times b_{\text{exp}} 算出 BtokB_{\text{tok}}(本文 §三 的算式),再除以你实测的顺序读带宽,得到一个「理论最低每步秒数」。如果这个值已经高于你的目标每步时间,那说明你必须依赖缓存命中,预测预取只控制「这一批要读哪些字节」,并不控制 OS 页缓存命中率(预取臂的冷页占比反而更高)。
  2. 量等待暴露度。 「加载次数 × 单次耗时」与实测等待之和相等只说明统计相容,不能判串行;要判必须做时间线或关键路径测量(论文自己就有「等待之和 575.0 ms > 墙钟 559.1 ms」的反例)。先拿到「主线程实际被阻塞多久 / 一步总时长」,再决定要不要上预取。
  3. 量复用率。 统计相邻 token 的专家集合重合比例。论文在自己的 traces 上只有约 1/4;如果你的场景更低(例如高熵采样、多轮短对话),预取来的专家未再次复用的比例会更高,收益上限也就更低(这仍不是「当步浪费」)。
  4. 量一下你的「不可回收分配」与页缓存的此消彼长。 把预取打开前后各采一次 MLX/CUDA 的分配峰值与 MemAvailable,看预取多占的内存里有多少是从页缓存抢来的。论文的比例是「多占 1.43 GiB、页缓存少 1.15 GiB」,约五分之四;如果你的机器上这个比例接近 1,说明预取几乎全在跟页缓存抢,收益会打折。

十八、小结:四句可以带走的话

  • MoE 的稀疏性省算力不省字节。 35B 级 MoE 的 19.5 GB 4-bit 权重里,18 GB 是路由专家,一个都少不了——问题只是它们存在哪。
  • 流式加载的瓶颈是等待的次序,不是总字节。 同样的 125 MiB/步,按需读是 1.8 tok/s,提前一个 token 发出去就是 3.3 tok/s。
  • 「预测即路由」把运行时的权衡换成训练时的一次性成本。 不丢专家、不回退、不补加载,代价是模型真的活在一条被替换过的路由上,且需要三阶段训练把它教回来。
  • 收益是区间性的,且随硬件退坡。 它在「内存紧、存储慢、单流交互」这个区间里把 3.9 tok/s 变成 20.4 tok/s、把 18.2 GiB 变成 2.9 GiB;离开这个区间,卖点会迅速变薄。

这套方案还顺便示范了一件事:论文把自己的基线(全驻留 3.9 tok/s)也一起报出来了,而不是只报自己最好的格子——这让读者能判断收益来自哪里。

论文最后那句话说得挺漂亮,也确实是这件事的形状:「你桌上的机器早就够大了,权重只是需要一个容身之处。」

📋 来源与核验记录

以下 URL 均于 2026-10-26(北京时间)本轮实际打开并核验(HTTP 200,标题与内容一致):

  1. Yu Lin, Yiming Wang, Runyuan Cai, Hanze Liu, Xiaodong Zeng,《The Other Half of the Memory Wall: Serving 35B MoEs from SSD with Trained Routing Prediction》,arXiv:2609.18063v1,2026-09-16 提交(v2:2026-09-17;本文逐节比对的是 v1 的 HTML 全文),CC BY 4.0。本文所有论文内数字(19.5 GB / 2.9 GiB / 20.4 与 3.9 tok/s / 453 MB 与 1.77 MB / Table 1–4 / 质量表 / 拟合式 / 局限)均出自此文的 HTML 全文:https://arxiv.org/html/2609.18063v1 (摘要页:https://arxiv.org/abs/2609.18063 单列)
  2. 论文仓库(Apache-2.0):https://github.com/Edge0-AI/Edge0 —— 页面描述明确写「Run 35B MoE models in ~2.5 GB of RAM. Weights stream from SSD; a trained router predicts loads a step ahead」,与论文口径一致(注意「约 2.5 GB」是仓库自己的宣传口径,论文实测 35B 档峰值活跃内存是 2.9 GiB,本文按论文的数字引用)。
  3. 发布模型(Apache-2.0):https://huggingface.co/Edge0/Edge0-35B-A3B-preview
  4. 基座模型 config 核验:https://huggingface.co/Qwen/Qwen3.6-35B-A3B 的 config.json(/raw/main/config.json,本轮实际下载),text_config.hidden_size = 2048、full_attention_interval = 4、layer_types 为 linear_attention 与 full_attention 交错——用于核对论文预测头输入维度 2048+2×256=25602048 + 2 \times 256 = 2560。模型页:https://huggingface.co/Qwen/Qwen3.6-35B-A3B
  5. Pre-gated MoE(被本文用来对照「提前一层 vs 提前一个 token」):Ranggi Hwang 等,《Pre-gated MoE: An Algorithm-System Co-Design for Fast and Scalable Mixture-of-Expert Inference》,arXiv:2308.12066 —— https://arxiv.org/abs/2308.12066
  6. 专家卸载的代表工作(用于 §十三 对照表):Artyom Eliseev, Denis Mazur,《Fast Inference of Mixture-of-Experts Language Models with Offloading》,arXiv:2312.17238 —— https://arxiv.org/abs/2312.17238
  7. 热/冷切分的代表工作:Yixin Song 等,《PowerInfer: Fast Large Language Model Serving with a Consumer-grade GPU》,arXiv:2312.12456 —— https://arxiv.org/abs/2312.12456

论文内的百分比数字(单次加载成本式中冷读比例的拟合、Table 4 的冷读比例 17%/20%/84%/90%/98%、Table 3 的增益 +80%/+82%/+84%、磁盘读占整步最多 12%、GPU 占用 35–41%、K=4K{=}4 时预测臂多读 16%、LoRA 保留率 34%/2%/18%(logits 层 0.18)、异步填充 62.5 ms、「相邻 token 约 1/4 专家重合」、单次基准 ±40% 轮间波动)均出自来源 1(arXiv:2609.18063)的正文、Table 1–4 与§5.3–5.4,未做二次换算。

本文自行推导的数字(与论文数字分列,不混用):

  • 20.4/3.9≈5.2320.4 / 3.9 \approx 5.23 倍、2.9/18.2≈0.162.9 / 18.2 \approx 0.16(§一)。
  • Btok=40×4×1.77 MB≈283 MBB_{\text{tok}} = 40 \times 4 \times 1.77\ \text{MB} \approx 283\ \text{MB} 与所需持续带宽 ≈5.8 GB/s\approx 5.8\ \text{GB/s}(§三,按发布档 K=4K{=}4、20.4 tok/s 计,假设专家字节全不命中页缓存)。
  • 「RSS 6.18 → 6.39 GiB 约 3%」的涨幅换算(§十二)。
  • 拟合式在两个臂上的复现值(§十:2.29 vs 2.28 ms;1.40 vs 1.29 ms)。
  • 「189.4 × 1.29 ≈ 244 ms 对上实测 244.0 ms」的串行度校验(§十、§十六)。
  • §十二 的内存记账式 Mpeak≈Mdecoder+Mslots+MKV+MadaptersM_{\text{peak}} \approx M_{\text{decoder}} + M_{\text{slots}} + M_{\text{KV}} + M_{\text{adapters}} 是本文为讲清结构写的,论文并未给出该式,也没有公开各分项的拆分,故不填数。
  1. 实体独立转引(不等于实验复现):AI Weekly 2026-09-17 的 alert《AutoArk’s Edge0 Serves 35B MoE From SSD at 20 tok/s on 24GB Mac》https://aiweekly.co/alerts/autoarks-edge0-serves-35b-moe-from-ssd-at-20-toks-on-24gb-mac ;Artificial Analysis 的 Qwen3.6 35B A3B 独立模型记录 https://artificialanalysis.ai/models/qwen3-6-35b-a3b ;论文所引 8B 基座对应页 https://huggingface.co/inclusionAI/Ling-3.0-tiny-base 。这三条只能证明「实体与第三方转引存在」,不能证明 tok/s、训练过程或质量结果被第三方复现过;不存在任何独立复现的证据。

未核实项(如实列出):

  • 论文的 8B 档基座写作「Ling 3.0 tiny」,其参考文献指向 inclusionAI/Ling-3.0-tiny-base(本轮核到该页存在),但实际部署用的是哪个 checkpoint 本文未确认;35B 档「Qwen3.6-35B-A3B」同样没有下载权重文件核对,只核到了后者 config.json 的隐藏维度与层类型;论文报告的 40 层 × 256 专家、8B 档 24 层 × 128 专家按其 Table 1 引用。
  • Edge0 仓库/HF 页面的实际文件大小、是否与论文的 19.5 GB 完全一致,本轮未逐字节核对(HF 页面为模型卡,未下载权重);仓库 About 里的「约 2.5 GB」也只当宣传口径引用,未核明它对应哪些内存分项。
  • 论文的 tok/s 与 ms 数字均为其本机实测(Mac mini M4 Pro 24 GB / MacBook M2 16 GB),本文未在任何机器上复测;本文所有「更快/更省」的表述都是相对论文自己的基线,而不是相对你手上的硬件。