RTX 3060 12GBで35B級LLMを動かす ― FreeToken + Qwen3.6-35B-A3B-NVFP4を試してみた


ローカルLLMというと、RTX 3060 12GBでは7B〜14Bクラスあたりが現実的という印象があります。

自分の環境でもこれまではOllamaを使ってQwen系モデルを動かしていましたが、今回は別のアプローチとして FreeToken を試してみました。

目的

今回の目的はシンプルです。

RTX 3060 12GBで35Bクラスのモデルを実用的な速度で動かせるのか?

使用したモデルは Qwen3.6-35B-A3B-NVFP4。

結論から書くと、いくつかの問題には遭遇したものの、最終的にはRTX 3060 12GBで動かすことができました。

さらに設定を調整したところ、約1,850トークンの生成を約60秒、単純計算で 約30.8 tok/s まで高速化できました。

実行環境

今回使用したPCは以下です。

項目環境
OSWindows 11 + WSL2 Ubuntu
CPUIntel Core i5-12400F
メモリ32GB
GPUNVIDIA GeForce RTX 3060 12GB
NVIDIA Driver591.74
CUDA13.1
Python3.12
推論エンジンFreeToken
モデルQwen3.6-35B-A3B-NVFP4

決してLLM専用のハイエンドマシンではなく、数年前のミドルレンジGPUを搭載した一般的なPCです。

Qwen3.6-35B-A3B-NVFP4は総パラメータ数こそ35Bですが、MoEモデルです。

すべてのパラメータを常に使うわけではなく、一部のExpertだけを選択して推論するため、FreeTokenのCPUオフロードやGPUキャッシュと相性が良さそうです。

FreeTokenのインストール

今回はuvで仮想環境を作成しました。

uv venv --python 3.12
source .venv/bin/activate

FreeTokenをインストールします。

uv pip install "freetoken[accel]"

FreeTokenではCUDAカーネルのJITコンパイルも行われるため、NVIDIA DriverだけでなくCUDA Toolkitとnvccも必要です。

確認します。

nvidia-smi

Wed Sep  2 17:02:13 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 590.52.01              Driver Version: 591.74         CUDA Version: 13.1     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 3060        On  |   00000000:01:00.0  On |                  N/A |
| 36%   33C    P8             15W /  170W |    1777MiB /  12288MiB |      1%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+
nvcc --version

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2026 NVIDIA Corporation
Built on Fri_Apr_24_07:22:02_PM_PDT_2026
Cuda compilation tools, release 13.3, V13.3.33
Build cuda_13.3.r13.3/compiler.37862127_0

まずはFreeTokenでモデルを起動してみる

インストールが完了したので、まずはFreeTokenからQwen3.6-35B-A3B-NVFP4をそのまま起動してみます。

FreeTokenではft serveコマンドでOpenAI互換APIサーバーを起動できます。

ft serve \
  --model nvidia/Qwen3.6-35B-A3B-NVFP4

正常に起動すれば、デフォルトでは以下のアドレスでAPIサーバーが待ち受けます。

http://127.0.0.1:1919

OpenAI互換APIなので、最終的には以下のようなエンドポイントからモデルを利用できます。

http://127.0.0.1:1919/v1/chat/completions

……と、ここまでは簡単に動くことを期待していたのですが、実際にはモデルのロード段階でいきなりエラーになりました。

最初の問題:model.safetensors.index.jsonがない

最初にft serveを実行したところ、以下のエラーが発生しました。

FileNotFoundError:
model.safetensors.index.json

FreeTokenが参照しているHugging Faceキャッシュを確認すると、必要なモデルファイルが揃っていませんでした。

そこで、モデルを明示的にローカルへダウンロードすることにしました。

mkdir -p ~/models

hf download \
  nvidia/Qwen3.6-35B-A3B-NVFP4 \
  --local-dir ~/models/Qwen3.6-35B-A3B-NVFP4

ダウンロード後、以下のようなファイルが揃っていることを確認します。

model-00001-of-00003.safetensors
model-00002-of-00003.safetensors
model-00003-of-00003.safetensors
model.safetensors.index.json

以降はHugging FaceのモデルIDではなく、ローカルパスをFreeTokenへ渡します。

ft serve \
  --model ~/models/Qwen3.6-35B-A3B-NVFP4

