vLLM Translation Performance Optimization Continuous Batching Machine Learning

Part 2:翻訳推論のスケーリング:スループット+82%

Ashar Mirza - VoicePing 3 分で読めます
Part 2:翻訳推論のスケーリング:スループット+82%

AsyncLLMEngineと適切なContinuous Batching設定により、vLLM推論スループットを82%向上させた方法

前回のおさらい

Part 1では、ボトルネックを特定しました。FastAPIサービスがマルチプロセッシングワーカーとIPCキューで翻訳タスクを分配しており、以下の問題が発生していました:

  • キューのシリアライゼーションオーバーヘッド
  • ワーカープロセス間のGPU計算リソースの競合
  • スパイク状のGPU使用率パターン

ベースライン:25並行リクエスト時に2.2 RPS

改善の方向性:マルチプロセッシングを排除し、vLLMのバッチ推論を活用する。


試行2:スタティックバッチ処理

既存のワーカープロセス内でスタティックバッチ処理を実装しました。

実装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# ワーカープロセス内
MAX_BATCH_SIZE = 16
BATCH_TIMEOUT = 0.05  # 50ms

while True:
    batch_keys = []
    batch_tasks = []

    # 最初のタスクを取得(ブロッキング)
    first_key = queue.get()
    batch_keys.append(first_key)
    batch_tasks.append(tasks[first_key])

    # 追加タスクの取得を試行(タイムアウト付きノンブロッキング)
    batch_start = time.time()
    while len(batch_keys) < MAX_BATCH_SIZE:
        time_remaining = BATCH_TIMEOUT - (time.time() - batch_start)
        if time_remaining <= 0:
            break
        try:
            key = queue.get(timeout=time_remaining)
            batch_keys.append(key)
            batch_tasks.append(tasks[key])
        except Empty:
            break

    # vLLMでバッチ処理
    results = translation_provider.translate_batch(
        texts=[t.text for t in batch_tasks],
        source_langs=[t.source_lang for t in batch_tasks],
        target_langs=[t.target_lang for t in batch_tasks]
    )

ポイント:

  • バッチサイズ:16リクエスト
  • タイムアウト:50ms(バッチが満杯になるまで無限に待たない)
  • vLLMが複数シーケンスをまとめて処理
  • マルチプロセッシングワーカーはそのまま使用

結果

図1:スタティックバッチ処理によるスループットとレスポンスタイムの大幅改善

スループットが約3倍に向上。 リクエストあたりの推論時間:452ms → 171ms。

トレードオフ

メリット:

  • 大幅なスループット向上
  • GPUの活用効率が改善
  • シンプルな実装

デメリット:

  • Head-of-Lineブロッキング:すべてのリクエストが最も遅いリクエストの完了を待つ
  • 入力長が可変の場合、短い翻訳が長い翻訳を待つ
  • 例:[50トークン, 50トークン, 200トークン] → 最初の2つが200トークンの翻訳完了を待機

良い進展でしたが、Head-of-Lineブロッキングの問題を解消したいと考えました。

試行3:Continuous Batching

解決策:vLLMのAsyncLLMEngineによるContinuous Batching。

Continuous Batchingとは

スタティックバッチ処理とは異なり、Continuous Batchingはバッチを動的に構成します:

  • 新しいリクエストが生成途中でもバッチに参加
  • 完了したリクエストはすぐに離脱(他のリクエストを待たない)
  • バッチ構成がトークンごとに更新
  • vLLMのAsyncLLMEngineがこれを自動的に処理

Head-of-Lineブロッキングなし。 短い翻訳は完了次第すぐに返される。

実装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from vllm import AsyncLLMEngine, EngineArgs

engine_args = EngineArgs(
    model=model_id,
    max_num_seqs=64,  # 初期設定
    max_num_batched_tokens=16384,
    gpu_memory_utilization=0.3,
    enable_chunked_prefill=True,
)

engine = AsyncLLMEngine.from_engine_args(engine_args)

@app.post("/translate")
async def translate(request: TranslateRequest):
    result_generator = engine.generate(
        request.text,
        sampling_params,
        request_id=generate_id()
    )

    async for output in result_generator:
        final_output = output
    return TranslateResponse(translation=final_output.text)

アーキテクチャの変更点:

  • AsyncLLMEngineをFastAPIで直接使用
  • vLLMがContinuous Batchingエンジンで内部的にバッチ処理を管理
  • 全体がpure async/await

テスト結果の実際

初期結果(均一な入力)

標準的な均一長の入力(類似の長さ)でテスト:

図2:均一入力でのContinuous Batching、15 RPSの高スループットを達成

15 RPS vs ベースライン2.2 — 約7倍の改善。素晴らしい結果に見えました。

可変長入力(現実のデータ)

次に、現実的な可変長入力(10〜200トークン、短文と長文の混合)でテスト:

可変入力でのベースライン再実行:

  • 非常に高い負荷:1.1 RPS(均一入力時の2.2 RPSと比較)
  • ベースラインすら現実的なデータでは性能が低下

可変入力でのContinuous Batching(max_num_seqs=64):

  • 非常に高い負荷:3.5 RPS(max_num_seqs=16に調整後)
  • 均一入力で15 RPSを達成した同じ設定

