Skip to content
Published on

26Bモデルを2GBのRAMで動かす原理 — 常駐メモリとワーキングセットは同じ数字ではない

シェア
Authors

はじめに — 「2GB」は何を数える数字なのか

2026年7月29日、Show HNにTurboFieldfareというプロジェクトが上がりました。SwiftとMetalで書かれた推論ランタイムで、Gemma 4 26B-A4BをMシリーズのMacで約2GBのRAMで動かすという主張が付いています。リポジトリのREADMEが書いている数値は、M2 MacBook Air 8GBで毎秒5.1~6.3トークン、M5 Pro 24GBで31~35トークンです。

「26Bを2GBに」という文は反射的に疑いたくなります。ところがこのプロジェクトの数字たちは互いによく噛み合います。実際に計算してみると大部分は正確です。興味深い地点は主張が間違っているところにあるのではなく、2GBが何を数える数字なのかにあります。それはプロセスの常駐メモリであり、システムが実際に使うメモリとは違う値です。そしてこの差が、M2 AirとM5 Proのあいだの6倍の性能差のかなりの部分を説明します。

この記事はREADMEとHNスレッドの数値をそのまま受け入れる代わりに、自分で導出して突き合わせます。算数が合う項目と、合わないので別の説明が必要な項目を分けて見ていきます。

14.3GBはどこから出るのか — まず保存容量の算数から

まずディスクです。READMEはテキスト専用モデルのインストール容量を約14.3GBと、量子化方式を「MLXアフィン4ビット、グループ64、ルーターは8ビット」と書いています。Gemma 4モデルカード基準で26B-A4Bは総パラメータ25.2B、活性3.8B、コンテキスト256Kです。

4ビットなのになぜ25.2Bの半分である12.6GBではないのでしょうか。グループ単位のアフィン量子化は重みだけを保存するのではないからです。

グループ64アフィン4ビットの実際の保存コスト

  重み64個     x 4ビット        = 32 バイト
  スケール1個  x fp16           =  2 バイト
  ゼロ点1個    x fp16           =  2 バイト
  ------------------------------------------
  合計                          = 36 バイト / 64 重み

  重みあたりビット数 = 36 x 8 / 64 = 4.5 bpw

  モデル全体 = 25.2e9 x 4.5 / 8 = 14.18e9 バイト = 約 14.2 GB

報告された14.3GBと1%以内で合います。残る0.1GBは8ビットのルーター、埋め込みの扱い方の違い、メタデータで説明すれば十分です。

ここですでに一つの教訓が出てきます — 「4ビット量子化」という表現は、ファイルサイズをパラメータ数の半分にしてくれません。グループサイズが小さいほど品質は良くなりオーバーヘッドは大きくなります。グループ32だったならbpwは5.0になってファイルは15.8GBになります。ローカル実行の計画を立てるとき、この0.5ビットがそのまま1.5GBの差です。重みとKVキャッシュを数式で計算する方法はローカルでLLMを動かすにはVRAMがどれだけ必要かの回に整理してあります。

MoEが作る二種類の重み

ではRAMに常駐する1.35GBが何なのかを見ます。READMEはそれを「共有コア」と呼んでいます。MoE構造ではパラメータが二つの種類に分かれます。

  • すべてのトークンが使うもの — 埋め込み、アテンション、共有エキスパート、ルーター。これは常駐させなければなりません。
  • トークンごとに選んで使うもの — ルーティングされるエキスパート。レイヤーごとに128個のうち8個ほどが活性化されます。

活性パラメータが3.8Bで総パラメータが25.2Bという二つの数字から、この分割を逆算できます。

S = 常に使うパラメータ, R = ルーティングされるエキスパートのパラメータ
活性比率 f = 活性エキスパート数 / 全エキスパート数

  S + R       = 25.2B
  S + f x R   =  3.8B      (f = 8/128 = 0.0625 と仮定)

  0.9375 R = 21.4B  ->  R = 22.8B,  S = 2.4B

  S を 4.5 bpw で保存すると
  2.4e9 x 4.5 / 8 = 1.35e9 バイト = 1.35 GB

READMEが明かした共有コア1.35GBと正確に一致します。活性エキスパート8対128という構造の仮定が当たっていたという意味であり、同時にこの設計の核心を見せています。全体の5%だけ常駐させれば、残りの95%はトークンごとに必要な断片だけ持ってくればよいのです。

ここで強調すべき点は、この方式がGemma 4はMoEであるがゆえに成立するということです。同じサイズの密(dense)モデルだったならトークンごとに25.2B全部を読まなければならず、それはどんなSSDでも耐えられません。ストリーミング推論は量子化の技法ではなく、アーキテクチャが開いてくれた扉です。

