MoE(Mixture of Experts)モデルの重みは、そのほとんどが常には使われない。Gemma 4 26B-A4B は各層に128個のエキスパートを持ち、ルーターが1トークンあたり8個だけを選ぶ。ならば全部をメモリに載せる必要はないはずだ——TurboFieldfare(drumih/turbo-fieldfare・★6,602・Apache-2.0・2026-09-04時点)は、その発想をApple Silicon向けにSwiftとMetalで実装したランタイムである。
公称は「26BのGemma 4を約2GBのRAMで動かす」。14.3GBあるモデルのうち、共有コア1.35GBとFP16のKVキャッシュだけをメモリに置き、各トークンで必要になったエキスパートだけをSSDから読む。
この記事はこの主張を検証しようとして、実行にすら到達できなかった記録である。手元の環境(M2 MacBook Air 16GB / macOS 14.5)では swift build がバージョン不一致で止まった。そこで論点を切り替え、「8GBのMacでも動く」という見出しの手前に実際は何のゲートがあるのかと、公式・コミュニティが公開している実測値から何が読み取れるのかを整理する。
Package.swift の platforms と README の Requirements、GitHub Releases の実測(16本すべて添付アセット0件)から起こしたもの。・26B(アクティブ約3.88B)のGemma 4を、共有コア+KVキャッシュだけメモリに置く設計
・MLXやllama.cppのラッパーではなく、このモデル専用のSwift+Metal実装
・実際のゲートはRAMではなくmacOS 26 / Metal 4 / Xcode 26 / Swift 6.2
・配布バイナリは1つも無い(16リリース・添付0件)。全員がソースからビルドする
Apple Silicon でのローカル推論全般の位置づけはLLMとは?仕組み・主要モデル比較・ローカル実行・量子化を一気にまとめる2026年版にまとめている。
TurboFieldfareとは:エキスパートをSSDに置いたままMoEを走らせる
設計の核は docs/SYSTEM_DESIGN.md に書かれている。Gemma 4 の構造のうち、実装を規定しているのは次の4点だ。
| Gemma 4 の性質 | ランタイム側の帰結 |
|---|---|
| 30層(スライディングウィンドウ注意25層+全注意5層) | KVキャッシュを2種類のレイアウトで持つ |
| 各層に128エキスパート、ルーターは1トークンにつき8個を選択 | 選ばれた8個ぶんだけをSSDから読む |
| 密な共有エキスパートが別ブランチで常に走る | 共有側(1.35GB)は常駐させる |
| 埋め込みと言語モデルヘッドが量子化重みを共有 | 常駐コアをさらに削れる |
量子化は MLX の affine 4bit(グループ64・BF16のスケールとバイアス)で、ルーター射影だけ8bit、共有・ルーテッド両方のエキスパートが4bit という配分になっている。ルーターの精度を落とすと選ぶエキスパートを間違えるので、そこだけ精度を残すという判断だ。
スライディングウィンドウの25層は直近1,024トークンだけを見て、K/Vを1,152行のリングに保持する(余分な128行はチャンク化プリフィルの書き込み用)。全注意の5層は追記のみで全文脈を持つ。この非対称なKV設計が「2GB」を成立させている実質的な部分で、単に量子化を強めただけの数字ではない。
+ FP16 KVキャッシュ"] -->|常駐| B["メモリ 約2GB"] C["ルーテッドエキスパート
128個 × 30層"] -->|層ごとのファイル| D["SSD 約14.3GB"] E["ルーター 8bit"] -->|1トークンにつき8個を選ぶ| D D -->|選ばれた分だけ読む| B B --> F["Metal 4 カーネルで
prefill / decode"]
.gturbo という独自のモデルディレクトリ形式を持ち、インストーラ(TurboFieldfareRepack)が Hugging Face の固定リビジョンから必要なバイト範囲だけをレンジリクエストで取得して、届いた端から再パックする。チェックポイント全体を一度ディスクに置かないので、14.3GBの設置に対して余分な14.3GBが要らない。8GB機を想定したときにディスクも効いてくる、という前提が設計に効いている。
turbo-fieldfareを動かす前にある3つのゲート(RAMではない)
ここが本記事の主眼だ。READMEの見出しは「Gemma 4 26B-A4B inference in about 2 GB of RAM / A custom Swift + Metal runtime for any Apple Silicon Mac, even the 8 GB ones」で、制約としてRAMだけが前に出ている。実際の Requirements 節と Package.swift を読むと条件はもっと厳しい。
// Package.swift(tag 0.7.1)冒頭
// swift-tools-version: 6.2
let package = Package(
name: "TurboFieldfare",
platforms: [
.macOS(.v26),
.iOS(.v26),
],
手元の M2 MacBook Air(macOS 14.5 / Swift 5.10 / Xcode 未導入・Command Line Tools のみ)で実行した結果はこうなった。
git clone --depth 1 --branch 0.7.1 https://github.com/drumih/turbo-fieldfare.git
cd turbo-fieldfare
swift build -c release
# → error: 'tf': package 'tf' is using Swift tools version 6.2.0
# but the installed version is 5.10.0
swift build はソースを1行もコンパイルする前に、マニフェストのバージョン不一致で停止する。つまりRAM 8GBのMacを持っていることは条件の一部でしかなく、そのMacが macOS 26 に上がっていて、かつ Xcode 26 が入っていることが実質的な前提になる。8GBのMacBook Airを使い続けている層と、最新のmacOSに上げてXcodeを入れている層は、必ずしも重ならない。
配布バイナリが1つも無い
これを避けようがない事実にしているのが、リリース資産の状況だ。
gh api repos/drumih/turbo-fieldfare/releases --paginate \
--jq '[.[]|{tag:.tag_name, n:(.assets|length)}]'
# → releases: 16 | total assets: 0
v0.1(2026-07-20)から 0.7.1(2026-08-28)まで16本のリリースがあり、添付アセットは合計0件だった。DMGもpkgもなく、Homebrewのformulaも見当たらない。README の Quick start が git clone → swift build -c release から始まっているのは省略ではなく、それが唯一の入手経路だからである。
上記のとおりビルドに到達しなかったため、デコード速度・メモリ使用量・出力品質はいずれも自分では測っていない。以下で扱う数値はすべて公式リポジトリまたはコミュニティ提出値であり、この記事の実測ではない。約15GBのモデルダウンロードも、実行できない以上は行っていない。
公式ベンチマークの読み方:tok/s より TTFT を見る
docs/BENCHMARKS.md にある8GB M2 MacBook Air(Mac14,15)の実測は次のとおり。
| プロンプト / 生成トークン | Prefill | TTFT | Decode | ピークRSS / フットプリント |
|---|---|---|---|---|
| 6 / 32 | 7,025 ms | 7,979 ms | 6.30 tok/s | 1,304 / 1,791 MiB |
| 121 / 64 | 7,934 ms | 8,862 ms | 5.10 tok/s | 1,528 / 1,776 MiB |
| 527 / 64 | 21,736 ms | 22,649 ms | 5.90 tok/s | 1,535 / 1,886 MiB |
| 1,017 / 128 | 36,729 ms | 37,656 ms | 5.38 tok/s | 1,455 / 1,971 MiB |
メモリの主張は数字で裏付けられている。ピークRSSは1,304〜1,535 MiB、フットプリントでも1,776〜1,971 MiBで、「約2GB」は誇張ではない。26Bのモデルをこの常駐量で回している事実は素直に評価してよい。
一方で同じ表に、見出しに出てこない数字が並んでいる。TTFT(最初のトークンが出るまで)が 8.0秒〜37.7秒である。1,017トークンのプロンプト——コードを数十行貼る程度の長さ——で、最初の1文字が出るまで36.7秒のプリフィルが走る。
| プロンプト長 | TTFT | 「5.4 tok/s」から受ける印象との差 |
|---|---|---|
| 6トークン | 8.0秒 | 短文でも8秒待つ |
| 1,017トークン | 37.7秒 | 対話としては成立しにくい |
デコードが5〜6 tok/s なら「遅いが読める速度」に聞こえる。だが実際の体感はプリフィル待ちが支配的で、8GB機では長いプロンプトを投げるほど不利になる。READMEも「The measured result is a reference point, not a performance ceiling」と断ってはいるが、TTFTの桁はもう少し前に出てよい情報だと思う。
なお公式の注記は誠実で、「4点は信頼区間を構成しないし、長時間生成の持続性能を示すものでもない」「各ワークロードは新規プロセスで1回ずつ実行し、ファイルキャッシュは温かいが統制していない」と明記されている。1回計測であることを隠していない点は、この種のベンチマークとしては信頼できる書き方だ。
コミュニティ実測から見える「同じチップでも8GBと16GBで2.3倍」
docs/COMMUNITY_BENCHMARKS.md には、固定プロンプト・固定シード(temperature 0.2 / Top-K 64 / Top-P 0.95 / 文脈4,096)でGitHubユーザーが提出した結果が集まっている。ここに同じチップでRAMだけが違う組み合わせがあった。
| Mac | RAM | 61/511 | 430/669 | 3,015/604 |
|---|---|---|---|---|
| M1 MacBook Air | 8 GB | 3.340 tok/s | 3.053 tok/s | 2.642 tok/s |
| M1 MacBook Air | 16 GB | 7.802 tok/s | 7.173 tok/s | 6.093 tok/s |
| 倍率 | 2.34倍 | 2.35倍 | 2.31倍 |
同一チップ・同一プロンプト・同一設定で、RAMが8GBか16GBかだけで2.3倍の差が出ている。しかも3つのプロンプト長すべてで2.31〜2.35倍と揃っており、たまたまの1点ではない。
理由は設計から素直に説明できる。エキスパートはSSDに置いてページキャッシュ越しに読むので、空きメモリが大きいほど直前に読んだエキスパートがキャッシュに残る。8GBではモデル以外のプロセスと奪い合って追い出され、そのぶん実SSDリードが増える。この構造は他のSSDストリーミング型推論と共通で、WASTE解説|Kimi K3 2.78TをNVMeで動かすC製推論エンジンと「RAM増設が効かない」実測が観測した「キャッシュを増やすと遅くなるページングの崖」とは逆向きだが、ページキャッシュの効き方が支配的という点では同じ土俵にある。
ストレージ側の結果も示唆的だった。
| Mac | RAM | ストレージ | 61/511 |
|---|---|---|---|
| M2 Max Mac Studio | 32 GB | 外付けPCIe NVMe | 25.310 tok/s |
| M1 Max MacBook Pro | 64 GB | 内蔵SSD | 22.888 tok/s |
| M2 Pro Mac mini | 16 GB | 内蔵SSD | 15.713 tok/s |
| M4 Mac mini | 16 GB | 内蔵256GB SSD | 10.847 tok/s |
外付けNVMeが最速の列にいる。SSDストリーミング型というと外付けは不利に見えるが、少なくともこの提出値ではそうなっていない。ただし提出はいずれも1回実行(一部は5回の中央値)で、統制されたハードウェア比較ではないと公式も明記している。傾向として読む以上のことはできない。
同じ「巨大MoEを小さいメモリで」という問題設定は、純Cで744BのGLM-5.2を25GBに載せるcolibri解説|GLM-5.2(744B MoE)を25GB RAMで動かす純Cランタイムの実測速度にも見られる。TurboFieldfareが違うのは、モデルを1つに絞って専用実装にした点だ。汎用性を捨てた代わりに、Gemma 4 のRoPE・正規化・KVレイアウトに合わせ込める。README自身が「model-specific rather than a wrapper around MLX or llama.cpp」と書いているとおりで、これは強みであると同時にGemma 4 以外には使えないという制約でもある。
OpenAI互換サーバーと、速度に効くつまみ
TurboFieldfareServer を使うと、ローカルにChat Completions互換のエンドポイントが立つ。既存のOpenAI SDKやエージェントクライアントをそのまま向けられる形だ。
swift build -c release --product TurboFieldfareServer
.build/release/TurboFieldfareServer --model scratch/gemma4.gturbo --port 8080 --max-context 16384
# 別ターミナルから
curl -s http://127.0.0.1:8080/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"gemma-4-26b-a4b-it","messages":[{"role":"user","content":"Reply with exactly READY."}],"temperature":0,"max_completion_tokens":16}'
サーバーは
127.0.0.1 に**認証もTLSも無し**でバインドする。公式ドキュメントは "Do not expose it through a proxy or tunnel." と書いており、社内共有のつもりでngrok等に通すと無認証のLLMエンドポイントを公開することになる。またモデルを持つプロセスは同時に1つだけという制約があり、起動前に pgrep -fl 'TurboFieldfareServer|TurboFieldfareMac|TurboFieldfareCLI' で衝突を確認する手順まで書かれている。
docs/RUNTIME_CONTROLS.md には、速度とメモリのトレードオフを直接触れるつまみが並んでいる。8GB機で効きそうなものを抜き出すと次のとおり。
| つまみ | 既定 | 効果(公式の記述) |
|---|---|---|
--expert-cache-slots |
16 | スロットを増やすと再読み込みが減るが、16を超えるとRAMを食う。チャンク化プリフィルには最低16必要 |
--expert-cache-policy |
LFU | 満杯時にどのエキスパートを追い出すか(lfu / lru) |
--prefill-chunk-tokens |
128 | チャンクは層の外側でループするため1チャンクごとにその層のエキスパートを読み直す。7,019トークンのプロンプトでは256が128より16%速く、出力はバイト単位で同一だった |
--max-context |
CLI/アプリ 8K、サーバー 16K | FP16 KVキャッシュの量に直結。アプリはメモリ差分を表示する |
--rdadvise |
Off | 前節の実験どおり既定オフ。「短いデコードを速くし長いものを遅くしうる」と明記 |
--prefill-chunk-tokens を 256 にすると16%速くなる、という具体的な数字が公式ドキュメントに書かれているのに既定が128なのは、チャンクを大きくするとピークメモリが上がるためだろう。8GBという設計上の下限を守る側に既定を寄せた結果で、16GB以上の機体では既定を触る余地があることを示唆している。
103件の実験記録が教える「効いたのに採用しなかった最適化」
このリポジトリで技術的にいちばん読む価値があるのは、README でも SYSTEM_DESIGN でもなく docs/experiments/EXPERIMENT_INVENTORY.md かもしれない。103件の計測済み実験が、採否の理由つきで分類されている。
| 分類 | 意味 |
|---|---|
| Production | 既定のランタイム、または凍結したM2参照プロファイルに残った |
| Conditional | 特定のホスト・ワークロード・フラグ・メモリ予算でだけ効く |
| Rejected | 計測したが採用しなかった |
| Correctness repair | 速度と無関係に、正しい出力のために必要だった |
| Reversed rejection | より良い判定基準が初期の却下を無効にし、後続案が採用された |
| Scope decision | 意図的に手を付けていない |
| Unexecuted hypothesis | 妥当だがランタイムでの判定を通していない仮説 |
103件の内訳は、プリフィル17件・デコードMoE/INT4/ルーター18件・注意とKVキャッシュ14件・フュージョンとオーケストレーション15件など、領域ごとに9本の要約へ整理されている。
いちばん教訓的なのが F_RDADVISE(macOSのfcntlでファイル読み出しのヒントを与える)の顛末だ。測ると確かに効いていた。
| 条件 | ヒント無し | ヒント有り |
|---|---|---|
| ペア中央値のI/O | 87.4 ms/token | 72.2 ms/token |
| 同・スループット | 5.176 tok/s | 5.449 tok/s |
| 1,536トークンのプローブ(別回) | 5.687 tok/s | 4.028 tok/s |
同じ最適化が、ある条件では5%速くし、別の条件では29%遅くした。しかも後の反復ではその崩壊が再現せず、勝つバリアントもあった。結論として「どのときにヒントが効くかを確実に判定できる方針が見つからなかった」ため、本番ではオフのままにしている。
読み側の他の候補も同じ形をたどっている。キャッシュとread-aheadのフラグは中立、投機的読み出しは単体では速く見えたのにデコードを遅くしプリフィルを82.50秒から123.64秒へ伸ばした、ファイルオフセット順の読み出し整列は1回勝って反復で負けた、MTLIOは既に温まったデータには速いが実際のミスの多くはその条件を満たさなかった——。
A/Bで一度勝った数字を採用根拠にしない、という判断が一貫している。当サイトでも同種の落とし穴(測定順序やページキャッシュの状態が結果を1桁動かす)を何度か踏んでいるので、この記録は他人事ではない。TurboFieldfareを使うかどうかとは別に、SSDストリーミング型の推論を自作しようとしている人には、この103件が最大の資産だと思う。
誰が今すぐ試せて、誰が待つべきか
| 条件 | 判定 | 理由 |
|---|---|---|
| macOS 26 + Xcode 26 が入っている Apple Silicon Mac | ○ | 唯一の入手経路であるソースビルドが通る |
| 8GBのMacで大きいモデルを動かしたい | △ | 動くが同一チップの16GB比で約2.3倍遅い |
| macOS 25以前を使っている | ✕ | Package.swift の platforms が .macOS(.v26) で弾かれる |
| Xcodeを入れたくない/CLTのみ | ✕ | Swift 6.2 が要る。手元では 5.10 で停止した |
| Gemma 4 以外を動かしたい | ✕ | このモデル専用実装。ラッパーではない |
| ディスクの空きが20GB未満 | ✕ | モデル設置だけで約14.3GB(画像対応で+1.1GB) |
| 長いプロンプトで対話したい | △ | 8GB機で1,017トークンのTTFTが37.7秒 |
①
sw_vers で macOS が 26 以上か(26未満ならここで終わり)②
swift --version が 6.2 以上か(Command Line Tools だけでは足りないことが多い)③
df -h で空きが20GB以上あるか(モデル14.3GB+ビルド成果物)3つとも通ったら
git clone → swift build -c release → .build/release/TurboFieldfareMac で、アプリ内の Download からモデルを入れる。
まとめ:主張は正しく、条件の説明が足りない
TurboFieldfareの技術的な主張——26Bのモデルを約2GBの常駐で動かす——は、公式ベンチマークのピークRSS 1,304〜1,535 MiB という数字で裏付けられている。103件の実験記録を docs/experiments/ に残し、ベンチマークの但し書きも誠実で、数字を盛る種類のプロジェクトではない。
問題は数字ではなく、条件の見せ方にある。「even the 8 GB ones」という一文が前面に出る一方で、macOS 26・Xcode 26・Swift 6.2・配布バイナリ0件という、RAMより厳しいゲートが Requirements 節の中に収まっている。8GBのMacを使い続けている人ほど最新OSに上がっていない可能性が高いことを考えると、この順序は実態と逆だと思う。
さらにコミュニティ実測は、同一チップでも8GBと16GBで2.3倍の差があることを示している。「8GBでも動く」は事実だが、それは「8GBが快適」という意味ではない。このプロジェクトが本当に届けたい相手(古い8GB機のユーザー)が、いちばん条件を満たしにくく、いちばん遅いという構図が、現時点の実像である。
なお本記事は手元でビルドできなかったため、性能値はすべて一次資料からの引用であり、独自の実測ではない。実測したのは、ビルドが止まる位置と、リリース資産が0件であることだけである。