RTX 4070 で Qwen 35B が 34.6 tok/s になった 2 フラグ実測
結論から書きます。RTX 4070 (12GB) 上で Qwen3.5-35B-A3B (Q4_K_M) を動かすとき、-ngl 99 と --cpu-moe の 2 つのフラグを両方入れることで、生成速度は 12.2 tok/s → 34.6 tok/s になりました。約 2.8 倍です。片方だけでは、この数字は出ません。
以前書いた記事では --cpu-moe 1 つを紹介しましたが、あの記事は「勝ち構成の入口だけ」を扱っていました。今回は 2 フラグの分解実測と、KV 量子化のトレードオフ、そして「速さは賢さを削っていないのか」の 7 問チェックを、まとめて残します。
なぜ 1 フラグでは足りないのか
-ngl 99 は「全レイヤーを GPU に載せろ」の意思表示です。48 層の実数を超える 99 を渡すのは、単に「全部」と言い切るための慣用句です。ただし、これだけを渡すと 12GB の VRAM に 20.49 GiB のモデルが載るわけがなく、実装は勝手にオフロード判断をします。ここが問題でした。
一方、--cpu-moe は「MoE のエキスパートテンソルだけは CPU に逃がす」という例外指定です。単独で使うと -ngl の既定値に引きずられて、そもそも GPU オフロードが十分に働きません。
2 つを セットで 入れて、はじめて次の役割分担が成立します。
- GPU 側: アテンションと KV キャッシュ (メモリ帯域が効く部分)
- CPU 側: MoE エキスパート (計算が疎で、CPU 帯域でも捌ける部分)
これが「勝ち構成」の物理的な意味です。

実測 1: フラグの組み合わせで速度がどう動くか
計測環境は RTX 4070 (12GB)、RAM 31GB、WSL2 Ubuntu 24.04、CUDA 12.9。モデルは Qwen3.5-35B-A3B の Q4_K_M 量子化 (20.49 GiB) です。llama.cpp を CUDA 有効でビルドし、llama-bench で tg128 (生成 128 トークン) を 3 試行しました。
第 2 章のスイープ表を再掲します。ここでは -ngl 99 を固定し、CPU に置くエキスパート層の数 n_cpu_moe を動かしています。n_cpu_moe=48 が --cpu-moe (全 CPU) と等価です。
| n_cpu_moe | GPU 側のエキスパート層 | 生成速度 tg128 (tok/s) | ベースライン比 |
|---|---|---|---|
| 48 | 0 (全 CPU) | 34.60 | 2.8 倍 |
| 44 | 4 | 27.19 | 2.2 倍 |
| 40 | 8 | 16.88 | 1.4 倍 |
| 36 | 12 | 15.29 | 1.3 倍 |
| 32 | 16 | 14.06 | 1.2 倍 |
| 28 | 20 | 12.85 | 1.1 倍 |
| 24 | 24 | 11.71 | 0.96 倍 |
きれいな単調変化です。エキスパートを 1 層でも GPU に戻すと、そのぶんアテンションと KV の帯域予算が削られ、速度は落ちます。24 層まで戻すと、もう Ollama の自動設定 (12.2 tok/s) 以下です。
つまり、直感的な「GPU に載るだけ載せる」は、MoE ではちょうど遅い側に倒れます。私はこれを最初に見たとき、しばらく画面の前で首をかしげました。
実測 2: KV 量子化のトレードオフを 3 段階で
-c 4096 の設定ではまだ VRAM に約 600 MiB の余白が残っていますが、文脈を 32768 まで伸ばそうとすると即座に天井にぶつかります。KV キャッシュが単純計算で 8 倍になるからです。
ここで効くのが KV キャッシュ側の量子化です。-ctk と -ctv を指定します。
llama-server -m qwen35.gguf -ngl 99 --cpu-moe -c 32768 \
-ctk q8_0 -ctv q8_0
f16 (16bit)、q8_0 (8bit)、q4_0 (4bit) の 3 段階を、標準 7 問 (後述) に通した結果がこれです。
| KV 型 | VRAM 使用 | 生成速度 tg128 (tok/s) | 7 問スコア | 備考 |
|---|---|---|---|---|
| f16 | 11.7 GiB | 34.6 | 7/7 | 既定、-c 4096 が限界 |
| q8_0 | 11.0 GiB | 34.1 | 7/7 | -c 32768 まで安全に伸ばせる |
| q4_0 | 10.4 GiB | 33.9 | 6/7 | 7 問目 (バット・ボール) で誤答 |
q8_0 なら、速度はほぼ据え置きのまま文脈だけを 8 倍に伸ばせます。これは「重みは量子化するけど KV は触らない」と身構えていた自分にとって、ちょっと悔しい交換でした。
一方 q4_0 は、速度こそ据え置きですが、標準 7 問のうち推論問題 (バットとボールの引っかけ) で 5 セントではなく 10 セントを返しました。VRAM は最も節約できますが、そのぶん「賢さ」を薄く削ります。エージェント用途で長文脈と精度を両立するなら q8_0 が現状の妥協点です。

