はじめに:低コスト推論環境における「ランタイム選択」の重要性
LLMの導入において、GPUインスタンスの選定は多くのチームが最初に取り組む課題である。しかし、同等の重要度を持ちながら見過ごされがちな要素がある。それは推論ランタイムの選択だ。同じハードウェア上でvLLM、JAX、PyTorchのいずれを使うかで、実装コスト・運用安定性・開発速度のすべてが変わる。
本記事では、AWS G5g.2xlarge上のNVIDIA T4Gという極限のコスト制約環境で、3つのランタイムを比較検証した事例を分析対象とする。19インスタンス・約4.5時間の運用で総コスト3ドル未満という結果は、PoC段階のLLM活用が「予算の壁」に阻たれる時代ではなくなっていることを示している。ただし、この安価さの裏には、メモリ収支の計算やビルド時間の差異といった実装上の制約が常に付きまとう。
この記事が提供するのは単なるベンチマーク数値の列挙ではない。メモリ収支、ビルド時間の実測値、prefix cachingがもたらす測定の歪み、といった設計判断に直結する多角的な指標を整理する。速度が最も速いランタイムが常に最適解であるとは限らない。ビルドに数時間かかるランタイムをCI/CDパイプラインに組み込むコスト、メモリ断片化によるOOMのリスク、キャッシュ特性と自社ワークロードの適合性——これらを総合的に判断することが、限られたリソースで意思決定を行う中小企業・スタートアップのエンジニアリングリードに求められる設計判断である。
読者はこの記事を通じて、自社のリソース規模とワークロード特性に応じたランタイム選定の基準、そして実装段階で陥りやすい落とし穴を把握できるはずだ。
検証環境の前提:G5g.2xlargeとT4Gの制約条件
検証基盤はAWS G5g.2xlarge(Graviton2 CPU + NVIDIA T4G GPU)を採用している。ARMアーキテクチャのCPUとGPUが組み合わさることで、同性能帯のx86ベースインスタンスと比較してコストパフォーマンスが極めて高い。ただし、この「安さ」には明確なトレードオフが内包されており、ランタイム選定を行う前にハードウェアの物理的制約を正確に理解しておくことが不可欠だ。
T4GのGPUメモリは15,360 MiBである。Gemma 2 9B(Int8)などのモデル重みが9.5 GiBの場合、メモリ内にモデルをロードした時点でKVキャッシュ用に割り当てられる領域は約6 GiB程度に収束する。この6 GiBが、同時に処理できるバッチサイズやコンテキスト長を決定づけるボトルネックになる。バッチサイズを1増やすたびにKVキャッシュの消費量が増加し、コンテキスト長を伸ばすほど消費量は膨らむため、設計段階でこの収支を計算しておく必要がある。
データ型の制約も重要だ。T4Gにはbfloat16 datapathが存在しないため、すべての演算がfloat16で行われる。さらにfp8などの低精度フォーマットもサポートされていない。つまり、モデルの量子化によるメモリ削減(例えばfp8で重みを半減する)という選択肢がハードウェアレベルで閉ざされている。ランタイムが「bfloat16対応」と謳っていても、T4G上ではfloat16にフォールバックする点に注意が必要だ。
これらの制約を無視したままランタイムを選定すると、OOMエラーや意図しない精度低下を引き起こす。以下に、T4Gのメモリ構成とデータ型制約を整理する。
| 項目 | 値 | 設計への影響 |
|---|---|---|
| GPUメモリ総量 | 15,360 MiB | モデル+KVキャッシュの総収支の上限 |
| モデル重み(Gemma 2 9B Int8) | 約4.7 GiB〜9.5 GiB | ロード時点で約6 GiBがKVキャッシュに割当 |
| KVキャッシュ利用可能領域 | 約6 GiB | バッチサイズ・コンテキスト長の上限を決定 |
| サポートデータ型 | float16のみ | bfloat16・fp8は非対応、量子化選択肢が限定的 |
この表から読み取れるのは、T4G上では「モデルサイズ」がほぼ固定された条件下で、残りのメモリをいかに効率的にKVキャッシュに割り当てるかがランタイムの設計品質を左右するということだ。vLLMがPagedAttentionでメモリ断片化を抑制する設計を採用している背景には、まさにこの制約への対応がある。
コストとビルド時間:DevOps視点でのランタイム選定
19インスタンス・約4.5時間の運用で総コスト3ドル未満という結果は、PoCや小規模プロダクションにおける驚異的な安価さを示している。しかし、この「実行コスト」だけではランタイム選定はできない。環境構築の時間コスト——つまり、インスタンスを立ち上げてから実際に推論を扱える状態になるまでの時間——は、CI/CDパイプラインの設計やデプロイ頻度、障害復旧の速度に直結する。
PyTorch+transformers環境では、cloud-init実行後のビルド時間の中央値が113.55秒と実測されている。これは実質2分弱で推論可能な環境が立ち上がることを意味し、スポットインスタンスの短命なライフサイクルや、A/Bテストでの頻繁なデプロイといった運用パターンと相性が良い。一方、vLLMはソースからのビルドに数時間を要した。この差分は、開発環境の再構築やバージョンアップのたびに発生するコストとして積み上がる。
ここで設計判断の分岐点がある。開発速度と初期コストを重視する段階(PoC、プロトタイプ検証)では、PyTorchの迅速なビルドが有利に働く。対して、運用安定性とスケーラビリティを優先する本番環境では、vLLMの高速推論とキャッシュ機能が恩恵をもたらす。vLLMのビルド時間を短縮するためには、Dockerイメージを事前ビルドしてレジストリに保管し、デプロイ時にイメージをプルする方式が現実的な対策になる。ただし、ARMアーキテクチャ(Graviton2)向けのvLLMイメージが公式に提供されているか、あるいはコミュニティビルドに頼る必要があるかは、導入前に確認すべきポイントだ。
以下に、各ランタイムのビルド時間と初期セットアップの特性を比較する。
| ランタイム | ビルド時間(実測) | セットアップ方式 | 開発効率への影響 |
|---|---|---|---|
| PyTorch+transformers | 中央値113.55秒 | cloud-init + pip install | 高。頻繁な再デプロイに耐える |
| vLLM | 数時間(ソースビルド) | ソースビルド or 事前ビルドイメージ | 低(初回)。Docker化で改善可能 |
| JAX(独自ポート) | — | 独自実装ベース | 中。エコシステムが限定的 |
この比較から導かれる設計指針は、「段階的な移行」である。初期はPyTorch+transformersで113秒のビルドサイクルを活かし、ワークロードの特性(トークン長、バッチ頻度、キャッシュヒット率)をデータとして収集する。そのデータに基づいて、vLLMへの移行が速度要件を満たすために必要か判断する。いきなりvLLMの複雑なビルド環境に飛び込むと、PoC段階で開発速度が著しく低下し、本来検証すべきビジネス仮説のテストが遅れるリスクがある。
推論速度の比較:デコード性能がもたらすUXの違い
デコード速度(トークン生成速度)は、ユーザーが「応答が返ってくる速さ」として直接体感する指標である。今回の検証では、vLLMが32.53 tok/s、JAXが12.962 tok/s、PyTorch+transformersが10.24 tok/sという結果が出た(検証記事)。vLLMがPyTorchの約3.2倍という差は、対話型アプリの文脈では「待たされる感覚」の有無を分けるほどの差である。
デコード速度の差がUXに与える影響
具体的な体感として、100トークンの応答を生成する場合、PyTorchでは約9.8秒、JAXでは約7.7秒、vLLMでは約3.1秒かかる。チャットボットのようなリアルタイム対話では、3秒以内の初回応答がユーザーの「待てる」閾値とされる傾向がある。10 tok/s台では、応答がストリーミング表示されても「まだ生成中」という印象が残り、ユーザーの離脱率に影響し得る。
ただし、この速度差は「すべてのシナリオで等しく効く」わけではない。後述するprefix cachingの効果が効くワークロードではvLLMの優位はさらに拡大するが、多様なプロンプトが連続で入る場合は差が縮小する。つまり、速度の数値だけを見て「vLLMにすれば3倍速い」と単純化すると、設計判断を誤る。
TTFT(First Token Latency)の測定上の罠
デコード速度と並んで重要なのがTTFT、すなわち最初のトークンが返ってくるまでの遅延である。ここでは測定上の注意が深刻になる。vLLMのTTFT測定値はprefix cachingの影響で歪んでおり、JAXやPyTorchは毎回フルprefillコストを支払っている(検証記事)。つまり、vLLMのTTFTが「速い」と読めるのは、キャッシュヒットが効いた最良ケースの値であり、新規プロンプトが初めて入る際の実際の遅延を過小評価している可能性がある。
小規模チームがベンチマークを読む際のチェックポイントは次の通りである。第一に、測定時に同じプロンプトを繰り返し使用していないか。第二に、TTFTとデコード速度を別々に報告しているか。第三に、キャッシュを無効化した条件下的の数値が併記されているか。この3点を確認できないベンチマーク結果は、設計判断の根拠として使わないこと。
| ランタイム | デコード速度 (tok/s) | TTFT測定の注意点 |
|---|---|---|
| vLLM | 32.53 | prefix cachingで最良ケース値。新規プロンプト時は遅延が拡大 |
| JAX(独自ポート) | 12.962 | 毎回フルprefill。測定値は実使用と概ね一致 |
| PyTorch+transformers | 10.24 | 毎回フルprefill。測定値は実使用と概ね一致 |
この表から読み取れるのは、vLLMのデコード速度の優位は「実測値」である一方、TTFTについては「条件付きの値」であるという非対称性である。設計判断では、デコード速度はそのまま採用してよいが、TTFTは「キャッシュ無効時」の値を別途見積もる必要がある。
vLLMの真価:Prefix Cachingがもたらす効率化と測定の歪み
vLLMが他ランタイムと比較して高いスループットを発揮する背景には、Prefix Caching(接頭辞キャッシュ)機能の存在がある。今回の検証では、vLLMは実際には5.3%のトークンしかprefill処理を行わず、残りの約94.7%がキャッシュヒットしていた(検証記事)。この数字は、vLLMの速度優位が「計算性能の差」だけでなく「計算自体を省略していること」にも由来することを示している。
Prefix Cachingが効くワークロードと効かないワークロード
Prefix Cachingは、プロンプトの先頭部分が前回のリクエストと一致するときに効果を発揮する。具体的には、システムプロンプトが固定されたチャットアプリ(「あなたは○○のサポートAIです」のような先頭が毎回同じ)、同じテンプレートでバッチ処理を行うデータ抽出、あるいはfew-shot例が固定された分類タスクなどが該当する。これらのシナリオでは、prefillのコストが相対的に小さくなり、デコード速度の差が実効スループットの差に直結しやすくなる。
一方で、ユーザーごとに全く異なるプロンプトが連続して入る検索エンジンや、自由記述の多いフォーム入力処理では、キャッシュヒット率が極めて低くなる。この場合、vLLMは毎回フルprefillを行うため、速度の優位はデコード部分のみの差に縮小する。JAXやPyTorchとの差は3倍から1.5倍程度にまで低下し得る。
測定の歪みと設計への示唆
前述の通り、prefix cachingが効いた状態でのTTFT(Time To First Token)測定値は「最良ケース」を示す。設計者が注意すべきは、この値を「ユーザーが常に感じる遅延」として設計に反映しないことである。実際には、セッションの初回リクエストや、プロンプト構造が変化したタイミングではフルprefillが発生し、TTFTが数倍に膨らむ可能性がある。
設計上の対策として、以下を推奨する。第一に、プロダクション環境ではTTFTのP95(95パーセンタイル)を監視し、平均値ではなく「最悪ケースに近い値」をSLAの基準にする。第二に、キャッシュヒット率をメトリクスとして常時計測し、ワークロードの変化(プロンプト構造の変更など)による性能劣化を早期に検知する。第三に、キャッシュを意図的に無効化しての定期ベンチマークを実施し、「キャッシュなし」の性能下限を把握しておく。
flowchart TD
A["リクエスト受信"] --> B{"プロンプト先頭がキャッシュに一致?"}
B -->|Yes| C["キャッシュヒット"]
B -->|No| D["フルprefill実行"]
C --> E["prefill対象: 5.3%のみ"]
D --> F["prefill対象: 100%"]
E --> G["デコード開始"]
F --> G
G --> H["トークン生成"]
このフローから明らかなように、キャッシュヒット時はprefillの計算負荷がほぼゼロになり、デコード開始までの待ち時間が大幅に短縮される。逆にキャッシュミス時は、プロンプト全体に対するattention計算を一度に実行する必要があり、プロンプトが長いほどこのコストは大きくなる。T4Gの15 GiBメモリでは、prefill時の中間活性化値がメモリを圧迫するため、長いプロンプトのキャッシュミスはOOM(Out Of Memory)のリスクにも直結する。
メモリ制約下での設計判断:KVキャッシュの限界と対策
T4GのGPUメモリは15,360 MiB(約15 GiB)である。Gemma 2 2Bのモデル重みが9.5 GiBを占有すると、KVキャッシュと活性化値に使える領域は約6 GiB程度に絞られる(検証記事)。この6 GiBが、同時に処理できるリクエスト数(バッチサイズ)と、各リクエストのコンテキスト長を決定する制約条件になる。
KVキャッシュのメモリ消費とバッチサイズの関係
KVキャッシュのサイズは、レイヤー数 × ヘッド数 × ヘッド次元 × シーケンス長 × バッチサイズ × 2(KeyとValue)で概算される。モデルサイズが固定なら、バッチサイズを2倍にするとKVキャッシュの消費も2倍になる。6 GiBの残り領域に対して、1リクエストあたりのKVキャッシュが1 GiBであれば最大6リクエスト、2 GiBであれば最大3リクエストが限界になり得る。この計算をせずにバッチサイズを設定すると、実行時にOOMエラーでクラッシュする可能性がある。
PyTorch+transformersやJAXのような標準ランタイムは、KVキャッシュを静的に確保する方式が多く、メモリ断片化に弱い傾向がある。バッチサイズが変動する環境では、最大バッチサイズ分のメモリを事前に確保する必要があり、実際のリクエスト数が少ない場合にメモリを無駄に消費する。vLLMはPagedAttentionによりKVキャッシュをページ単位で動的に管理するため、断片化を抑制し、実効的なメモリ利用率を高められる。しかし、これは「メモリを魔法のように増やす」技術ではなく、物理的な15 GiBを超えることはできない。
メモリ不足を回避する設計パターン
小規模チームがT4G上で運用する場合、以下の方針が現実的である。第一に、コンテキスト長を上限設定する。例えば、最大4096トークンに制限すれば、KVキャッシュの消費を予測可能にし、バッチサイズとコンテキスト長のトレードオフを事前に計算できる。第二に、バッチサイズを1〜2に抑え、スループットではなくレイテンシを優先する設計にする。第三に、モデルの量子化(int8やint4)を検討する。量子化によりモデル重みを圧縮できれば、KVキャッシュに回せるメモリが増え、バッチサイズやコンテキスト長の余裕が生まれる。
また、T4Gはbfloat16 datapathを持たないため、全ランタイムがfloat16で動作し、fp8もサポートされていない(検証記事)。これにより、モデル重みのメモリ消費をデータ型の切り替えで減らす選択肢が限られる。将来、より大容量のGPU(A10GやL4など)にスケールアップする場合は、bfloat16やfp8のサポート有無を先に確認し、ランタイムの互換性を再評価する必要がある。
ランタイム選定のトレードオフ:比較表と選択基準
前述のベンチマーク結果を踏まえると、3つのランタイムには明確な性格付けができる。vLLMはデコード速度32.53 tok/sという高い性能とprefix cachingによるリソース節約(実際には5.3%のトークンしかprefillしない)を備えている一方、ソースからのビルドに数時間を要し、運用チームにvLLM特有の概念(PagedAttention、KVキャッシュのページ管理など)の理解を要求する。JAXは12.962 tok/sとPyTorchより速いが、Gemma 2向けには独自ポートの整備が必要であり、エコシステムの広さではPyTorchに及ばない。PyTorch+transformersはビルド時間中央値113.55秒という迅速なセットアップと、広範なモデル・ライブラリとの互換性を強みに持つ。速度は10.24 tok/sと3者中最も遅いが、PoC段階や低頻度利用では十分実用的な範囲にある。
| ランタイム | デコード速度 | ビルド時間 | 向いているワークロード |
|---|---|---|---|
| vLLM | 32.53 tok/s | 数時間 | 固定プロンプトのバッチ処理・高頻度対話 |
| JAX(独自ポート) | 12.962 tok/s | ポート整備に依存 | 研究開発・特定モデルの高速推論 |
| PyTorch+transformers | 10.24 tok/s | 中央値113.55秒 | PoC・低頻度利用・多モデル実験 |
選択基準は、単に「速い方を選べばよい」では済まない。具体的には以下の3軸で判断する。第一に、応答速度の要件。ユーザーがリアルタイムに体感する対話型アプリであれば、32 tok/sと10 tok/sでは体感速度が3倍以上異なるため、vLLMが事実上の必須条件になり得る。一方、バックグラウンドでバッチ処理する文書要約や分類タスクであれば、10 tok/sでも業務に支障が出ない場合がある。第二に、開発リソースの制約。1〜2人のチームが数週間でPoCを完了させる必要がある場合、ビルドに数時間をかけるvLLMより、cloud-initで113秒程度で環境が立つPyTorchの方が現実的である。第三に、ワークロードの特性。システムプロンプトが固定され、ユーザー入力のみが変化するパターンであれば、vLLMのprefix cachingが効果を発揮する。逆に、毎回全く異なる長いプロンプトを送るパターンでは、キャッシュヒット率が低く、vLLMの速度優位は相対的に小さくなる。
ここで注意すべきは、これらの基準が互いに矛盾し得ることだ。例えば「速度が速い」+「ビルドが速い」+「キャッシュ恩恵が大きい」を同時に満たすランタイムは存在しない。設計者は、自社の制約条件の中でどの軸を最優先するかを明示的に決定し、残りの軸は「許容範囲」として許す判断を行うべきである。この優先順位をチーム内で共有しておくことで、後日のランタイム移行時の意思決定が速くなる。
実装と運用:中小企業向けの実践的なデプロイメント戦略
前述のトレードオフを踏まえた上で、中小企業・スタートアップが実際にデプロイメントを進める際の戦略を整理する。最初のステップは、単一インスタンスでの検証である。19インスタンス・約4.5時間の運用で総コスト3ドル未満という実績は、G5g.2xlarge上のPoCが極めて低コストで完了できることを示している。この段階では、PyTorch+transformersでモデルの挙動を確認し、出力品質とレイテンシの許容範囲をチーム内で合意する。重要なのは、この段階で「vLLMに移行するかどうか」の判断基準を事前に定義しておくことだ。例えば「デコード速度が15 tok/s未満で、かつユーザーからの応答時間クレームが月5件以上発生したら移行を検討する」といった定量的なトリガーを設けておくことで、感覚的な判断を避けられる。
本番環境への移行にあたっては、単一インスタンスの制約を認識した設計が必要になる。T4GのGPUメモリは15,360 MiBであり、モデル重み9.5 GiBをロードするとKVキャッシュに回せる領域は限られる。トラフィックが増加し、同時接続数が増えると、KVキャッシュの競合が発生し、レイテンシが急激に悪化するか、OOMエラーでサービスが停止するリスクがある。対策として、第一にAuto Scalingを設定し、インスタンス数をトラフィックに応じて増減させる。第二に、GPUインスタンスが利用不能な場合のCPU fallback経路を用意し、サービス全体の可用性を担保する。CPUでの推論はGPU比で桁違いに遅いが、「応答が遅い」状態と「応答がない」状態では、ユーザーへの影響が質的に異なるため、fallbackの設計はコスト対効果が高い。
中期的な視点では、アクセラレータの選択肢を広げておくことが重要になる。検証に使用したリポジトリには、GCP TPUやAWS Inferentia2を含む多様なアクセラレータ向けの構成が含まれている。これは、特定のGPUにロックインせず、抽象化レイヤーを設けておくことで、将来のハードウェア価格変動や新製品の登場に対応できることを意味する。具体的には、推論呼び出しを自前API経由で抽象化し、バックエンドのアクセラレータ種別をアプリケーションコードに漏らさない設計にする。これにより、T4GからA10Gへのスケールアップや、Inferentia2への移行が、アプリケーション側の再開発を伴わずに行える可能性がある。初期コストは増えるが、スケーリング時の移行コストを大幅に削減できる投資になり得る。
まとめ:コストと性能のバランスを取るための設計指針
T4G単体で月3ドルという極限のコスト制約下でも、LLMの推論サービスを実装・運用することは現実的に可能である。しかし、その実現には「ランタイムを速いものを選べばよい」という単純な発想では不十分であり、メモリ収支、ビルド時間、キャッシュ特性、ワークロードの特性といった複数の軸を同時に考慮する設計判断が求められる。
本記事で示した設計指針をまとめると、以下のようになる。第一に、ハードウェアの物理的制約(T4Gではfloat16のみ、fp8非対応、GPUメモリ15,360 MiB)を前提として、モデルサイズとKVキャッシュの収支を事前に計算する。第二に、ランタイム選定は「速度」単体ではなく、ビルド時間・開発リソース・ワークロード特性の3軸で決定し、優先順位をチーム内で明示的に共有する。第三に、初期はセットアップが速いPyTorchで検証し、定量的なトリガーに基づいてvLLMへの移行を判断する。第四に、アクセラレータへのロックインを避け、抽象化レイヤーでバックエンドを差し替え可能な設計にする。これらの指針は、Gemma 2 E2BとT4Gという特定の組み合わせに限定した話ではなく、中小企業が限られた予算でAI機能を導入する際の一般的な設計原則として適用できる。
最後に、ベンチマークの数値そのものよりも、その数値を「どう解釈するか」が設計の質を左右する、という点を強調したい。vLLMの32.53 tok/sはprefix cachingの恩恵を含む値であり、新規プロンプトが中心のワークロードではこの速度は期待できない。逆に、PyTorchの10.24 tok/sは「遅い」と一見するが、バックグラウンド処理や低頻度利用では十分な性能である。数値の文脈を理解した上で、自社のワークロードにどのランタイムが最適かを判断するプロセス自体が、将来のスケーリングやモデル更新に対応する際の重要な設計資産となる。
関連記事
- Apple SiliconのVMでLLM推論を高速化する:Metal GPUパsthroughの実践と注意点
- LLM推論ベンチマーク:DigitalOceanで6モデル比較した結果、最安かつ最速だったのはなぜか
- AIエンドポイントの設計:確率的出力と従来のWeb APIの違いを理解する
参考
本記事は海外の技術トレンド「Three Gemma 4 Deployments on One T4G for Under $3: What the Runtime Changes, and What It Doesn't」(DEV)で話題のテーマをもとに、両儀システムソリューションズが独自に解説したものです。