これでmodel.safetensors.index.jsonのエラーは解消し、モデルのロードが開始されました。

ログも、

Loading Qwen3.5 NVFP4 experts: 100%

まで進むようになりました。

これで動くかと思ったのですが、今度はロード途中で別のエラーが発生します。

次の問題:cudaHostRegister failed

表示されたのが以下のエラーです。

RuntimeError: cudaHostRegister failed: out of memory

さらにログを見ると、

RuntimeError: cudaHostRegister failed for 0.2 GiB

となっています。

モデルファイルの問題は解決したものの、次はメモリ周りで詰まりました。

最初はRTX 3060の12GBというVRAM容量が足りないのではないかと疑いました。

しかし、調べてみるとこれはGPUのVRAM不足とは別の問題でした。

FreeTokenでは、GPUに収まりきらないMoE ExpertをCPU側のRAMへ配置できます。

その際、高速にGPUへ転送できるようにホストメモリをPinned Memoryとして登録します。

今回問題になっていたのは、

RTX 3060のVRAM不足

ではなく、

Windows / WSL2環境におけるPinned Host Memoryの上限

でした。

32GBのRAMが搭載されていても、そのすべてをCUDAのPinned Memoryとして利用できるわけではありません。

GitHub版のFreeTokenを使う

この問題について調べていくと、FreeToken側にはPinned Memory使用量を制限する仕組みが追加されていました。

そこでPyPI版だけでなく、GitHubのソースを直接使うことにしました。

git clone https://github.com/FlashML-org/FreeToken.git ~/FreeToken-src

cd ~/FreeToken-src

uv pip install -e ".[accel]"

バージョンを確認します。

ft --version

表示上は、

freetoken version 0.1.2

となりました。

一瞬、

GitHub版に切り替わっていない?

と思いましたが、パッケージ側のバージョン自体が0.1.2のため、この表示だけでは判断できません。

実際にどこのFreeTokenを読み込んでいるかは以下で確認できます。

python -c "import freetoken; print(freetoken.__file__)"

~/FreeToken-src/...を指していれば、editable installしたGitHub版を使用しています。

Pinned Memoryを制限する

FreeTokenでは環境変数でPinned Memoryの上限を指定できます。

今回は10GBに設定しました。

export FREETOKEN_PIN_BUDGET_GB=10

さらに、

export FREETOKEN_MAMBA_SSM_DTYPE=float16

も指定します。

これでWindows / WSL2側のPinned Memory上限を超えないように調整します。

まずはCPU MoEで起動

いきなり性能を追求するのではなく、まずは「この環境でモデルを最後まで起動できること」を確認することにしました。

そこで最初は安定動作を優先して、MoE ExpertをCPU側で処理します。

export FREETOKEN_PIN_BUDGET_GB=10
export FREETOKEN_MAMBA_SSM_DTYPE=float16

ft serve \
  --model ~/models/Qwen3.6-35B-A3B-NVFP4 \
  --memory-ratio 0.90 \
  --num-tokens 512 \
  --max-seq-len-override 1024 \
  --attention-backend triton \
  --moe-backend cpu \
  --expert-load serial \
  --moe-cpu-threads 8 \
  --max-running-requests 1

この構成で、ようやくFreeTokenが起動しました。

OpenAI互換APIは以下です。

http://127.0.0.1:1919/v1

試しにcurlからリクエストしてみます。

curl http://127.0.0.1:1919/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen3.6-35B-A3B-NVFP4",
    "messages": [
      {
        "role": "user",
        "content": "Pythonでフィボナッチ数列を生成する関数を書いてください。"
      }
    ],
    "max_tokens": 2048
  }'

無事に回答が返ってきました。

RTX 3060 12GB + RAM 32GBという環境で、35Bクラスのモデルが実際に動いています。

ここまで来ればひとまず成功です。

……ただ、動作中のGPUを見てみると少し気になる点がありました。

GPUメモリをあまり使っていない

Windowsのタスクマネージャーを見ると、GPUメモリ使用量は4GB台でした。

12GBあるVRAMの半分も使っていません。

そこで、WSL2側からnvidia-smiを監視してみます。

watch -n 0.5 nvidia-smi

推論中でもGPU使用率はそれほど高くありません。

原因として大きいのが、このオプションです。

--moe-backend cpu

