tk3.biz
ブログ一覧に戻る Back to Blog

使っていない画像機能を外したら、メモリは1GiB減り、速度は変わらなかった 使っていない画像機能を外したら、メモリは1GiB減り、速度は変わらなかった

AIローカルLLMQwenllama.cpp測定

はじめに

プロンプトキャッシュの回で、サーバの起動ログにこの行を見つけました。

srv    load_model: cache_reuse is not supported by multimodal, it will be disabled

使っている Qwen3.6-35B-A3B は画像も扱えるモデルで、 -hf で指定すると画像用の mmproj(約 0.9 GB のファイル)が自動で読み込まれます。

ところが、llm コマンドも dev-orchestra のレビューも、テキストしか使っていません。 使わない機能のために、毎回ファイルを読んでメモリを使っていたことになります。

外したら何が変わるのか。候補は 3 つありました。

  • メモリが減る(ほぼ確実)
  • 起動が速くなる(読むファイルが 1 つ減る)
  • 読み込み・生成が速くなる(llama-bench と実サーバの速度差、約 2 割の原因かもしれない)

3 つ目が当たれば、毎日の llm がそのまま速くなります。

チェックリストから設計する

前回、測定で 19 回間違えた経験をチェックリストにまとめました。 今回はそれを最初から当てはめています。

チェック項目今回の対応
設定が本当に有効か起動ログに mmproj の読み込み行が出ていないことを確かめ、想定と違えば止まる
比べ方あり → なし → あり → なし → あり と交互に 5 回。最初と最後の「あり」が揃うかでずれを見る
本番と同じ条件serve.ps1 と同じ引数(-c 65536、MTP あり)。違いは --no-mmproj だけ
割り込みllama 以外が使った CPU・GPU を 10 秒ごとに記録
安全装置スリープ抑止が効かなければ止まる。サーバが起動しなければ止まる
生データ生成した文章の冒頭を記録し、両条件でまともな出力かを見る

1 つ目は --cache-reuse の件の反省です。 「入れた設定が効いているはず」で 4 日間間違えていたので、効いていなければ計測自体を止めるようにしました。

測ったのは、起動時間、メモリ、32K トークンの読み込み時間、生成速度(256 トークン × 3 回)です。

1 回目の結果

#mmproj起動メモリ(起動後)生成(平均)32K の読み込み
1あり9.7 秒25.11 GiB18.00 t/s140.4 秒
2なし8.3 秒24.12 GiB19.13 t/s133.6 秒
3あり9.4 秒25.21 GiB18.42 t/s144.9 秒
4なし9.6 秒24.12 GiB17.25 t/s134.9 秒
5あり10.1 秒25.21 GiB19.22 t/s125.5 秒

メモリは、はっきり 1 GiB 減った

「あり」は 25.11〜25.21 GiB、「なし」は 2 回とも 24.12 GiB。差は約 1.0 GiB です。 5 回の中で値がほとんど動かず、両者の範囲は重なりません。これは言い切れます。

速度は、何も言えなかった

一方で速度は、表を見ても方向が定まりません。 生成は「なし」の 1 回目が速く、2 回目が遅い。読み込みは「なし」が真ん中あたりに来ています。

決め手は、最初と最後の「あり」が揃っていないことでした。

#1(あり)#5(あり)ずれ
32K の読み込み140.4 秒125.5 秒−11%
生成18.00 t/s19.22 t/s+7%

同じ条件なのに、計測の始めと終わりで 1 割動いています。 「あり」どうしのばらつき(読み込み 125〜145 秒)の中に、「なし」の 134 秒がすっぽり収まっています。

条件の差より、計測中のずれのほうが大きい。 この状態で「速くなった」とも「変わらない」とも言えません。 挟み込みで測ったおかげで、それが分かったとも言えます。 1 回ずつの比較だったら、#4 と #5 を並べて「mmproj ありのほうが速い」と書いていたかもしれません。

監視に引っかかったもの

監視ログを見ると、16:14〜16:20 に llama 以外の CPU が 13〜22% まで上がっていました。 上位は python3.13 と StreamDeck です。python3.13 は計測スクリプトではありません。

