GPUなしで13年前のXeon上でGemma 4 26Bを毎秒5トークンで実行する
(neomindlabs.com)- 2013年型のデュアルXeon E5-2690 v2とDDR3で構成されたサーバー上で、Gemma 4 26B-A4B Q8_0をCPUのみで動かし、デコード約5.2トークン/秒、プロンプト評価約16トークン/秒を達成
ik_llama.cppの高速パスはAVX2・FMA3を前提としているが、Ivy Bridge CPUはAVX1のみ対応のため、非AVX2ビルド向けのコンパイル修正と演算フォールバックが必要だった- グラフビルダーが
MOE_FUSED_UP_GATEとFUSED_UP_GATEを無条件に生成する一方、非AVX2ディスパッチャーには処理パスがなく、順伝播ごとに約240個のテンソルが計算されないままになり、支離滅裂な多言語出力につながっていた - Claudeがロジットの計測とコード解析でエラーを見つけ、融合演算を2回の
ggml_mul_mat_idとggml_fused_mul_unaryに置き換えた。ユーザーは実験を実行し、正常な結果の基準を判定した - 古い企業向けサーバーも、有料API障害時のローカル代替手段や低速なバッチ作業に活用できるが、AVX2専用レイアウトを作る
--run-time-repackは使ってはいけない
13年前のストレージサーバーの実行環境
- 再利用したHP StoreVirtual機器は、2013年型のデュアルXeon E5-2690 v2とDDR3メモリを使用しており、GPUはない
- Ivy Bridge世代のためAVX1のみ対応で、AVX2とFMA3には対応していない
- もともとはディスクストレージ用に作られたもので、購入費用は300ドル未満
- 実行モデルはGoogleのGemma 4 26B-A4Bで、260億パラメータを持つオープンウェイトの**混合エキスパート(MoE)**モデル
- Q8_0量子化モデルで、デコードは約5.2トークン/秒、プロンプト評価は約16トークン/秒と測定された
出発点となった2016年型Xeonの事例
- Hacker Newsで共有されたA 10 year old Xeon is all you needは、GPUなしで2016年型の単一Xeonと128GB DDR3上でGemma 4を実行した事例
- その構成ではik_llama.cppと、約25個の細かな実行フラグを使用している
- 投機的デコード
- CPUを考慮したMoEルーティング
- CPU向けFlash Attention
- 実行時の重み再パッキング
- 同じアプローチをIvy Bridgeサーバーに適用したが、起動段階で実行が停止した
- 2016年型Broadwell CPUと異なり、E5-2690 v2にはAVX2とFMA3がない
- これらの命令セットは2014年のHaswell、つまりIntelのv3世代から提供されており、高速カーネルはそれを前提に書かれていた
Claudeを使った診断とパッチ
- 起動失敗の情報を受け取ったClaudeは、CPU命令セットの違いを原因として特定した
- 以前に無料モデルで試みた未完成のアプローチを引き継ぎ、性能上重要なC++パスがAVX2以前のCPUで正しくフォールバックするよう修正した
- 作業は単一の
fix itリクエストで終わるものではなかった- 別の開発者が書いた性能重視のC++コードを読む
- 特定のマイクロアーキテクチャでカーネルが有効でない理由を分析する
- フォークの既存最適化を捨てずに、サポートされないパスを迂回する
- ユーザーはC++カーネルのフォールバックを直接書く代わりに、実験を実行して出力を読み、次の質問を決め、正常な結果の基準を判定する役割を担った
- 診断とパッチは、そのサーバー上で実行されたClaudeインスタンスが行った
非AVX2ビルドで壊れていた演算パス
- Gemma 4 MoE推論に使用した
ik_llama.cppはllama.cppのフォークで、基本的にAVX2を最小条件と仮定している - ビルド時に
GGML_USE_IQK_MULMATを無効にすると、大半の高速パスが除外され、通常のスカラー/SSE演算へフォールバックする- 一般的なQ8_0行列乗算では、このフォールバックが機能する
- ただし2つのグラフ演算にはフォールバックが適用されていなかった
- Gemma 4のMoEフィードフォワードネットワークは次の演算を生成する
MOE_FUSED_UP_GATE: エキスパートごとのgate・up行列乗算とSwiGLUを結合した演算FUSED_UP_GATE: 非MoE層で使う密な演算バージョン
- コンピュートディスパッチャーの2つの演算は
GGML_USE_IQK_MULMAT条件で保護されていたが、グラフビルダーは条件なしで演算を生成していた- 非AVX2ビルドのディスパッチャーには、該当enumを処理する
caseがなかった - 演算がデフォルト分岐に流れ、すべてのエキスパート・フィードフォワードネットワークの出力先テンソルが静かに計算されないままになった
- 非AVX2ビルドのディスパッチャーには、該当enumを処理する
- Gemma 4 26Bは30層でトークンごとにアクティブなエキスパートを8個使うため、順伝播ごとに、メモリバッファに残っていた値を含むテンソル約240個を消費することになった
支離滅裂な出力に現れた手がかり
- エラー出力は流暢に見えるものの、意味のない多言語文字列だった
- 26万2,000語彙全体にトークンIDが均等に散らばっていた
- タイ語、日本語、
<unused>センチネル、英語の断片などが同程度の頻度で生成された
- 温度0では出力は決定的で、単一スレッドとマルチスレッドの実行結果もバイト単位で同一であり、NaNは発生しなかった
- 層ごとに大きな定数が隠れ状態を押し上げ、最終softmaxが平坦になる形を示していた
- Claudeはサンプリング前の生ロジットを計測し、上位5トークン、範囲、平均、NaN数を出力した
- 最初の予測トークンの平均ロジットは0付近ではなく**+16**だった
- 語彙全体の約80%が正のロジットを持っていた
- ランダムな破損と異なりバイアスが一定だったため、隠れ状態の大きな部分が、小さな正の浮動小数点値が残った初期化されていないメモリを使っていると範囲を絞り込んだ
3段階で構成された修正
- パッチはフォークの
main上に3つのコミットで構成されている -
非AVX2コンパイル修正
iqk_quantize.cppのquantize_row_q8_0_x4とquantize_row_q8_1_x4_Tにあるスカラー#else分岐が、実際にはhsum_i32_8などのAVX2ヘルパーを参照していた- その分岐を移植可能なスカラーループとして書き直した
ggml.cとggml-quants.cに漏れ出していた一部のIQK呼び出しに、#if GGML_USE_IQK_MULMAT保護条件を追加したiqk_cpu_ops.cppが単独でコンパイルされるよう、不足していたincludeを追加した- この修正がないと、フォーク自体が非AVX2ハードウェアでビルドできない
-
実行時グラフフォールバック
- ディスパッチャーを直す代わりに、グラフビルダーがそのビルドですでに計算パスを持つ演算を生成するよう変更した
ggml_moe_up_gateでGGML_USE_IQK_MULMATが無効なとき、結合されたup_gate_exps重みを処理する- テンソル形状は
[n_embd, 2*n_ff, n_experts]で、前半がgate、後半がup - 2つの
ggml_view_3dスライスに分離する - 各スライスに
ggml_mul_mat_idを実行する ggml_fused_mul_unary(gate, up, SILU)で2つの結果を結合する- gateとupの重みがすでに分離されている場合は、スライスなしで2回の
ggml_mul_mat_idと融合乗算・単項演算を使う - 非MoE層で使う
ggml_fused_up_gateにも同じフォールバックを適用する mul_mat_idは基本のggml実装を使い、fused_mul_unaryはSILUと乗算を一度に処理するため、すべての構成演算には非IQK実装がすでに存在する- 変更全体は
#if !GGML_USE_IQK_MULMATの後ろにあるため、AVX2ビルドの結果は従来とビット単位で同一
-
CIスタブの整備
- IQKソースの
#elseスタブがiqk_mul_mat.hと異なっており、非AVX2ハードウェアではci/run.shもビルドできなかった <cstdint>includeが欠けていた- 一部のスタブは不要な先頭パラメータを持っていたり、
sinksパラメータが抜けていたりするなど、シグネチャが異なっていた - 一部の関数はスタブ自体がなく、リンク段階で未定義参照が発生した
- スタブをヘッダーに合わせることで、非AVX2環境でもテストスイートの実行が可能になった
- IQKソースの
フォールバックの性能コストと再パッキングの不具合
- フォールバックは1つの融合カーネルの代わりに2回の
mul_mat_idを実行するため、追加コストが発生する - このCPUはすでにメモリ帯域幅の制約を受けており、元の融合カーネルもAVX2専用だったため、この環境で諦める既存の実行パスはなかった
- 最終性能は26B-A4B MoEで、デコード約5.2トークン/秒、プロンプト評価約16トークン/秒
--run-time-repackは起動時に量子化重みをAVX2専用のインターリーブ形式であるQ8_0_R8へ再配置する- AVX1環境で出力を再び破損させる別のバグ
- 現在のパッチはこれを修正せず、実行スクリプトからこのフラグを削除している
原因を絞り込んだ過程
- 命令セットの不一致は簡単に確認できたが、ディスパッチャーがエラーなしでデフォルト分岐に流れる問題は見つけにくかった
- コードレビューによって複数の候補を除外した
- RMSNormヘルパーは正しく見えた
ggml_vec_dot_q8_0_q8_0のAVX1フォールバックも正しく見えた- 単一スレッドの結果がビット単位で同一だったため、スレッド処理のエラーを除外した
- ロジット平均が+16に固定され、ロングテールのトークン群が似た値を持つことから、残差ストリームの大きな部分が初期化されていないと判断した
- ディスパッチャーで
#if GGML_USE_IQK_MULMATを検索した後、約1分で欠落していた2つの演算パスを見つけた
再現条件と活用範囲
- AVX2以前の機器での再現条件は次のとおり
- ハードウェア: デュアルXeon E5-2690 v2、Ivy Bridge、AVX1、DDR3、GPUなし
- ビルド: パッチブランチの
ik_llama.cppをGGML_USE_IQK_MULMATなしでコンパイル - モデル: Gemma 4 26B-A4B Q8_0
- 実行: 一般的な
ik_llama.cppCPUフラグを使うが、--run-time-repackは除外
- 正確な変更はikawrakow/ik_llama.cpp PR #2138で確認できる
- 執筆時点ではまだオープンで、管理者レビュー待ちの状態のため、ブランチから直接実行する必要がある
- 同じ機器で発生したバグはPRスレッドに報告できる
- 古い企業向けサーバーにローカルモデルを保持しておけば、有料APIが停止したときの代替手段として使ったり、トークン単価が見合わない低速なバッチ作業を処理したりできる
- サブスクリプションサービスを利用することよりも、見慣れないコードと古いシステムを自分で掘り下げる能力を重視しており、これは15年前のRailsアプリケーションや担当者が去ったデータベースを保守する作業にも同じように当てはまる
1件のコメント
Hacker Newsのコメント
2027年半ばまでには、2,000億超のMixture of Experts(MoE)モデルを一般消費者向けハードウェアで動かせるようになると予想している
16GB MacでQwen3.6-35B-A3Bをローカル実行し、毎秒7~9トークン出ている: https://github.com/deepanwadhwa/samosa-chat
16GB RAMのMacBook AirでGPT-4級モデルがこの速度でローカル実行できていることになる
すでに大半の2,000億超モデルのアクティブパラメータ数に近いレンジなので、Prismがその気になればそうしたモデルを出せるはず。ただしHRMのような再帰ニューラルネットワークはそこまで多くのパラメータを必要としないため、本当にその規模が必要かは議論の余地がある: https://huggingface.co/sapientinc/HRM-Text-1B
例えば各層に256ビットのシードを1つだけ持たせ、それをノイズ関数に入れて16K個の実際の重みを生成すれば、重み1個あたりの保存サイズを1ビット未満まで下げられる
今月と来月にゲームチェンジャー級の新モデルが続々出そうで楽しみだ。Ornithはぜひ試す価値がある
聞きたくない人もいるだろうが、推論プロバイダーのトークン単価はローカル実行の電気代より安いと思う
単純化のため出力生成だけで計算すると、毎秒5トークンは1時間あたり18,000トークンで、プロバイダーの費用は約0.005ドル。サーバーが推論中に約500Wを使い、電力料金が1kWhあたり0.3ドルのドイツを前提にすると、同じ量をローカル生成するのに0.15ドルかかり、30倍高い
プライバシーが気になるならローカル実行には依然として価値があるが、推論プロバイダーよりはるかに非効率だという点は認識しておくべきだ。新しいGPUが推論効率を高めるにつれて、この差はさらに大きくなるだろう
最初は18万トークンと誤計算していたが、実際には18,000トークンなので、電気をほぼ無料で使えない限り競争は難しい。プロバイダー側も小型モデルにまだH200/H100を使っているだろうが、GB300や来年のRuby GPUに切り替われば推論コストは30分の1まで下がり、ローカルモデルの主な価値はプライバシー保護になりそうだ
1時間で360万入力トークンを処理するか、144,000トークンを生成し、電気代は約0.15ドル。Sonnetで同じ量を処理すると入力は7.2ドル、生成は1.4ドルなので、クラウドは生成で10倍、処理ではほぼ50倍高い
推論プロバイダーは巨額の負債を抱えて市場シェア争いをしているので、価格は確実に上がるだろう
AIをコミュニティが直接コントロールできるほど民主化すれば、データセンター問題も解決できるし、検閲やアラインメントの水準もコミュニティが民主的に決められる。Geohotzの一部の投稿に近い構想だ
オープンソースモデルは、まもなく中古ハードウェアで安価に提供できるほど高性能かつ高効率になるだろう。小さなコミュニティごとに技術に明るい人が1人いて、数百ドルの初期費用を集めれば、その後は電気代なしでほぼ無料運用できる
https://solar.lowtechmagazine.com/
ローカルという代替手段がなければ、クラウドモデルの価格ははるかに高くなっているはずだ
この世代のデュアル Xeon は負荷がかかると 300W 以上を消費する可能性が高い。米国の平均的な電気料金では 1 日 1.35 ドルで、夏場に室内を冷房する必要があるならさらにかかる
プロンプト処理時間を無視し、24 時間回し続けても 1 日あたり約 40 万トークンなので、出力 100 万トークンあたり約 0.30 ドルになる。偶然にも現在の OpenRouter におけるこのモデルの価格と同じだが、OpenRouter の生成速度は 8 倍速い
データを家の外に出さないようにするなど、ローカル LLM を試す理由は多いが、金銭的に得をするのは難しい。自宅のローカル推論機材にずっと多くの金を投じた立場からすると、面白くはあっても節約手段ではないと思う
GPU なしで デュアル Xeon と DDR4 256GB 上で複数モデルを動かした結果をまとめている
https://gist.github.com/hparadiz/f3596d00a62d8ebb2dadcc46ee5822c7
かなり遅い部類だ。13 年前の CPUでも毎秒 8〜12 トークン出ていて、コンテキストサイズや他の設定によって変わりうる
https://news.ycombinator.com/item?id=48354801
元記事の筆者です。元のコメントが何らかの理由で通報処理されたようだ。修正は上流プロジェクトの PR #2138 として開いてある: https://github.com/ikawrakow/ik_llama.cpp/pull/2138
この StoreVirtual 機材には、ソフトウェアをインストールするのに使えそうなポートがないように見える。USB ポートくらいしかないが、シリアルコンソール経由でインストールするのだろうか
同じ構成で 2013 Mac Pro 上で Gemma 4 26B を動かしており、毎秒約 5 トークン出ている。デュアル GPU はここでは役に立たないが、一部の作業には十分実用的だ
金曜に届いたら自分でも試してみるつもりなので楽しみだ
https://echalupa.com/blog/mac-pro-6-1-llama-cpp-firepro-d300-vulkan-ubuntu
https://matthewgribben.com/blog/mac-pro-6-1-llama-cpp-firepro-d700-vulkan-ubuntu
地下室の構成を実際に見たが、本当にすごい。次は 3D プリンターも紹介してほしい
やや関連のある動画として、Pentium 4 で LLM を動かし、NetburstGPTというあだ名を付けていた。もちろん非常に遅い
https://www.youtube.com/watch?v=ILV-eu90te8