この設定ではMoE Expertの計算をCPU側で行うため、モデルは動くもののRTX 3060のVRAMと演算性能を十分に活用できていません。

安定動作の確認には役立ちましたが、このままではRTX 3060を積んでいる意味が薄くなってしまいます。

そこで次は、CPU固定をやめてGPUをもっと使わせてみます。

--moe-backend cpuを外してGPUを使わせる

CPU固定をやめて、FreeToken側にGPUを利用させます。

export FREETOKEN_PIN_BUDGET_GB=10
export FREETOKEN_MAMBA_SSM_DTYPE=float16

ft serve \
  --model ~/models/Qwen3.6-35B-A3B-NVFP4 \
  --memory-ratio 0.90 \
  --num-tokens 2048 \
  --max-seq-len-override 4096 \
  --attention-backend triton \
  --expert-load serial \
  --max-running-requests 1

この状態で再び推論中のGPUを確認すると、

Memory-Usage
10562MiB / 12288MiB

GPU-Util
35%

Power
62W / 170W

となりました。

VRAM使用量は約10.5GB。

先ほどの4〜5GB程度から大幅に増えています。

12GBのVRAMをかなり使えるようになりました。

GPU使用率自体は35%程度なのでGPUが常にフル稼働しているわけではありません。

残りのExpertをRAM側から利用するため、CPU処理やメモリ帯域、PCIe転送なども関係していると考えられます。

それでも、CPU固定時と比べると明らかにGPUを活用できています。

推論速度が約2倍になった

実際にどの程度速くなったのか、同じプロンプトをtime付きで実行して測定してみました。

time curl http://127.0.0.1:1919/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen3.6-35B-A3B-NVFP4",
    "messages": [
      {
        "role": "user",
        "content": "Pythonでフィボナッチ数列を生成する関数を書いてください。"
      }
    ],
    "max_tokens": 2048
  }'

結果は以下です。

prompt_tokens: 25
completion_tokens: 1850
total_tokens: 1875

real    1m0.040s

約1,850トークンを60秒で生成しています。

単純計算すると、

1850 ÷ 60.04 ≒ 30.8 tok/s

約30.8 tok/sです。

CPU MoEで動かしていたときと比較すると、処理時間はおよそ半分になりました。

RTX 3060 12GBで35B級モデルが約31 tok/sという結果は、個人的にはかなり驚きました。

もちろん、これは「35B DenseモデルをRTX 3060だけで31 tok/s出している」という意味ではありません。

Qwen3.6-35B-A3B-NVFP4はMoEモデルであり、FreeTokenによってGPUとSystem RAMを組み合わせて動作しています。

概念的には以下のような構成です。

Qwen3.6-35B-A3B-NVFP4
        │
        ├─ GPU
        │   ├─ Attention
        │   ├─ Hot Expert Cache
        │   ├─ KV Cache
        │   └─ VRAM 約10.5GB
        │
        └─ System RAM
            └─ 残りのExperts
                 │
                 └─ PCIe経由でGPUと連携

それでも、12GBしかVRAMのないRTX 3060でこのクラスのモデルを実用的な速度で扱えるのは面白いところです。

OpenCodeと連携する

せっかく35B級モデルが動いたので、次は単純なチャットではなくCoding Agentとして使ってみます。

今回は軽量なCLIベースのCoding AgentであるOpenCodeを試しました。

FreeTokenからは以下のコマンドで起動できます。

ft launch opencode

FreeTokenのOpenAI互換APIを利用する形でOpenCodeが起動します。

ところが、ここでも問題が発生しました。

OpenCodeで単純に、

hello

と入力しただけなのに、

prompt is too long:
7812 tokens > 2048 maximum
(prompt + generation)

というエラーになります。

なぜhelloだけで7,812 tokensになるのか

最初は、

helloしか入力していないのに、なぜ7,812 tokens?

と思いました。

しかし、Coding AgentがLLMへ送っているのはユーザーが入力したhelloだけではありません。

裏側では、概ね以下の情報も一緒に送信されます。

  • System Prompt
  • Tool Definitions
  • Agent Instructions
  • Project Context
  • Conversation History

つまり実際には、

ユーザー入力
+
System Prompt
+
Tool Definitions
+
Agent Instructions
+
Project Context
+
Conversation History