調べると、この PC の Python は Microsoft Store 版の 3.13 で、プロセス名が python3.13 と見えます。 そして同じ時間帯に、別の Claude のセッションが dev-orchestra で作業していました。 dev-orchestra はこの Python で動くツールです。裏で動いていたのは、それだと見ています (別セッションのログまでは確かめていないので、状況からの判断です)。

この区間は #3 の終盤と #4 にあたります。#4 の生成(17.25 t/s)が低いのは、その影響かもしれません。 ただ、重なっていない #1 と #5 のあいだでも 1 割ずれているので、ずれの原因がこれだけとは言えません。

裏を止めて、測り直した

チェックリストの「割り込み」に**「別の Claude のセッションが動いていないか確かめる」**を足し、測り直しました。

測り始める前に確かめると、dev-orchestra のセッションの最終操作が 40 秒前でした。 まさに使われていたわけです。そのセッションを止めてもらい、python のプロセスが無いことを確かめてから始めました。

回数も増やしています。あり → なし を交互に 7 回(あり 4 回、なし 3 回)。 こうすると「なし」の各回を、前後の「あり」の平均と比べられます。 計測中にゆっくりずれていっても、前後で挟めば打ち消せます。

mmprojありとなしの比較を1回目と2回目で並べたもの。メモリはどちらの回も約1 GiB離れて重ならない。生成速度と32Kの読み込み時間は、1回目は同じ「あり」どうしで1割ばらついたが、2回目は2%前後に収まり、その中で「なし」と「あり」が重なった

#mmproj起動メモリ(起動後)生成(平均)32K の読み込み
1あり25.0 秒 ※25.06 GiB18.91 t/s133.2 秒
2なし8.3 秒23.97 GiB19.39 t/s136.6 秒
3あり8.0 秒25.06 GiB18.87 t/s136.0 秒
4なし7.1 秒23.97 GiB18.77 t/s134.7 秒
5あり8.1 秒25.06 GiB18.91 t/s136.8 秒
6なし7.3 秒23.97 GiB18.85 t/s136.9 秒
7あり7.8 秒25.06 GiB18.86 t/s136.3 秒

※ #1 は、直前まで動いていたサーバを止めた直後の起動です。

監視では、llama 以外の CPU は中央値 7.1% で、1 回目のような連続した山はありませんでした。

ずれが、1 割から 2% 前後まで縮みました。

