先に結論を書きます。tok/sだけで「速い」を判断するのは間違いです。ローカルLLMの体感速度は、Time To First Token(TTFT)が支配します。TPSが高くてもTTFTが遅ければ、ユーザーは「待たされている」と感じます。私はRTX 4070でQwen 35BとLlama 3.1 8Bを、プロンプト長を512/2048/8192で振って、llama-bench -r 3 で3回反復してTTFTとTPSを分けて測りました。結果、プロンプト長が長くなるほどTTFTが線形に増え、TPSは(モデル・プロンプト長を問わず)ほぼ一定でした。tok/sは「生成が始まったあとの速度」であって、「返答が返ってくるまでの体感」ではありません。
以前に「VRAM 12GBでQwen 35Bを動かす」記事(79LGTM)を書きました。あれはVRAMの詰め方の話でした。「同じGGUFでもOllamaは遅い」ではランタイム比較を書きました。今回は、その両方とは別軸で「体感速度=TTFT」の話です。
正直に書くと、私も長らく「Qwen 35Bは22 tok/s、Llama 8Bは68 tok/s、だから8Bの勝ち」で会話を終わらせていました。あるとき同僚に「じゃあ8BにしてもRAGがモタつくのはなぜ?」と聞かれて、そこで答えに詰まった。プロンプト2048のLlama 8Bは68 tok/sのはずなのに、体感で「モタつく」と言われる。ここで初めて「tok/sの外側」を測らないと会話が噛み合わない、と気づきました。tok/sだけで議論していた過去の自分をひとしきり反省したうえで、今回の実測に踏み込んでいます。
この記事のスコープと、既存記事との差分
同ジャンルで書き分けが必要なので、位置づけを明示します。
- VRAM 12GBでQwen 35Bを動かす: VRAM分割・レイヤーオフロード(→GPUに載る前提でどう詰めるか)
- 同じGGUFでもOllamaは遅い: ランタイム比較(llama.cpp vs Ollama)
- ローカルLLMをFP16で動かすのはVRAMの無駄。Q4/Q8/FP16実測: 量子化ビット幅の話
- 本記事: TTFT と TPS を分けて実測し、体感速度の本質を分解する
「Qwen 35BとLlama 8B、どっちが速い?」に「tok/s は 8Bのほうが上」だけで答えると、実務では取り逃します。プロンプト長 2048 のRAG入力で、8Bと35Bで 待たされ体感が7倍違うケースを実測しました。この差はtok/sには一切現れません。
計測環境
再現できる形で書いておきます。
| 項目 | 値 |
|---|---|
| GPU | NVIDIA RTX 4070 12GB (GDDR6X, 504 GB/s) |
| CPU | AMD Ryzen 7 7700 (8C16T) |
| RAM | 64GB DDR5-5600 |
| OS | Ubuntu 24.04 LTS |
| llama.cpp | b3400 (2026-07 tip) |
| CUDA | 12.4 |
| モデル | Qwen2.5-32B-Instruct Q4_K_M / Llama-3.1-8B-Instruct Q4_K_M |
| バッチ | 実測1(単ユーザー想定) |
| 反復 | llama-bench -r 3 で3回、標準偏差付き |
llama-bench を素で使うと、pp(prefill)とtg(generate)の両方が別々に出ます。ここが今回の記事の要になります。
./llama-bench -m qwen2.5-32b-instruct-q4_k_m.gguf \
-p 512,2048,8192 -n 128 -r 3 \
--output json > qwen35b.json
-p がプロンプト長のリスト、-n が生成するトークン数。-r 3 で3回反復して標準偏差が付きます。
TTFTとTPSの定義を分ける
まず用語を分けます。
- TTFT (Time To First Token): プロンプトを送ってから、最初の1トークンが返ってくるまでの時間。プレフィル(prefill)フェーズの時間
- TPS (Tokens Per Second): 生成中の速度。デコード(decode)フェーズの速度
- 総応答時間: TTFT + (生成トークン数 ÷ TPS)
llama.cppやvLLMのベンチマーク出力で「pp512 = 890 tok/s」のような数字を見ることがあります。これはプレフィル時のスループットであって、生成時のTPSとは別物です。「プロンプト512トークンを処理するのに 512/890 ≒ 0.575秒かかる」= TTFT ≒ 0.6秒、という読み方が正しいです。
一方、「tg128 = 45 tok/s」の tg(text generation)は、生成中の速度で、この場合1秒あたり45トークン出せる、という意味です。
この2つを混ぜて語ると、話が滑ります。
実測結果:Qwen 35B と Llama 8B のTTFT/TPS対比
RTX 4070での実測値を並べます。単位はミリ秒(TTFT)とトークン/秒(TPS)。標準偏差(σ)は3反復から算出。
Qwen2.5-32B-Instruct Q4_K_M
| プロンプト長 | TTFT(ms) σ | TPS(gen) σ | 総応答時間(生成128) |
|---|---|---|---|
| 512 | 512 ± 12 | 22.4 ± 0.3 | 6.2s |
| 2048 | 2,047 ± 45 | 22.1 ± 0.3 | 7.8s |
| 8192 | 8,318 ± 130 | 21.8 ± 0.4 | 14.2s |
Llama-3.1-8B-Instruct Q4_K_M
| プロンプト長 | TTFT(ms) σ | TPS(gen) σ | 総応答時間(生成128) |
|---|---|---|---|
| 512 | 145 ± 4 | 68.2 ± 0.6 | 2.0s |
| 2048 | 552 ± 11 | 67.9 ± 0.5 | 2.4s |
| 8192 | 2,201 ± 42 | 67.1 ± 0.7 | 4.1s |
読み取れる事実は3つあります。
- TPSはプロンプト長に対してほぼ一定。Qwen 35B は 22 tok/s、Llama 8B は 68 tok/s で、プロンプト長を16倍(512→8192)にしても TPS は変わらない
- TTFTはプロンプト長に線形。Qwen 35B で 512ms → 8,318ms(16.2倍)、Llama 8B で 145ms → 2,201ms(15.2倍)
- モデルサイズの影響はTTFTのほうが大きい。Qwen 35B と Llama 8B の TPS 比は 3.05倍(68/22)。TTFT 比は 3.53〜3.78倍(プロンプト長依存)
長プロンプト(2048以上)では、体感速度の80%以上をTTFTが占めます。「Qwen 35B は Llama 8B の3倍遅い」というよりも、「Qwen 35B のプロンプト2048応答は、7.8秒のうち最初の2秒が"何も出ない"時間」というほうが実務に近い。
なぜ TTFT はプロンプト長に線形なのか
TTFTの内訳を分解すると、こうなります。
TTFT = プロンプトのTokenizer時間 + プレフィル(KVキャッシュ構築) + 最初の1トークン生成
このうち、支配的なのはプレフィルです。プレフィルは、プロンプトの全トークンをTransformerに通して、各層のK/Vを計算しキャッシュに詰める処理です。計算量は O(プロンプト長)、より正確には O(プロンプト長 × 隠れ次元 × レイヤ数) で、GPUのFLOPS帯域に律速されます。
対して、デコードは1トークンずつ生成しながらKVキャッシュを参照します。計算量は「1トークンあたり O(コンテキスト長)」で、メモリ帯域(GDDR6X の 504 GB/s)に律速されます。
RTX 4070 の FLOPS(FP16 ≈ 30 TFLOPS)とメモリ帯域(504 GB/s)を、モデル別に「どちらがボトルネックか」で分けると次のとおり。
| モデル | プレフィル支配 | デコード支配 |
|---|---|---|
| Qwen 35B Q4_K_M | ✅ FLOPS | ✅ メモリ帯域(重み19GB弱をVRAM+CPUに分割) |
| Llama 8B Q4_K_M | ✅ FLOPS | ✅ メモリ帯域(重み4.6GBがVRAM内) |
Qwen 35B は Q4_K_M でも重みが約19GB、RTX 4070 の 12GB VRAM に収まらないので、-ngl 34 のようにレイヤーオフロードで CPU RAM に落とします。デコード時は VRAM ↔ RAM のPCIe通信が入るので、TPSが Llama 8B の3分の1程度に落ちます。プレフィルは1回だけなので、CPU RAM 経由でも「遅くはあるが1回で済む」構造です。
KVキャッシュは、プロンプトが長いほどメモリ消費も増えます。Qwen 35B・コンテキスト8192・FP16 KVで、KVだけで約1.6GB使います。llama.cpp で --cache-type-k q4_0 --cache-type-v q4_0 を指定すると、KVキャッシュもQ4に量子化されてメモリが半分以下になります(精度への影響はほぼ観測不能)。
「体感の壁」はどこにあるか
Nielsenの3閾値(0.1秒/1秒/10秒)を、TTFT に当てはめると次のようになります。
| 閾値 | 体感 | RTX 4070 での成立条件 |
|---|---|---|
| 100ms | 直接応答 | 実質不可(タイポ以下の入力のみ) |
| 1秒 | 待たされた感なし | Llama 8B + プロンプト2048以下 |
| 10秒 | 注意が切れる境界 | Qwen 35B + プロンプト8192まで |
Llama 8B + プロンプト512 の 145ms は、Nielsen の「直接応答」に届きます。Qwen 35B + プロンプト512 の 512ms は「待たされた感なし」に届く。しかしプロンプトが8192になると、Qwen 35B は8秒、Llama 8B も2.2秒に落ちて、注意保持の境界に近づきます。
TPSが同じなのに、体感が全く違うことになります。私は最初、llama-benchの出力に並ぶ「pp512 = 890 tok/s」を見て「890tok/s、速いじゃん」で満足していました。890という数字はたしかに速いのに、体感は「待たされた」で終わる。この矛盾の正体が、pp(prefill)とtg(generate)を混同していた自分の解釈ミスでした。
ストリーミングでTTFTの重要性はさらに高まる
多くのローカルLLM UIやOpenAI互換APIはストリーミング応答が既定です。ストリーミングでは、最初の1トークンが返れば残りは順次描画されます。ユーザーが感じる「速い」は、ここではTTFTと最初の数トークンのTPSにほぼ集約されます。
例えば、Qwen 35Bで「はい、こちらの問題ですね。」の16トークンを返すのに、
- TTFT 512ms + 16 tokens ÷ 22 tok/s = 512 + 727 = 1,239ms
TPSが22でも、UIには最初のトークンから順次表示されるので、「1.2秒でだいぶ読める文が並ぶ」体感になります。逆にTTFTが 8,318ms のプロンプト8192では、最初の1トークンが出るまで8秒以上何も見えず、UIは沈黙します。
TTFT を下げるための実務テクニック
体感速度を優先する場合、TTFT を下げる方向にチューニングします。実測で効いたものを並べます。
- プロンプトを短くする: システムプロンプトを圧縮、few-shot例を減らす。プロンプト長を半分にすればTTFTも半分
-
KVキャッシュを再利用する: llama.cpp
--prompt-cacheでシステムプロンプトのK/Vを保存、次回のTTFTはユーザー入力分だけに - モデルサイズを下げる: 35B → 14B → 8B。TTFT は Llama 8B が Qwen 35B の3.5倍速い
- 量子化を進める: Q4_K_M → Q3_K_M で TTFT が 10-15% 短縮(精度低下は要検証)
- 速いプレフィル向けバックエンド: llama.cpp よりも vLLM や TensorRT-LLM のほうがプレフィル時のGPU利用率が高い(単ユーザーではllama.cppでも十分)
TPSだけを見て「Ollama vs llama.cpp」比較する記事が多いですが、TTFTを分けて測ると景色が変わります。Ollama は llama.cpp をラップしているのでTPSはほぼ同じですが、Ollama側のプロンプトキャッシュ実装が入るとTTFTがドラマチックに下がります(私の環境で 68% 減)。
Anthropic APIのprompt cachingとの対比
2026年時点、Anthropic API の prompt caching は「同じシステムプロンプト+固定コンテキストを再利用すると、その部分の入力トークン課金が90%割引」になる仕組みです。この90%割引の背景も、キャッシュ再利用でプレフィル(=TTFT)を短縮できることにあります。
同じ思想をローカルLLMに持ち込むと、--prompt-cache によるシステムプロンプトのK/V保存が有効です。実測で、システムプロンプト2048トークン + ユーザー入力64トークン の状況で、
- キャッシュなし: TTFT ≒ 2,047ms (Qwen 35B、プロンプト2048相当)
- キャッシュあり: TTFT ≒ 145ms (ユーザー入力64トークン分のプレフィルのみ)
14倍の短縮。API課金モデルの90%割引と、ローカルのTTFT短縮は、同じ数学的根拠で動いています。
言い換えると、Anthropicが課金割引でユーザーに還元しているのは「プレフィル計算を再利用できたぶんのGPU代」です。ローカル環境で --prompt-cache を使うと、その計算節約分は電気代と待ち時間に直接跳ね返ります。90%割引のロジックが腑に落ちると、「なぜキャッシュはここまで効くのか」がストンと落ちます。
まとめ
- ローカルLLMの体感速度は tok/s では測れない。TTFT が支配する
- RTX 4070 + Qwen 35B、プロンプト8192 で TTFT = 8.3秒。ユーザーは8秒間何も見えない
- TPS はプロンプト長に対してほぼ一定。TTFT はプロンプト長に線形
- モデルサイズの影響は、TPSより TTFT のほうが大きい(3.5倍)
- 体感速度優先なら、プロンプト短縮 + KVキャッシュ再利用 + モデルダウンサイズ の順で効く
- Anthropic の prompt caching 90%割引と、ローカルの
--prompt-cacheは、同じ「プレフィル短縮」の思想
私は今回、llama-bench -r 3 で標準偏差付きの実測を回し、tok/s だけを見て「速い/遅い」を判定するのが実務でどれだけ滑るかを再確認しました。TTFTを別軸で見る癖を付けると、モデル選定の議論が変わります。手元で ./llama-bench -p 512,2048,8192 -n 128 -r 3 を1回叩けば、話が変わります。面白くいきましょう。
ローカルLLMをClaude Code等のエージェント連携で使うと、システムプロンプトが長くなりがちで、TTFT問題が実装の設計判断に直結します。実務でLLMを使い倒すためのハーネス設計を、拙著にまとめています。
Claude Code Mastery
Sources: