はじめに
!!!!! モデルおよびコードの利用は自己責任でお願いします !!!!!
本稿は「ローカル LLM で AI コーディング支援環境を構築する」シリーズの第 5 回です。
第 4 回では EvaPlus ベンチマークの結果を整理しましたが、その中で下記 2 点が未解決事項が残っていました。
- Bonsai-8B のモバイルワークステーションでの実行時間が突出して長い
- Azure VM での Bonsai-8B の VRAM 使用量が突出して大きい
今回はこの 2 点に対してリソース使用量の分析とプロファイリングを行いボトルネックの原因を特定します。
リソース使用量の分析
第 4 回の結果を改めて整理しますと、Bonsai-8B のモバイルワークステーション(T500)での実行時間は 1:34:19 であるのに対し、Azure VM(T4)での 0:15:43 と約 6 倍の差があります。
まずはリソース使用量の分析を行うことで、この差の理由の調査を進めます。
仮説検証
最初に思い当たる原因は「VRAM に収まりきらない処理が CPU 側に流れている(CPU オフロード)」です。
モバイルワークステーションの VRAM 使用量は 2,323 MB であり、T500 の 4GB VRAM に余裕があるように見えますが、Bonsai-8B(8.19B パラメーター)を圧縮なしで読み込むには 8bit 換算で約 8GB 程度が必要なため、量子化なしでは全層を載せられません。
それではまず llama-server の起動ログで実際の状況を確認します。
load_tensors: offloading output layer to GPU
load_tensors: offloading 35 repeating layers to GPU
load_tensors: offloaded 37/37 layers to GPU
load_tensors: CPU_Mapped model buffer size = 83.31 MiB
load_tensors: CUDA0 model buffer size = 1015.99 MiB
結果 全 37 層が GPU に載っており、CPU オフロードは発生していませんでした。
合わせて推論中の GPU 稼働状況を nvidia-smi dmon で確認したところ、EvaPlus 実行中の GPU 使用率はほぼ 100% に張り付いており、GPU は確実に使用されていることが確認できました。
残る原因
となると残る原因は GPU そのものの性能差です。T500 と T4 のスペックを比較すると以下のようになります。
| T500 | T4 | |
|---|---|---|
| メモリ帯域 | ~80 GB/s | ~320 GB/s(4倍) |
| 演算性能(FP16) | ~6 TFLOPS | ~16 TFLOPS(約2.7倍) |
| コアクロック | 500MHz | 定格 |
| 実行時間 | 1:34:19 | 0:15:43(6倍差) |
両者は同一の Bonsai-8B を実行しているため差は GPU 性能のみに起因します。構造的な差はメモリ帯域(4倍)が支配的ですが、実行時間差(6倍)は帯域比を上回っており、純粋なメモリ帯域の差異だけでは説明できません。
この増大分には、第 3 回で熱暴走対策として T500 のコアクロックを 500MHz に固定していた影響が含まれます。コアクロックの制限では帯域は維持されますが、演算スループットが直接低下するため実行時間を押し上げます。
各要因の正確な内訳は T500 を定格クロックで再計測しない限り確定できませんが、この 6 倍差は「メモリ帯域の差異 + クロック制限と演算性能差の上乗せ」と理解するのが妥当です。
プロファイリングによる深掘り
モバイルワークステーションと Azure VM の実行時間の差は、ハードウェア性能差と熱対策によるクロック制限で説明できることが確認できました。
しかしもう一つの疑問として「Bonsai-8B は Qwen2.5-Coder-3B よりモデルファイルが小さいにもかかわらず実行時間が約 2.2 倍長くなっている。」という点が残ります。これを定量的に確認するために、NVIDIA のプロファイリングツール(Nsight Systems / Nsight Compute)を使って Bonsai-8B と Qwen2.5-Coder-3B の GPU 内部の挙動を比較します。
計測環境
| 項目 | 内容 |
|---|---|
| GPU | NVIDIA Tesla T4(VRAM 16GB) |
| 実行環境 | PrismMLフォーク llama-cli(両モデル共通) |
| プロンプト | あなたの自己紹介を1000文字程度でお願いします |
両モデルを同一の実行環境で動かすことで、環境差の影響を排除しています。
nsys:処理時間の内訳
Nsight Systems で推論中の GPU 処理時間の内訳を取得しました。
Bonsai-8B
| 処理 | 時間占有率 | 呼び出し回数 | 1回あたり平均(ns) |
|---|---|---|---|
| 重み計算(主要) | 62.2% | 21,834 | 88,868 |
| 重み計算(補助) | 20.6% | 22,443 | 28,681 |
| アテンション計算 | 3.4% | 7,416 | 14,254 |
| 重み計算 合計 | 85.5% | - | - |
Qwen2.5-Coder-3B
| 処理 | 時間占有率 | 呼び出し回数 | 1回あたり平均(ns) |
|---|---|---|---|
| 重み計算(主要) | 56.6% | 46,364 | 36,129 |
| 重み計算(補助) | 13.4% | 9,327 | 42,539 |
| アテンション計算 | 4.6% | 9,324 | 14,485 |
| 重み計算 合計 | 82.2% | - | - |
両モデルとも推論時間の 80% 以上を重み計算が占めています。LLM の推論では 1 トークン生成するたびにモデル全体の重みを読み出して計算するため、この処理が支配的になります。
ここで注目すべきは 1 回あたりの平均処理時間の差です。
| Bonsai-8B | Qwen2.5-Coder-3B | 比率 | |
|---|---|---|---|
| 重み計算の平均時間 | 88,868 ns | 36,129 ns | Bonsai 2.46倍遅い |
| 呼び出し回数 | 21,834 | 46,364 | Bonsai 0.47倍少ない |
| 合計時間 | 1,940,350,605 ns | 1,675,078,996 ns | Bonsai 1.16倍遅い |
Qwen は呼び出し回数が多い分だけ合計時間が増える方向に働きますが、1 回あたりの処理時間の差がそれを上回るため、合計時間でも Bonsai-8B が 1.16 倍遅い結果になっています。
ncu:重み計算の詳細
次に Nsight Compute で重み計算 1 回あたりの詳細な挙動を計測しました。行列サイズの大小に対応する 3 段階で比較しました。
メモリ読み出し量
| 行列サイズ | Bonsai-8B | Qwen2.5-Coder-3B | 差分 |
|---|---|---|---|
| 小 | 876.40 KB | 386.88 KB | Bonsai 2.26倍多い |
| 中 | 5.42 MB | 3.63 MB | Bonsai 1.49倍多い |
| 大 | 10.85 MB | 19.87 MB | Bonsai 0.55倍少ない |
カーネル実行時間
| 行列サイズ | Bonsai-8B | Qwen2.5-Coder-3B | 比率 |
|---|---|---|---|
| 小 | 23.10 us | 10.94 us | Bonsai 2.11倍遅い |
| 中 | 96.41 us | 30.82 us | Bonsai 3.13倍遅い |
| 大 | 193.94 us | 131.18 us | Bonsai 1.48倍遅 |
最も影響のある大サイズの処理では、Bonsai-8B のメモリ読み出し量は Qwen2.5-Coder-3B の 0.55 倍(少ない) にもかかわらず、実行時間は 1.48 倍遅くなっています。つまり対象のデータ量が小さいにもかかわらず実行時間が長く、処理効率を us/MB で見ると Bonsai-8B 17.9 us/MB に対して Qwen2.5-Coder-3B 6.6 us/MB と Bonsai-8B が 2.7 倍非効率 と言う結果になっています。
Bonsai-8B の超低ビット圧縮はモデルファイルのサイズとメモリ読み出し量を大幅に削減しますが、GPU で計算するには圧縮を元に戻す処理(展開処理)が必要です。このコストが、サイズが小さいことの優位性を打ち消した上に逆転させてしまっているようです。
この展開処理の実装は PrismML によるカスタム開発であり、一般的な 4bit 量子化(Q4_K_M)のような長年の最適化の蓄積がありません。これが今回の処理効率の差の一因になっている可能性があると考えられます。
最適化された場合の試算
参考までに、今後最適化が進み展開処理の効率が Qwen2.5-Coder-3B 並みになった場合の理論値を試算すると。
Qwen2.5-Coder-3B の処理効率: 6.6 us/MB
Bonsai-8B のメモリ読み出し量: 10.85 MB
Bonsai-8B の期待実行時間 = 6.6 us/MB × 10.85 MB = 71.6 us
とかなりの改善が期待されます。
ただしパラメーター数差(8.19B vs 3.09B)による計算量の差は残るため、実際の改善幅はこれより小さくなるとは思います。
Azure VM VRAM 突出の調査
第 4 回のベンチマーク結果では、Azure VM 上の Bonsai-8B の VRAM 使用量が 10,661 MB であり、同等パラメーター規模の Qwen2.5-7B(4,885 MB)の約 2 倍でした。モデルファイル自体は 1.07 GiB しかないにもかかわらずこれほどの差が出た原因を調査しました。
コンテキストサイズ未指定の影響
llama.cpp ではコンテキストサイズ(一度に扱えるテキストの長さ)をパラメーターで明示的に指定しない場合、モデルが学習時に使用した最大値(Bonsai-8B は 65,536 トークン)がそのまま使われます。
LLM は過去のやり取りを記憶するためのバッファ(KV キャッシュ)をコンテキスト長に比例して確保します。コンテキスト長が 65,536 になるとこのバッファだけで 9,216 MiB を使用します。
実際に CONTEXT_SIZE=4096 を指定して計測し直した結果が以下の通りです。
コンテナ起動コマンド
sudo docker run -d --name bonsai_gpu_run --gpus all \
-p 11435:11435 \
-e USE_GPU=1 \
-e PORT=11435 \
-e CONTEXT_SIZE=4096 \
-e GPU_LAYERS=99 \
-e MODEL_PATH=/models/Bonsai-8B.gguf \
-v /opt/models:/models \
bonsai-llama
VRAM 使用量の比較
| 項目 | コンテキストサイズ指定なし | コンテキストサイズ=4096 |
|---|---|---|
| 実際のコンテキスト長 | 65,536 | 4,096 |
| バッファ(KV キャッシュ) | 9,216 MiB | 576 MiB |
| モデル本体 | 1,015.99 MiB | 1,015.99 MiB |
| 計算バッファ | 304.23 MiB | 304.23 MiB |
| VRAM 合計 | 10,661 MB | 2,021 MB |
コンテキストサイズを指定するだけで VRAM 使用量が 10,661 MB から 2,021 MB まで削減されました。
ベンチマーク結果への影響
VRAM を抑えた構成で EvaPlus を再実行したところ、スコアはほぼ同等でした。
| 項目 | コンテキストサイズ指定なし | コンテキストサイズ=4096 |
|---|---|---|
| base | 0.744 | 0.750 |
| base+extra | 0.695 | 0.701 |
| 実行時間 | 00:15:43 | 00:15:36 |
| VRAM 使用量 | 10,661 MB | 2,021 MB |
この件はモデルやツールの問題ではなく、llama.cpp を直接使う場合はコンテキストサイズを明示的に指定する必要があるという設定上の注意点であることが確認できました。
得られた知見
ここまでの検証を通して得られた知見をまとめます。
- LLM 推論の速度は GPU のメモリ帯域に依存するが、圧縮の展開処理の実装品質も実際のスループットに大きく影響する
- 圧縮率が高いほどファイルサイズは小さくなるが、展開処理の実装が未成熟な場合はかえって遅くなるケースがある
- 公式リポジトリに取り込まれていないカスタム実装では、理論上の優位性が実測値に反映されないことがある
- llama.cpp を直接使う場合はコンテキストサイズの明示指定を忘れずに
おわりに
今回は Bonsai-8B のボトルネックの分析を通じて以下のことがわかりました。
- T500 での実行時間増大の主因はメモリ帯域差で、熱対策のクロック制限が上乗せされている
- 展開処理の実装が未成熟でオーバーヘッドが大きく、条件によってはサイズのメリットを覆してしまう
- 圧縮効果は高く 4GB VRAM でも全層を GPU に載せられている
Bonsai-8B は 4GB VRAM という厳しい制約の中で 8B クラスのモデルを動かすという点で技術的に興味深い取り組みです。展開処理の実装が改善されれば実用的な選択肢になり得るため、今後の動向を引き続き注目していきたいと思います。
さて今回で Bonsai-8B については一旦完了となります。
次回以降では Azure Arc・AKS・Foundry Local を取り上げて、 ローカル LLM の環境構築について整理していく予定です。
本稿を読んでいただきありがとうございました。