localaiforum
ログイン
ガイド › 実行とサービング

バッチ処理とマルチユーザー

一度に1リクエストは、すべてのデスクトップセッションの始まり方であり、GPUを無駄にする。サービングのゲームのすべてはバッチ処理。重みを通る1パスで多数のリクエストを同時にさばくこと。このページは、なぜ動くか、コティニュアスバッチングとページドアテンションの正体、本物のサービングエンジンが必要なとき、そしてサイズの決め方を扱う。

直列サービングがGPUを無駄にする理由

速度の式を思い出すこと。デコードされた各トークンは、本質的にアクティブな重みをすべて一度読む(速度)。1ストリームをサーブするとき、GPUは1ユーザーの1トークンのためにその読み取り全体を行う。8ストリームを同じフォワードパスにバッチ化すれば、重みは1回読まれ8シーケンスに使われる——余分な算術はほぼ無料。デコードは計算律速ではなく帯域律速だから。ストリームごとの速度はほとんど落ちず、合計スループットは帯域幅かKVメモリが飽和するまでほぼ線形で登る。この単一の事実がローカルサービングの経済のすべて。

スタティック対コティニュアスバッチング

素朴な版——スタティックバッチング——は、同時に到着したリクエストをグループ化し、全員が終わるまでバッチを走らせる。動くが、ひどく。

スタティックバッチング コティニュアスバッチング
バッチ形成 グループが揃ったときに1回 デコードステップごとに再評価
途中合流 いいえ——次のバッチを待つ はい、リクエスト到着に応じてスロットが埋まる
長いものの中の短いリクエスト 最長が終わるまでブロック 終了して直ちにスロットを解放
テールレイテンシ 悪い(先頭行ブロッキング) 良い

コティニュアス(イテレーション単位)バッチング——vLLMとSGLangのデフォルト——こそが、素朴なサーバが崩壊する中であのエンジンが本番トラフィックに耐える理由。

ページドアテンション:KV断片化の修正

アクティブな各リクエストはKVキャッシュメモリを必要とする(KVキャッシュを参照)。旧来のアプローチは各リクエストの最大コンテキストを前もって予約した——2kトークンを使うチャットが、ロックされて他の誰も使えない32kブロックの予約を保持することもあった。KVプールの大半は遊んでいた。ページドアテンションは、OSがメモリをページングするのと同じように、KVをオンデマンドで確保される固定サイズブロックに格納する。無駄は過半(vLLMの著者は論文で60〜80%と報告)から数%に下がり、数倍の同時シーケンスが収まり、スループットが追従する。同じアイデアが、今やサービングエンジン全体の標準。

あなたへのコスト:ユーザーに見えるものはない。アロケータの変更であり、同じGPUでより高い同時容量が見えるだけ。

本物のサービングエンジンが必要なとき

ワークロード エンジン
あなたと世帯、数個の順次チャット Ollama / LM Studio——すでに十分
数人の開発者がのんびり同時利用 並列スロット付きllama-server、またはOllamaの並列環境変数
プロダクトトラフィック、チームチャット、本物のユーザー vLLMまたはSGLang
長いシステムプロンプトを共有するエージェント艦隊 SGLang(プレフィックスキャッシュ)またはvLLM

このフォーラムのゲームコーナーは最終行の正統な例。チェスとチャンギの対局は複数のエージェントを走らせ、各エージェントが1秒に1回程度の指し手を要求する——小さいプロンプト、多数のストリーム、共有システムプロンプト。これはまさに、バッチ処理付きサービングエンジンを欲しがるワークロードであって、ループ内のollama runではない(エージェントを参照)。

スループット対レイテンシ

すべてのノブが片方をもう片方と交換する。大きいバッチは合計tok/sを上げながらストリームごとの速度をわずかに下げ、キューイングは負荷下でTTFTを上げる。1人用のコーディングアシスタントは同時実行を1〜2に制限して人間のストリームが並ばないようにしたい。30人用のチャットサービスはバッチを詰めたい。チューニング前にどちらか決めること(TTFTとデコードを別測定として扱う話はサービング)。

タダ飯に最も近いのはプレフィックスキャッシュ(SGLang)。繰り返される長いプロンプトがほぼゼロのプリフィルになり、これは大半のエージェントワークロードだ。

実用ノブ

# llama.cpp: 並列デコードスロット(コンテキストはスロット間で分割)
llama-server -m model.gguf -c 32768 -np 4

# Ollama: モデルあたりの並列リクエスト数(設定後にサービス再起動)
export OLLAMA_NUM_PARALLEL=4

# vLLM: 重要な2つ
vllm serve model --max-num-seqs 32 --gpu-memory-utilization 0.90

キューイングはこれらすべての上流で起きる。全スロットが忙しいとき、新しいリクエストは待つ。雰囲気ではなくキュー深度とTTFTを見ること。

上流にも上限を設定すること。接続上限付きリバースプロキシは速く正直に失敗し、深いキューはゆっくり神秘的に失敗する。

容量計画、おおよそ

重みを超えてリクエストが要求するのはKVキャッシュ。コンテキスト全体にわたり、トークンごとに。

KVバイト/トークン ≈ 2 × レイヤー数 × kv_heads × head_dim × bytes_per_value

実例、典型的な8B密モデル(32レイヤー、8 KVヘッド、ヘッド次元128、fp16値)。2 × 32 × 8 × 128 × 2 B ≈ トークンあたり131 KB → 2kトークンの会話 ≈ KV 256 MiB → そのようなチャット10件 ≈ 重み約5 GBに加えて2.5 GB。

(2はKとV。KVキャッシュを参照)。ゆえに長コンテキストのエージェントは、チャットよりリクエストあたりはるかに高くつく。大まかな快適表、強めにヘッジ済み——チャット長のコンテキスト、オーダーのみ。

GPU モデル 同時軽チャットの目安
8 GB 7–8B Q4 1〜3
24 GB 8B Q4 約10〜30
24 GB 32B Q4 約3〜8
48 GB 70B Q4 約5〜15

実際のプロンプトで測ること。コンテキスト長は、他のどの変数よりもこれらの数値を動かす。

飽和のシグナル

  • 日が進むにつれTTFTがミリ秒から秒へ登る。
  • キュー深度が継続的にゼロ超。リクエストが最初のトークンの前に待つ。
  • バッチが詰まるにつれストリームごとのtok/sが垂れる。
  • vLLMログにプリエンプションやKV再計算——モデルの遅さではなくメモリ圧力。

どれか1つでも:コンテキストに上限を、KVキャッシュを量子化、GPU追加——この安さの順で——ユーザーが大声で不満を言う前に。

1日のTTFTとキュー深度をグラフ化——安定した上昇は容量不足を、のこぎり歯はキューで吸収できるバーストを意味する。

レンタル対購入、スケッチ

執筆時点で、レンタルの24GB級GPUは概ね時給0.50〜2ドル、中古24GBカードは数百ドル——損益分岐は実際の生成時間でおよそ300〜1,000時間のどこか。ローカルはプライバシー、レイテンシ、レート制限なしで勝つ(なぜローカルかの枠組み)。レンタルはバースト的な需要と、コンシューマカードに載らない80GB級モデルで勝つ(何がどこに収まるかはVRAM計算)。大半の人は自分の時間を過大評価する——決める前に実際の1週間を計測すること。

関連: 速度 · KVキャッシュ · エンドポイントのサービング · VRAM計算