標準 7 問のたねあかし
「7 問スコア」の中身も残しておきます。日本語と英語、知識と推論、自然言語とコードを混ぜた 7 問セットです。
- 東京の人口 (日本語・短答)
- WebRTC と WebSocket の違いを 3 点 (日本語・技術説明)
- 機械学習とディープラーニングの違いを 2 文で (日本語・比較)
- What is the capital of France? (英語・短答)
- クイックソートの計算量 (英語・技術短答)
- sort を使わずに上位 3 つの最大値 (コード生成)
- バットとボールで 1 ドル 10 セント、バットはボールより 1 ドル高い、ボールはいくら? (論理推論)
最後の 1 問は認知バイアスを突く有名な問題で、直感で 10 セントと答えたくなります。正解は 5 セントです。KV q8_0 までは、モデルが「直感では 10 セントと答えたくなるが、式を立てると 2x+1=1.10、よって x=0.05」と自分で否定してくれました。私が初めてこの問題を見たときは普通に 10 セントと答えたので、モデルに一敗です。
再現手順 (2 フラグを両方入れる)
私の環境で 34.6 tok/s を再現したコマンドはこの 2 つです。
# ベンチ (llama-bench)
./build/bin/llama-bench -m qwen35.gguf -ngl 99 --cpu-moe -n 128 -r 3
# サーバとして常駐 (llama-server)
./build/bin/llama-server -m qwen35.gguf -ngl 99 --cpu-moe -c 4096
長文脈まで踏み込むなら、-c 32768 -ctk q8_0 -ctv q8_0 を足してください。Qwen Code CLI のような重めのエージェントは、初回リクエストだけで約 19,000 トークンを消費するので、この設定がないと動きません。
雑にコピーして 34.6 tok/s が出ないケースは、たいてい次のいずれかです。
- VRAM が本当は空いていない (Windows 側の常駐プロセスが握っているとよく起きます)
- 量子化が違う (Q4_K_M 以外だと VRAM の使い方がずれます)
- CUDA ビルドしていない (
-DGGML_CUDA=ONを忘れると CPU のみで動きます) - llama.cpp の古いビルドで
--cpu-moeが未実装 (2026 年 3 月以降のビルドを推奨)
まとめ: 2 フラグと 1 組の量子化指定で全部
RTX 4070 で 35B の MoE モデルを実用速度で動かすうえで、重要なのは 2 フラグと 1 組の量子化指定だけでした。
-ngl 99と--cpu-moeを セットで 入れる (片方だけでは意味が薄い)- 長文脈が必要なら
-ctk q8_0 -ctv q8_0を足す (速度ほぼ据え置き、7 問品質も維持) - q4_0 は VRAM が最も浮くが、推論問題で薄く賢さを失う (エージェント用途では避ける)
12GB の VRAM を「ちょうど 95% まで使い切る」構成に落とし込めれば、家庭用 GPU で 35B は無理ではなくなります。VRAM は残り 600 MiB。もう少しだけ、余白の使い方を工夫する余地はあります。そこから先は、書籍側で全 10 章にわたって扱いました。
もっと深く追いたい方へ。Ollama から llama.cpp への切り替え、KV 量子化の全パターン計測、エージェント CLI (Qwen Code / claude-code) との相性、Qwen3.5 と 3.6 の世代差ベンチまで通しで扱ったのが RTX 4070 で動かす 35B ローカル LLM です。
この記事は役に立ちましたか?