1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RTX 4060 8GBでQwen2.5-32Bが動く — M4超えの10.8 t/sを叩き出した最適化全手順

1
Last updated at Posted at 2026-03-22

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帯の唯一の現実的な選択肢だが、競争があった方がユーザーとしてはありがたい。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?