実録⑦:ベンチ16.7・実運用6.6 ― 帯域律速の現実

オンプレLLM/技術記録

3回にわたって、VRAM 12GBの機体で177BのMoEを動かした話を書いてきました。最後はいちばん重要で、いちばん語られない話です。llama-bench の数字は、実運用の数字ではありません。我々の環境では2.5倍ずれました。

ベンチマーク値と実運用値の乖離

  • llama-bench(文脈ゼロの瞬間値):生成 16.7 / プロンプト処理 131 tok/s
  • 常駐サーバ・文脈663トークン(実運用):生成 6.2〜7.1 / プロンプト処理 61.7 tok/s

llama-bench の tg128 は文脈をほぼ持たない状態での瞬間値です。実運用では会話履歴とシステムプロンプトが積み上がり、アテンションの計算量とCPU⇔GPUの往復が増えます。ハードウェアの購入判断を llama-bench の値で行うと、確実に見誤ります。

同じ罠を3回踏み、間違った理屈を組み立てた

ここからが、この検証でいちばん書いておくべき話です。我々は同じ測定ミスを3回繰り返しました。

  • 1回目:初回ベンチ 5.2 tok/s(正しくは 16.7)
  • 2回目:プロンプト処理 12.9 tok/s(正しくは 88.9)
  • 3回目:要因計画の基準条件 5.39 tok/s(正しくは 16.8)

原因はいずれも同じ、104GBがページキャッシュに載り切る前に測っていたことです。3回目は -p 64 -n 16 というウォームアップを入れていたにもかかわらず起きました。64トークンでは全専門家に触れず、モデルが読み込まれないのです。

問題は2回目でした。「入力4,066トークンで12.9 tok/s、短い入力の5分の1」——この数字に対して、我々は次の説明を組み立てました。

(誤った説明)生成時は1トークンあたり「512人中10人」で済むが、プロンプト処理はバッチで流すため、バッチ内トークンが活性化する専門家の和集合はほぼ全員になる。長い入力では104GBを丸ごと舐めることになり、トップKルーティングの利得が消える——MoE+CPUオフロードの構造的な弱点である。

もっともらしく聞こえます。MoEの仕組みとも矛盾しません。そして完全に間違っていました。

汚染の原因は単純でした。llama-bench(mmap使用)が104GBをページキャッシュに載せた直後、常駐サービスを再起動して --no-mmap別の104GBを匿名メモリに確保したのです。125GiBのRAMに208GBを載せようとした状態で測っていました。

条件を揃えて測り直すと、入力3,895トークンで88.9 tok/s短い入力(663トークンで61.7 tok/s)よりも速い——バッチが大きいほどGPUの利用効率が上がるという、ごく標準的な挙動でした。劣化などしていなかったのです。

教訓はチューニングの話ではありません。おかしな数字が出たとき、我々は「それを説明する理屈」を作れてしまうということです。しかも理屈が整っているほど、測定そのものを疑わなくなります。この説明は記事の草稿にも一度書かれ、公開直前に実測で覆りました。

実タスク検証:素の設定では失敗し、文法束縛で通った

社内の要約メモリ基盤には、登録内容を別のLLMに査読させる仕組みがあります。入出力の機密性が高く、非同期のバックグラウンド処理——理屈の上ではローカルLLMの最良の適用先です。本番と同じシステムプロンプト・同じJSONスキーマで、223行のPythonを査読させました。

  • 1回目(json_object・思考あり):679秒/reasoning 10,565字/出力2,500トークンで上限到達/JSON 0バイト
  • 2回目(json_schema・思考抑制):105秒/reasoning 0字/453トークンで正常終了/json.loads が直接成功

1回目は、推論モデルが思考だけで max_tokens を使い切りました。11分待って成果物ゼロです。response_format: json_object は「JSONで答えてください」というお願いにすぎず、強制力がありません。

2回目は2点変えました。chat_template_kwargs で思考を止め、response_formatjson_schema に切り替える。llama.cppは渡されたJSON SchemaをGBNF文法へ変換し、サンプリング時に文法外のトークンを禁止します。verdictを3値、severityを4値のenumにしておけば、構造的に不正な出力を生成できません

結果は105秒で3件の指摘。中身を実コードと照合したところ、行番号は3件とも正確、存在しないコードの捏造はゼロでした。うち1件は「docstringに書かれた『弾かずに丸める』という契約と、型が違えば例外が出る実装の矛盾」を突いたもので、人間が見落としやすい類です。

ただし1件あたり105秒という時間は変わりません。非同期のバッチ処理なら十分ですが、対話的なエージェント動作には向きません——往復回数がそのまま所要時間になるからです。

GPUを増やせば解決するのか:傾きから外挿する

構築編で得た「専門家1層をVRAMに移すと0.73ms/token短縮、1層=1.73GB」という傾きから、増設効果を計算できます。

  • 3060×1(現在):3/48層がVRAM / 生成 17.3 tok/s
  • 3060×2:約10/48層 / 19〜20 tok/s(+12〜18%)
  • 3060×4:約23/48層 / 22〜24 tok/s(+35%)

GPUを4倍にしても1.35倍にしかなりません。生成時間の72%を占めるのはRAMからの専門家読み出しで、GPUが肩代わりできるのはそのうちVRAMに収まる分だけだからです。一方、メモリ帯域そのものを増やす——例えば4chから12ch構成へ移す——なら、その72%全体に効きます。この構成における投資対効果は、GPU枚数ではなく帯域で決まります。

なお、推論エンジン側でも打ち手はあります。オフロードしたMoEをテンソル並列で分割し、各GPUが専門家の一部だけを保持する方式です。層分割と違いスロットあたりの容量が実際に減るため、同じVRAMにより多く載ります。当社では別系統で検証を進めており、稿を改めて書きます。

結論:代替ではなく、受け皿

  • 外部に出せない情報を扱う:ローカルが唯一の選択肢
  • 非同期のバッチ処理(査読・要約・分類):ローカルで実用(1件105秒。待てる処理なら問題にならない)
  • 短い対話・下書き・整理:ローカルで十分(6〜7 tok/sは黙読速度と同等)
  • 長文の読解・横断調査:クラウド(文脈8,192トークンの上限がある)
  • エージェント動作・実装作業:クラウド(往復回数がそのまま時間になる)

ローカルLLMを「クラウドAPIの安い代替」として導入すると、ほぼ確実に期待外れになります。クラウドに出せない仕事の受け皿として位置づければ、既存機材とメモリ増設だけで成立します。今回の追加投資はメモリ代の数万円のみ、GPUの買い足しはゼロでした。

当社は、お手元の遊休ハードウェアを活用した「社内専用AI基盤」の構築を支援しています。
既存機材の適性診断は無料。買う前に、config.json とメモリ帯域から「動かせるモデルと速度」を計算してお示しします。

お問い合わせフォーム(「社内AI基盤 相談希望」とお書き添えください)/ info@flagship-ai.jp

シリーズ「レガシー to AI 実録」

※本記事の数値は2026年9月時点、Threadripper 1950X / DDR4-2400 4ch 128GB / RTX 3060 12GB / llama.cpp CUDAビルドでの自社実測です。構成により結果は異なります。

カテゴリー:

タグ:

🌐 日本語