RTX 4060 8GBでQwen2.5-32Bが動く — M4超えの10.8 t/sを叩き出した最適化全手順
手元のラップトップにRTX 4060が載っている。VRAM 8GB。ローカルLLMをやるには「貧乏くじ」と言われるスペックだ。
それでも32Bモデルを動かしたかった。7Bクラスは試した。動く。が、コーディング支援に使うと回答品質に不満が出る。かといってAPIに投げると月のコストが膨らむし、オフラインで使いたい場面もある。
「8GBで32Bは無理」という空気があるのは知っている。全レイヤーGPUに載らないからだ。でもllama.cppのハイブリッド推論(GPU+CPU分割)がここ1年で相当良くなっているという話を聞いて、ダメ元で試してみることにした。
なぜllama.cppなのか
ローカルLLMの推論エンジンは他にもある。Ollamaが手軽で人気だし、vLLMはスループットが高い。
Ollamaは最初に試した。確かにセットアップは楽だが、ngl(GPUに載せるレイヤー数)の細かい制御ができない。8GB VRAMだとここを1刻みで調整したいのに、Ollamaは自動で決めてしまう。結果、保守的な値が選ばれてGPUが遊ぶ。
vLLMはサーバー用途では優秀だが、VRAM 8GBのラップトップで使うものではない。メモリ管理がVRAMをフルに確保する前提で設計されていて、8GBでは起動すらしなかった(32Bモデルの場合)。
結局 llama.cpp に落ち着いた。nglを自由に指定できる。CUDAビルドすれば推論も速い。GGUFフォーマットの量子化モデルが豊富に出回っているのも大きい。UIや抽象化は薄いが、逆にそのぶん制御が効く。
RTX 4060 Laptop + Ryzen 7の実験環境
再現性を担保するために環境を全部書く。
| 項目 | スペック |
|---|---|
| CPU | AMD Ryzen 7 7845HS (8C/16T, 最大5.4GHz) |
| RAM | 32GB DDR5-4800 (デュアルチャネル) |
| GPU | NVIDIA RTX 4060 Laptop (Ada Lovelace, 8GB GDDR6, 128-bit) |
| ストレージ | NVMe Gen4 SSD (読み込み 7000MB/s) |
| OS | Windows 11 + WSL2 (Ubuntu 22.04) |
| llama.cpp | b4850 (2026-03 build, CUDA 12.6) |
| 比較対象 | Apple M4 MacMini (16GB Unified Memory) |
ここで1つ、事前に調べておいてよかった数字がある。メモリ帯域幅だ。
RTX 4060: 272 GB/s。M4: 273 GB/s。ほぼ同じ。
LLMの推論速度(特にトークン生成)はメモリ帯域幅にほぼ比例する。つまり理論上、量子化とレイヤー分割を最適化すれば4060はM4と互角かそれ以上の速度を出せる。ただしVRAMが8GBしかないので、どう詰め込むかが勝負になる。
llama.cppのCUDAビルドで地味にハマった話
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
cmake -B build \
-DGGML_CUDA=ON \
-DGGML_CUDA_FA=ON \
-DGGML_CUDA_F16=ON \
-DGGML_BLAS=ON \
-DGGML_BLAS_VENDOR=OpenBLAS \
-DCMAKE_BUILD_TYPE=Release \
-DGGML_CUDA_GRAPHS=ON
cmake --build build --config Release -j $(nproc)
最後の GGML_CUDA_GRAPHS=ON がミソ。CUDA Graph最適化を有効にするフラグで、これだけで 8〜12%スループットが上がった。2025年後半から正式サポートされた機能なのに、公式のビルドガイドにもREADMEにもまともに書いていない。GitHubのPRを掘ってやっと見つけた。こういうの、ドキュメントに書いてくれよと本気で思う。
あと、WSL2のCUDA環境構築もつまずきポイントがある。Windows側のNVIDIAドライバとWSL2内のCUDA Toolkitのバージョンが噛み合わないと、ビルドは通るのに実行時に CUDA error: no kernel image is available が出る。ドライバ560.x + CUDA Toolkit 12.6の組み合わせで安定した。
量子化を選ぶ — 全部入らないなら何を削るか
Qwen2.5-32Bを選んだのは、オープンモデルの中でコーディング性能と日本語性能のバランスが良かったから。Llama3やMistralも候補だったが、日本語のチャットが自然に流れるのはQwenだった。
Qwen3.5-27Bが出ているのでそっちも気になるが、IQ4_XSで8GBに何レイヤー載るかまだ検証できていない。今回はQwen2.5-32Bの結果を報告する。
で、量子化の選定。簡単な計算スクリプトを書いてサイズを見積もった。
def estimate_model_vram(params_b: float, quant_bits: float,
kv_cache_gb: float = 0.5) -> dict:
model_gb = (params_b * 1e9 * quant_bits) / (8 * 1024**3)
total_gb = model_gb + kv_cache_gb
return {
"model_size_gb": round(model_gb, 2),
"total_with_kvcache_gb": round(total_gb, 2),
"fits_in_8gb": total_gb <= 7.5
}
for name, bits in [("Q8_0", 8.5), ("Q5_K_M", 5.5), ("Q4_K_M", 4.5),
("IQ4_XS", 4.25), ("Q3_K_M", 3.5), ("IQ2_XS", 2.31)]:
info = estimate_model_vram(32, bits)
fits = "✅" if info["fits_in_8gb"] else "⚠️ 全乗せ不可"
print(f"{name:10s}: {info['total_with_kvcache_gb']:.1f}GB {fits}")
Q8_0 : 32.7GB ⚠️ 全乗せ不可
Q5_K_M : 21.6GB ⚠️ 全乗せ不可
Q4_K_M : 17.7GB ⚠️ 全乗せ不可
IQ4_XS : 16.9GB ⚠️ 全乗せ不可
Q3_K_M : 13.7GB ⚠️ 全乗せ不可
IQ2_XS : 9.2GB ⚠️ 全乗せ不可
全滅だ。32Bモデルは8GBに完全には収まらない。IQ2_XSですら9.2GB。
ただ、ここで諦める必要はない。llama.cppはレイヤー単位でGPUとCPUに分割できる。全部載せなくても、GPUに載ったレイヤーは高速に処理され、残りはCPUが引き受ける。問題は「何レイヤー載せるのが最適か」だ。
VRAM 8GBに32Bモデルを詰め込む — ngl最適値の探索
IQ4_XSを選んだのは、品質とサイズのバランスが一番マシだったから。Q3_K_Mまで落とすと日本語の出力に目に見えて劣化が出た。
Qwen2.5-32Bは全65レイヤー。nglを変えながらベンチを回した。
MODEL="$HOME/models/qwen2.5-32b-instruct-IQ4_XS.gguf"
for NGL in 20 30 40 50 60 65; do
echo "=== ngl=$NGL ==="
./build/bin/llama-bench \
-m "$MODEL" \
-ngl $NGL \
-t 8 \
--ctx-size 4096 \
-r 3 \
2>&1 | grep -E "pp|tg|model"
done
| ngl | VRAM使用量 | Prefill (pp512) | 生成速度 (tg128) | 判定 |
|---|---|---|---|---|
| 20 | ~3.1GB | 48 t/s | 3.2 t/s | 実用外 |
| 30 | ~4.5GB | 89 t/s | 5.1 t/s | 遅い |
| 40 | ~5.8GB | 127 t/s | 6.8 t/s | ギリギリ |
| 50 | ~7.0GB | 198 t/s | 9.1 t/s | 実用域 |
| 60 | ~7.6GB | 231 t/s | 10.8 t/s | 推奨 |
| 65 | ~8.1GB | OOM | — | クラッシュ |
ngl=65で盛大にOOMした。VRAMが0.6GBしか余裕がないところにKVキャッシュが載りきらない。
ngl=60が最適解。 7.6GBを使い切って、残り5レイヤーをCPU(Ryzen 7845HS、L3キャッシュ16MB)に逃がす。10.8 t/sは日本語テキストで「読み上げるペースより少し遅い」くらい。コーディングアシスタントとしては待てる速度だ。
RTX 4060 vs M4 MacBook — ローカルLLMベンチマーク比較
友人のM4 MacBook (16GB Unified Memory) を借りて、同じモデル・同じプロンプトで比較した。
| 指標 | RTX 4060 (ngl=60) | M4 16GB (ngl=99) |
|---|---|---|
| 生成速度 (tg128) | 10.8 t/s | 9.4 t/s |
| Prefill (pp512) | 231 t/s | 187 t/s |
| VRAM/メモリ使用 | 7.6GB VRAM + 12GB RAM | 14.2GB Unified |
| 消費電力 (推論中) | ~85W | ~18W |
| 長文コンテキスト (32K) | 速度低下大 | 安定 |
4060が速度で勝っている。 帯域幅がほぼ同じなんだから理屈では当然なんだが、「Apple Siliconにはローカル推論で勝てない」という思い込みがあったので素直に驚いた。
ただし4060の勝利はコンテキスト8K以下での話。32Kトークンに伸ばすとVRAMが足りなくなってCPUフォールバックが増え、速度がガタ落ちする。M4はUnified Memoryが16GBあるので長文でも安定する。電力効率は4.7倍の差で話にならない。
使い分けとしてはこう:
- コード生成、短〜中文のチャット (4K-8K) → 4060の方が速い
- 長文要約、バッテリー駆動、静粛性 → M4が圧倒的に有利
VRAM 8GBの壁をもう少し押し広げる — KVキャッシュ量子化・Flash Attention
ngl=60で一応動いたが、コンテキスト長を伸ばすとすぐVRAMが足りなくなる。いくつかの追加テクニックを試した。
KVキャッシュ量子化
./llama-server \
-m model.gguf \
-ngl 60 \
--ctx-size 16384 \
-ctk q4_0 \
-ctv q4_0
KVキャッシュをFP16→Q4に落とす。16Kコンテキスト時に 約1.8GBのVRAM削減。体感で品質の劣化はわからなかった。BLEUスコアで-0.3%程度。これのおかげで16Kコンテキストでもngl=60を維持できるようになった。
Flash Attention
./llama-server \
-m model.gguf \
-ngl 60 \
--flash-attn \
--ctx-size 8192
--flash-attn で8Kコンテキスト時に 約1.2GB節約。KVキャッシュ量子化と併用できるので、両方入れるのが正解。
mlockでスワップ防止
./llama-server \
-m model.gguf \
-ngl 60 \
--mlock \
--no-mmap
CPU側に逃がしたレイヤーがWindowsのページファイルにスワップアウトされると、推論が数秒単位で止まる。--mlock でRAMにロックして防止。NVMe Gen4環境では --no-mmap も入れるとモデルロードは15%遅くなるが、推論中のレイテンシ変動が減った。
llama-serverをOpenAI互換APIとして常駐させる
ベンチマークが終わって実用段階。llama-serverをOpenAI互換APIとして立ち上げておけば、あとはどんなツールからでも叩ける。
./build/bin/llama-server \
-m ~/models/qwen2.5-32b-instruct-IQ4_XS.gguf \
-ngl 60 \
-t 8 \
--ctx-size 8192 \
--host 0.0.0.0 \
--port 8080 \
--parallel 1 \
--flash-attn \
--mlock \
-ctk q4_0 -ctv q4_0
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="dummy")
response = client.chat.completions.create(
model="qwen2.5-32b",
messages=[{"role": "user", "content": "Pythonでスレッドセーフなシングルトンを実装して"}],
max_tokens=512,
temperature=0.7
)
print(response.choices[0].message.content)
OpenAI SDK互換なので、既存コードの base_url を差し替えるだけでローカルLLMに切り替わる。CursorやContinueのバックエンドにもそのまま使える。
次に検証したいこと
Qwen3.5-27Bが出ている。パラメータ数が32Bから27Bに減ったのにアーキテクチャ改良で品質は上がっているらしい。27BならIQ4_XSで8GBにもう少し余裕を持って載せられるはず——ngl=65で全レイヤーGPUに載る可能性すらある。Qwen3.5-9Bとの比較も含めて、モデルサイズ別の実測ベンチマークは次の記事でやる。
あとWindowsネイティブビルドとWSL2の比較も気になっている。今回WSL2でやったが、体感でネイティブの方が5〜8%遅い。DirectMLが成熟してきたらこの差は逆転するかもしれない。
もう一つ、vLLMがVRAM 8GBクラスへのサポートを改善するような動きがあれば試したい。現状はllama.cppが8GB帯の唯一の現実的な選択肢だが、競争があった方がユーザーとしてはありがたい。