vLLMを最大性能で設定する

出典

vLLM Engineで議論するパラメータ:

  • max-num-batched-tokens
  • max-model-len
  • gpu-memory-utilization
  • enable-prefix-caching
  • enable-chunked-prefill
  • enforce-eager

max-model-len:

TL;DR 使用されるトークンの最大数(入力+出力)に従う

  • デフォルトでは、モデルの最大長は使用しているモデルの最大コンテキスト長に等しい。例えば、llama 3 8B instructモデルでは8192となる。
  • あなたの使用ケースでモデルの最大コンテキスト長を明確に定義でき、それがモデルの最大コンテキスト長よりも短い場合、このパラメータにその値を設定するのが望ましい。
  • これにより、モデルの読み込みや使用時にメモリ不足のエラーを防ぎ、他のパラメータ(max-num-batched-tokensやgpu-memory-utilization)の設定にも役立つ。
  • この値は入力と出力のトークン数を含むため、もし使用ケースで合計トークン数が2046以下であるなら、このパラメータにその値を設定する。

max-num-batched-tokens:

TL;DR max-model-lenから始めて、次にmax-model-len2、次に3…と増やしていき、メモリ不足エラーまたはページアウトの警告が発生するまで試す。性能が良好な値を選ぶ。enable-chunked-prefillを使用している場合は、128程度の小さな値から試す。以下に説明がある。

  • このパラメータはトークン数を表すのではなく、バッチサイズを表す。
  • 各リクエストは事前フィル(入力トークンのKV値の計算とKVキャッシュブロックへの配分)とデコードフェーズ(トークンを1つずつ再帰的に直接計算)の2つのフェーズを経る。
  • したがって、このパラメータの最小値は、事前フィルの最大長(chunked-prefillを使用していない場合)に等しい。
  • バッチ内のトークン数が大きいほど、事前フィルの数が増えるため、最初のトークンがより速く生成される。vLLMのデフォルトスケジューラは事前フィルを優先する。
  • よって、大きなバッチサイズはスループットを向上させる可能性があるが、すべてのケースで効果があるわけではない。スループットと遅延はGPUの計算リソースとメモリの比率(chunked-prefillに関する詳細は後述)に依存し、事前フィルは計算リソースを多く必要とするため、最大のバッチトークン数に早く到達する。
  • したがって、max-model-lenから始めて、2倍、3倍…と増やしていき、メモリ不足エラーまたはページアウトの警告が発生するまで試す(ここ参照)ことで、適切なバッチトークン数を決定できる。その後、この理論を適用して、あなたのシステムがどのように動作するかを理解する。

enable-prefix-caching:

TL;DR enable-chunked-prefillを使用していない限り、有効にすることを推奨

  • これは、KV値の計算結果をキャッシュし、再利用することで多くの時間を節約する。
  • 入力データが大部分固定で、リクエストの大部分が変更されない場合、この設定では性能の向上が顕著になる。
  • enable-chunked-prefillを使用している場合は、このパラメータは無効(この投稿時点での制約)。

gpu-memory-utilization:

TL;DR メモリ不足エラーが発生しない限り、なるべく高く設定する。デフォルトは0.9

  • GPUメモリのキャッシュKVに割り当てられるメモリ量が大きいほど、性能が向上する。
  • メモリ不足エラーが発生する場合や、GPUが他のタスクを実行している場合、この値を下げることを検討する。

enforce-eager:

TL;DR メモリ不足の場合はこの機能を有効にする

  • CUDAグラフの作成が行われないため、性能に影響を与える可能性があるが、グラフに必要なメモリを節約できる。
  • 一部の設定では、CUDAグラフが性能に大きな影響を与えない場合があり、節約されたメモリはgpu-memory-utilization、max-num-batched-tokensなどの他のパラメータの値を増やすのに役立つ。

enable-chunked-prefill:

TL;DR この機能を有効にして、バッチトークン数を128から始めて徐々に増やす。この設定と無効な設定を比較してみる。以下に詳細を説明する。

  • デコードフェーズは事前フィルよりも計算リソースに要求が少なく(ベクトルと行列の乗算ではなく、行列と行列の乗算)、メモリに要求が高くなる(KV値の読み込みが必要)。
  • 詳細については、この論文vLLMのページを参照。
  • 既に述べたように、vLLMのデフォルトスケジューラは事前フィルを優先し、事前フィルは計算リソースを必要とするが、デコードフェーズではメモリがボトルネックとなり、計算リソースが少ないがメモリが多くのデコードを効率的に実行できない。
  • Chunk prefillは事前フィルフェーズをチャンクに分割することで、バッチ内のトークン数を減らし、まずデコードリクエストのバッチを形成し、事前フィルのチャンク分割リクエストを追加して、GPUリソースを効率的に活用する。これにより、事前フィルの計算リソースを必要とするチャンク分割と、メモリを多く必要とするが計算リソースが少ないデコードリクエストを組み合わせて、効率的に処理できる。
  • また、この機能を有効にして、小さなバッチトークン数(例:128)から始めて、必要なスループットと遅延を達成するまで徐々に増やす。
  • この手法は、バッチサイズが小さいためデコードの優先度が高くなり、トークン間の遅延を短縮する可能性があるが、スループットに影響を与える可能性があるため、さまざまなバッチサイズを試し、このパラメータを使用しない場合の結果と比較する必要がある。

例:

  • vLLMのAPIエントリポイントを使用し、OpenAI互換サーバーではなく使用する。
  • 事前フィルチャンクを使用しない場合のサーバー起動例:
python3 -m vllm.entrypoints.api_server — model MaziyarPanahi/Meta-Llama-3–8B-Instruct-GPTQ — gpu-memory-utilization 0.95 — port 5000 — dtype half — enforce-eager — max-model-len 4096 — max-num-batched-tokens 8192 — enable-prefix-caching
  • 事前フィルチャンクを使用する場合のコマンドは次のように見える:
python3 -m vllm.entrypoints.api_server — model MaziyarPanahi/Meta-Llama-3–8B-Instruct-GPTQ — gpu-memory-utilization 0.95 — port 5000 — dtype half — enforce-eager — max-model-len 4096 — max-num-batched-tokens 256 — enable-chunked-prefill
  • 上記のサーバーのエンドポイントにリクエストを送信し、さまざまなパラメータでの性能を確認する。
  • vLLMが提供するベンチマーキングスクリプトを使用してベンチマークを実施し、私も類似のスクリプトを書く予定でここに共有する。

著者