という巨大なプロンプトになります。

ユーザー入力が数トークンしかなくても、Coding Agentを経由すると実際のリクエストが数千トークンになるわけです。

今回のFreeTokenは、

--num-tokens 2048

で起動していたため、7,812 tokensのリクエストは当然入りません。

Coding Agent用途ではContext Windowが重要

通常のチャット用途とCoding Agent用途では、必要なContext Windowがかなり違うことが分かりました。

今回のOpenCodeでは、何もしていない状態でも約7,800 tokensが必要になっています。

そのため、次は、

--num-tokens 16384
--max-seq-len-override 16384

程度まで拡張して試していく予定です。

ただし、Context Windowを大きくするとKV Cacheも増えます。

RTX 3060 12GBでは、

MoE Cacheを増やす
        ↓
GPUに多くのExpertを置ける
        ↓
推論速度を上げやすい

KV Cacheを増やす
        ↓
長いContextを扱える
        ↓
Coding Agentで有利

しかしVRAMは12GB

というトレードオフがあります。

今回、約31 tok/sまで高速化できたのはVRAMを約10.5GB使用する構成です。

OpenCode用にContext Windowを大きくすると、その分KV CacheにもVRAMが必要になります。

つまりRTX 3060 12GB環境では、

推論速度を優先してMoE Cacheを増やすのか、Coding Agent用途を優先してContext Windowを確保するのか

というバランス調整が次の課題になりそうです。

今回分かったこと

今回FreeTokenを試してみて一番印象的だったのは、

VRAM容量だけでは、動かせるモデルサイズを判断できなくなってきた

という点です。

従来ならRTX 3060 12GBで35Bクラスはかなり厳しい印象でした。

しかし、

  • MoE
  • NVFP4量子化
  • CPU RAMへのExpert Offload
  • GPU側のExpert Cache
  • Pinned Memory
  • PCIe転送

を組み合わせることで、35Bクラスでも実際に動かせます。

しかも今回の環境では約31 tok/sまで出ました。

一方で、単純にモデルをロードすれば動くわけではありません。

今回だけでも、以下のような試行錯誤がありました。

FreeTokenをインストール
        ↓
ft serveでモデルを起動
        ↓
model.safetensors.index.jsonがない
        ↓
モデルをローカルへダウンロード
        ↓
再度ft serve
        ↓
cudaHostRegister failed
        ↓
WSL2のPinned Memory制約を調査
        ↓
FREETOKEN_PIN_BUDGET_GBを設定
        ↓
CPU MoEで起動成功
        ↓
GPU使用量が少ない
        ↓
CPU固定を解除
        ↓
VRAM 約10.5GB使用
        ↓
約31 tok/sまで高速化
        ↓
OpenCode接続
        ↓
Context Window不足

振り返ってみるとなかなかの試行錯誤です。

ただ、この「どこがボトルネックなのかを調べながら少しずつ動かしていく」過程も、ローカルLLMの面白いところだと思います。

まとめ

RTX 3060 12GB + RAM 32GBという構成で、FreeTokenを使ってQwen3.6-35B-A3B-NVFP4を動かしてみました。

現時点で確認できた結果は以下です。

項目結果
モデルQwen3.6-35B-A3B-NVFP4
GPURTX 3060 12GB
System RAM32GB
VRAM使用量約10.5GB
生成トークン約1,850 tokens
生成時間約60秒
推論速度約30.8 tok/s
APIOpenAI互換API
Coding AgentOpenCodeで接続確認

特にFreeTokenのCPU/GPUオフロードを利用することで、RTX 3060の12GBという限られたVRAMでも35B級モデルを実用的な速度で動かせたのは興味深い結果でした。

ただし、OpenCodeのようなCoding Agentで実用するとなると、今度はContext Windowの問題が出てきます。

次は、

RTX 3060 12GBでQwen3.6-35B-A3BをOpenCodeのCoding Agentとしてどこまで実用的に使えるのか

を試してみたいと思います。

余談

それにしてもメモリーもグラフィックボードも高いですね・・・
RTX3060も買ったときは52,000円くらいだった記憶・・・
今使ってるやつは中古で54,000円て・・・

「PR」

↓安くない!?ほかのRTX3060 12GBモデル軒並み50,000円越えな気がするんだけど・・・追加しようかな・・・

「PR」