図3:均一テストデータと現実の可変長入力との性能差

設定のチューニング

max_num_seqs=64での可変長入力の性能不足を受けて、vLLMの内部メトリクスを分析しました。

判明したこと

1
2
3
4
5
# 監視したvLLM Prometheusメトリクス:
# - vllm:time_to_first_token_seconds (TTFT)
# - vllm:time_per_output_token_seconds (デコード時間)
# - vllm:gpu_cache_usage_perc (KVキャッシュ使用率)
# - vllm:num_requests_running / waiting (キュー深度)

問題点:

  • 実際のワークロード:サーバーあたり2〜20の同時リクエスト(本番ピーク時はサーバーあたり約20)
  • 設定:max_num_seqs=64
  • 結果:60以上の空きスロットがオーバーヘッドを生成

設定が過大な場合に起きること:

  • 64シーケンス分のKVキャッシュが事前確保される
  • vLLMスケジューラが64スロットを管理するが、実際に使用するのは5〜10個
  • トークンあたりのデコード時間が増加
  • 未使用シーケンススロットによるメモリの浪費
  • 空きスロットに対するスケジューラオーバーヘッド

チューニングのアプローチ

vLLM Continuous Batchingチューニングガイドに基づき:

  1. 本番環境での実際の同時リクエスト分布を計測
  2. max_num_seqs=1から開始し、段階的に増加:2 → 4 → 8 → 16 → 32
  3. 各ステップでデコード時間とテールレイテンシを監視
  4. 性能が劣化した時点で停止
max_num_seqs結果
8レイテンシは良好だが、スループットが制限される
16最適なバランス
32デコード時間が増加し、テールレイテンシが悪化

最終設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from translation_lib.config import AsyncVLLMTranslationProvider

provider = AsyncVLLMTranslationProvider(
    model_name=model_id,
    revision=model_revision,
    gpu_memory_utilization=0.3,  # RTX 5090で約10GB
    max_num_seqs=16,  # 実際のサーバーあたりのワークロードに適合
    huggingface_token=hf_token,
    supported_language_pairs=None,  # 多言語モデル
)

await provider.initialize_engine()

設定の根拠

max_num_seqs=16:

  • 本番ピーク:サーバーあたり約20の同時リクエスト
  • テスト:25の同時リクエストまで検証済み
  • リソースを浪費せず余裕を確保
  • スケジューラのオーバーヘッドが実負荷に対応

max_num_batched_tokens=8192:

  • デフォルトの16384から削減
  • 平均シーケンス長に適合
  • メモリ圧力を軽減

gpu_memory_utilization=0.3:

  • RTX 5090(32GB)でモデル+KVキャッシュに約10GBのVRAMを割り当て
  • vllm:gpu_cache_usage_percで追跡
  • 構成に適したバランス

注: 原則:設定は理論的な上限ではなく、実際のワークロードに合わせる。

図4:全最適化試行を通じたスループットの推移

本番環境の結果

最適化した設定をRTX 5090 GPU搭載の本番環境にデプロイしました。

改善前 vs 改善後

指標改善前(マルチプロセッシング)改善後(最適化AsyncLLM)変化
スループット9.0 RPS16.4 RPS+82%
GPU使用率スパイク状(93% → 0% → 93%)安定した90〜95%安定化

図5:スループット82%向上を示す本番デプロイ結果

図6:各最適化試行でのP95レイテンシの改善

図7:可変長入力でのレスポンスタイムの推移

本番環境でも改善効果が維持されました。 実際のトラフィック下で9 RPSから16.4 RPSへ。

まとめ

効果があった施策

vLLMのContinuous Batching

  • AsyncLLMEngineがバッチ処理を自動管理
  • 手動のバッチ収集オーバーヘッドなし
  • FastAPIとの直接async/await統合

適切な設定の選定

  • max_num_seqs=16(サーバーあたりの実ワークロードに適合)
  • 64(オーバーヘッドを生む理論的最大値)ではなく
  • gpu_memory_utilization=0.3で10GBを確保

現実のデータでのテスト

  • 可変長入力が設定の問題を露呈
  • 均一テストデータでは誤解を招く15 RPSという結果

vLLMメトリクスの監視

  • KVキャッシュ使用率
  • トークンあたりのデコード時間
  • キュー深度
  • 設定の判断材料に活用

改善の全過程

アプローチスループットベースライン比備考
ベースライン(マルチプロセッシング)2.2 RPS-IPCオーバーヘッド、GPU競合
2ワーカー2.0 RPS-9%悪化
スタティックバッチ5.9 RPS+168%Head-of-Lineブロッキング
Async(64, 均一)15.0 RPS+582%誤解を招くテストデータ
Async(16, 可変)3.5 RPS+59%現実的だがチューニングが必要
最終最適化版10.7 RPS+386%ステージング検証
本番環境16.4 RPS+82%実トラフィック、RTX 5090

関連記事: Part 1:翻訳推論サーバーのスケールを阻むボトルネック

この記事をシェア

VoicePingを無料で試す

AI翻訳で言語の壁を超えましょう。今すぐ無料でお試しください。

無料ではじめる