背景:TTFTがボトルネックになるとき
半年前、私たちのチームは法務文書の読解とマルチターン会話を行うAIエージェントのバックエンドをアップグレードしました。しかし、すぐに課題に直面しました。同時接続ユーザー数が15名を超えたあたりから、Time to First Token (TTFT)が1.8秒以上に跳ね上がったのです。各リクエストには2,000トークンに及ぶシステムプロンプトと固定の契約書コンテキストが含まれており、GPUは同一の入力データに対してKVキャッシュの再計算を繰り返していました。これは極めて大きなリソースの浪費でした。
チームではvLLMとTGIの両方を検証しました。どちらも優れたエンジンですが、頻繁に繰り返される長い共通プレフィックスの課題を根本的に解決するには至りませんでした。そこでLMSYSおよびUC Berkeley発の推論エンジンであるSGLangに移行したところ、劇的な改善が得られました。
SGLangは、RadixAttentionメカニズムによってKVキャッシュをRadix Tree(基数木)構造で管理します。トークン生成後にキャッシュを破棄するのではなく、メモリ上に保持して共通のプレフィックスを持つ後続リクエストで再利用します。RAGや複数ターンのチャットボット環境において、初動トークンの応答レイテンシは1,800msから220ms未満へと一気に短縮されました。
リクエスト増加に伴って毎月のGPUコストが急増しているなら、SGLangは真っ先に検討すべき有力な選択肢です。
Linuxサーバー環境のセットアップ
NVIDIA Ampereアーキテクチャ以降のGPUを搭載したUbuntuサーバー(22.04 LTSを推奨)を用意します。具体的には、RTX 3090、4090、A10G、A100、H100などのシリーズで快適に動作します。事前にNVIDIA Driver(バージョン535以上)およびCUDA 12.1以上をインストールしておいてください。
Python 3.10または3.11を使用して専用の仮想環境を構築します:
# 仮想環境の作成と有効化
python3 -m venv venv-sglang
source venv-sglang/bin/activate
# パッケージツールのアップデート
pip install --upgrade pip setuptools wheel
アテンション計算カーネルを高速化するFlashInferを含め、最新バージョンのSGLangをインストールします:
# flashinferとともにsglangエンジンをインストール
pip install "sglang[all]>=0.4.0" --find-links https://flashinfer.ai/whl/cu121/torch2.4/flashinfer/
簡単なワンライナーでGPUの認識状況を確認します:
python3 -c "import torch; print('CUDA Ready:', torch.cuda.is_available(), '| Device:', torch.cuda.get_device_name(0))"
本番向け起動とチューニング設定
OpenAI互換サーバーの起動
SGLangの最大の利点の一つは、OpenAI API仕様に標準で対応している点です。アプリケーションロジックを変更することなく、バックエンドの接続先エンドポイントを切り替えるだけで運用できます。以下はポート30000番でMeta-Llama-3.1-8B-Instructをホストする起動コマンドです:
python3 -m sglang.launch_server \
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
--host 0.0.0.0 \
--port 30000 \
--mem-fraction-static 0.85 \
--context-length 8192 \
--log-level info
押さえておくべき主要パラメータ
--mem-fraction-static: モデルの重みをロードした後にKVキャッシュ専用として確保するVRAMの割合です。デフォルトは0.88です。サーバー上でエンベディングモデルやCeleryワーカーを併せて動かしている場合は、予期せぬCUDA OOMエラーを防ぐために0.80〜0.83程度に下げておくことをおすすめします。--tp (Tensor Parallelism): Qwen-2.5-72Bのような大規模モデル向けのテンソル並列設定です。たとえば4x A100 (80GB)環境では--tp 4を指定し、高速なNVLink/NCCL通信を介してモデルの重みを分散配置します。--schedule-policy lpm: Longest Prefix Match(最長プレフィックス一致)アルゴリズムです。これはRadixAttentionの中核となる機能で、既存のKVキャッシュと最も多く一致するリクエストを優先的にバッチへスケジューリングします。
バックエンドクライアントからの疎通テスト
おなじみの公式openai SDKからそのまま呼び出せます:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:30000/v1",
api_key="EMPTY" # 内部サーバーのためAPIキーは不要
)
# キャッシュ可能な共通プレフィックスとなる長いシステムプロンプト
system_prompt = "あなたは優秀なシニアバックエンドエンジニアです。簡潔に回答し、本番環境で即座に使えるコード例を優先して提示してください。"
response = client.chat.completions.create(
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": "asyncpgにおけるコネクションプールの設定例を教えてください。"}
],
temperature=0.1,
max_tokens=256
)
print(response.choices[0].message.content)
負荷テストと実運用のモニタリング
組み込みベンチマークツールによるストレステスト
十分な負荷検証を行わずに本番投入するのは禁物です。SGLangには、実トラフィックをシミュレートできる非常に便利なベンチマークツールが標準で付属しています:
python3 -m sglang.bench_serving \
--backend sglang \
--num-prompts 500 \
--request-rate 25 \
--dataset-name sharegpt \
--random-input-len 1024 \
--random-output-len 256
ここで特に注視すべきは、Median TTFTとOutput Throughput (tok/s)の2点です。同一のA10G GPU環境において、プロンプト間で約35%のプレフィックスが共有されている条件下では、SGLangのスループットは約720 token/sに達し、キャッシュ再利用を無効にした場合と比較して約2.2倍の性能向上を記録しました。
Prometheusを経由したGrafanaへのメトリクス可視化
SGLangは自動的にhttp://0.0.0.0:30000/metricsでメトリクスエンドポイントを公開します。監視システムにこのエンドポイントを登録し、以下の重要指標3つについてアラートを設定しておきましょう:
sglang:radix_cache_hit_rate: キャッシュヒット率です。チャットボットやRAGアプリケーションでは、理想的には60%〜85%の維持が目安となります。30%を下回る場合は、入力プロンプトの構成順序を見直す必要があります。sglang:num_waiting_reqs: 処理待ちリクエストのキュー数です。この値が長期間にわたって10を超え続けている場合、ハードウェアリソースが飽和している兆候です。GPUインスタンスのスケールアウトやバッチサイズの調整を検討してください。sglang:gpu_cache_usage_sys: KVキャッシュのメモリ使用率です。グラフが継続的に95%に張り付いている場合は、--context-lengthを小さく設定するか、VRAM容量の拡張が必要です。
これらの指標を正しく把握・管理することで、クラウド費用を高騰させることなく、LLMサービング基盤を安定してコントロールできるようになります。
