十天前我有一个基准测试:根据测得的共激活情况对 MoE 模型文件中的专家权重进行重新排序,磁盘每令牌读取次数下降了 2.23 倍。那是真实的、可复现的、完全在我自己的机器上测量的——也就是说,它目前还几乎没什么价值。

今天与这项工作相关的最强数字并不是我自己产生的:在 48 GB MacBook 上运行 235B 参数模型,解码吞吐量提升 +32.3%,首令牌延迟降低 −26.3%,这个结果由一位素未谋面的人、在我从未运行过的推理引擎上、使用我未经修改的脚本、并将双臂交换作为页缓存控制后测得的。在此过程中,我最初的三个论点中有两个被数据证伪,我预先注册的一个扩展预测未能达到自身阈值,而我对一个关键参数的两次预测均出现了方向相反的偏差。

这些证伪正是最有成效的部分。本文讲述的是让这些证伪变得低成本的过程:五种引擎、六位陌生人、一个 issue 线程,以及在这个过程中形成的纪律——因为我认为这个过程具有普适性,而我通常看到的文章并未提及它。

实验设置

这个想法(项目:mbolt)是将 BOLT/PGO 应用于模型二进制文件。检查点张量的顺序是训练流水线的偶然结果;推理时的 MoE 路由远非随机——专家以团簇形式激活。如果引擎从 SSD 流式读取专家,则文件布局决定了令牌的缺页是少数几次长顺序读取,还是数千次零散读取。因此:追踪路由、聚类共激活、重写文件,并保持权重字节完全一致。

独立工作一周后,我在 80B 模型上实现了 2.23× 读取减少、相比打过补丁的 llama.cpp 获得 1.55× 端到端解码增益、正确性门控(权重字节完全一致、路由映射 100.000% 通过置换、输出差异比同一引擎的 CPU↔Metal 后端差异低 5 倍),以及 500 提示盲 A/B 测试中 48 比 46(26 平)的得分——对于布局变更而言,这正是目标。

我还亲自测量到一个空结果:在原版 llama.cpp 上,这一切都不起作用。mmap 缺页路径对布局不敏感;我测得持平并如实报告了。增益需要一种发出显式读取的引擎。

这个空结果决定了发布策略,尽管当时我并未把它当作策略。运行显式读取专家流式读取器的人屈指可数。我没有发 Show HN,而是把数据发到他们的 issue 跟踪器:ml-explore/mlx-lm#1438,@mabaeyens 正在为 MLX 发布专家卸载流式读取器;以及 JustVugg/colibri#119,一个从 NVMe 流式读取 744B 模型的 C 引擎。开场白是:分享数据,因为你的引擎是布局处理最自然的主场——而且我的一个发现表明,这个受众正是你。

首次接触:两条论点在一条回复中死亡

mabaeyens 没有与我的框架争论。他直接测量了它。

他的回复把我的三个论点——将最热专家固定在 RAM 中、按共激活预取下一层的专家、以及重新排序文件——中的前两个,用自己引擎的计数器击垮了。在相同主题、相同会话流量下,固定驻留集的热专家输给了普通的每层 LRU:LRU 已经缓存了复用,而每一个被冻结的槽位都是工作负载漂移无法使用的槽位。跨层预取没有可运行的信号:相邻层之间的专家重叠度测得 0.017,而随机打乱对照组为 0.016。独立每层路由器加上负载均衡损失使各层去相关。没有什么可以预测。

当天,我在该线程中撤回了这两个论点。

我想强调的是,这正是整个故事的关键:该线程把这些证伪视为范围界定,而非失败。主张没有死亡,而是迁移了。如果驻留会杀死解码增益,那么布局杠杆就会转移到仍然存在读取的地方——冷预填充,以及(正如他接下来的测量所示)一旦专家表远大于 RAM、缺页永远保持冷态时的解码。他的碎片化数字让论据比我最初的说法更清晰:他的读取器每个解码令牌发出约 471 次文件打开,每个冷专家有九个字节范围。杠杆并未死亡。它只是被错误地贴了标签。

线程收敛出的规则

