問題の概要:ローカルLLMの推論速度測定の難しさと課題
ローカル環境で大規模言語モデル(LLM)を実行する際、多くの開発者が直面する課題の一つが「推論速度の正確な測定」です。単に「速い」「遅い」という主観的な評価ではなく、定量的な指標で性能を比較・評価する必要があります。しかし、以下のような問題が発生することがあります。
- 異なるハードウェア間での公平な比較が難しい
- トークン生成速度(tokens/sec)の測定方法が統一されていない
- プロンプト処理時間と生成時間を分離して計測できない
- バッチ処理時のスループット測定が複雑
- VRAM使用量やメモリ帯域のボトルネックを特定できない
具体的なエラーメッセージや問題例としては、以下のようなものがあります:
# よくある問題の例
WARNING: 推論中にOOM(Out Of Memory)エラーが発生
ERROR: CUDA out of memory. Tried to allocate 2.00 GiB
# 速度測定の不正確さ
INFO: 計測結果が実行ごとに大きくばらつく
測定点1: 45 tokens/sec
測定点2: 28 tokens/sec
測定点3: 52 tokens/sec
原因の解説:なぜ正確なベンチマークが難しいのか
ローカルLLMの推論速度測定が複雑な理由は、以下の要因が相互に影響し合うためです。
1. ハードウェア要因の多様性
GPUのVRAM容量・帯域幅、CPUのコア数・周波数、システムメモリの速度、ストレージのI/O性能など、多数の要素が推論速度に影響します。特に、モデルサイズがVRAMに収まるかどうかで、速度が劇的に変化します。
2. ソフトウェアスタックの複雑さ
LLMの推論エンジン(llama.cpp、vLLM、TensorRT-LLMなど)、量子化方式(GPTQ、AWQ、GGUF)、フレームワーク(PyTorch、ONNX Runtime)の選択によって、最適化レベルが異なります。
3. 測定条件の標準化不足
以下の条件を統一しないと、公平な比較ができません:
- 入力トークン数と出力トークン数の比率
- 温度パラメータやtop-pなどのサンプリング設定
- コンテキストウィンドウのサイズ
- キャッシュのウォームアップ有無
4. システムリソースの競合
バックグラウンドプロセスや他のアプリケーションによるCPU/GPU/メモリの使用状況が、測定結果に影響を与えます。
解決方法:体系的なベンチマーク手法と測定ツール
正確で再現性のあるベンチマークを行うためのステップバイステップの方法を紹介します。
ステップ1:測定環境の整備と標準化
まず、測定環境をクリーンな状態にし、条件を標準化します。
# システムリソースの確認コマンド(Linux例)
# GPU情報の確認
nvidia-smi
# CPU情報の確認
lscpu
# メモリ使用状況の確認
free -h
# バックグラウンドプロセスの一時停止(可能な場合)
sudo systemctl stop docker
ステップ2:適切なベンチマークツールの選択と導入
目的に応じて適切なツールを選択します。主要なツールは以下の通りです。
1. llama.cppのベンチマーク機能
llama.cppには組み込みのベンチマークツールが含まれており、様々なモデルと量子化形式に対応しています。
# llama.cppのビルドとベンチマーク実行例
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make
# ベンチマークの実行(7Bモデル、Q4_K_M量子化)
./llama-bench -m ./models/llama-2-7b.Q4_K_M.gguf -n 128 -t 8
# 出力例
llama_print_timings: load time = 1000.00 ms
llama_print_timings: sample time = 50.00 ms
llama_print_timings: prompt eval time = 1500.00 ms / 128 tokens ( 11.72 ms per token)
llama_print_timings: eval time = 10000.00 ms / 127 tokens ( 78.74 ms per token)
llama_print_timings: total time = 11550.00 ms
2. LM Evaluation Harness(lm-eval)
推論速度だけでなく、モデルの精度も含めた総合的な評価が可能です。
# lm-evalのインストールと実行
pip install lm-eval
# ベンチマークの実行例
lm_eval --model hf
--model_args pretrained=meta-llama/Llama-2-7b-hf
--tasks hellaswag
--device cuda:0
--batch_size 8
--output_path ./results.json
# 速度関連の出力メトリクス
"runtime_info": {
"samples_per_second": 15.2,
"tokens_per_second": 305.7
}
3. vLLMのベンチマーク機能
バッチ処理のスループット測定に最適化されたツールです。
# vLLMのインストールとベンチマーク
pip install vllm
# ベンチマークスクリプトの実行例
python -m vllm.benchmarks.throughput
--model meta-llama/Llama-2-7b-hf
--dataset ShareGPT_V3_unfiltered_cleaned_split.json
--num-prompts 1000
--request-rate 10
--output results.csv
# 結果の解釈
Throughput: 125.4 requests/sec
Average latency: 45.2 ms/token
P95 latency: 78.9 ms/token
4. カスタム測定スクリプトの作成
特定のユースケースに合わせた測定を行う場合は、カスタムスクリプトが有効です。
import time
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
class LLMBenchmark:
def __init__(self, model_name):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
def benchmark(self, prompt, max_new_tokens=100, num_runs=10):
times = []
tokens_generated = []
inputs = self.tokenizer(prompt, return_tensors="pt").to("cuda")
for i in range(num_runs):
torch.cuda.synchronize()
start_time = time.time()
with torch.no_grad():
outputs = self.model.generate(
**inputs,
max_new_tokens=max_new_tokens,
do_sample=False
)
torch.cuda.synchronize()
end_time = time.time()
generated_tokens = outputs[0][inputs['input_ids'].shape[1]:]
num_tokens = len(generated_tokens)
times.append(end_time - start_time)
tokens_generated.append(num_tokens)
avg_time = sum(times) / len(times)
avg_tokens = sum(tokens_generated) / len(tokens_generated)
tokens_per_sec = avg_tokens / avg_time
return {
"avg_time": avg_time,
"avg_tokens": avg_tokens,
"tokens_per_sec": tokens_per_sec,
"std_dev_time": torch.std(torch.tensor(times)).item()
}
# 使用例
benchmark = LLMBenchmark("meta-llama/Llama-2-7b-hf")
results = benchmark.benchmark(
"日本の首都は",
max_new_tokens=50,
num_runs=20
)
print(f"平均速度: {results['tokens_per_sec']:.2f} tokens/sec")
print(f"時間の標準偏差: {results['std_dev_time']:.4f}秒")
ステップ3:測定条件の統一と記録
以下の条件を明記し、すべての測定で統一します。
# 測定条件テンプレート
測定条件:
- モデル: Llama-2-7b-chat-hf
- 量子化: GPTQ 4bit
- 入力トークン数: 128 tokens
- 出力トークン数: 128 tokens
- バッチサイズ: 1
- コンテキスト長: 4096
- 温度: 0.0 (貪欲サンプリング)
- ハードウェア: RTX 4090, 64GB RAM, Ryzen 9 7950X
- ソフトウェア: PyTorch 2.1, CUDA 11.8
- ウォームアップ実行: 3回
- 測定実行: 10回の平均
ステップ4:複数メトリクスの測定と分析
単一の数値ではなく、以下の複数のメトリクスを測定します。
- Time to First Token (TTFT): 最初のトークンが生成されるまでの時間
- Tokens per Second (TPS): 1秒あたりの生成トークン数
- Inter-token Latency: トークン間の生成間隔
- Peak Memory Usage: ピーク時のVRAM使用量
- Throughput: バッチ処理時のスループット
コード例・コマンド例:実践的なベンチマークワークフロー
実際のワークフローを示す完全な例です。
#!/bin/bash
# 完全なベンチマークワークフロー例
echo "=== LLMベンチマークセットアップ開始 ==="
# 1. 環境情報の記録
echo "## システム情報" > benchmark_report.md
nvidia-smi >> benchmark_report.md
lscpu | grep "Model name" >> benchmark_report.md
free -h >> benchmark_report.md
# 2. モデルのダウンロード(例: llama-2-7bのGGUF形式)
echo "## モデルダウンロード" >> benchmark_report.md
wget https://huggingface.co/TheBloke/Llama-2-7B-GGUF/resolve/main/llama-2-7b.Q4_K_M.gguf
-O models/llama-2-7b.Q4_K_M.gguf 2>&1 >> benchmark_report.md
# 3. llama.cppでのベンチマーク実行
echo "## llama.cppベンチマーク結果" >> benchmark_report.md
cd llama.cpp
for threads in 4 8 16; do
echo "### スレッド数: $threads" >> ../benchmark_report.md
./llama-bench
-m ../models/llama-2-7b.Q4_K_M.gguf
-n 256
-t $threads
-p "日本の人工知能研究について" >> ../benchmark_report.md
echo "" >> ../benchmark_report.md
done
# 4. カスタムPythonスクリプトでの測定
echo "## Pythonスクリプトでの詳細測定" >> benchmark_report.md
cd ..
python3 custom_benchmark.py >> benchmark_report.md
echo "=== ベンチマーク完了 ==="
echo "結果は benchmark_report.md を確認してください"
まとめ・補足情報
ローカルLLMの推論速度を正確に測定するためには、体系的なアプローチが必要です。重要なポイントを以下にまとめます。
ベンチマークのベストプラクティス
- ウォームアップ実行を行う: 最初の数回の実行はキャッシュが効いていないため除外します。
- 十分な回数を測定する: 統計的有意性を得るために、少なくとも10回以上の測定を行います。
- システムリソースを監視する: nvidia-smiやhtopでリソース使用率を同時に監視します。
- 測定条件を詳細に記録する: 後で再現できるようにすべてのパラメータを記録します。
よくある落とし穴と回避策
問題: 測定結果が実行ごとに大きくばらつく
解決策: バックグラウンドプロセスを停止し、システムをクリーンな状態で測定します。また、測定回数を増やして平均値を取ります。
問題: 異なるハードウェア間での比較が難しい
解決策: 相対的な指標(例: 理論性能に対する達成率)を使用するか、同じモデルサイズに対する正規化された指標を開発します。
問題: 長時間実行時の性能低下
解決策: 長時間実行テストを実施し、メモリフラグメンテーションや熱スロットリングの影響を評価します。
将来の展望
LLM推論のベンチマークは急速に進化しています。特に以下の分野が注目されています:
- 標準化されたベンチマークスイート: MLPerfのような業界標準の出現
- リアルワールドワークロードのシミュレーション: 実際のユースケースに近い負荷テスト
- エネルギー効率の測定: 性能だけでなく電力消費も考慮した評価
- マルチモーダルモデルのベンチマーク: 画像や音声を含む総合的な評価
ローカルLLMの推論速度測定は、単なる「速さ」の競争ではなく、実際のアプリケーションでの実用性を評価する重要なプロセスです。適切なツールと方法論を用いて、公平で再現性のある測定を行うことで、モデル選択やハードウェア投資の意思決定をデータ駆動で行えるようになります。