Appleシリコンの統合メモリは速いが、量は買った時点で決まる。16GBのMacBook Airに後からメモリを挿すことはできない。そこで「重みの大半をSSDに置いたまま、必要な分だけ流し込む」という発想が出てくる。slotstream(carloslfu/slotstream・★246・MIT)は、125BのMoEモデルであるQwen3.8-Flash-Next(4bitで約104GB)をSSDからストリーミングし、48GBのMacで約12 tok/sを出すと公称するSwift製の推論エンジンだ。Show HNでは225ポイント・106コメントを集めた(2026-09-03時点で226ポイント)。
doctor は8.1GBの下限プランを出し、pull は「107.3GB必要・50.9GB空き」で拒否した(録画時は8GBのベンチ用ファイルが残っていたため空き容量が少なめに出ている)。筆者環境で撮影。30秒でわかる
・やっていること:104GBのうち常駐は3.8GBだけ。68GBのエキスパートと32GBのn-gramテーブルをSSDから随時読む
・検証できたこと:バイナリの真正性(SLSA来歴)、16GB実機でのプラン、ディスク不足時の拒否、そしてSSD速度が開発機の8〜10分の1であること
・検証できなかったこと:12 tok/sという速度そのもの。重みに107.3GBの空きが要り、手元は約56GBだった
・新しさ:ストリーミング手法は新しくない(同種OSSが7本実在)。差別化点は公称値の全てに測定記録を紐づけCIで縛る運用のほう
この記事はLLMとは?仕組み・主要モデル比較・ローカル実行・量子化を一気にまとめる2026年版の系列にある個別ツール解説として、公式の一次情報と、16GB MacBook Airでの実測を突き合わせている。
slotstreamとは——104GBのモデルを「載せずに」動かす設計
slotstreamが対象にするQwen3.8-Flash-Nextは、125BパラメータのMixture-of-Experts(MoE)モデルだ。MoEの特徴は、パラメータの総量が巨大でも、1トークンの生成に使うのはそのごく一部だという点にある。slotstreamはここに賭けている。
公式のMEASUREMENTS.mdは、モデルの内訳をsafetensorsのヘッダからバイト単位で読み出して公開している。HTTPのrangeリクエストで11個のシャードのヘッダ(3,215テンソル)だけを取得しており、全体をダウンロードせずに数えた値だ。
| 構成要素 | 実測サイズ | 割合 | 常駐するか |
|---|---|---|---|
| ルーテッドエキスパート | 67.948 GB | 65.5% | ❌ SSDから随時読む |
| n-gram / PLEストア | 32.000 GB | 30.8% | ❌ SSDから随時読む |
| その他(norm・lm_head等) | 1.255 GB | 1.2% | ✅ 常駐 |
| Gated DeltaNet | 1.189 GB | 1.1% | ✅ 常駐 |
| QSAアテンション | 0.376 GB | 0.4% | ✅ 常駐 |
| ハイパーコネクション | 0.367 GB | 0.4% | ✅ 常駐 |
| embed_tokens | 0.358 GB | 0.3% | ✅ 常駐 |
| 共有エキスパート | 0.133 GB | 0.1% | ✅ 常駐 |
| ルーター | 0.126 GB | 0.1% | ✅ 常駐 |
| 合計 | 103.770 GB | 常駐は3.822 GB |
要点は最終行だ。常に載せておく必要があるのは3.822GBしかない。残りの約100GBは「その瞬間に使う分だけ」読めばよく、そこがストリーミングの余地になる。エキスパートは48層×512個で、1トークンが起動するのは各層10個だけだ。
「104GB」「105.3GB」「103.770GB」の食い違い
数字が3つ出てくるので先に整理しておく。これは矛盾ではなく、測っている対象が違う。
・103.770 GB:safetensorsのインデックスが持つ total_size。モデル本体のバイト数で、最も厳密な値
・105.3 GB:pull がダウンロードする25ファイルの合計。1.5GBのドラフトヘッド(投機的デコード用)などを含む配布物としての大きさ
・104 GB:Show HNのタイトルで作者が使った丸め値
記事タイトルや見出しで見かけるのはたいてい3つ目だ。実際にディスクを何GB空ければいいかを知りたいなら、見るべきは2つ目の105.3GB——さらに pull は2GBのマージンを足して107.3GBを要求する。
仕組み——なぜ単純なmmapではだめなのか
「ファイルをmmapして、OSのページングに任せればいいのでは」と考えるのが自然だ。llama.cppは実際にmmapを使う。だが公式READMEはこれを明確に否定している。
公式の説明(筆者は未検証)
MLXはメモリマップしたテンソルの一部だけを実体化できない。ある層のtop-10エキスパートを取り出すgather操作が、その層の512エキスパート全部を評価してしまう。結果、mmap経路は約100GBを読み込んで破綻する。標準の mlx_lm.load() を使った経路は、48GBの開発機を1トークンも出さないまま48GBのスワップに叩き込んだ——これがslotstreamが存在する理由だと作者は書いている。
この主張は重みが無いと再現できないため、筆者は検証していない。公式の説明として扱ってほしい。
slotstreamの答えは、OSのページングに任せず自前で管理することだ。エキスパートを固定サイズの「スロット」プールに pread で読み込む。プールは48層で共有されるので、よく使われる層が使われない層のスロットを借りられる。
(ルーター・アテンション・norm)"] B --> C{"ルーターが選ぶ
各層 top-10 / 512"} C -->|"ヒット"| D["スロットプール
(メモリ内キャッシュ)"] C -->|"ミス"| E["SSDから pread
1件 2.7648 MB"] E --> D D --> F["gather_qmm
4bit量子化行列積"] F --> G["次トークン"] G --> A
このとき重要なのは、キャッシュの大きさは速度を変えるが出力は変えないという性質だ。公式は「4GBキャッシュと24GBキャッシュでgreedyデコードの出力はバイト単位で一致する」ことを常設テストにしていると書いている。メモリが足りない機械では遅くなるだけで、答えが劣化するわけではない——ローカル実行の判断材料としてはありがたい設計だ。
なお、エキスパート1件のレコードは2,764,800バイトで、16KiBページに合わせて2,768,896バイト(169ページ)にパディングされている。この「約2.76MB」という単位が、後で見るSSDベンチマークの主役になる。
【実測】16GB MacBook Airでどこまで動いたか
ここからは筆者の実機での検証だ。環境は以下のとおりで、公称値の前提(48GB M5 Pro)とはかなり違う。
| 項目 | 筆者の環境 | 公式の開発機 |
|---|---|---|
| 機種 | MacBook Air(Mac14,2) | MacBook Pro |
| チップ | Apple M2(4P+4E) | Apple M5 Pro(18コア) |
| 統合メモリ | 16 GB | 48 GB |
| SSD | APPLE SSD AP0256Z(251 GB) | APPLE SSD AP2048Z(2 TB) |
| macOS | 14.5(23F79) | 26(Darwin 25.6.0) |
| Swift | 5.10(Command Line Tools) | 6.3.3 |
ソースからのビルドは失敗した(ツールチェーンの下限が未記載)
本記事の検証対象は v0.2.2(2026-09-02公開)である。執筆中の2026-09-03に v0.2.3 が出たが、以下の測定はすべて v0.2.2 に対するものだ。
READMEは「Command Line Toolsだけで足りる、Xcodeは要らない」と書いている。しかし手元では通らなかった。
error: 'slotstream': package 'slotstream' is using Swift tools version 6.0.0 but the installed version is 5.10.0
Package.swift は swift-tools-version: 6.0 を宣言しており、Swift 6を含むCommand Line Tools(16以降)が要る。READMEが間違っているのではなく、CLTのバージョン下限が書かれていないというのが正確な言い方だ。Xcodeが不要なのは事実で、必要なのは十分新しいCLTである。
配布バイナリは真正性を検証できた
ビルドが通らないので、v0.2.2のリリース資産を使った。READMEは curl | sh のワンライナーを案内しているが、ここでは資産を直接取得して検証した。
gh release download v0.2.2 --repo carloslfu/slotstream
shasum -a 256 slotstream-arm64.tar.gz
gh attestation verify slotstream-arm64.tar.gz --repo carloslfu/slotstream
公開されている .sha256 と計算値は一致し、来歴の検証も終了コード0で通った。ただし手元の gh 2.92.0は成功時に何も出力しなかったため、GitHubのAPIから中身を突き合わせて裏を取っている。
・述語:https://slsa.dev/provenance/v1
・対象:slotstream-arm64.tar.gz
・ビルド元:refs/tags/v0.2.2 の .github/workflows/release.yml(同一リポジトリ)
「タグ付きコミットからCIが署名付きで作っている」という公称は、そのとおりだった。
macOS 14でもバイナリは動いた
READMEは「macOS 14と15はインストーラしか動作確認していない(ランタイムは未確認)」と断っている。筆者のmacOS 14.5では --version が 0.2.2 を返し、doctor も正常に動作した。推論本体は重みが無いので試せていないが、少なくとも起動と診断系は動く。
実機のプランは「16GBの行」ではなく最下限だった
ここが一番おもしろい結果だ。READMEの階層表は「16GB Mac → slotstreamは10GB使用 → 約4 tok/s」と書いている。ところが実機の doctor はこう答えた。
device: applegpu_g14g | 17 GB RAM (2.1 GB reclaimable now), 11.5 GB Metal working set
target: 8.1 GB total for this process
cache: ~13 of 512 experts per layer (640 global slots = 1.8 GB pool)
expect: ~7.9 GB peak, ~3 tok/s warm decode (est. from M5 Pro anchors)
note: only 2.1 GB of 17 GB RAM is reclaimable right now — running at the 8.1 GB floor anyway
10GBではなく8.1GBの下限、約4 tok/sではなく約3 tok/s(いずれも推定値)。理由は最終行にある。17GBのうち回収可能なメモリが2.1GBしかなかったからだ。ブラウザやエディタが開いている実際の作業マシンでは、公称表の行には届かない。
READMEはこの点を隠していない。「中央の列は他に何もメモリを掴んでいないことを前提にしている」と書いてある。つまり階層表はアイドル状態の機械の話で、日常的に使っているMacの数字ではない。
プランナーの決定性は確認できた
--sim-ram で仮想的なメモリ量を指定すると、READMEの行を完全に再現できた。
・--sim-ram 16 → target 10.0 GB / 20-of-512 / 約4 tok/s(READMEの16GB行と一致)
・--sim-ram 48 → target 33.0 GB / 152-of-512 / 20.1GBプール / 約12 tok/s(READMEの48GB行と一致)
ただしこれが示すのはプランナーが決定的に同じ計算をすることだけで、tok/sの正しさではない。READMEの階層表そのものが doctor --sim-ram N の出力だからだ。同じ関数を2回呼んで同じ答えが出た、という以上の意味はない。
面白い副産物として、--sim-ram 48 のプリフィル値は約125 tok/sと出た。READMEが実機で測った同じ行は220 tok/sだ。つまりデコードのtok/sは目標メモリ量だけで決まる曲線引きで、プリフィル速度のほうは実機の性能に合わせてスケールしている。シミュレーションは完全な仮想機械ではなく、一部は手元のハードウェアを見ている。
ディスク不足の拒否は完全に検証できた
READMEの「ディスクが足りなければきっぱり断る」という主張は、手元の条件がちょうど反例づくりに向いていた。107.3GB必要なところに約56GBしかない。
Error: not enough disk: need 107.3 GB (105.3 GB to download + 2 GB margin),
have 59.6 GB free at /Users/user/.slotstream/models/qwen38-flash-next-mlx-4bit
・終了コードは64(EX_USAGE)
・実行前後で空き容量は変化なし
・~/.slotstream には空のディレクトリだけができ、書き込まれたデータは0バイト
必要量・内訳・空き容量・保存先を全て名指しして止まる。ダウンロードを始めてから容量不足で死ぬ実装が多いなかで、これは素直に good behaviour だと言っていい。この記事で完全に検証できた公称は、ここと来歴検証の2つである。
【実測】本当のボトルネックはSSDだった——開発機の8〜10分の1
階層表はメモリ量だけで速度を並べている。だがこの設計で本当に効くのはSSDの速度だ。エキスパートのミスが出るたびに2.76MBを読むのだから当然である。
公式リポジトリには Tools/diskbench.c という、まさにこのIOパターン(エキスパートレコードサイズのランダム pread)を測るベンチマークが同梱されている。同じツール・同じレコードサイズで筆者のMacBook Airを測り、MEASUREMENTS.mdに載っている開発機の値と並べた。
| レコード / 条件 | M5 Pro 2TB(公式) | M2 Air 256GB(筆者実測) | 比 |
|---|---|---|---|
| エキスパート 2.7648MB・QD1 | 9.46 GB/s | 1.19 GB/s | 7.9× 遅い |
| エキスパート 2.7648MB・QD8 | 17.25 GB/s | 2.16 GB/s | 8.0× 遅い |
| エキスパート 2.7648MB・QD32 | 17.30 GB/s | 1.71 GB/s | 10.1× 遅い |
| 16 KiB・QD1 | 0.27 GB/s | 0.04 GB/s | 6.8× 遅い |
| 4 KiB・QD1 | 0.08 GB/s | 0.01 GB/s | 8× 遅い |
| 単発レイテンシ(2.76MB・QD1) | 292 µs | 2,906 µs | 10× 遅い |
(筆者測定は8GBの実ファイルに対する F_NOCACHE 付きランダム pread。スパースファイルでは測定にならないため実データを書き込んで実施した。)
Tools/diskbench.c・同じ2.7648MBレコードでの比較。筆者環境で実測。8〜10倍の差である。これは階層表のどこにも現れない。MEASUREMENTS.mdの設計計算は開発機の実測17.3 GB/sを前提にしており、その3分の1以下しか出ない機械では、同じメモリ量を与えてもIO待ちの絵がまったく変わる。
公平を期すと、公式はこの落とし穴を正しく予告している。MEASUREMENTS.mdには「これは計測した機械の値であって、どのMacでも出る数字ではない。ベースストレージのMacBook Airははるかに遅いだろう」と明記されている。筆者の機械はまさにその「ベースストレージのMacBook Air」(256GB構成)で、警告が正しかったことを数字で裏づけた形になる。256GBモデルはNANDのダイ数が少なく速度が落ちるのはApple Siliconでよく知られた挙動だ。
16GB以下のMacを検討している人へ
メモリ以前にディスクで詰む。105.3GBの重みに107.3GBの空きが要るので、256GBのMacでは(OSと既存データを考えれば)事実上不可能だ。READMEが「512GB以上のMacが現実的な最低ライン」と書いているのは誇張ではない。さらに小さいMacはSSD自体も遅いので、推定tok/sはおそらく楽観的な側に外れる。
公称値のうち、何が測定で何が推定か
この種の記事でいちばん誤解を生みやすいのが「12 tok/s」の扱いだ。線を引いておく。
| 公称 | 位置づけ | 筆者の検証 |
|---|---|---|
| 48GB機で約12 tok/s | 実測(M5 Pro・測定手順つき) | 未検証(重みを置けず) |
| 8/16/24/32GB機のtok/s | 推定(48GBの曲線から) | 未検証。プランナーの再現性のみ確認 |
| 起動 約2秒・常駐3.8GB | 実測 | 未検証 |
| 105.3GB / 107.3GB必要 | 実測 | 検証済み(拒否メッセージと一致) |
| 署名付き来歴つきリリース | 仕様 | 検証済み(SLSA v1・タグ由来) |
| SSDが小さいMacでは遅い | 警告 | 検証済み(8〜10倍の差を実測) |
作者は推定を推定として表示している。doctor の出力にも est. from M5 Pro anchors の但し書きが必ず付く。「48GB Macで12 tok/s」は作者の実測、「16GB Macで4 tok/s」は誰も走らせていない外挿——ここを混同しないことが、この種のプロジェクトを読むときの一番の要点だ。
測定記録をCIで縛る仕組みと、その穴
slotstreamで技術的に一番めずらしいのは、実はストリーミングではなく数字の管理だ。リポジトリには db/ という記録ストアがあり、測定73件・クレーム32件・意思決定13件・機械4件が1件1ファイルで入っている。README/docsは Tools/projections.py がそこから生成する「投影」であり、Tools/claims_gate.py がCIで整合を検査する。
動かしてみた。
python3 Tools/claims_gate.py
# claims gate: 55 needle checks, 0 failures
55件の検査が通る。では本当に効くのかを確かめるため、数字を書き換えてみた。
・READMEの表の ~12 tok/s を ~20 tok/s に変更 → ゲートは通ってしまった
・READMEに4箇所ある 12 tok/s を全て書き換え → 失敗(CLAIM FAIL warm-decode-12-tok-s-on-48-gb.md: current claim needle '12 tok/s' missing from README.md)
仕組みが分かった。このゲートは「その数字が対象ファイルに1回以上出現するか」を見る存在チェックであって、出現箇所ごとの一致は見ていない。だから同じ数字が4箇所ある文書で1箇所だけ古くなっても、緑のまま通過する。
これは欠陥というより設計の範囲の問題だ。ゲートのdocstring自身が「READMEとdocsとllms.txtの間で数字が3リリースにわたってずれていたので作った」と動機を書いており、文書間のずれを捕まえる目的には十分に効く。文書内のずれは守備範囲外、というのが正確な理解になる。
slotstreamは何が新しいのか——同種OSSは実在した
Show HNの最上位コメントは厳しかった。「同じことをやるリポジトリが既に一山ある。もう1つ増やすより協力するか、MLX/MLX-LMに上流化してほしい」という趣旨で、mlx-moe-offload・streamlx・mlx-moe・mlx-flash・deepseek-v4-flash-mlx の5本を名指ししていた。
これは主張であって出典ではないので、1本ずつGitHubで確認した。5本すべて実在した。
| リポジトリ | ★ | 言語 | 作成 | 内容 |
|---|---|---|---|---|
| SharpAI/SwiftLM | 757 | Swift | 2026-03-21 | MLX SwiftのLLM推論サーバ。100B+ MoEのSSDストリーミング |
| carloslfu/slotstream | 246 | Swift | 2026-08-28 | 本記事の対象 |
| matt-k-wong/mlx-flash | 130 | Python | 2026-03-20 | RAMより大きいモデルをApple Siliconで動かす重みストリーミング |
| NeelM0906/Mference | 109 | Swift | 2026-07-31 | Swift+MetalのMoE推論 |
| huckiyang/mlx-moe-offload | 8 | Python | 2026-07-20 | MoEオフロード |
| mu-hashmi/mlx-moe | 6 | Python | 2026-02-05 | ルーター選択エキスパートのみSSDから読む |
| ssd-moe/deepseek-v4-flash-mlx | 3 | Python | 2026-07-04 | DeepSeek V4-Flashを48GB Macでエキスパートストリーミング |
コメント欄で追加で挙がった Mference と SwiftLM も実在し、しかもSwiftLMは★757・Swift・MLX・SSDストリーミングと、slotstreamと同じ組み合わせを5か月早くやっている(2026-03-21作成、2026-09-01時点で更新継続中)。
作者の反応は率直だった。「NIH(Not Invented Here)」と指摘されて「自分は(まだ)何も発明していない。RAMをそれほど使わずにどこまで速くできるかを見るのが目的だ」と答えている。つまり手法の新規性を売りにしていない。
では何が差別化点か
技術ではなく運用だ。7本のうち、公称値の一つひとつに測定記録IDを紐づけ、失敗した実験(誤った測定方法とその理由)まで公開し、数字のずれをCIで落とす仕組みを持つのはslotstreamだけだった。MEASUREMENTS.mdには「最初の測定は間違いだった」という節が明示的に残されている——エキスパート1件ごとに mx.eval() を呼んでいたためGPU同期を測ってしまい、実際の12倍遅い数字を出していた、という自己申告だ。
この分野で読者が本当に困るのは「誰のベンチマークを信じるか」である。 手法が横並びなら、測定を検証可能にしている実装に価値がある。
slotstreamを試す価値があるMac/ないMac
| あなたのMac | 判断 | 理由 |
|---|---|---|
| 512GB以上のSSD・48GB以上のメモリ | 試す価値あり | 公称の実測条件に近い。12 tok/sの再現を狙える |
| 512GB以上のSSD・24〜32GBメモリ | 条件付き | 動くはずだが推定値。SSD速度次第で下振れする |
| 256GBのSSD(メモリ問わず) | 不可 | 107.3GBの空きを作れない。pull が拒否する |
| Intel Mac / Windows / Linux | 不可 | Apple Silicon専用。移植予定なしと明言 |
| Qwen3.8-Flash-Next以外を動かしたい | 不可 | 1モデル専用。pull は他の名前を知らない |
加えて0.2.2時点の既知の制限として、Ollama CLIからは接続できない(作者の申告。空の name などを厳格バリデータが弾く。main では修正済みで次リリースに入る)。curl・Open WebUI・OpenAI SDKは動作するとされている。
同じ「メモリより大きいモデルを動かす」系では、NVMeからストリーミングするC実装のWASTE解説|Kimi K3 2.78TをNVMeで動かすC製推論エンジンと「RAM増設が効かない」実測、CPU/GPUヘテロ構成でMoEを捌くKTransformers解説|24GB VRAMでDeepSeek-R1級MoEを動かすCPU/GPUヘテロ推論が近い。Apple Silicon上のローカルサーバとしてはomlx徹底解説|Apple Silicon専用・OpenAI/Anthropic両対応のローカルLLMサーバーを実測も併せて見ておくと、選択肢の地図が描ける。
まとめ——検証できた線引き
slotstreamは6日前に公開されたばかりのプロジェクトで、★246・MITという若さだ。リリース間隔も短く、筆者が検証した v0.2.2 の翌日には v0.2.3 が出ている(本記事の測定値はすべて v0.2.2 のもの)。筆者の16GB MacBook Airでは、重み107.3GBの壁に阻まれて推論そのものは動かせなかった。それでも切り分けられたことがある。
・検証済み:リリース資産の真正性(SLSA v1・タグ由来)、ディスク不足時の安全な拒否(終了コード64・0バイト書き込み)、macOS 14での起動、プランナーの決定性、そしてSSD速度が開発機の8〜10分の1という実測
・未検証:12 tok/sのデコード速度、Ollama互換API、投機的デコード、長文プリフィルの所要時間——いずれも重みが必要で、107.3GBの空きが作れなかった
・誤解しやすい点:階層表の16GB行(10GB・4 tok/s)はアイドル機の推定値。実際の作業マシンでは8.1GBの下限に落ちた
「48GB Macで104GBのモデルが12 tok/sで動く」という見出しは、作者の実測として正しい。ただしそれは48GB・2TB SSD・M5 Proという1台の機械の数字であって、他のMacに素直に外挿できるものではない。SSDが8倍遅ければ、同じ設計でも見える景色は変わる。作者自身がその但し書きを全部書いているのが、このプロジェクトのいちばん良いところだ。
参照ソース
・carloslfu/slotstream — 公式リポジトリ・README(★246・MIT。2026-09-03時点の最新は v0.2.3、本記事の検証は v0.2.2)
・MEASUREMENTS.md — 全公称値の測定記録。失敗した測定とその原因を含む
・docs/HARDWARE.md — 実機で測った行を追加する手順
・Show HN: Running 104GB Qwen3.8-Flash-Next on 48GB Mac with at ~12 tok/s — 作者本人の投稿(225ポイント・106コメント、2026-09-01)