没有人宣布一种方法论。它是一次次烫伤后逐渐形成的,最终看起来是这样的:

在运行实验前锁定实验。当我们设计决定性测试——仅合并(无配置文件、确定性)与合并加共激活重排(需要追踪)——时,实验臂、指标和决策规则已在任何人运行任何东西之前就在线程中达成一致,因为答案决定了是否会破坏他的引擎“直接从分片读取”的不变性。

预先注册数字,并设定可能伤害自己的阈值。我公开的预测是:在更深的卸载下,合并增益可达约 +20%,而“如果落在 +15% 以下,诚实的文章会说这个杠杆最多只是‘不错但非必要’。”最终结果是 +9.7%。我完全认输——没有诉诸配置差异,因为预先注册已经放弃了这些。

以提交速度认输。我的分析模型高估了一个 36 µs 的 open() 开销,而他的读取器早已消除了这个开销——他的共享 fd 修复在他引用我所依据的数字后大约四分钟就发布了。当他的测量结果低于我的模型时,模型当天就公开败北。记分备注:在整个线程中,我对冷读份额的预测 0-for-2(两次方向相反),社区的规则因此硬化为每个引擎分别测量,绝不跨引擎搬用

有对照组,否则不算发生。线程中的金丝雀是我最喜欢的产物。一个“2.23× 加速”实际上是 macOS 拒绝驱逐驻留页面(F_NOCACHE 并不像我们三人假设的那样工作;迹象是控制臂出现了不可能的 0.18×)。一个预取 A/B “证实”了预测臂,直到作者发现两个单元运行的代码完全相同——字节完全一致的命中率就是迹象——并正确重新运行:预测器以 2.9% 的差距输了,每节省一个字节却读取了 6.9 个字节。跨架构重叠声称显示出 1.17× 的信号(相对于分析均匀基线)和 0.98×(相对于打乱对照组)——分析基线正在制造信号。双臂交换以控制页缓存状态,成为线程中每个 A/B 的标准做法。

指明基线,否则数字无法迁移。同一个重写在每个专家发出九次零散读取的读取器上为 +32%,在深度卸载下每个投影三次读取的场景下为 +31%,而在已经将每个专家连续存储的容器上则接近零。不指明基线读取模式就引用布局增益,正是数字停止复现的原因。

维护一个活的工件。在过程中,我以评论形式发布了一个合并摘要,并对其进行版本控制——目前已到 v1.0 至 v1.5.1——通过 API 就地编辑,带有变更日志,并明确允许参与者划线更正。人们使用了它。一位参与者的划线纯粹是归属性的(引擎名称——我随后连续两天拼错;在 v1.4.1 中更正,是的,这也在变更日志中)。这个摘要,而非任何单一结果,才是线程的产出:范围界定的发现、测量的点,以及陷阱部分。

成为回报的证伪

+9.7% 的失败是这个项目发生过的最好的事。

其背后的机制——合并是一个 IOPS 杠杆,而 4 MB 的切片已经带宽受限——被推广为线程现在所称的切片大小定律:增益 ≈ 1 + saved_ops · t_op / (slice_bytes / BW),其中 t_op ≈ 100 µs,BW ≈ 6 GB/s 在该硬件类别上。端到端地,它与冷读份额 s 组合为 speedup = 1/((1−s) + s/g)。这是一个工程拟合,而非物理定律——但它现在有来自五种引擎的六个测量点,而且它做了一件好模型该做的事:提前告诉你你的配置是否值得麻烦。

该定律的检验来自最初交流后加入的两名参与者。@lBroth 运行了我的转换脚本(未经修改),针对 119 GB 的 Qwen3-235B 构建,并将输出接入他自己的引擎:+32.3% 解码——不是因为每次读取的增益很大(并不大;大切片,g ≈ 1.34),而是因为在 2.6× DRAM 超配时,冷读份额约为 0.96,定律的另一项占主导地位。随后他在引擎内构建了更深的变体,并测得 +31.2%,而定律(针对他指明的基线定价)预测的是 +31.3%。@pierre427——运行生产级 GLM-5.2 分页器——预先注册了我们推导的每次读取增益区间(1.2,区间 1.15–1.3),并测得 1.195。这是定律的第一个先注册后测量的点。

