Qwen3.8-27B 相当が VRAM 10GB の RTX 3080 で 64 tok/s — 普通は載らない 27B を ternary 量子化 Bonsai 2 で動かした
Qwen3.8-27B の GGUF は BF16 で 54.7GB、4bit(Q4_K_M)でも 16.5GB あります。3bit でも最小で 10.9GB なので、VRAM 10GB の RTX 3080 には、3bit 以上はひとつも載りません。
| 量子化 | ファイルサイズ | 10GB に載るか |
|---|---|---|
| BF16 | 54.7 GB | × |
| Q8_0 | 29 GB | × |
| UD-Q4_K_M | 16.5 GB | × |
| UD-IQ3_XXS | 10.9 GB | × |
| UD-IQ2_XXS | 7.27 GB | ○(2bit 級は FP16 比 約14ポイント落ちとされる) |
| Bonsai 2 PQ2_0 | 7.21 GB | ○(約2ポイント落ち、ベンダー公称) |
※ Bonsai 2 以外のサイズは unsloth/Qwen3.8-27B-GGUF の値です。
2bit まで落とせば載りますが、今度は品質が大きく落ちます。Bonsai 2 の PQ2_0 は、UD-IQ2_XXS とほぼ同じサイズで、品質の落ちを約2ポイントに抑えています。
実際に 3080 で動かしたところ、オフロードなし・全層 GPU で 64 tok/s 出ました。
| ファイルサイズ | VRAM 使用 | decode | prefill | |
|---|---|---|---|---|
| Bonsai 2 27B PTQ1_0 | 5.95 GB | 8.4 GB | 53〜54 tok/s | 430〜660 tok/s |
| Bonsai 2 27B PQ2_0 | 7.21 GB | 9.6 GB | 64〜65 tok/s | 830〜1250 tok/s |
いずれも -c 65536、KV は q8_0 です。
前半は動かすまでの手順と罠、後半は VRAM の上限と、3080 で速かったパッキング(PQ2_0)の話です。
検証環境
| 項目 | 内容 |
|---|---|
| ホスト | Proxmox VE 9 |
| ゲスト | Ubuntu 26.04(VM、20 vCPU、80GB RAM) |
| CPU | Core i7-12700K |
| メモリ | DDR4-3600 |
| GPU | RTX 3080 10GB(PCIe パススルー) |
| ドライバ | 610.57.04 / CUDA UMD 13.3 |
| CUDA Toolkit | 13.3 |
GPU はパススルーなので、ゲストから 10240MiB がまるごと使えます。
80GB の RAM は、同じ VM で別のモデル(MoE のエキスパートをシステム RAM に逃がす構成)を動かすための割り当てです。Bonsai 2 は全層を GPU に載せるので、システム RAM を大量に使う構成ではありません。
Bonsai 2 とは
PrismML が公開した ternary 量子化モデルです。Qwen3.8-27B の重みを {−1, 0, +1} の3値に圧縮したもので、実効 1.72〜2.13 bits/weight。9分の1のサイズで品質の 98.2% を保つとされています。ベースと同じくハイブリッドアテンション(約75%が linear attention)で、ツール呼び出しと thinking に対応しています。
- モデル: https://huggingface.co/prism-ml/Ternary-Bonsai-2-27B-gguf
- fork: https://github.com/PrismML-Eng/llama.cpp
本家 llama.cpp では動きません。 PrismML の fork が必要です。量子化タイプが独自(PTQ1_0 / PQ2_0)で、Hadamard 回転の実行時処理が要るためです。
llama.cpp(fork)のビルド
prebuilt バイナリも配布されていますが、私が試したときにはダウンロードリンクが切れていたので、ソースからビルドしました。踏んだ罠を順に書きます。
罠1: リリースページのリンクが 404
最新タグのリリースページには全プラットフォーム分のリンクが並んでいますが、実体が揃っていませんでした。9バイトの Not Found が落ちてくるので、ダウンロード後は必ずサイズを確認してください。
ログ
$ curl -LO https://github.com/PrismML-Eng/llama.cpp/releases/download/prism-b10687-5d80cff/llama-prism-b10687-5d80cff-bin-linux-cuda-12.8-x64.tar.gz
100 9 100 9 0 0 26 0
$ cat llama-prism-b10687-5d80cff-bin-linux-cuda-12.8-x64.tar.gz
Not Found
罠2: prebuilt が CUDA ランタイムを同梱していない
一つ前のリリースには実体があったので展開したところ、libcudart.so.12 が無くて起動しません。ドライバの libcuda.so.1 は見えていて、ランタイム側だけが欠けている状態です。CUDA 12.x を入れたくなかったので、ソースからビルドしました。
ログ
$ ./bin/llama-server --version
./bin/llama-server: error while loading shared libraries: libcudart.so.12: cannot open shared object file: No such file or directory
$ ldd bin/llama-server | grep -i -e cuda -e "not found"
libggml-cuda.so.0 => /home/masa/bonsai/bin/libggml-cuda.so.0
libcudart.so.12 => not found
libcublas.so.12 => not found
libcuda.so.1 => /usr/lib/x86_64-linux-gnu/libcuda.so.1
ソースビルド(CUDA 13.3)
mkdir -p ~/bonsai && cd ~/bonsai
TAG=prism-b10687-5d80cff
curl -LO https://github.com/PrismML-Eng/llama.cpp/archive/refs/tags/$TAG.tar.gz
tar -xzf $TAG.tar.gz && cd llama.cpp-$TAG
sudo apt install -y build-essential cmake libcurl4-openssl-dev
cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=86 -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc) --target llama-server llama-cli
-DCMAKE_CUDA_ARCHITECTURES=86 は 3080(Ampere)向けの指定です。自分のカードの分だけ生成するので、ビルド時間が大幅に短くなります。40系なら 89、50系なら 120 です。
GCC 15.2 + CUDA 13.3 の組み合わせで、ホストコンパイラの指定なしに通りました。
罠3: CUDA Toolkit を入れたら nvidia-smi が死ぬ
Toolkit のインストールに巻き込まれてドライバのユーザ空間ライブラリだけが 610.57 に上がり、カーネルモジュールが 610.43 のまま、という状態になりました。
$ nvidia-smi
Failed to initialize NVML: Driver/library version mismatch
NVML library version: 610.57
再起動でも直りますが、モジュールの入れ替えでも済みます。
sudo systemctl stop <GPUを使っているサービス>
sudo rmmod nvidia_uvm nvidia_drm nvidia_modeset nvidia
sudo modprobe nvidia
nvidia-smi # KMD と NVML が同じバージョンになっていればOK
nvidia_drm が使用中で rmmod が失敗する場合は、素直に再起動してください。
モデルの取得
Ubuntu 26.04 は PEP 668 で pip install が弾かれるので、hf CLI を入れずに curl で直接落としました。
cd ~/bonsai
curl -L -o Ternary-Bonsai-2-27B-PQ2_0.gguf \
https://huggingface.co/prism-ml/Ternary-Bonsai-2-27B-gguf/resolve/main/Ternary-Bonsai-2-27B-PQ2_0.gguf
ls -la Ternary-Bonsai-2-27B-PQ2_0.gguf # 数バイトなら 404 の本文です
起動
./llama.cpp-$TAG/build/bin/llama-server \
-m Ternary-Bonsai-2-27B-PQ2_0.gguf \
--host 0.0.0.0 --port 8080 --jinja \
-np 1 -ngl 99 -fa on -c 65536 \
--temp 1.0 --top-p 0.95 --top-k 20 \
-ctk q8_0 -ctv q8_0 --alias bonsai2-27b
サンプリングパラメータは thinking mode の推奨値(temperature 1.0 / top_p 0.95 / top_k 20)です。
--jinja は必須です。 これが無いと /v1/chat/completions が標準の tools 配列を受け付けず、構造化された tool_calls を返しません。Open WebUI の Native function calling で使うなら外せません。
-np 1 も入れてください。 既定は 4 スロットで、-c の値がスロット間で分割されます。-c 65536 を指定しても、1 本あたり 16384 しか使えなくなります。
# -np 4 の場合
initializing, n_slots = 4, n_ctx_slot = 16384, kv_unified = 'false'
# -np 1 の場合
initializing, n_slots = 1, n_ctx_slot = 65536, kv_unified = 'false'
Open WebUI から使う
Settings → Connections → OpenAI API に追加します。
- Base URL:
http://<サーバのIP>:8080/v1 - API Key: 認証を無効にできない UI なら任意の非空文字列(llama-server は
--api-key未指定なら検証しません)
Open WebUI がコンテナの場合、localhost ではコンテナ内を見に行ってしまうので、ホストの IP を指定してください。
VRAM 実測(10GB での上限探し)
PTQ1_0(5.95GB)でコンテキスト長を振った結果です。
| 設定 | VRAM | 残り |
|---|---|---|
-np 1 -c 32768(KV fp16) |
8,083 MiB | 2.1 GB |
-np 1 -c 65536 -ctk q8_0 -ctv q8_0 |
8,457 MiB | 1.7 GB |
-np 1 -c 98304 -ctk q8_0 -ctv q8_0 |
9,705 MiB | 0.5 GB |
-np 1 -c 131072 |
OOM | — |
PQ2_0(7.21GB)は -c 65536 で 9,615 MiB でした。
98304 は起動はしますが残りが 0.5GB を切るので、ディスプレイ出力を繋いでいる構成だと実行中に落ちる余地があります。常用するなら 65536 が安全側です。
落ち方は2種類ありました。KV 本体が足りない場合と、q8_0 KV なしで 65536 を試したときに出た rs cache 不足です。rs cache は recurrent state のキャッシュで、Qwen3.8-27B は約75%が linear attention(Gated DeltaNet)なので、通常の KV とは別にこの領域が要ります。
OOM 時のエラー(2種)
# -c 131072(KV 本体が足りない)
ggml_backend_cuda_buffer_type_alloc_buffer: allocating 4352.00 MiB on device 0: cudaMalloc failed: out of memory
alloc_tensor_range: failed to allocate CUDA0 buffer of size 4563402752
llama_init_from_model: failed to initialize the context: failed to allocate buffer for kv cache
# -c 65536、KV fp16(rs cache が足りない)
ggml_backend_cuda_buffer_type_alloc_buffer: allocating 149.62 MiB on device 0: cudaMalloc failed: out of memory
llama_init_from_model: failed to initialize the context: failed to allocate buffer for rs cache
PTQ1_0 と PQ2_0、どちらを選ぶか
冒頭の表の通り、3080 では PQ2_0 が全面的に速いという結果でした。decode で約20%、prefill で約2倍です。
Reddit では「PTQ1_0 は CUDA 向けに最適化されているので NVIDIA ユーザーは PTQ1_0、AMD は Q2_0」という助言が流れていますが、その根拠は「PTQ1_0 は dot kernel が無いので AMD では 13 tok/s まで落ちる」という AMD 側の観察です。AMD で遅い理由が、そのまま NVIDIA で速い理由になるわけではありません。
PrismML 公称の throughput 表を見ると、PTQ1_0 が有利なのは Ada 世代と L4 で、Ampere の A100 では PQ2_0 が勝っています。3080 は Ampere なので、実測はこの並びに揃った形です。
NVIDIA か AMD かではなく、世代で分かれるという理解の方が実態に近いと思われます。30系をお使いなら、両方試す価値があります。
参考までに、Reddit の投稿者が 7900XTX(Q2_0 / Vulkan)で prefill 約900 tok/s、decode 60 tok/s と報告しています。帯域で上回るカード(960 vs 760 GB/s)に対して 3080 が並んでいるので、CUDA カーネルの実装は素直に効いているようです。
ただし、長考タスクの所要時間は別の話
同じ論理パズル(25人の正直者・嘘つき、正直者はちょうど11人)を両方に解かせたところ、decode が速い PQ2_0 の方が完了までは遅いという結果になりました。
| decode | 生成トークン | 所要時間 | 正誤 | |
|---|---|---|---|---|
| PTQ1_0 | 53 tok/s | 37,533 | 14分30秒 | 正解 |
| PQ2_0 | 64 tok/s | 46,486 | 16分 | 正解 |
| (参考)Python 総当たり | — | — | 10.3秒 | — |
トークン数で割るとどちらも約12分相当で、時間差はほぼ生成量の差です。temperature 1.0 でサンプリングしている以上、走るたびに生成量は変わるので、パッキングの差というより試行のばらつきと見るのが妥当かと思います。
言えるのは、推論モデルでは tok/s がタスク完了時間をほとんど説明しないということです。日常の検索や要約では PQ2_0 の速さがそのまま効きますが、長考させる用途では思考量の方が支配的になります。
他のモデル(Ornith 1.5 の 9B / 35B-A3B)で同じパズルを解かせた比較は、前回の記事にまとめています。問題文もそちらの付録に載せてあります。
実運用での挙動
ツール呼び出しは一発で通りました
Open WebUI の Native function calling(Workspace > Tools に search / fetch を関数登録)で、system prompt の指示だけで素直に発火します。reasoning_content が本文と別フィールドで返るので、<think> を剥がす処理も不要です。
curl での確認例
curl -s http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model":"bonsai",
"messages":[{"role":"user","content":"仙台の今日の天気を調べて"}],
"tools":[{"type":"function","function":{
"name":"search_web",
"description":"Search the web",
"parameters":{"type":"object","properties":{"query":{"type":"string"}},"required":["query"]}
}}]
}' | jq '.choices[0].message'
{
"role": "assistant",
"content": "",
"reasoning_content": "The user is asking me to check today's weather in Sendai (in Japanese). Let me search for this information.",
"tool_calls": [
{
"type": "function",
"function": {
"name": "search_web",
"arguments": "{\"query\":\"仙台 天気 今日 天気予報\"}"
}
}
]
}
実タスクでも、検索結果のスニペットに紛れていた誤った数字(別の月の土曜日の気温)に飛びつかず、「検証が必要」と判断してページ本文を取得してから答える、という振る舞いが見られました。
弱点
単位変換を間違えます。 「56°F は約33.3℃」と出力しました。正しくは13.3℃です。同じ回答内の 48°F→8.9℃、42°F→5.6℃ は正確なので毎回ではありませんが、「最高33.3℃・平均8.9℃・最低5.6℃」という自明に破綻した並びをそのまま出しており、出力のサニティチェックが働いていません。
日本語の量化表現を読み違えることがあります。 論理パズルで「2人以上」を「3人以上」と解釈し、その読みでは解が出ないことを原文の誤記のせいにして、条件の1つを書き換えて辻褄を合わせました。同じ「2人以上」が他に3箇所あるのに、そこは疑っていません。
思考に中国語が混ざることがあります。 「推导」「步骤」といった単語が本文側に出てきました。
どれも1〜2回の観測なので頻度は分かりませんが、数値を扱うタスクでは検算を挟む前提で組んだ方が安全そうです。
まとめ
- 普通は 10GB に載らない Qwen3.8-27B 相当を、GPU だけで動かせます。 品質の落ちは約2ポイント(公称)です
- 3080(Ampere)なら PQ2_0 を選んでください。 decode 約20%、prefill 約2倍速いです
- コンテキストは 65536 が常用ラインです。98304 まで起動はしますが余裕がありません
--jinjaと-np 1は忘れずに
8GB のカードでも PTQ1_0(5.95GB)なら載るはずです。KV を q8_0 に落とせば、それなりのコンテキストも取れると思われます。
3080 以外のカードで試された方は、カード / パッキング / decode / prefill / VRAM をコメントで教えていただけると、世代ごとの傾向が見えてきそうです。
測定の留保
- 各条件1〜2回の測定です。統計的な主張はできません
- PTQ1_0 / PQ2_0 の優劣は 3080(Ampere、sm_86)での結果です。他の世代では変わる可能性があります
- VRAM 実測値はディスプレイ出力を接続した状態のものです。ヘッドレスならもう少し余裕が出ます
- 長考中の GPU は 339W / 340W に張り付き、78℃(サーマルスロットル点より下)で14分間フルロードでした。消費電力は
nvidia-smiの瞬時値です - 品質の「約2ポイント落ち」はベンダー自己申告で、第三者検証は見ていません
- 「2bit 級は約14ポイント落ち」も比較対象は Q2_XXS とされており、Unsloth の Dynamic 量子化(UD-IQ2_XXS など)が同じだけ落ちるかは確認していません。Unsloth 側は自社の Dynamic 量子化が他より高精度だと主張しています
- 画像入力(mmproj)は未検証です。
BONSAI_MMPROJ_CPU=1で projector をシステム RAM に置き、約0.9 GiB を KV に回せるとされています - thinking は
xhighが既定のまま測っています。mediumに落とすと思考量が減る代わりに品質が落ちる、とされていますが未検証です - fork のタグは頻繁に更新されます。本記事は
prism-b10687-5d80cff時点のものです
