26B 参数模型塞进 2GB 内存跑,通常会被当作一个标题党。TurboFieldfare 把它做成了真事。这个开源项目用 Swift + Metal 从头搭建了一套推理引擎,在 8GB 内存的 M2 MacBook Air 上运行 Google 的 Gemma 4 26B-A4B 指令微调模型,常驻内存控制在 2GB 左右,解码速度 5-6 tokens/秒。在 24GB M5 Pro 上可达 31-35 tokens/秒。
26B 总参数、3.88B 激活参数的 MoE 模型,4-bit 量化后磁盘占用约 14.3GB。把它装进 8GB 内存的机器,常规方案直接走不通。TurboFieldfare 的做法是将模型拆成两部分:1.35GB 的共享权重(embedding、attention、router、shared expert、norm、scalar)常驻内存,12.9GB 的路由专家池留在 SSD 上按需读取。每生成一个 token,GPU 只需要用到 router 选出的 8 个专家(每层),而不是全部 128 个。

MoE 专家流式加载:mmap 为什么输了
TurboFieldfare 最核心的设计决策是专家的按需流式加载,而实现这个机制的第一版方案用了 mmap。
mmap 的思路很直觉:把专家文件映射到虚拟内存空间,第一次访问某页时操作系统自动从磁盘读入。代码零拷贝,看起来既简单又高效。问题出在 8GB 机器上专家工作集太冷。TurboFieldfare 作者记录的实测数据:
| 方案 | 冷专家读取延迟 | 完整流式模拟吞吐 |
|---|---|---|
| mmap(demand paging) | 9.88 ms | ~0.50 tok/s |
| 显式 pread | 2.79 ms | 3.97 tok/s |
pread 快了 3.5 倍,吞吐快了 8 倍。原因在于显式 pread 给了运行时对读取时机和并发的完全控制权,而 demand paging 把这些决策丢给了虚拟内存系统,后者无法预知哪些专家即将被用到。
切换到显式 pread 后,运行时为每层维护一个 16 槽 LFU(最少使用频率)缓存。路由器选定 8 个专家后,CPU 先检查缓存命中情况,命中的直接复用,未命中的分配一个可驱逐槽位启动有界并行读取。Metal GPU 同时计算常驻的共享专家分支,实现读取与计算的粗粒度重叠。
缓存策略也从 LRU 换成了 LFU。实测中 LFU 将专家 I/O 从 72.6 ms/token 降到 64.8 ms/token。原因在于专家选择模式存在重复性(同一个专家在不同 token 中被反复选中),LFU 能更准确地识别高频专家并保留。
GPU 内核优化:持久化工作组的 75% 提升
专家加载解决的是 I/O 瓶颈,GPU 计算侧同样有大量优化空间。MoE 路由计算是解码阶段最大的 GPU 开销。
第一个尝试是 SIMD 协作内核:让多个 lane 合作处理一个专家。这个方案看起来更规整,但实际把 GPU 可调度的独立工作拿走了。路由计算阶段从 230 ms 翻倍到 527 ms。
正确方向是持久化工作组(persistent workgroups)。独立的工作组不断认领行直到整个 dispatch 完成,给 GPU 更多可调度的工作。同一个阶段从 239 ms 降到 60 ms,降幅 75%。解码速度从 2.19 tok/s 提升到 3.31 tok/s,提升 51%。这是整个项目中端到端提升最大的单次内核重写。
| 优化项 | 路由 MoE GPU 时间 | 解码速度 |
|---|---|---|
| 基线 | ~239 ms | 2.19 tok/s |
| SIMD 协作内核(失败) | ~527 ms | 下降 |
| 持久化工作组 | ~60 ms | 3.31 tok/s |
注意力计算同样受益于工作划分方式的改变。Split-KV attention 将缓存序列拆分给不同 threadgroup,第二轮 merge 在线 softmax 的部分结果。单独测试中,滑窗注意力快 3.3 倍,4K 全注意力快 4.1 倍。
被推翻的优化:103 项实验中的失败案例
TurboFieldfare 最有参考价值的部分恰恰是 103 项实验中那些"看起来很对、实际不行"的案例。这些失败记录对做本地 LLM 推理优化的开发者有直接的参考价值。
读取提示(F_RDADVISE)的不稳定
fcntl 的 F_RDADVISE 是一个看起来完美的优化:告诉操作系统即将读取某个区域,让它提前预取。某次配对测试中,I/O 从 87.4 降到 72.2 ms/token,吞吐从 5.18 提升到 5.45 tok/s。但另一次 1536 token 的探测中,开启提示反而从 5.69 tok/s 暴跌到 4.03 tok/s。多次重复后,崩溃无法稳定复现,某些变体又能赢。
没有可靠的策略能判断什么时候该用这个提示。最终生产环境保持关闭。同样的模式出现在缓存预读标志、MTLIO、投机读取等方案上:隔离测试快,完整运行慢。
TurboQuant KV 缓存的失败
将 KV 缓存从 FP16 压缩到 K4/V4(4-bit)看起来是明显的内存优化。在 4K 上下文时确实省了约 82 MiB。但随着上下文增长,问题出现了:压缩缓存需要覆盖全部 30 层,而 FP16 方案只对 5 层全注意力使用线性增长存储,25 层滑窗用固定 1152 行环形缓存。长上下文时,压缩版本反而更大。同时质量评估也未通过。
粗粒度重叠胜过细粒度
更细粒度的重叠理论上更好:每个路由专家组读取完成就立即启动计算。实际测试中吞吐从 4.80 降到 4.65 tok/s,还改变了生成结果。粗粒度方案(先处理所有缓存命中的专家,再处理需要读取的)更快也更容易同步。重叠有效的前提是所有权和依赖关系保持明确。
Benchmark:不是最快,但能用
| 硬件 | 引擎 | 解码速度 | 内存占用 |
|---|---|---|---|
| 8GB M2 MacBook Air | TurboFieldfare | 5.1-6.3 tok/s | ~1.9-2.1 GB |
| 24GB M5 Pro | TurboFieldfare | 31-35 tok/s | ~2.1 GB |
| 24GB M5 Pro | mlx-lm | 76-82 tok/s | 8.3-9.8 GB RSS / 14.7-15.3 GB GPU 分配 |
M5 Pro 上 MLX 的吞吐是 TurboFieldfare 的 2 倍多,但内存占用也高出 5-7 倍。TurboFieldfare 的目标是在 8GB 机器上让 26B 模型跑起来,这个场景下 MLX 根本无法加载。
8GB M2 上的解码分解(每 token 162.8 ms):
| 工作项 | 耗时 |
|---|---|
| 专家读取 | 83.1 ms |
| 命令缓冲区管线等待 | 55.6 ms |
| 绑定输出头 | 14.2 ms |
| 其他运行时工作 | 9.9 ms |
超过一半的时间花在等 SSD 读取专家数据。随着 SSD 速度提升和专家预测算法改进,这部分有进一步优化空间。
安装器同样遵守有界内存规则
TurboFieldfare 的安装器也遵循与推理相同的有界内存原则。它不从 HuggingFace 下载完整的 safetensors 分片到磁盘,而是直接从远程源的字节范围(byte range)流式重打包到 .gturbo 格式。
安装过程中,最大 payload 和 scratch heap 各被限制在 524,288 字节(512KB)。完整的 15GB 级源文件从不存在于 Swift 堆缓冲区中。验证安装时,安装器通过 223 个 range 请求传输了 14,620,479,420 字节的源数据。完成的纯文本模型安装占用 14,291,921,884 字节(约 14.3GB)。
取消安装会暂停事务而非删除数据。恢复安装会重新验证已完成范围的摘要,只下载缺失或损坏的范围。
运行时架构
TurboFieldfare 的模型架构基于 Gemma 4 26B-A4B 的关键属性构建:
- 30 层 transformer:25 层滑窗注意力 + 5 层全注意力
- 128 个路由专家,router 每 token 选 8 个
- 1 个稠密共享专家,输出直接相加,不经过路由权重
- MLX affine 4-bit 量化:每 64 个权重一组,BF16 scale + BF16 bias
- 8-bit router + 4-bit 共享和路由专家
- embedding 和 LM head 共享量化权重
内存分配拆分:
| 资源 | 大小 | 说明 |
|---|---|---|
| 共享模型文件 | 1,353,771,068 bytes (~1.35 GB) | 只读映射,Metal buffer 包装 |
| FP16 KV cache(4K) | ~305 MiB | 25 层滑窗用 1152 行环形,5 层全注意力线性增长 |
| 运行时 scratch | ~17.6 MiB | 128-token prefill arena + split-attention scratch |
| 路由专家槽位 | 16/层,共 ~1.50 GiB 容量 | 2MiB 对齐,仅在读取后变为常驻 |
TurboFieldfare 提供四种使用方式:原生 SwiftUI/AppKit Mac 应用、命令行界面(CLI)、本地 OpenAI 兼容服务器、以及 Swift 库。CLI 支持 --messages-file 指令聊天和 --prompt 原始补全两种模式。OpenAI 兼容服务器监听 127.0.0.1:8080/v1,支持 Chat Completions、流式输出和函数工具调用。
谁需要这个
TurboFieldfare 解决的问题非常具体:在低内存 Apple Silicon Mac 上运行大参数 MoE 模型。对于拥有 16GB+ 内存的用户,MLX 或 llama.cpp 可以直接全量加载,速度更快。TurboFieldfare 的价值在于它证明了 8GB 机器可以运行 26B 模型,前提是有足够精细的内存管理和 I/O 策略。
项目作者 Andrey Mikhaylov 是一名 iOS 和 Metal 工程师,日常工作中处理图像、视频和端侧 AI。TurboFieldfare 以 Fieldfare(田鸫)命名,是作者最喜欢的鸟。项目采用 Apache 2.0 开源许可,模型权重不随源码分发,安装时从 pinned HuggingFace checkpoint 独立下载。