実行プログラムと拡張機能

vLLMでローカルサーバーを作る:速い応答と多くのリクエストは別の話

コーディングツールにローカルモデルを接続しようとすると、vLLMという名前をよく見かけます。ただ、紹介記事の高いtok/sと画面上の速さは一致しないことがあります。何を速くしたいのかを整理し、小さなサーバーの動作を確認してから必要な設定を加えていきましょう。

1. どの待ち時間を減らしたいのか

一人でコードを書きながらローカルモデルに質問する場面を考えてみます。欲しいのは、すぐに回答が始まり、コードが途切れずに出てくることです。一方、夜間に大量の文書を処理するサーバーなら、個々の待ち時間が少し延びても全体が早く終わるほうが都合よい場合があります。同じエンジンでも、この二つの目的では設定が変わります。

vLLMは複数のリクエストをまとめてGPUを使い、生成中に必要なKVキャッシュを管理します。そのため、複数ユーザーの出力を合計すると高い処理量になることがあります。その数字を一つのチャット画面の速さだと受け取ると、期待がずれます。比較では同時リクエストを1にし、最初のトークンまでの時間と生成速度を別々に記録してください。

2. 同じNVIDIAでも使うビルドは違う

ここでのコマンドは、対応するLinux・NVIDIA CUDA環境にvLLMがインストール済みであることを前提にしています。既存の作業環境へ上書きせず、別の仮想環境か機器に合うコンテナを用意してください。PyTorch、CUDA、GPUドライバーの組み合わせが合う必要があります。インストール完了の表示だけでは、GPU推論の動作確認にはなりません。

DGX SparkではARM64のCPU構成とBlackwell GPUへの対応を両方確認します。一般的なx86 PC向けイメージをそのまま使うことはできません。Macはさらに別です。macOSのCPU実行と、MLXを使うコミュニティのvllm-metalプラグインはCUDAとは異なる経路です。Metalで動くという説明だけで、量子化やMTPのオプションも同じだと判断しないでください。

3. 小さなモデルで接続だけを確認する

最初から大きな目的のモデルと高速化オプションを一緒に指定すると、失敗の原因を見分けにくくなります。下のコマンドは、インストール確認用のQwen2.5-1.5B-Instructをコンテキスト上限4,096トークン、同時リクエスト1で起動するものです。普段使う最新モデルの推奨ではありません。保存済みでなければ初回にモデルファイルをダウンロードします。その準備時間は回答速度の測定に含めません。

サーバーの準備完了を確認したら、別のターミナルから短い質問を送ります。二つのコマンドでモデル名とポートを一致させてください。127.0.0.1は、このコンピューター内からだけ接続するアドレスです。正常に回答が返れば、アプリの接続先をhttp://127.0.0.1:8000/v1に設定できます。この段階の構成を保存しておけば、次の変更に失敗しても戻れる場所ができます。

CUDA環境での接続確認用サーバー

vllm serve Qwen/Qwen2.5-1.5B-Instruct \
  --host 127.0.0.1 --port 8000 \
  --max-model-len 4096 --max-num-seqs 1

vLLMをインストールしてから実行します。Sparkでは先にARM64・Blackwell対応ビルドを用意してください。

別のターミナルから質問する

curl --fail-with-body http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"Qwen/Qwen2.5-1.5B-Instruct","messages":[{"role":"user","content":"Explain what a local AI server does in two sentences."}],"max_tokens":64,"temperature":0}'

接続確認用の質問です。この1回の応答をベンチマーク記録として扱いません。

4. メモリを使い切ることが目的ではない

普段使うモデルに替えたら、まずファイル名、量子化形式、入力長を固定します。NVFP4とGGUF Q4は、どちらも4ビットだからといって置き換えられる設定ではありません。GPU世代とモデル構造に合う実行対応が必要です。重みが載った後にもKVキャッシュと計算用の空間が残っていなければ、長い文書を処理できません。

まず--max-model-lenを実際に必要な長さにし、--max-num-seqs 1で確認してください。同時リクエストを増やすのはその後です。--gpu-memory-utilizationを上げるとキャッシュ用の領域を増やせる場合がありますが、ほかのプロセスの余裕は減ります。リクエストの中断と再計算が繰り返されるなら、割り当てを限界まで上げる前に、コンテキストと同時リクエスト数を減らして原因を切り分けます。

5. MTPはモデルを確認してから

MTPは後続トークンの候補を先に作り、確認する方式です。対応するモデル、必要な重み、vLLMのバージョンがそろう必要があり、すべてのモデルへ同じ設定を付けられるわけではありません。現在のvLLMは--speculative-configで設定を受け取ります。他のエンジンのNEXTNや--spec-typeを書き写さず、使うモデルのレシピで対応経路を確認してください。

高速化の有無は、質問と出力の長さをそろえて比較します。候補が多く採用されるコード作業と、自由な文章作成では結果が違うことがあります。長い入力に短く答える用途では、デコードの改善より回答開始前の待ち時間が重要かもしれません。初回入力とキャッシュの残った再入力も分けないと、効果を取り違えます。

6. 失敗したときに戻れる構成を残す

読み込み中にメモリが足りなくなるなら、最初にモデルファイルの大きさと他のGPUプロセスを確認します。リクエスト後だけ失敗するならコンテキストと同時リクエスト数を減らし、MTP追加直後なら高速化を外して再確認します。コンパイルやCUDA graphの段階だけで問題が起きる場合、--enforce-eagerを一時的な切り分けに使えます。ただし、そのまま残すと通常時の生成性能が変わることがあります。

正常に動いたバージョン、モデルrevision、起動コマンドを一緒に保存してください。更新時は古い環境を残し、新しい環境で短い質問と普段の長い文書を確認すれば、復旧しやすくなります。一人で使う速度が十分なら、複雑な複数GPU構成へ進む必要はありません。足りないのがメモリなのか、同時に処理できる仕事量なのかを決めてから機器を比較すればよいのです。

変更履歴

サイトの説明を変更した記録です。インストール済みのエンジンやモデルのバージョンを自動で確認した結果ではありません。

  1. vLLMの実行条件・リクエスト処理の案内を追加

    CUDA環境の短いサーバー・API例を追加し、一般的なPC、ARM64のSpark、Macの実行経路を区別しました。1件の生成速度とサーバー全体の処理量を分け、モデルごとに量子化とMTPの対応を確認するよう説明しました。