OOMから262Kへ:8GB VRAMでローカルにQwen3-Coder 30Bを動かす

Dev.to / 2026/5/5

💬 オピニオンDeveloper Stack & InfrastructureSignals & Early TrendsTools & Practical UsageModels & Research

要点

  • 著者は、コード実験のたびに有料クラウドLLMへ依存するのをやめたいとして、コード生成・デバッグ・リファクタリング・リポジトリQ&Aといった用途でローカルコーディングモデルがどれほど現実的かを検証した。
  • 大きなモデルをローカルで動かす際の最大の障壁は、モデル実行中のVRAM(GPUメモリ)の逼迫であり、モデル重み・ルーティングされるエキスパート・KVキャッシュ・コンテキストウィンドウ・計算バッファの競合がすぐ問題になると結論づけている。
  • Windows上の一般的なPC環境(NVIDIA RTX 3060 Ti、8GB VRAM)で、量子化済みGGUFを用いてQwen3-Coder-30B(約30B級のMoEコーディングモデル)を動かし、実用に足るコンテキスト長(262K)を狙った。
  • Qwen3-CoderがMoEであることを活かし、常時アクティブな部分はGPUに保持しつつ、ルーティングされたエキスパートの多くをシステムRAM側に置くことで推論を成立させる方針を試した。
  • 結果としてローカル実行は可能だと述べているが、動く構成に到達するまでには複数の試行錯誤が必要だったとも説明している。

最近、毎回のコーディング実験のたびに有料のクラウドモデルに依存するのに疲れてきました。

クラウドモデルは素晴らしいです。速いし、便利だし、たいてい非常に高性能です。

ただし、いつもの「お荷物」も一緒に付いてきます。コスト、レート制限、インターネットへの依存、プライバシーに関する疑問、そして「真面目なコーディングのワークフローはすべて、誰かのGPUをレンタルしているだけだ」という小さな感覚です。

そこで、ローカルLLMをちゃんと検証し始めました。

カジュアルに「小さめのチャットモデルなら動く?」という話ではありません。

知りたかったのは次のことです:

  • ローカルのコーディング用モデルは今、どれくらい賢いのか?
  • 実際のコード生成、デバッグ、リファクタリング、リポジトリのQ&Aに役立つのか?
  • OpenAI互換APIを通じて、エディタのエージェントに組み込めるのか?
  • そして最も重要なのは、何が結局それを「役に立つ」状態にできないのか?

十分に調べた結果、答えはかなり明白になりました。

壁はハードウェアです。

より具体的に言うと、VRAMです。

モデルファイルは用意できます。ランタイムも用意できます。Dockerも用意できます。スクリプトも用意できます。ですが、モデルの重み、ルーテッド・エキスパート、KVキャッシュ、コンテキストウィンドウ、計算バッファがGPUメモリを奪い合い始めると、すべてがあっという間に厳しくなります。

それで私は考えました。

実用的な回避策はあるのか?

幸運なことに、非常に普通のコンシューマ向け構成が手元にありました。

ハードウェアはとても普通です:

  • GPU: NVIDIA RTX 3060 Ti
  • VRAM: 8 GB
  • OS: Windows
  • RAM: 約32 GB
  • CPU: Intel i5-14600KF

これは4090搭載機ではありません。ワークステーションでもありません。まさに、多くの人が「7Bモデルを動かして済ませよう」と言いそうなマシンです。

そこで、私はチャレンジにしました:

コンシューマ向けのハードウェアで、実際に役に立つだけのコンテキスト量を確保しつつ、ちゃんとした30Bのコーディングモデルをローカルで動かせるか?

狙ったモデルは野心的でした:

Qwen3-Coder-30B-A3B-Instruct

具体的には、次のGGUFです:

unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF

私が使った量子化(quant)は:

Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf

これは「30B級」のコーディング特化MoEモデルです。重要なのはMoEです。Mixture of Experts(エキスパートの混合)。総パラメータ数は大きいですが、トークンごとに有効になるのは一部のエキスパート重みだけです。

これにより、ローカル推論の戦略全体が変わります。

密な30Bモデルなら、8GBのVRAMから始めるのは私なら避けます。ですが、コンパクトなMoEのコーディングモデルでは、話が面白くなります:

常に動いている部分は高速に保ち、ルーテッド・エキスパートは主にシステムRAMに置いて、それでも使える速度が得られるのか?

短い答え:はい。

長い答え:いくつも試行錯誤が必要でした。

最初に退屈な監査

何か巨大なものをダウンロードする前に、マシンを確認しました。

これは当然のように聞こえますが、ローカルAIのセットアップはこれを飛ばすと急に混乱します。