mmapを捨ててpreadへ行った理由

重みを遅延ロードするとき最初に思い浮かぶ方法はmmapです。ファイルをアドレス空間にマッピングしておいてアクセスすればカーネルが勝手にページを持ってきます。コードが単純で、llama.cppを含む大部分のローカル推論エンジンがこの方式を使います。

ところがHNスレッドで著者が明かしたところによると、mmapの実装は毎秒0.5トークンにとどまり、並列のpread呼び出しに変えることで約4トークンまで上がりました。八倍です。理由は二つの方式の並行性モデルが違うからです。

mmapでアクセスしたページがまだRAMになければページフォルトが起き、そのスレッドはフォルトが解決するまで止まります。カーネルの先読み(readahead)はシーケンシャルアクセスを前提にしますが、エキスパートルーティングが作り出すアクセスパターンはファイル全体に散らばったランダム読み出しです。予測が外れるので毎回一つずつ待つことになります。一方preadは明示的な要求なので、必要なオフセットのリストを一度に投げてSSDのキュー深度を埋められます。NVMeは要求ひとつを速く処理する装置ではなく数十個を同時に処理して帯域を埋める装置なので、この差がそのまま八倍として現れます。

実際の実装はここに二つを追加で載せています。レイヤーごとに16枠のLFUキャッシュを置いてよく使われるエキスパートを掴んでおき、プリフィル段階では最大128トークンずつまとめて処理して、一度持ってきたエキスパートが複数のトークンを処理するようにします。そしてCPUが次のエキスパートを読み込んでいるあいだ、Metalは共有エキスパートの分岐を計算します — 入出力と演算を重ねる古典的な技法です。

トークンあたり285MB — 帯域が上限を決める

これで性能の上限を計算できます。

ルーティングされるエキスパート全体 = 22.8e9 x 4.5 / 8 = 12.8 GB
トークンあたり活性比率             = 8 / 128 = 6.25%

  キャッシュがまったくないときトークンあたり読むべき量
  12.8 GB x 0.0625 = 800 MB

  HNで著者が報告した実測: トークンあたり 250~320 MB

  逆算したキャッシュヒット率 = 1 - (250~320) / 800 = 60% ~ 69%

著者が別途報告した値はM2で59~69%、16枠のキャッシュで約67%でした。私が導出した60~69%と噛み合います。つまりREADMEとHNに散らばった数字たちは一つの一貫したモデルから出てきています。トークンあたりの実質転送量を285MB程度と置いて機器別の上限を計算するとこうなります。

機器RAM引用されたSSDシーケンシャル読み出しトークンあたり285MB基準の理論上限(導出)報告されたトークンあたりディスク時間報告されたスループット
M2 Air8 GB確認できず83 ms5.1~6.3 tok/s
M4(帯域の参考用)2,031 MB/s約 7 tok/s
M5 Pro24 GB6,323 MB/s約 22 tok/s12 ms31~35 tok/s

M2 Airのほうは計算がよく合います。トークンあたり83ミリ秒のディスク時間なら毎秒12トークンが上限で、残りの演算時間を足せば実測5~6トークンは自然です。

問題はM5 Proの行です。引用されたSSD帯域で計算した上限が約22トークンなのに、実測は31~35トークンです。トークンあたり12ミリ秒で285MBを読むには毎秒23.75GBが必要ですが、これはどんな消費者向けNVMeでも出せない速度です。算数が合わないのではなく、その読み出しのかなりの部分がSSDから来ていないという意味です。

常駐メモリとワーキングセット — 2GBが真である地点と誤解を招く地点

答えはmacOSの統合バッファキャッシュです。preadで読んだファイルページはカーネルがRAMに保管し、同じオフセットをもう一度読めばSSDに触りません。24GBのマシンで14.3GBのモデルファイルはかなりの部分がキャッシュに残りえます。ウォームアップが終わったあとの「ディスク読み出し」は実際にはRAMコピーに近くなります。

ところがこれらのページはプロセスの常駐メモリとして集計されません。ファイルベースのページはカーネルのページキャッシュに属し、メモリ圧迫が来ればカーネルが回収します。だからアクティビティモニタでプロセスは依然として2GBに見えます。主張は嘘ではありません — ただ数える対象が違うだけです。

