【後編】HPE ProLiant DL385 Gen10 × TITAN RTX でローカル LLM ─ 推論エンジン 4 種と 17 モデルの比較
はじめに
前編では、HPE ProLiant DL385 Gen10 に TITAN RTX を積み、電力上限を 240W に決めるまでを書きました。
後編は、この GPU でどの推論エンジンとモデルを使うかを決めた話です。用途は、ツールを呼びながら調査を進めるエージェントです。エージェントは毎回、長いツール定義(数千〜数万トークン)を入力として読むので、入力の処理速度(prefill)が効きます。
先に結論です。
- TITAN RTX(Turing、Compute Capability 7.5)では、llama.cpp 系(Ollama / llama-server)が prefill で vLLM の 3〜5 倍速い。生成速度はほぼ同じ
- SGLang は生成が 7〜13 tok/s しか出ず、この GPU では実用になりませんでした
- モデルは MoE 系(gemma4:26b-a4b、gpt-oss:20b、qwen3-coder:30b など)が速く、24GB の VRAM にも収まります
- Ollama の OpenAI 互換 API(
/v1)は思考(reasoning)が既定で有効です。ここに気づかないと、ツール呼び出しの精度を見誤ります
構成
Ollama は OS に直接入れた
本番で使う推論ランタイムは Ollama にし、Docker ではなく OS に直接入れて systemd で常駐させています。常駐させるものが 1 つだけなら、コンテナにしても層が増えるだけだからです。Docker の公開ポートは iptables を直接書き換えるので、ufw の制御を素通りしてしまう点も避けたかった理由です。
比較用のエンジン(llama-server / vLLM / SGLang)は Docker で動かしました。/etc/docker/daemon.json に "ip": "127.0.0.1" を書き、コンテナのポートを既定でループバックにだけ公開するようにしています。
Ollama の設定
# /etc/systemd/system/ollama.service.d/override.conf(抜粋)
Environment=OLLAMA_CONTEXT_LENGTH=65536
Environment=OLLAMA_NUM_PARALLEL=1
Environment=OLLAMA_KV_CACHE_TYPE=q8_0
Environment=OLLAMA_MAX_LOADED_MODELS=2
Environment=OLLAMA_KEEP_ALIVE=30m
Environment=OLLAMA_NO_CLOUD=1
最初はコンテキスト 32k × 並列 2 にしていました。ところが、エージェントの会話が 43.9k トークンに達したところで、Ollama が入力を黙って切り詰めていました。
Ollama は内部で llama-server を -c(コンテキスト長 × 並列数) で起動し、それを並列数で分け合います。32k × 2 は「64k を 2 人で分ける」設定で、1 リクエストあたりは 32k までです。エージェント用途では 64k × 1 に変えました。KV キャッシュは q8_0 に量子化して VRAM を節約しています。
Turing では FlashAttention 2 が使えない
エンジンの比較に入る前に、前提を 1 つ書いておきます。
TITAN RTX は Turing 世代で、Compute Capability は 7.5 です。FlashAttention 2 は Compute Capability 8.0 以上(Ampere 以降)が必要なので、この GPU では使えません。
- vLLM は FlashAttention 2 を使えないため、Triton で書かれた代替のバックエンド(
TRITON_ATTN)で動きます - SGLang は既定の FlashInfer が sm75 にも対応していますが、後で書くとおり生成(decode)が極端に遅くなりました
- llama.cpp 系は独自の CUDA カーネルを持っているので、この制約の影響をあまり受けません
推論エンジンの比較
同じ Qwen3-14B を 4 つのエンジンで動かしました(電力上限 200W のとき)。
| エンジン(バックエンド) | 形式 | prefill(tok/s) | 生成(tok/s) | VRAM | ツール正答 |
|---|---|---|---|---|---|
Ollama(/v1、思考なし) | GGUF Q4_K_M | 1,004 | 50.6 | 14.4GB | 30/30 |
| llama-server(Docker) | GGUF Q4_K_M | 1,063 | 51.1 | 14.4GB | 27/30 |
| vLLM(TRITON_ATTN) | AWQ 4bit | 355 | 53.4 | 20.7GB | 30/30 |
| vLLM(FLEX_ATTENTION) | AWQ 4bit | 352 | 53.0 | 20.8GB | 30/30 |
| SGLang(FlashInfer) | AWQ 4bit | 1,933 | 7.6 | 20.4GB | 30/30 |
| SGLang(triton) | AWQ 4bit | 85 | 7.5 | 19.9GB | 30/30 |
- 量子化の形式が違う点には注意してください(llama.cpp 系は GGUF Q4_K_M、vLLM / SGLang は AWQ 4bit)
- OpenAI 互換 API では、prefill を最初のトークンが返るまでの時間(TTFT)から計算しています
- ツール正答は、13 個のツール定義を渡して 10 問 × 3 回、正しいツール名を選べたかの割合です
- vLLM はバックエンドを変えても差がありませんでした。起動にも約 3 分かかります
- SGLang は prefill こそ速いものの、生成が 7.6 tok/s しか出ません。バックエンドを triton にすると prefill まで崩れました
モデルのサイズを変えても構図は同じ
モデルのサイズで差が変わるかを見るため、Qwen3 の 4B / 8B / 14B でも測りました(電力上限 240W、思考なし)。
| サイズ | Ollama | llama-server | vLLM |
|---|---|---|---|
| 4B | 2,938 / 130.7 | ─ | 600 / 136.0 |
| 8B | 2,047 / 86.1 | ─ | 565 / 88.2 |
| 14B | 1,102 / 52.6 | 1,144 / 53.3 | 364 / 55.4 |
数字は「prefill / 生成(tok/s)」です。SGLang は 14B で生成が成り立たなかったため、8B だけ再確認しました(200W で 3,292 / 13.4)。結果は同じでした。
どのサイズでも、prefill は Ollama が vLLM の 3〜5 倍、生成は vLLM が数 % 速い、という構図は変わりません。エージェントは長い入力を毎回読むので、prefill の差のほうが体感に効きます。本番のエンジンは Ollama にしました。
この評価は Turing だからこそです。FlashAttention 2 が使える Ampere 以降の GPU なら、vLLM の評価は変わるはずです。
モデルの比較
Ollama で 17 のモデルを測りました(電力上限 200W)。
| モデル | 提供元 | 種別 | VRAM / GPU 率 | prefill | 生成 | ツール | 日本語 |
|---|---|---|---|---|---|---|---|
| gpt-oss:20b | OpenAI | MoE | 13.0GB / 100% | 2,765 | 95.6 | 90% | 26 |
| gemma4:26b-a4b-it-qat | MoE | 15.3GB / 100% | 2,762 | 93.6 | 90% | 43 | |
| glm-4.7-flash | Z.ai | MoE | 21.0GB / 100% | 1,264 | 96.6 | 90% | 38 |
| qwen3-coder:30b | Alibaba | MoE | 22.1GB / 100% | 1,830 | 105.9 | 90% | 39 |
| gemma4:31b-it-qat | dense | 19.8GB / 100% | 600 | 25.1 | 80% | 42 | |
| qwen3.6:27b | Alibaba | dense | 17.4GB / 100% | 604 | 34.9 | 100% | 39 |
| qwen3.8:27b | Alibaba | dense | 17.4GB / 100% | 604 | 30.7 | 90% | 34 |
| devstral-small-2:24b | Mistral | dense | 20.1GB / 100% | 828 | 32.5 | 100% | 30 |
| mistral-small3.2:24b | Mistral | dense | 20.1GB / 100% | 827 | 33.2 | 50% | 31 |
| granite4.2:30b | IBM | dense | 23.7GB / 86% | 477 | 16.5 | 30% | 19 |
| nemotron-3-nano:4b | NVIDIA | 小型 | 4.0GB / 100% | 4,052 | 143.6 | 80% | 26 |
| gemma4:e4b-it-qat | 小型 | 3.8GB / 100% | 3,563 | 90.1 | 0% | 36 | |
| granite4.2:8b | IBM | 小型 | 11.2GB / 100% | 1,973 | 78.7 | 35% | 17 |
| Llama-3-ELYZA-JP-8B | ELYZA | 国産 | 6.0GB / 100% | 2,425 | 88.3 | 非対応 | 21 |
| llm-jp-4.1-8b-thinking | NII | 国産 | 10.2GB / 100% | 2,388 | ─ | 動作せず | 動作せず |
| deepseek-r1:14b | DeepSeek | 蒸留 | 15.7GB / 100% | 1,116 | 50.8 | 非対応 | 19 |
| deepseek-r1:32b | DeepSeek | 蒸留 | 23.7GB / 80% | 403 | 12.7 | 非対応 | 26 |
測り方は次のとおりです。
- prefill は約 7〜10k トークンの入力、生成は 500 トークンで、どちらも 3 回の中央値(tok/s)
- ツールは、13 個のツール定義 × 10 問 × 3 回(思考なし。思考を切れない gpt-oss は
low)で、正しいツール名を選べた割合 - 日本語は、ネットワークの技術的な質問 3 問 × 3 軸(正確さ・自然さ・指示どおりか)× 5 点の 45 点満点。AI による仮の採点なので参考程度に見てください
- 「非対応」は、チャットテンプレートがツール定義をプロンプトに載せないもの
分かったこと
- MoE は速い。 gpt-oss:20b、gemma4:26b-a4b、qwen3-coder:30b、glm-4.7-flash は、prefill 1,200〜2,800 tok/s、生成 93〜106 tok/s でした
- dense の 27〜31B は遅い。 prefill 600 前後、生成 25〜35 tok/s で、エージェントの応答としては待たされます
- 24GB に収まらないモデルがある。 granite4.2:30b と deepseek-r1:32b は一部が CPU にはみ出し、大きく遅くなりました
- 国産モデルは使いどころが限られた。 llm-jp-4.1 は Ollama では動きません(llama.cpp のチャット形式の解析が未対応で、公式の README にも明記されています)。ELYZA は速いものの、ツール呼び出しに対応していません
- ツール定義が多くても VRAM に収まる。 90 個のツール定義(約 34k トークン)でも、コンテキスト 64k で gemma4:26b-a4b が 15GB、glm-4.7-flash が 22GB と、どちらも GPU に収まりました。正答率は 90% です。Ollama のプレフィックスキャッシュが効くので、2 回目以降は 0.1 秒で読み終わります
電力上限を 240W にすると、上位 6 本の prefill は最大で 1 割ほど伸びました(例: gemma4:26b-a4b は 2,987 / 96.9、gpt-oss:20b は 2,884 / 97.4)。gpt-oss:20b のツール正答は 240W の試験で 30/30 でした。
Mac との比較
参考として、以前 Mac(M5・24GB)で動かしたときの gpt-oss:20b と比べます。条件は揃っていないので、あくまで目安です。
| prefill(tok/s) | 生成(tok/s) | |
|---|---|---|
| Mac(M5・24GB) | 350 | 14 |
| TITAN RTX(200W) | 2,765 | 95.6 |
用途ごとの選び方
| 用途 | モデル | 理由 |
|---|---|---|
| エージェント(日本語) | gemma4:26b-a4b-it-qat | 日本語の点が最も高く、速い。画像入力にも対応 |
| エージェント(英語中心・最軽量) | gpt-oss:20b | 速く、VRAM も 13GB で済む。日本語の技術説明には誤りがあった |
| ツール精度を最優先 | qwen3.6:27b | 正答 100%。ただし dense なので遅い |
| コーディング | qwen3-coder:30b | 生成が最も速い MoE |
| 小型の補助(タイトル付けなど) | nemotron-3-nano:4b | 4GB で、大きなモデルと同時に載せられる |
ハマったところ
1. Ollama の /v1 は思考が既定で有効
最初の比較では、Ollama のツール正答率が 80%、vLLM が 100% と差が出ました。原因はエンジンではなく、Ollama の OpenAI 互換 API(/v1)では思考が既定で有効になることでした。思考に生成上限を使い切って、ツール呼び出しまでたどり着かなかった問が出ていたのです。
思考を切って同じ条件にそろえると、Ollama も 100% になりました。
- gemma4 / Qwen3 系: リクエストに
"reasoning_effort": "none"を付ける - gpt-oss:
noneが効かないので"low"にする
思考を切るかどうかでモデルごとに指定が違うため、クライアント側ではエンドポイントを 2 本に分けています。
2. llama-server では gpt-oss のツール呼び出しが解釈されない
素の llama-server で gpt-oss:20b を動かすと、ツール正答が 0/30 でした。gpt-oss はツール呼び出しを独自の harmony 形式で出力しますが、--jinja を付けてもこれを解釈しません。Ollama は harmony 形式のパーサを持っているので、同じモデルが Ollama では動きます。
3. 同梱テンプレートが enum の候補を出さない
Ollama に同梱されている gpt-oss のテンプレートは、ツール定義の enum(選択肢)と、省略できる引数の印を出力しません。モデルは選択肢を知らないまま引数を作るので、スキーマに合わない値を返していました。テンプレートを直した派生モデルを Modelfile で作って解決しました。
4. 引数の多いツールで JSON が途中で切れる
引数が 26 個あるツール定義を渡すと、モデルが全部の引数を null で埋めようとして、出力の JSON が途中で切れ、Ollama が HTTP 500 を返しました。ツール側で、必要な引数だけに絞ったスキーマを渡すことで防げます。
5. 前のモデルが残っていると、次のモデルが CPU にはみ出す
連続で計測していたら、nemotron-3-nano:4b だけ極端に遅い回がありました(prefill 572 / 生成 41.6、GPU 率 66%)。直前の qwen3.6:27b(17.4GB)が keep_alive で VRAM に残っていたため、収まりきらずに CPU にはみ出していました。単独で測り直すと 3,862 / 140.5(GPU 率 100%)です。計測の前には ollama stop で VRAM を空にします。
6. CPU が空回りしている
Ollama は既定で CPU のスレッドを大量に使い、GPU で推論していても CPU 使用率が 1,600% 前後になっていました。num_thread=8 にしても生成速度は 95.1 tok/s で変わらず、CPU の無駄な消費だけを抑えられます。
まとめ
- Turing(Compute Capability 7.5)では FlashAttention 2 が使えないため、llama.cpp 系(Ollama)が prefill で vLLM の 3〜5 倍速い。エージェントのように長い入力を読む用途では、この差が効きます
- SGLang は、この GPU では生成が成り立ちませんでした
- 24GB の VRAM では、MoE の 20〜30B クラスが速さと精度のバランスがよい選択です
- Ollama の
/v1は思考が既定で有効です。ツール呼び出しの精度を比べるときは、思考の有無をそろえてから比べます
TITAN RTX は 2018 年の GPU ですが、24GB の VRAM があれば、今の MoE モデルでも十分に実用的な速度が出ました。



ディスカッション
コメント一覧
まだ、コメントがありません