3か月前、私は同じRTX 4070でこんな検証記事を書きました。結論は「35BのMoEモデルは、待てるなら動く」。測ったのは10.6 tok/s。動くには動くが、チャットで使うには指が止まる速度です。
先週、同じGPU・同じモデルで測り直したら34.6 tok/sが出ました。ハードは1ミリも変えていません。変えたのは推論エンジンと、たった1つのフラグだけです。
そして一番おかしかったのは、その過程で「エキスパートをGPUに載せれば載せるほど遅くなる」という、自分の常識と真逆の結果が出たことでした。VRAMを増やせば速くなる、ではなかったのです。本記事はその検証ログです。
前提: なぜ12GBのGPUで35Bが動くのか
まず、ここが腑に落ちていないと先の話が宙に浮きます。
今回のモデルはQwen3.5-35B-A3Bです。総パラメータは34.66B。普通に考えれば、Q4量子化しても20GB前後になり、12GBのVRAMには到底入りません。3か月前の私は「9B(密モデル)すら実用域に届かないのに、なぜ35Bが動くんだ」と首をかしげていました。
答えは A3B の部分にあります。これはMixture of Experts(MoE)で、128個のエキスパートのうち1トークンあたり8個、約3.3Bぶんしか活性化しません。総量は35Bでも、1トークンで実際に動くのは3B級にとどまります。
この「使うのは一部だけ」という性質が、後で効いてきます。
出発点: Ollamaの自動オフロード
ローカルLLMをとりあえず動かすなら、まずOllamaです。私もそこから始めました。ollama run qwen3.5:35b-a3b を叩くだけで、Ollamaが「GPUに載る分だけ載せて、残りはCPUに回す」という分割を自動でやってくれます。
クリーンなVRAM(他プロセスを止めた状態)で測り直した結果がこちらです。
| 項目 | 値 |
|---|---|
| 生成速度 | 12.2 tok/s |
| 自動分割 | 58% CPU / 42% GPU |
| コンテキスト | 4096 |
| VRAM使用 | 11.4GB |
3か月前の10.6 tok/sから少し上がっていますが、これはOllamaのバージョンが上がったぶんで、傾向は同じです。ここで注目したいのは分割比です。Ollamaはモデルの42%しかGPUに載せていません。VRAMは11.4GBまで埋まっているのに、です。
つまりOllamaは「VRAMが満杯だから、これ以上はCPU」という素直な判断をしています。賢いのですが、MoEの構造を知っているわけではありません。ここに伸びしろがありました。
乗り換え: llama.cppの--cpu-moe
Ollamaの中身はllama.cppですが、エキスパートの配置を細かく指定するフラグはOllamaからは触れません。そこでllama.cppを直接ビルドして使います。
# 1. CUDA有効でビルド(cmakeが要る。なければ pip install cmake で入る)
git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j
# 2. GGUFを入手(unsloth版 Q4_K_M、約22GB)
wget https://huggingface.co/unsloth/Qwen3.5-35B-A3B-GGUF/resolve/main/Qwen3.5-35B-A3B-Q4_K_M.gguf
ここで使うのが --n-cpu-moe N(短縮 -ncmoe)です。「最初のN層分のエキスパートをCPUに置く」という指定で、Nを大きくするほどエキスパートがCPUに退避します。今回のモデルは48層なので、-ncmoe 48 で全エキスパートがCPU行きになります。
私は素朴にこう考えていました。「VRAMが許す限りエキスパートをGPUに残したほうが速いはずだ。だからNは小さいほうがいい」と。逆でした。
スイープ結果: 載せないほど速い
-ngl 99(全層をGPUに指定)を固定し、-ncmoe を48から24まで振って生成速度を測りました。
| n-cpu-moe | エキスパート配置 | 生成 tok/s |
|---|---|---|
| 48 | 全部CPU | 34.60 |
| 44 | ほぼCPU | 27.19 |
| 40 | 16.88 | |
| 36 | 15.29 | |
| 32 | 半々 | 14.06 |
| 28 | 12.85 | |
| 24 | 半分GPU | 11.71 |
きれいな右肩下がりです。エキスパートをGPUに移すほど(=Nを小さくするほど)、速度は単調に落ちました。比較のために、-ncmoeなしの素の-ngl 99は12.94 tok/sで、Ollamaの自動とほぼ同じでした。
つまり順位はこうです。
- Ollama自動 / 素のllama.cpp: 約12 tok/s
- 全エキスパートをCPUに退避: 34.6 tok/s(2.8倍)
3か月前に「待てるなら」と書いた速度が、フラグ1つで実用域に乗りました。-ncmoe 24(エキスパートを半分GPUに入れた状態)が最下位、というのが何とも示唆的です。
なぜGPUに入れると遅くなるのか
結果を見て頭を抱えたあと、理由を整理して納得しました。鍵は「何がボトルネックか」を部品ごとに分けて考えることです。
アテンションとKVキャッシュはメモリ帯域律速です。データを舐める速度がそのまま効くので、帯域500GB/s級のVRAMに置くと一気に速くなります。一方エキスパートは、1トークンで触るのが3.3Bと疎で、しかも20GBぶん全部は12GBにそもそも入りません。
ここで中途半端にエキスパートをVRAMに詰めると、帯域が一番効くアテンションとKVの置き場を奪ってしまいます。結果、一番速くしたい部分が押し出されて全体が遅くなります。だから「帯域が効くアテンションはGPUに全部、計算が疎なエキスパートはCPUに全部」というクリーンな分離が最速になるわけです。
VRAMを埋めることが目的化すると、かえって損をします。GPUは万能の置き場ではなく、帯域を活かす相手を選んで置く場所だった、ということでした(そして私はしばらくそれを忘れていました)。
つまずきどころ(私がハマった3つ)
手順だけ書くと簡単そうに見えますが、私は何度か遠回りしました。同じ轍を踏まないように残しておきます。
1. ベンチの前にVRAMを空にする。 最初の測定で、Ollamaを動かしていないのにVRAMが9.4GB埋まっていました。犯人はGPUを貸し出すSaladという別アプリでした。残り2.8GBでは何を測っても歪みます。nvidia-smi を一度見て、自分のモデル以外がVRAMを食っていないか確認してから測りましょう。
2. Ollamaのモデルをそのままllama.cppに渡そうとしない。 Ollamaが落としたGGUFはblobとしてシステム領域に置かれ、権限の壁で素直に読めないことがあります。再現性も考えると、HuggingFaceから同じQ4_K_MのGGUFを wget で別途取るのが結局いちばん速くて確実でした。
3. llama-cli の素のcompletionで測ると終わらない。 Qwen3.5は思考(thinking)を出すモデルなので、チャットテンプレートなしで生で回すと延々と考え続けて生成が止まりません。速度の計測は llama-bench を使うか、llama-server にチャットテンプレート経由で投げるのが安全です。私はこれで1回、GPUを5分間空回しさせました。
実用設定とまとめ
最終的に、4070でQwen 35Bを実用速度で回す起動はこれだけです。
./build/bin/llama-server \
-m Qwen3.5-35B-A3B-Q4_K_M.gguf \
-ngl 99 \
--cpu-moe \
-c 8192
# -ngl 99 : 全層をGPUに指定
# --cpu-moe : エキスパートだけCPUに戻す(= -ncmoe 48 と同じ)
この構成でVRAM使用は約11.7GB、12GBをほぼ使い切りますが収まります。長い文脈を扱ってVRAMが苦しくなったら、KVキャッシュを -ctk q8_0 -ctv q8_0 で量子化すると、KVぶんのVRAMが半分ほどに減り、速度はほぼ変わらず文脈を伸ばせます。
検証の要点を3行で。
- 4070で35B-A3Bは「12 tok/s」が天井ではなく、
--cpu-moeで34.6 tok/sまで伸びる - エキスパートはGPUに載せず全部CPUに逃がすのが最速(載せるほど遅くなる)
- 理由は帯域律速のアテンションにVRAMを使い切らせるため
次は、出たばかりのQwen3.6世代や、同じ手で他のMoEモデル(gpt-oss系など)がどこまで伸びるかを測ってみる予定です。
ところで、あなたのGPUのVRAMはいま何に使われているでしょうか。私はこの検証中、9GBを別アプリ(GPU貸出のSalad)に食われていたことに測定直前まで気づきませんでした。ベンチの前に nvidia-smi を一度見るだけで、無駄な遠回りが1つ減ります。
📖 本記事を体系化した書籍(発売中)
本記事のような実測ノートを、Ollamaの 12.2 tok/s を -ngl 99 --cpu-moe で 34.6 tok/s(2.8倍)まで引き上げる勝ち構成から、KV キャッシュ量子化、Qwen 3.5 vs 3.6 の世代差、ローカル 35B のエージェント化まで、通しで 1 冊にまとめました。
RTX 4070 で Qwen 35B を 2.8 倍速くする ― 自分の数字を疑え、実測ノート
序章 + 第1章 + 第2章は無料公開中(¥1,200)。
無料の 12 枚スライド版もあります。社内共有や勉強会の資料としてもどうぞ。