另一端也被定价了,这同样重要:@philipjohnbasile测量了他的 744B 引擎容器——已经将每个专家存储为一次连续的约 19 MB 读取——残余增益为 +1.3%,低于他自己的推广门槛。他将其作为“不可行”发布,我同意了,而我早先提出的约 +20% GLM 级别数字也被撤回。合并阶梯现在已完全定价:九次零散读取 → +14–32%(取决于份额);每投影 → 深度卸载时 +31%;每专家连续容器 → 近似于无,达到设备带宽上限。一个其不适用域已被测量并发布的优化,比只有成功案例的优化更有价值。

空结果也是产品

截至今日,该线程的墓园包括:预测性预取(三个独立的空结果——结构性空结果、正确控制的 A/B、跨架构打乱测试)、热固定与 LRU 的比较、更智能的 LRU 驱逐,以及我最初的跨层框架(已撤回)。每个空结果都是有范围的——batch-1、解码、这些架构——而每一个都是某人现在不必构建的路线图项目。

然后结局自己写就了。线程后期,PhilipJohnBasile 开源了他的引擎——而其 README 竟然包含了几乎整个设计理论的独立复现,在我们交谈之前就在第五个引擎上进行了测量:路由器前瞻预取以 −6.96% 回滚、更智能的驱逐以 −26% 死亡且命中率零变化、静态固定死亡、热集训练死亡。他的总结是:“缓存策略前沿已关闭:容量与复用距离就是那堵墙。”我们从四个不同的代码库收敛到了同一堵墙。

(这次开源还解决了一个引文谜团,是线程中一个很小的精彩时刻:大家一直归因于我们测量的吞吐量数字,原来是他的引擎所 fork 的项目第 6141 行的一条意大利语代码注释——在 7 月初于 Linux 上测量,他的 fork 副本丢掉了平台限定符。我们两人“验证”了错误的出处,因为 GitHub 的代码搜索默认不索引那么大的文件。这也写进了陷阱部分。)

我的收获

  • 把主张交给有机器能击垮它的人。四位引擎作者在几天内证伪、界定或扩展了每一个主要主张。普通受众做不到。利基线程不是谦逊的替代启动方式,而是更高带宽的渠道。
  • 预先注册让犯错成本低廉。我提前承诺的每一个数字,要么存活下来(现在具有分量),要么干净地死亡(并产生了一条定律)。我犹豫不决的两次预测,正是需要后续多轮才能理清的。
  • 公开认输就是货币。该线程的作者在结尾时说得比我好:“我从我错了的部分学到的,比我正确的部分多。好的线程。”
  • 保持一个可版本控制、可划线更正的摘要。它优于滚动回放和任何论文:它是当前的、可编辑的,每位参与者都有权更正它。
  • 把空结果和成功一起发布。在原版 llama.cpp 上的持平结果,放在最前面发布,正是正确的人参与进来的原因。

该线程仍在开放。共激活感知排序现在是 mlx-lm PR 上命名的后续任务;colibri 社区正在用本工作的共激活格式构建专家图谱;有一个已共同签署、预先声明的跨引擎实验正在等待两份路由追踪。合并摘要在这里(固定在线程末尾附近),可复现的流水线公开,仓库在github.com/doramirdor/mbolt

如果你有一个你相信的基准测试,找到那五个其引擎能证伪它的人,并发布到他们所在的地方。最坏的情况是你失去一个主张。最好的情况是你失去两个,却得到一条定律。


按测量顺序致谢:@mabaeyens(mlx-lm 专家卸载,以及大部分证伪)、@lBroth(规模验证、引擎内构建)、@philipjohnbasile(744B 端点,iliria)、@pierre427(生产级分页器,已注册区间点),以及 colibri 团队——@JustVugg、@ZacharyZcR、@bokiko、@mohamedmastouri2000-boop——他们正在把共激活工作带到我们谁都没有计划过的地方。

更多信息和其他项目请访问 https://getnadir.com