はじめに
前回、メモリ1GBのさくらVPSで qwen2.5:0.5b を動かして 約14トークン/秒 という結果を得ました。CPU推論としては十分実用的で、ストリーミングなら快適に使えます。
今回はその続きで、EC2のGPUインスタンス(NVIDIA L4)に 27Bクラスのモデルを載せました。パラメータ数にして54倍。当然、桁違いに速くなるものと思っていました。
結果は14.7トークン/秒でした。
パラメータ54倍、GPU導入、それで速度はほぼ同じ。この数字の意味を追いかけたのが今回の記事です。
執筆時点(2026年7月)、g6.xlarge / NVIDIA L4 / Ollama v0.32.5 / qwen3.6:27b での測定です。
先にまとめ
- LLMの生成速度はメモリ帯域でほぼ決まる。 演算性能でもGPU世代でもありません。実測値は理論上限の83%まで達していました。
- 新しいGPUのほうが遅いことがある。 L4(2023年)はA10G(2021年)に対して世代は新しいものの、メモリ帯域は約半分です。
- カタログのコンテキスト長は使い切れない。 262,144トークン対応と書かれていますが、24GBのVRAMで実際に使えるのは約9万トークンでした。
測定結果
| num_ctx | tok/秒 | VRAM(MiB) | 出力tok | 総時間(秒) |
|---|---|---|---|---|
| 4096 | 14.7 | 17,091 | 117 | 130.1 |
| 8192 | 14.7 | 17,351 | 114 | 143.3 |
| 16384 | 14.7 | 17,871 | 115 | 131.3 |
| 32768 | 14.7 | 18,911 | 111 | 132.1 |
| 65536 | 14.7 | 20,991 | 101 | 130.9 |
測定環境
| 項目 | 値 |
|---|---|
| インスタンス | g6.xlarge(東京・オンデマンド $1.1672/時) |
| GPU | NVIDIA L4 / VRAM 23,034MiB / TDP 72W |
| ドライバ | 580.173.02 / CUDA 13.0 |
| CPU / RAM | 4 vCPU / 15GiB |
| モデル | qwen3.6:27b(Q4量子化 / 約17GB) |
| Ollama | v0.32.5 |
比較対象の前回環境は、さくらVPS(仮想2コア / メモリ1GB / GPUなし)で qwen2.5:0.5b(約398MB)です。
実際にやったこと
ベンチマークを自動化する
GPUインスタンスは1時間$1.17かかります。手作業で何度も叩くより、一気に流したほうが安上がりです。
cat > ~/bench.sh << 'EOF'
#!/bin/bash
MODEL="${1:?usage: bench.sh <model>}"
PROMPT="桜の季節を舞台にした恋愛小説の冒頭を200文字程度で書いてください。"
echo "| num_ctx | tok/秒 | VRAM(MiB) | 出力tok | 総時間(秒) |"
echo "|---|---|---|---|---|"
for CTX in 4096 8192 16384 32768 65536; do
ollama stop "$MODEL" 2>/dev/null; sleep 3
R=$(curl -s http://localhost:11434/api/chat -d "{
\"model\": \"$MODEL\",
\"messages\": [{\"role\":\"user\",\"content\":\"$PROMPT\"}],
\"stream\": false,
\"think\": false,
\"options\": {\"num_predict\": 300, \"num_ctx\": $CTX}
}")
VRAM=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits)
echo "$R" | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'error' in d: print('| $CTX | ERROR: '+d['error'][:40]+' | | | |'); sys.exit()
e=d.get('eval_count',0); ed=d.get('eval_duration',1); t=d.get('total_duration',0)
print(f'| $CTX | {e/(ed/1e9):.1f} | $VRAM | {e} | {t/1e9:.1f} |')
"
done
EOF
chmod +x ~/bench.shMarkdownの表がそのまま出るので、記録にそのまま貼れます。
Ollamaは応答のメタデータに eval_count(生成トークン数)と eval_duration(生成時間・ナノ秒)を含めてくれるので、割るだけで正確な生成速度が出ます。time コマンドで測るとモデルのロード時間が混ざるので、こちらのほうが正確です。
結果: 全行が14.7
| num_ctx | tok/秒 | VRAM(MiB) | 出力tok | 総時間(秒) |
|---|---|---|---|---|
| 4096 | 14.7 | 17091 | 117 | 130.1 |
| 8192 | 14.7 | 17351 | 114 | 143.3 |
| 16384 | 14.7 | 17871 | 115 | 131.3 |
| 32768 | 14.7 | 18911 | 111 | 132.1 |
| 65536 | 14.7 | 20991 | 101 | 130.9 |コンテキスト長を16倍にしても、生成速度は1桁目まで一致。 ここまで安定するとは思っていませんでした。
そして前回の記録と並べます。
| ハードウェア | モデル | パラメータ | 速度 | |
|---|---|---|---|---|
| さくらVPS | 仮想2コア・GPUなし | qwen2.5:0.5b | 0.5B | 13.9 tok/秒 |
| EC2 g6.xlarge | NVIDIA L4 | qwen3.6:27b | 27B | 14.7 tok/秒 |
54倍のモデルを、GPUで動かして、6%速い。
なぜこうなるのか
生成の律速はメモリ帯域
LLMがトークンを1つ生成するとき、モデルの重み全体を一度読み出します。27Bを4bit量子化した約17GBなら、1トークンごとに17GBをVRAMから読む計算です。
つまり理論上の上限はこうなります。
生成速度の上限 ≒ メモリ帯域 ÷ モデルサイズL4のメモリ帯域は公称約300GB/s。モデルは約16.8GB。
300 ÷ 16.8 ≒ 17.9 トークン/秒実測は14.7。理論値の82%です。
推論エンジンとしてはかなり優秀な数字で、逆に言えばこれ以上の高速化余地はほとんどありません。演算を最適化しても、帯域という物理的な壁が先にあります。
実効帯域に直すと以下になります。
14.7 tok/秒 × 16.8GB = 約247 GB/s(公称300GB/sの82%)VPSのほうも計算が合う
同じ式を前回のVPSに当てはめます。qwen2.5:0.5b は約398MB、DDR4のメモリ帯域を仮に20GB/s程度とすると、
20 ÷ 0.4 = 50 トークン/秒実測は13.9でした。GPUの82%に比べると効率は悪いのですが、CPUではメモリコントローラの効率が落ちるうえ、演算そのものもボトルネックになります。それでも桁としては説明がつく範囲です。
重要なのは、GPUかCPUかではなく「帯域÷モデルサイズ」で大枠が決まるということです。小さいモデルなら遅い環境でも速く、大きいモデルなら速い環境でも遅い。
新しいGPUのほうが遅い
今回L4を使ったのは積極的な選択ではなく、A10G(g5.xlarge)が容量不足で起動できなかったためです。結果的にこれが興味深い比較材料になりました。
| A10G (g5.xlarge) | L4 (g6.xlarge) | |
|---|---|---|
| 発売 | 2021年 | 2023年 |
| アーキテクチャ | Ampere | Ada Lovelace(新しい) |
| VRAM | 22GB | 24GB |
| メモリ帯域 | 約600 GB/s | 約300 GB/s |
| TDP | 300W | 72W |
| 東京・オンデマンド | $1.4590/時 | $1.1672/時 |
世代は新しく、VRAMは多く、価格は安い。しかし帯域は半分です。
L4は推論の効率とデータセンターの電力密度を重視した設計で、TDP 72Wという数字がそれを物語っています。A10Gの300Wに対して4分の1以下。性能を削って電力効率を取ったわけです。
上の式に当てはめると、A10Gなら理論上35トークン/秒前後、実効82%として約29トークン/秒が期待できたことになります。同じモデルで倍近い差です。
教訓
GPUを選ぶとき、つい「世代」「VRAM容量」「価格」を見てしまいます。しかしLLM推論では、カタログの目立つ位置に書かれていないメモリ帯域が最も効きます。
「新しい世代だから速い」は成り立ちません。用途によって最適化の方向が違うためです。
ここに挙げた帯域値は一般的な公称値です。厳密にはNVIDIAの製品ページで確認してください。
VRAMとコンテキスト長の関係
測定表のVRAM列を見ると、きれいな規則性があります。
| num_ctx | VRAM(MiB) | 前行からの増分 |
|---|---|---|
| 4,096 | 17,091 | — |
| 8,192 | 17,351 | +260 |
| 16,384 | 17,871 | +520 |
| 32,768 | 18,911 | +1,040 |
| 65,536 | 20,991 | +2,080 |
コンテキストを倍にするたび、増分もちょうど倍。1トークンあたり0.0635MiB(約65KiB) で、誤差なく線形です。
これはKVキャッシュのサイズで、モデルの構造から決まる定数です。ここから逆算できます。
モデル本体のみ(ctx=0) ≒ 16,831 MiB
実VRAM 23,034 MiB
────────────────────────────────────
KVキャッシュに使える上限 ≒ 6,200 MiB
→ 最大コンテキスト長 ≒ 97,000 トークン実際には推論中の一時バッファも要るので、安全に使えるのは9万トークン程度でしょう。
カタログとの差
qwen3.6の公称コンテキスト長は 262,144トークンです。
使える割合 = 90,000 ÷ 262,144 ≒ 34%カタログスペックの3分の1しか使えません。 24GBのVRAMでは、モデル本体を載せた時点で残りが6GBしかないためです。
「256K対応」を額面どおり受け取ると、実運用で足をすくわれます。モデルサイズとVRAMから、KVキャッシュに回せる容量を先に計算しておくべきでした。
なお、コンテキストを長くしても生成速度は落ちませんでした(全行14.7)。VRAMに収まっている限りは影響しないようです。溢れた瞬間に急落するはずですが、今回は65,536でも収まったため、その境界は未測定です。
見落としていたモデルのロード時間
測定表をよく見ると、おかしな点があります。
| 8192 | 14.7 tok/秒 | 114トークン | 総時間 143.3秒 |114トークンを14.7トークン/秒で生成すれば 7.8秒のはずです。しかし総時間は143秒。
差の約135秒はモデルのロード時間でした。17GBをEBSから読み出してVRAMに転送しています。ベンチスクリプトで毎回 ollama stop していたため、5回ぶん律儀に読み直していました。
これは実運用に効く
Ollamaは最後に使ってから5分でモデルをメモリから降ろします。 つまり利用者が久しぶりにアクセスするたび、2分以上待たされることになります。
対処は簡単で、常駐させるだけです。
sudo tee -a /etc/systemd/system/ollama.service.d/override.conf > /dev/null << 'EOF'
Environment="OLLAMA_KEEP_ALIVE=-1"
EOF
sudo systemctl daemon-reload && sudo systemctl restart ollama-1 で無期限に保持します。インスタンスを起動している間は課金されているので、VRAMを空けておく理由がありません。
「使っていない時間のメモリを節約する」というOllamaの既定は、ローカルPCでは合理的ですが、時間課金のクラウドでは逆効果でした。
コストに換算する
$1.1672/時で14.7トークン/秒。100%稼働させた場合の単価を出してみます。
14.7 tok/秒 × 3,600秒 = 52,920 トークン/時
$1.1672 ÷ 52,920 × 1,000,000 = 約 $22.1 / 1Mトークン出力100万トークンあたり$22。 これは生成し続けた場合の理論値で、実際にはアイドル時間があるぶん実効単価はさらに悪化します。1日2時間起動して実際の生成が10分なら、単価は12倍になる計算です。
「自前でホストすれば安い」という直感は、使用密度が十分に高い場合にのみ成立するようです。個人や家庭の利用頻度では、API課金のほうが安く済む可能性が高いと感じました。
ハマりどころまとめ
| 症状 | 原因 | 対処 |
|---|---|---|
| GPUにしたのに速くならない | 生成はメモリ帯域律速 | 帯域÷モデルサイズで上限を見積もる |
| 新世代GPUのほうが遅い | L4は電力効率重視で帯域が低い | カタログの世代でなく帯域を見る |
| 256K対応なのに長文が入らない | VRAMの残りがKVキャッシュ上限 | (VRAM − モデルサイズ) ÷ 65KiB で計算 |
| 生成時間より総時間が極端に長い | モデルのロード | OLLAMA_KEEP_ALIVE=-1 で常駐 |
time で測ると数字がぶれる | ロード時間が混入 | eval_count ÷ eval_duration を使う |
やってみての所感
数字が理論と合うのは気持ちがいい
「300 ÷ 16.8 = 17.9」という単純な割り算が、実測14.7という結果を82%の精度で説明してしまいました。LLM推論という複雑そうな処理が、帯域という一つの数字でほぼ決まるというのは、直感に反しつつも納得感があります。
VRAMの増分が1トークンあたり65KiBぴったりだったのも同様です。実測データがここまで綺麗に線形になると、逆算した「最大9万トークン」という予測にも自信が持てます。
「速いハードを買えば速くなる」は限定的
今回いちばんの学びは、投資の方向を間違えやすいということでした。
GPUを導入し、54倍のモデルを載せても、トークン生成の速度は変わりませんでした。速くしたいなら、より高帯域なGPUを選ぶか、モデルを小さくするかのどちらかです。VRAM容量を増やしても「より大きなモデルが載る」だけで、速くはなりません。
一方で、同じ速度でも1トークンあたりの価値はまるで違います。0.5Bは「100文字で」という指示を無視して652トークン書き続けましたが、27Bは113トークンで的確に終わりました。速度が同じでも、必要なトークン数が6分の1なら実質6倍速いことになります。
「トークン/秒」だけを見ていると、この差を見落とします。
向く人・向かない人
自前ホストが向くのは、生成量が多く、稼働率を高く保てるケースです。バッチ処理や常時稼働のサービスなら、時間課金は有利に働きます。
向かないのは、今回のような個人・家庭利用です。1日数回の会話のために時間課金のGPUを起動するのは、単価で見ると割高です。学習目的なら別ですが、実用性だけを見るならAPIを使うほうが合理的でしょう。
その「合理的でない」ことを自分の数字で確認できたのが、今回いちばんの収穫かもしれません。
まとめ
27BのモデルをNVIDIA L4で動かした結果は14.7トークン/秒。メモリ1GBのVPSでCPU推論した0.5Bと、ほぼ同じでした。
理由はメモリ帯域律速という一点に尽きます。生成のたびにモデル全体を読み出す以上、帯域とモデルサイズの比が上限を決める。GPUの世代でも演算性能でもありません。
そして容量不足でやむなく選んだL4が、A10Gの半分の帯域だったことも今回わかりました。新しくてVRAMが多くて安い、しかし遅い。 GPUのカタログを読む視点が一つ増えました。
次はこのモデルで、生成された文章の中身を見ていきます。実は今回の測定に至るまでに、curlが何も返さないという別の問題を解いていたのですが、それはまた別の話です。