私は次を確認しました:

  • Windowsのバージョン
  • GPUの型番
  • NVIDIAドライバ
  • nvidia-smi をPowerShellで
  • WSL2
  • Docker Desktop
  • DockerのGPUパススルー
  • CUDAコンテナからGPUへアクセスできるか
  • システムRAM
  • ディスク容量
  • CPU

DockerのGPUパススルーは動きました:

docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi

つまり、最初にまっすぐ行ける道はこれでした:

Docker + llama.cpp CUDA server

最初のサーバー用イメージ:

ghcr.io/ggml-org/llama.cpp:server-cuda

また、インターネット上のどんなコマンドも信じる前に、llama-server --help も確認しました。

これが、繰り返し出てくるテーマになりました。

そのフラグが存在すると決めつけないでください。バイナリに聞きましょう。

モデルのダウンロード

対象のモデルリポジトリは:

unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF

ダウンロード前に、実際のファイル名を確認しました:

Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf

ダウンロードされたファイルサイズは:

17,665,334,432 bytes

すべてを1つのローカルなプロジェクトフォルダ配下に置きました:

local-qwen-coder/
  models/
  scripts/
  configs/
  docs/

グローバルな謎フォルダはなし。「この17GBのファイルはどこに行った?」みたいな瞬間もなし。

小さな勝利です。

最初の本当の詰まり:Dockerのメモリ

最初の深刻な問題はGPUではありませんでした。

Dockerのメモリでした。

Windowsでは約32GBのRAMが使えますが、Docker DesktopはLinux VMに対してRAMを約16GBしか公開しておらず、さらにスワップは4GBです。

これは重要でした。というのも、私の最初の直感が次のようにすることだったからです:

--no-mmap
--mlock

これは、後でディスクからページフォルトさせるのではなく、モデルをRAMにロードしたいときには良い発想です。

しかし、そのコンテナには十分なRAMがありませんでした。

プロセスが殺されました。

終了コード:

137

Docker inspectで確認すると:

OOMKilled=true

なので、最初の修正は華やかなものではありません:

Dockerのパスではmmapを有効のままにする。

「技術的により良い」フラグは、実際のコンテナのメモリ上限には合っていませんでした。

安定したデフォルトのllama.cppサーバーを動かす

標準のllama.cppを使うDockerでは、モデルはロードされ、OpenAI互換のエンドポイントとして提供できました。

ベースURL:

http://127.0.0.1:8080/v1

重要なMoEフラグは:

--cpu-moe

これにより、MoEのエキスパート重みをCPUに置きます。

モデルは使えるようになりましたが、まだ十分に速くはありません。

ベースライン:

モード プロンプト評価 生成
--cpu-moe ~2.78 tok/s ~13.38 tok/s

生成はまあ良いです。ですがプロンプト評価がつらい。

次のつまみが登場しました:

--n-cpu-moe N

これは最初のN個のMoE層をCPUに置き、GPU上に置けるエキスパートの重みを増やします。

Nを下げるほど、通常はGPU常駐が増え、速度が上がり、VRAMの余裕(headroom)が減ります。

そこでベンチマークしました。

MoEオフロードのチューニング

役に立つ結果は次のとおりです:

モード 使用VRAM 空きVRAM プロンプト評価 生成
--cpu-moe 4388 MiB 3637 MiB 2.78 tok/s 13.38 tok/s
--n-cpu-moe 48 4392 MiB 3633 MiB 2.51 tok/s 13.83 tok/s
--n-cpu-moe 46 5224 MiB 2801 MiB 6.03 tok/s 18.75 tok/s
--n-cpu-moe 44 5893 MiB 2132 MiB 38.36 tok/s 29.40 tok/s
--n-cpu-moe 42 6568 MiB 1457 MiB 44.49 tok/s 30.26 tok/s
--n-cpu-moe 40 7265 MiB 760 MiB 51.63 tok/s 32.49 tok/s
--n-cpu-moe 38 7664 MiB 361 MiB 53.14 tok/s 33.64 tok/s

テストした中で最も速かった値は:

--n-cpu-moe 38

しかし、それだと空きVRAMが約361 MiBしか残りませんでした。

きつすぎる。

実用上の勝ち手は:

--n-cpu-moe 40

これにより、空きVRAMが約760 MiBの状態で生成が約32.49 tok/sになりました。

この時点で、ローカルのコーディング用バックエンドは良い感じになっていました。

ただ、私は実際に欲しかったものを持っていませんでした。

実際の目標:262Kコンテキスト

Qwen3-Coder-30B-A3Bは、長いコンテキストをネイティブにサポートしています。

モデルのメタデータには次が表示されていました:

n_ctx_train = 262144

そこで問題になったのは:

8 GBのVRAMで、実際に262Kコンテキストとして動かせるのか?