生成32K の読み込み
最初と最後の「あり」(#1 と #7)−0.2%+2.3%
#2(なし)と前後の「あり」+2.6%+1.5%
#4(なし)と前後の「あり」−0.6%−1.2%
#6(なし)と前後の「あり」−0.2%+0.3%

「なし」と前後の「あり」の差は、向きが回ごとにばらばらで、大きさは 3% 以内です。 mmproj を外しても速度は変わらない、と言ってよさそうです。 正確には、差があったとしても 3% 未満です。

メモリは今回も、7 回とも同じ値でした(あり 25.06 GiB、なし 23.97 GiB、差 1.09 GiB)。

1 回目の 1 割のずれが、すべて dev-orchestra のせいだったのかは分かりません。 重なっていない #1 と #5 のあいだでもずれていたからです。 ただ、裏を静かにしただけで、同じスクリプトのばらつきが 5 分の 1 になったのは確かです。

出力は一字一句同じ

2 回の計測の計 12 回とも、生成した文章は冒頭から同じでした。

ローカルLLMをiGPU(統合グラフィックス)で動作させる最大の利点は、専用メモリ(VRAM)を必要とせず、…

mmproj は画像を読むための部品なので、テキストだけなら影響しないのは予想どおりです。 ただ、予想どおりかどうかも確かめておくのが、今回のやり方です。

期待していた 2 つは外れた

--cache-reuse は、外しても使えませんでした。

srv    load_model: cache_reuse is not supported by this context, it will be disabled

理由が「マルチモーダルだから」から「このコンテキストでは非対応」に変わるだけです。 このモデルは SSM とのハイブリッド構成で、KV をずらして使い回す仕組みとは両立しません。 前回確かめたとおりでした。

llama-bench と実サーバの速度差も、mmproj では説明できませんでした。 読み込みが 2 割速くなれば当たりでしたが、測り直した結果は 3% 未満の差です。原因は未解明のままです。

既定を変えた

既定は mmproj なしにしました。

ありなし
メモリ約 25.1 GiB約 24.0 GiB
起動約 8 秒約 7〜8 秒
テキストの出力—同じ
画像の入力使える使えない

失うのは画像の入力だけで、今は使っていません。 93.6 GiB 積んだ機体で 1 GiB は小さな差です。それでも、使わない機能のためにメモリを使い続ける理由もありません。

serve.ps1 には -Vision を足し、画像が必要なときだけ読み込めるようにしました。

# 普段(llm の自動起動もこちら)
pwsh -File scripts\serve.ps1            # --no-mmproj が付く

# 画像を使うとき
llm -Stop
pwsh -File scripts\serve.ps1 -Vision    # mmproj を読み込む

変更後に llm から自動起動させ、--no-mmproj 付きで立ち上がって応答が返ること、 メモリが 24.27 GiB になっていることを確認しています。

まとめ

  • テキストしか使わないのに、画像用の mmproj を毎回読み込んでいた
  • 外すとメモリが約 1 GiB 減る。2 回の計測の 12 回ともほぼ同じ値で、言い切れる
  • 1 回目は同じ条件の最初と最後が 1 割ずれ、速度について何も言えなかった
  • 裏で別の Claude のセッションが dev-orchestra を動かしていた。止めて測り直すと、ずれは 2% 前後に縮んだ
  • 測り直した結果、速度の差は 3% 未満。mmproj を外しても速くも遅くもならない
  • --cache-reuse は外しても使えず、llama-bench との速度差の原因でもなかった
  • 既定は mmproj なしに変更。画像を使うときは -Vision

速くなる話を期待して測り始めましたが、速さは変わらず、確かに得たのは 1 GiB のメモリでした。

1 回目で「分からない」と書けたのも、2 回目で「変わらない」と言えるようになったのも、 挟み込みで測って、ずれの大きさを見ていたからです。 そして、ずれを 5 分の 1 にしたのは、測り方ではなく裏を静かにしたことでした。

関連記事 Related Posts

128K の文書の奥まで、ちゃんと読めているのか 128K の文書の奥まで、ちゃんと読めているのか

ローカルLLMに128Kトークンの文書を読ませ、20か所に仕込んだ事実を拾えるか測りました。深さによる取りこぼしは無く、文書に無いことは「記載なし」と答えました。唯一の誤りは、先頭2文字が同じ別の番号との取り違えで、片方の名前を変えると消えました。 ローカルLLMに128Kトークンの文書を読ませ、20か所に仕込んだ事実を拾えるか測りました。深さによる取りこぼしは無く、文書に無いことは「記載なし」と答えました。唯一の誤りは、先頭2文字が同じ別の番号との取り違えで、片方の名前を変えると消えました。

128K の文書は、読ませ直さずに使い回せるのか 128K の文書は、読ませ直さずに使い回せるのか

ローカルLLMに128Kトークンの文書を読ませると20分以上かかります。2回目以降を速くできるか、プロンプトキャッシュを6通りの使い方で測りました。質問を変えるだけなら数秒で済みますが、ディスクに保存して再起動すると全部読み直しになります。理由はモデルのハイブリッド構成にありました。以前の記事の誤りも見つかりました。 ローカルLLMに128Kトークンの文書を読ませると20分以上かかります。2回目以降を速くできるか、プロンプトキャッシュを6通りの使い方で測りました。質問を変えるだけなら数秒で済みますが、ディスクに保存して再起動すると全部読み直しになります。理由はモデルのハイブリッド構成にありました。以前の記事の誤りも見つかりました。

コンテキストを 128K まで伸ばしたら、壁はメモリではなく待ち時間だった コンテキストを 128K まで伸ばしたら、壁はメモリではなく待ち時間だった

ローカルLLMのコンテキストを32Kから128Kまで伸ばして測りました。予想していたメモリ不足は起きず、理由はモデルの構造にありました。代わりに見えた壁はプロンプトの取り込み時間で、128Kを読ませるだけで21〜24分かかります。途中で自分の自動起動に計測を汚された話も含みます。 ローカルLLMのコンテキストを32Kから128Kまで伸ばして測りました。予想していたメモリ不足は起きず、理由はモデルの構造にありました。代わりに見えた壁はプロンプトの取り込み時間で、128Kを読ませるだけで21〜24分かかります。途中で自分の自動起動に計測を汚された話も含みます。