LLMが長い回答を生成する際、最初のプロンプト入力直後の勢いが消え、なぜ動作が遅くなるのか疑問に思ったことがあるなら、あなたはまさにハードウェアのボトルネックを目の当たりにしています。多くの開発者は、Pythonコードやフレームワーク、あるいはモデルの巨大さに原因があると決めつけます。関数をプロファイリングし、オプティマイザを入れ替え、前処理のミリ秒単位の時間を削ろうとします。しかし、そのどれもが真の問題を解決することはありません。速度の限界はソフトウェアにあるのではなく、シリコン(半導体)にあるのです。

すべての大型言語モデル(LLM)の推論ジョブは、サーバーに搭載されているGPUの2つの物理的特性に依存しています。一つは、どれだけ速く数値を計算できるか、もう一つは、それらの数値を計算可能な位置へどれだけ速く移動できるかです。

計算は安価だが、データの移動は安価ではない

GPUのマーケティングでは、演算性能(compute)が強調されがちです。毎秒数兆回の浮動小数点演算。その数字は圧倒的です。しかし、演算性能は物語の半分に過ぎません。もう半分はメモリ帯域幅(memory bandwidth)です。これは、高帯域幅メモリから、実際の演算が行われる演算コアへとデータが転送される速度のことです。

LLMは、これら2つの要素のうち、より遅い方の速度を超えることはできません。20人の熟練シェフがいる業務用厨房を想像してみてください。オーブンは熱く、包丁は研ぎ澄まされ、料理人は全員準備万端です。しかし、食材の配送が自転車で、一度にカゴ1杯ずつしか届かないとしたらどうでしょう。厨房は停滞します。シェフを増やしても解決しません。より速いオーブンを買っても解決しません。ボトルネックは「道」にあるのです。

現代のデータセンター向けGPUでは、演算ユニットが非常に強力であるため、計算を終えた後に、重み(weights)や活性化関数(activations)がメモリから流れてくるのを待ちながら、アイドル状態でサイクルを浪費してしまうことがよくあります。この不均衡はコードのバグではありません。チップの構造上の物理的な現実なのです。メモリ帯域幅は生の演算性能の進化に追いついておらず、LLMはその不均衡の影響を特に受けやすい性質を持っています。なぜなら、フォワードパス(順伝播)において、出力トークンごとにすべてのパラメータにアクセスする必要があるからです。

なぜプロンプトは速く、生成は遅く感じるのか

LLMの推論は2つの異なるフェーズに分かれており、それぞれがハードウェアに対して全く異なる負荷を与えます。

**Prefill(プリフィル)**は、プロンプトが最初にモデルに入力されたときに発生します。すべてのトークンが同時に届くため、GPUは大規模な行列・行列積(matrix-matrix multiplications)を用いて、それらを並列に処理できます。数千の演算ユニットが一斉に稼働し、ワークロードは高密度に保たれます。このフェーズは演算束縛(compute-bound)です。開始時に見えるあの突然のスピードアップは、GPUが本来設計された通りの働きをしている証拠です。

**Decode(デコード)**になると、状況は一変します。モデルが次のトークンを生成する際、それは一度に1トークンずつ行われます。この段階では行列・ベクトル演算(matrix-vector operations)に依存しますが、これはGPUの並列能力のほんの一部しか使いません。さらに悪いことに、新しいトークンが生成されるたびに、GPUはメモリからモデルの全重みを再ロードする必要があります。演算ユニットは仕事をしたがっていますが、代わりに待機することになります。デコードはメモリ束縛(memory-bound)です。GPUは実質的に、演算エンジンが休んでいる間にメモリバスを介してパラメータを往復させる、高価な交通整理係として機能しているのです。これが、最初のプロンプト解析が瞬時に感じられたにもかかわらず、100単語の回答に10秒もかかる理由です。

KVキャッシュは、この問題をさらに複雑にします。デコード中、モデルはアテンションを最初から再計算しなくて済むよう、過去のすべてのトークンに対するKeyとValueのテンソルを保存します。このキャッシュはシーケンス長とともに増大し、メモリ上に存在します。つまり、GPUは単に重みを再ロードしているだけでなく、フォワードパスのたびに拡大し続けるキャッシュを読み書きしているのです。演算コアはほとんど汗をかいていない一方で、メモリバスは両方のために必死に働いています。

メモリの壁との戦い

エンジニアは、移動すべきデータ量を減らす、あるいは少なくともその移動コストを分散させるために、いくつかの手法を開発してきました。

**Batching(バッチ処理)**は最も直接的な方法です。1人のユーザーのリクエストでメモリから全重みをロードしなければならないのであれば、8つまたは16のリクエストを同時に処理することで、その負荷をすべてのリクエストに分散させることができます。重みは一度だけ読み込まれ、バッチ内のすべてのシーケンスで再利用されます。本番環境では、高度なスケジューリングシステムがリクエストを動的にグループ化します(これは「continuous batching」や「in-flight batching」と呼ばれることもあります)。これにより、GPUが停止することはほとんどありません。これは、同じルートを走る1台のバスと、16台の別々の乗用車の違いのようなものです。

Quantization attacks the bandwidth problem directly. Model weights are usually stored in sixteen-bit floating-point formats. By compressing them down to eight-bit or even four-bit integers, you literally cut the amount of data traveling across the bus by half or more. The model still needs enough precision to produce coherent output, but modern post-training quantization methods can shrink a model’s memory footprint dramatically without destroying quality. Less data in flight means less time spent waiting at the memory controller.

FlashAttention restructures the attention mechanism to keep intermediate results inside the GPU's fast on-chip memory. Standard attention had to write large attention matrices out to slow external memory and then read them back. FlashAttention breaks the calculation into smaller tiles that fit in SRAM, performs the softmax and scaling steps on-chip, and only writes the final outputs back to high-bandwidth memory. It trades a bit of extra compute for far fewer round trips to main memory, which is almost always a winning bet.

PagedAttention solves a different kind of memory waste. During decode, the KV cache grows unpredictably. Traditional systems allocate fixed, contiguous chunks of memory for each sequence, leaving large holes as some sequences end early and others expand. PagedAttention borrows the concept of virtual memory from operating systems. It stores KV cache entries in fixed-size blocks that can be allocated non-contiguously and mapped through an indirection table. This stops memory from sitting idle inside reserved but half-empty buffers and allows larger batch sizes, which in turn improves overall throughput by keeping the memory bus busy with useful work instead of fragmentation overhead.

Shift the Question

When latency spikes, too many teams ask whether they should switch to a smaller model or rewrite their inference server. Those questions matter, but they are secondary. The first question should be about the hardware itself. Is your GPU actually busy computing, or is it starved for data?

Look at your utilization metrics. Profile memory bandwidth saturation alongside GPU compute occupancy. If you see high memory contention and low arithmetic intensity during decode, you do not have a model architecture problem. You have a physics problem. The solution will not come from cleaner Python. It will come from batching more aggressively, quantizing your weights to squeeze through the pipe faster, restructuring attention to stay on-chip, and managing the KV cache so you can fit larger batches without running out of room.

Once you see inference through this lens, optimization becomes mechanical. You stop chasing myths about model intelligence slowing things down and start making engineering decisions grounded in what the hardware can actually deliver. That is the shift that separates production systems that scale from those that merely function.