標準のDockerビルドでは、思った通りにそこへ到達できませんでした。

通常のllama.cppの型を使ってKVキャッシュの精度を下げれば、次のようにできます:

q8_0
q4_0
iq4_nl

ただ、見ていた動画ではTurboQuantの話をしていました。

それが決定的な違いでした。

そしてここで、私はほぼ自分をだましそうになりました。

私はまだ実際にはTurboQuantを使っていなかった

標準のDockerイメージを確認しました:

docker run --rm --gpus all ghcr.io/ggml-org/llama.cpp:server-cuda --help

サポートされているKVキャッシュの型は:

f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1

turbo3なし。

turbo4なし。

tbq3_0なし。

tbq4_0なし。

つまり答えは明確でした:

標準のランタイムはTurboQuantを実行していない。

TurboQuantはモデル重みの量子化ではありません。GGUFモデルファイルの変更は不要です。

ランタイムがKVキャッシュをどう保持するかを変えるのです。

同じモデル。

別のランタイム。

別のキャッシュ形式。

これが本当の転機でした。

TurboQuant対応ランタイムの発見

Windows向けのCUDAランタイムビルドを見つけました:

atomicmilkshake/llama-cpp-turboquant-binaries

ダウンロードしたファイル:

llama-turboquant-triattention-win-cu13-x64.zip

それを次の場所に展開しました:

runtimes/turboquant/win-cu13

そして試しました:

.\llama-server.exe --help 

即座に失敗しました。

有用な出力はありません。

プロセスの終了コードは:

0xc0000135

通常これは、WindowsでDLLが見つからないことを意味します。

READMEでも、想定される問題が確認できました:

cublasLt64_13.dll

このビルドは、CUDA 13のcuBLASLtランタイムを必要としていました。

その1つのDLLのためだけに、CUDA Toolkit全体をグローバルにインストールしたくありませんでした。

そこで公式のNVIDIA cuBLASホイールを取得しました:

python -m pip download nvidia-cublas==13.4.0.1 --only-binary=:all:

次に展開しました:

cublasLt64_13.dll

そしてllama-server.exeの隣にあるローカルのランタイムフォルダへコピーしました。

その後:

.\llama-server.exe --help

動きました。

そして今度は、キャッシュの型として次が含まれていました:

turbo2, turbo3, turbo4

どちらでも:

--cache-type-k
--cache-type-v

これがきっかけで、セットアップは「通常のllama.cppチューニング」から「本当のTurboQuantの道筋」へ切り替わりました。

最終的な262K起動

最終的なコマンドの形は:

.\runtimes\turboquant\win-cu13\llama-server.exe `
  -m .\models\qwen3-coder-30b-a3b\Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf `
  --alias qwen3-coder-30b-a3b-turbo-262k `
  --host 127.0.0.1 `
  --port 8080 `
  --jinja `
  --gpu-layers all `
  --cpu-moe `
  --flash-attn on `
  --ctx-size 262144 `
  --cache-type-k turbo4 `
  --cache-type-v turbo3 `
  --parallel 1 `
  --batch-size 256 `
  --ubatch-size 64 `
  --temp 0.3 `
  --top-p 0.8 `
  --top-k 20 `
  --repeat-penalty 1.05 `
  --fit off `
  --cache-ram 0 `
  --no-mmap `
  --mlock

私は強制しました:

--fit off

llama.cppがこっそりコンテキストを縮めて、それでいてすべて問題ないふりをするのを避けたかったからです。

読み込むなら、262144を本当に読み込ませる必要がありました。

そして、そうなりました。

The proof

ランタイムのログにはこう表示されました:

llama_context: n_ctx         = 262144
llama_context: n_ctx_seq     = 262144
llama_context: n_batch       = 256
llama_context: n_ubatch      = 64

KVキャッシュの行が本当の証拠でした:

llama_kv_cache: size = 5664.00 MiB (262144 cells, 48 layers, 1/1 seqs), K (turbo4): 3264.00 MiB, V (turbo3): 2400.00 MiB

ロード後のVRAM:

7525 MiB used
500 MiB free

かなりギリギリです。

それでも読み込みました。

次に、OpenAI互換エンドポイント経由で小さなコーディング用プロンプトを送りました。

応答が返ってきました。

所要時間:

prompt eval time = 1125.54 ms / 46 tokens = 40.87 tokens per second
eval time        = 3672.56 ms / 107 tokens = 29.13 tokens per second

これが勝因でした。

Qwen3-Coder-30B-A3B。

262Kコンテキスト。

8 GB VRAM。

ローカルのエンドポイント。

同じモデルファイル。

TurboQuantのKVキャッシュ。

The repeatable script

TurboQuantの起動を次にラップしました:

scripts/run-qwen-coder-turboquant.ps1

つまり、反復可能なコマンドは次のとおりです:

.\scripts\run-qwen-coder-turboquant.ps1 -Replace

標準のDockerフォールバックも引き続きあります:

.\scripts\run-qwen-coder-docker.ps1 -Profile daily-fast

Dockerルートは、より安全な日次プロファイルに役立ちます。

TurboQuantルートは、フルコンテキストのプロファイルです。

Important caveats

これは魔法ではありません。

262KプロファイルはVRAMがかなり逼迫します。

私のRTX 3060 Tiでは、おおよそ500 MiBしか空きがありません。つまり:

  • 単一クライアントのみ
  • 複数のエディタエージェントを同時に動かさない
  • GPUヘビーなアプリを閉じる
  • 32Kプロファイルより融通が利かないと考えてください

また、このセットアップが実際のコーディング作業で優れていることは、まだ証明できていません。

インフラは動きます。

エンドポイントは動きます。

コンテキストはロードされます。

スモークテストは通ります。

ただし次のテストは、実開発作業です:

  • 実際のリポジトリをリファクタリングできるか?
  • UnityのC#を、まともにデバッグできるか?
  • 複数ファイルのコンテキストを扱っても、話がそれないか?
  • 長いセッションにわたって安定できるか?

それが次のマイルストーンです。

What I learned

大きな学びは、ローカルAIインフラは単にこういうものではないということです:

download model
run server
profit

デフォルト設定がボトルネックになることが多いです。

このセットアップでは:

  • MoEの配置が重要でした。
  • Dockerのメモリ制限が重要でした。
  • KVキャッシュの形式が重要でした。
  • ランタイムのビルドが重要でした。
  • llama-server --helpがとても重要でした。

30Bモデル自体が全ての問題ではありません。

ランタイム戦略が問題でした。

そして場合によっては、「不可能」と「動く」の違いは、1つ足りないDLLと、正しいKVキャッシュタイプです。

Repo

私はセットアップを、次の内容でGitHubリポジトリとして公開しました:

返却形式: {"translated": "翻訳されたHTML"}
  • 起動スクリプト
  • ベンチマークのメモ
  • トラブルシューティングのドキュメント
  • クライアント設定
  • 再現可能なセットアップのメモ

GitHubリンク:

GitHubロゴ UpayanGhosh / local-qwen-coder-turboquant

8GB VRAMでのコーディングワークフロー向け Local Qwen3-Coder 30B TurboQuant セットアップ

Local Qwen Coder TurboQuant セットアップ

8GBのNVIDIA GPU上で、Qwen3-Coder-30B-A3B-Instruct をローカルの「コーディング専用」OpenAI互換バックエンドとして実行するための、実用的なWindows向けセットアップ手順とスクリプトです。

このリポジトリは、安定した標準の llama.cpp Docker セットアップから、フルコンテキストの TurboQuant KV-cache ランタイムへ至るまでの道のりを記録しています:

  • RTX 3060 Ti、8 GB VRAM
  • Windows
  • Qwen3-Coder-30B-A3B-Instruct GGUF
  • MoE の専門家(expert)における CPU/GPU 常駐(residency)チューニング
  • OpenAI互換のローカルエンドポイント
  • TurboQuant KVキャッシュで 262144 コンテキストを検証済み

含まれているもの

  • バックエンドの起動とテストのためのPowerShellスクリプト
  • Cline、Continue、Roo Code、OpenCode、および汎用のOpenAI互換クライアント向けのクライアント設定
  • ベンチマークのメモ
  • TurboQuant の調査とトラブルシューティングのメモ
  • ビルドの物語を説明する LinkedIn 投稿下書き

含まれていないもの

このリポジトリは意図的に次のものを追跡しません:

  • GGUFモデルファイル
  • CUDA/ランタイムDLL
  • ダウンロード済みのホイール/zip
  • ログ
  • ローカルキャッシュ

それらのファイルは大きいおよび/またはマシン固有です。.gitignore を参照してください。

主な結果

検証済み TurboQuant プロファイル:

Context: 262144
KV cache: K=turbo4, V=turbo3
VRAM: ~7525 MiB 使用 /

このリポジトリには、GGUFモデル、CUDA DLL、ホイール、またはダウンロード済みのバイナリは含まれません。それらは大きすぎて、かつマシン固有のためです。

終わりにひとこと

これは最初、こういう発想でした:

「役に立つローカルのコーディング用バックエンドを作れる?」

そして次に、こうなりました:

「8GB VRAMでフルの262Kコンテキストを動かせる?」

最初のバージョンは、ただ動くだけでした。

最終バージョンは、実際に目標を達成しました。

私はこれを勝ちだと思っています。