整理するとこうです。

  • 真である部分: このランタイムが自分のヒープに掴んでおく重みは共有コア1.35GBに4KのKVキャッシュを足した程度で、プロセスのRSSは2GB近辺です。RAMが8GBしかない機器で26B級のモデルがそもそも動くという事実自体がこの設計の成果です。
  • 誤解を招く部分: システム全体が使うメモリは2GBではありません。性能を出すにはカーネルのページキャッシュがモデルファイルの大きな断片を持っていなければならず、そのメモリは他のアプリと競合します。ブラウザのタブを二十個開いてキャッシュが押し出されれば、スループットはSSD帯域の上限まで落ちます。M2 Airの5~6トークンとM5 Proの31~35トークンの差はチップ性能だけの差ではなく、キャッシュに使える余裕RAMの差でもあります。
  • したがって疑うべき部分: HNで出た「利得が技法ではなくOSのキャッシングから来ているのではないか」という指摘は半分当たっています。ただしmmapからpreadに変えて八倍になったことはキャッシュでは説明されないので、技法の寄与も実在します。二つの要因が一緒にあります。

そして算数が閉じない項目がもう一つあります。レイヤーあたり16枠のキャッシュは全128個のうち12.5%なので、全レイヤーにわたって丸ごと常駐するなら12.8GBの12.5%である約1.6GBになります。共有コア1.35GBと合わせると2GBを超えます。実際の実装が枠を別の数え方でしているか、そのバッファのかなりの部分がファイルベースなのでRSSに計上されないかのどちらかでしょうが、私はどちらなのかをソースを読んで確認できませんでした。どちらであっても結論は同じ方向です — 2GBはプロセスの会計帳簿の数字です。

4ビットの品質コスト、そしてこの方式が合う場所

最後に、ただではない部分です。4ビット量子化の品質損失について広く引用されるまとめは、FP16比でパープレキシティが1~3%悪くなり、総パラメータが大きいほど損失が小さいというものです。MoEは総パラメータが大きいので活性が小さくても4ビットに比較的よく耐えるという主張もあります。ただしこれらは集約記事で繰り返される一般論であって、この特定のビルドに対する測定ではありません。

正確に言うとこうです。Gemma 4 26B-A4Bの公開ベンチマークは量子化されていないモデルのものです。モデルカードが載せるMMLU Pro 82.6、GPQA Diamond 82.3、LiveCodeBench v6 77.1のような数字は、4.5 bpwに押し込んだこのビルドのスコアではありません。そしてこのビルドのスコアを測った人はまだいません。READMEも「モデルが繰り返したり誤った答えを出したりしうるので重要な結果は確認せよ」という文を付けているだけです。量子化されたローカルモデルの品質を語るときに元のモデルカードの数字を引用することは、この記事がやめようと主張している類のことです。

ではこの方式はどこに合うのでしょうか。

  • 合う場所: RAMが足りない機器でオフラインに、遅延に敏感でない作業。文書要約、ローカルのコード説明、ネットワークのない環境。毎秒5トークンは対話的にはもどかしいですがバックグラウンドのバッチには十分です。データを外に出してはいけない状況なら代替はあまりありません。
  • 合わない場所: RAMが十分な機器。24GB以上なら14.3GBのモデルをそのまま丸ごと載せるほうが単純で速いです。この方式の存在理由はRAM不足であって性能ではありません。そして常時稼働するサーバーはなおさらではありません — このランタイムはインスタンスあたり単一のプロセスだけがモデルを所有し、同時実行をサポートしないと明示されています。
  • 確認されていないこと: SSD寿命についての定量資料がありません。読み出し中心なので書き込み摩耗は大きくないと見られますが、リポジトリもこれを数値化していないと明かしています。またmacOS 26とMetal 4が要件で、macOS 15で使うには2.4倍のプリフィル加速を諦める回避策が必要です。M1からM5まで全世代で同じ結果が出るという根拠もまだありません。

おわりに — 数字が数える対象を先に確認すれば、大部分の魔法は説明される

「26Bを2GBに」は誇張ではありませんでした。4ビットグループ64が作る4.5 bpw、総25.2Bと活性3.8Bから逆算される共有コア1.35GB、ルーティングされるエキスパート12.8GBの6.25%であるトークンあたり800MB、キャッシュで285MBまで減る実転送量 — この四つの数字が一本につながり、報告された値と合います。良いエンジニアリングです。

同時に2GBはプロセスの常駐メモリであってシステムのワーキングセットではありません。M5 Proの実測スループットがSSD帯域の上限を超えるという事実がその証拠であり、その差を埋めるのはカーネルのページキャッシュです。ですからこのプロジェクトを8GBのMacに掛けてみるとき期待すべき数字は31~35トークンではなく5~6トークンです。

ローカル推論の性能主張を読むときに投げるべき問いはいつも同じです。その数字は何を数えていて、数えていないものはどこにありますか。