26万円のマイニングGPU「CMP 170HX」で LLM を本番運用する
#SRG(Service Reliability Group)は、主に弊社メディアサービスのインフラ周りを横断的にサポートしており、既存サービスの改善や新規立ち上げ、OSS 貢献などを行っているグループです。
本記事は、Alibaba から関税・輸入税込26万円で購入した NVIDIA のマイニング専用 GPU「CMP 170HX」を解錠し、VRAM 64GB で Qwen3.8-27B を本番運用するまでの記録です。
ここでいう本番運用というのは個人がローカル LLM として利用していることを指します。
26万円で手に入る VRAM 64GB の異形 GPU「CMP 170HX」
マイニング専用カード
LLM を自前ホストで運用する際、常に最大のボトルネックとなるのが GPU の VRAM 容量です。
特に 20B〜30B クラスのモデルを実用的なコンテキスト長と複数並列で動かそうとすると、民生用の 24GB カード(GeForce RTX 3090 や RTX 4090)では VRAM が急速に枯渇します。
そこで今回注目したのが、暗号資産マイニング向けに製造されていた専用アクセラレータ「NVIDIA CMP 170HX」です。
CMP 170HX は、データセンター向けのフラッグシップ GPU である NVIDIA A100 と同じ GA100 ダイ(Ampere 世代、Compute Capability 8.0、sm_80)を搭載しています。
メモリには広帯域な HBM2e が採用されており、物理的には 64GB の容量を備えています。
ただしマイニング専用として売られたカードなので、映像出力端子はありません。標準のドライバ環境ではグラフィックス機能や一部の演算が制限され、流通形態によってはメモリが 8GB しか見えないこともあります。
この制限を突破したのが「cmpunlocker」です。
cmpunlocker の具体的な使い方、動作手法については紹介しません。
筆者はこの CMP 170HX を Alibaba のベンダーから個人輸入の形で手配し、関税および輸入消費税をすべて含めて約26万円で購入しました。
VRAM 64GB という容量を正規の現行製品で確保しようとした場合、NVIDIA A100 80GB や H100、あるいは RTX 6000 Ada などのエンタープライズ向けカードが必要となり、新品・中古を問わず数十万円後半から数百万円規模の予算が要求されます。
正規のサポートも保証もありません。それを承知の上でなら、26万円で 64GB の HBM2e が手に入る選択肢は他にありません。
実際に組んで動かすまで — ソフトウェアの解錠と物理の制約

最大の壁:PCIe Gen2 x16 という帯域制約
このカードを採用するなら、接続バスの帯域だけは理解しておく必要があります。
本カードは既定では Gen1 x16(理論帯域 約4 GB/s)でリンクし、cmpunlocker が提供する早期ブート時のリトレインサービス()を通しても、上限は PCIe Gen2 x16(理論帯域 約8 GB/s)です。
しかもこのリトレインはスロット側が Gen2 の速度を広告している場合にしか成功しません。
Alibaba から購入したこの CMP170HX はハードウェアレベルでの改造(x16化)がされてい他ので難しいことなく実現できました。
現代の一般的な GPU 環境である PCIe Gen4 x16(約32 GB/s)や PCIe Gen5 x16(約64 GB/s)と比較すると、バス帯域は1/4から1/8に制限されています。
この細さは、取れる推論アーキテクチャを絞ります。
- 複数 GPU による分散推論には向かない:Tensor Parallelism(TP)や Pipeline Parallelism(PP)を用いて複数枚の GPU にモデルを分割配置する場合、レイヤーごとに大量のテンソル同期や通信が発生します。PCIe Gen2 の帯域ではバス通信が即座にボトルネックとなり、GPU を束ねてもスループットが大幅に低下します。
- ホスト RAM へのオフロードには向かない:VRAM から溢れたモデル重みや MoE(Mixture of Experts)の Expert、あるいは KV キャッシュをホストのメインメモリ(RAM)に退避し、推論イテレーションごとに PCIe 経由で転送するアプローチは、PCIe Gen2 の遅延と転送待ちによって完全に破綻します。
- モデルの初期ロードが遅い:約18〜20GB のモデル重みを VRAM に転送するだけで約45秒を要します。
つまり、CMP 170HX は「複数枚で 70B クラスを分散して動かす」「ホストメモリへ重みの一部を逃がして大モデルを動かす」といった用途には全く向いていません。
裏を返せば、「重み・KV キャッシュ・アクティベーションのすべてを1枚の 64GB VRAM に収め切る」設計なら、この欠点はほぼ消えます。
推論中に走る計算は、すべてカード上の HBM2e メモリと GA100 コアの間で完結するためです。
PCIe バスを通過するのはリクエストの入力トークン列と出力トークンのみとなり、PCIe Gen2 の細さは実推論スループットに何の影響も及ぼさなくなります。
ベンチマーク — llama.cpp vs SGLang
比較環境と注意点
本検証では、実用的なコーディング・推論能力を持つオープンモデルとして「Qwen3.8-27B」を採用しました。
Qwen3.8-27B は、全64層のうち48層が Gated DeltaNet(linear attention)、16層が full gated attention で構成されたハイブリッドアーキテクチャを持ち、ネイティブで262,144トークンのコンテキスト長を誇る 27B クラスのモデルです。
推論ホストではこれまで llama.cpp を用いて運用していましたが、推論エンジンの近代化と長文コンテキストへの完全対応を目指し、SGLang への移行を実施しました。
移行前後の比較条件は以下の通りです。
| 項目 | llama.cpp(移行前) | SGLang(移行後) |
|---|---|---|
| バージョン | v0.4.0 | 0.5.19 |
| モデル | unsloth/Qwen3.8-27B-GGUF(17.6 GB) | philbert440/Qwen3.8-27B-W4A16-AWQ (19.56 GB) |
| KV キャッシュ | q8_0 | bf16 |
| 収容設定 | ||
| コンテキスト窓 | 合計 ctx 873,472(1 slot あたり 218,368) | 262,144 |
| 投機デコード | FastMTP depth 2 | なし(理由は後述) |
| 主な実行引数 | triton attn + mamba, pytorch sampling | |
| 電力制限 | PL 250W | PL 250W |
公平な比較のために、以下の点をご留意ください。
- 同一モデルによる比較ではない(構成比較である):後述の通り GGUF 形式は SGLang で読み込めないため、safetensors 形式の AWQ 量子化モデルを採用しています。両者とも Qwen3.8-27B ですが、レシピが異なります。本ベンチマークは純粋なエンジン比較ではなく、それぞれのランタイムで組める「本番構成同士の実力比較」となります。
- 実プロンプト長を厳密に統一:両エンジンの エンドポイントでキー名(llama.cpp は 、SGLang は )が異なっており、測定スクリプトを両対応で実プロンプト長()を厳密に揃えた上で計測しています。
- prefix cache の無効化:プロンプト先頭にユニークなプレフィックスを付与し、キャッシュの影響を排除した純粋なコールド実行性能を測定しています。
1. Prefill スループットと TTFT(Time to First Token)
単発リクエスト()におけるプロンプト長別の prefill スループットと TTFT の測定結果です。
| プロンプト長 (tokens) | llama.cpp prefill (t/s) | SGLang prefill (t/s) | スループット差 | llama.cpp TTFT (ms) | SGLang TTFT (ms) | TTFT 改善率 |
|---|---|---|---|---|---|---|
| 512 | 511.4 | 989.8 | +94% | 1,463 | 478 | -67% |
| 2,048 | 764.6 | 1,649.0 | +116% | 3,457 | 1,220 | -65% |
| 8,192 | 843.6 | 1,883.8 | +123% | 10,762 | 4,328 | -60% |
| 32,768 | 821.6 | 1,528.0 | +86% | 40,649 | 21,427 | -47% |
| 131,072 | 626.3 | 785.7 | +26% | 209,987 | 166,783 | -21% |
| 245,760 | 投入不可 | 499.1 | — | — | 492,337 | — |

全プロンプト長で SGLang が上回っています。
実務で頻出する8,192トークン付近では、843.6 t/s から1,883.8 t/s へと約2.2倍(+123%)です。

初回トークン生成時間(TTFT)は全域で約20%から67%短縮されました。
8,192トークンの入力で約10.8秒かかっていた待ち時間が約4.3秒です。
対話用途では、この差がそのまま体感になります。
これまで llama.cpp の運用においては、「コールド prefill で730〜860 t/s 付近が GA100 の計算能力の限界値である」と判断していました。
しかし、その天井は GPU ハードウェアの限界ではなく、ランタイム側のカーネル実装やバッチング処理に起因するものでした。
同一の CMP 170HX カード、同一の電力制限(250W)のままでも、SGLang の Triton 最適化カーネルを用いることで約1,884 t/s までスケールすることが実証されました。
2. Decode スループットの検証
続いて、生成時(decode)のスループット比較です。
| プロンプト長 (tokens) | llama.cpp decode (t/s) | SGLang decode (t/s) | 差 |
|---|---|---|---|
| 512 | 55.63 | 70.23 | +26% |
| 2,048 | 55.06 | 69.07 | +25% |
| 8,192 | 52.94 | 65.28 | +23% |
| 32,768 | 48.83 | 54.26 | +11% |
| 131,072 | 41.84 | 31.14 | -26% |

32K トークン以下では SGLang が優位ですが、131,072トークンだけ llama.cpp が逆転しています。
32K トークンまでは SGLang が54〜70 t/s で推移し、llama.cpp を10〜26%上回っています。
131,072トークンでは llama.cpp の41.84 t/s に対し SGLang は31.14 t/s で、順位が入れ替わりました。
この逆転現象は、llama.cpp 側で FastMTP(投機デコード、depth 2)が動作しており、1イテレーションあたり平均2.3トークンを一括生成していたことによるものです。
SGLang 側を投機デコードなしで動作させている理由については、後述の検証で詳しく触れます。
3. 同時実行性能と Head-of-Line Blocking
実運用環境を想定し、8,192トークンのプロンプトを入力して256トークンを同時生成させる並列負荷テスト()を実施しました。
| 並列数 (concurrency) | llama.cpp ストリーム単体 (t/s) | llama.cpp 合計生成 (sum_tg) | SGLang ストリーム単体 (t/s) | SGLang 合計生成 (sum_tg) |
|---|---|---|---|---|
| 1 | 52.77 | 52.77 | 23.61 | 23.61 |
| 2 | 18.31 | 36.61 | 14.76 | 29.52 |
| 4 | 9.79 | 58.62 | 8.37 | 33.57 |

llama.cpp は並列度2で合計スループットが並列度1を下回ります。SGLang にこの落ち込みはありません。
「SGLang は同時実行に強い」という一般的な評価が存在しますが、投機デコードを積んだ llama.cpp と比較した場合、合計生成スループット(sum_tg)では llama.cpp が上回る結果となりました。
単一並列時の差(52.77 t/s vs 23.61 t/s)を見ても明らかなように、この差は主に FastMTP の有無に起因しています。
一方で、llama.cpp には深刻な「Head-of-Line Blocking」が存在していました。
llama.cpp は1イテレーションの中で「生成中スロットの1ステップ」と「待機中プロンプトの prefill(最大 トークン)」を直列に処理する構造を持っています。
そのため、あるスロットで巨大な prefill が開始されると、イテレーション周期そのものが数秒単位に引き延ばされ、他スロットの decode が道連れにされて極端な遅延(実測で1 t/s 程度まで低下)に陥ります。
並列度2の合計スループット(36.61 t/s)が並列度1(52.77 t/s)を下回っているのは、この干渉が原因です。
一方 SGLang 側は、合計スループットこそ低いものの並列度に対して単調に増加しており、この不連続な落ち込みは観測されませんでした。
なお、この比較は投機デコードの有無という大きな差を含んだ上での数字です。
「SGLang のスケジューラが優れている」と結論づけられるほど条件は揃っていません。
4. 実効コンテキスト窓の解放
今回の推論エンジン比較において、最も重要だった決定的な動機が「コンテキスト窓の物理的な解放」です。
llama.cpp のメモリ設計では、引数 は全スロットの合計トークン数を指します。
1スロットあたりが利用できるコンテキスト窓は、 という形で厳密に割り算されてしまいます。
64GB の VRAM 容量において、q8_0 KV キャッシュを用いて4並列()を確保した場合、設定可能な上限は でした。
これにより、1スロットあたりの窓は最大218,368トークン(安全マージンを差し引いた実効入力上限は200,704トークン)に切り縮められていました。
並列度を5に引き上げると、スロットあたりのコンピュートバッファ(約 900MiB)と KV キャッシュの増加により VRAM が 64GB を超過して起動 OOM となります。
つまり、llama.cpp では「収容力(並列度)」と「コンテキスト窓長」が完全なゼロサムのトレードオフ関係にありました。
対照的に、SGLang のメモリモデルでは が「1リクエストあたりの上限値」を定義し、収容力は が独立して管理します。
KV キャッシュ領域(Radix Cache)は全スロットで動的に共有されるため、窓を並列数で割る必要がありません。

llama.cpp は並列度を増やすほど窓が割れます。SGLang は4並列のまま245,724トークンを1リクエストで受けられます。
実機テストでは 245,724トークン のプロンプトを単一リクエストで受け切り、prefill 499.1 t/s で完走しました。
この窓は実運用の課題に直結しています。
LiteLLM の実トラフィック(累計27,343リクエスト)を分析したところ、64K トークンを超える長大リクエストが全体の23.3% を占めていました。
llama.cpp で運用していた頃は、200,704トークンを超える長文リクエストはゲートウェイの事前チェックで弾かれ、単一並列()で運用しているフォールバック専用の別ホストへ直列化して押し込まれていました。
SGLang への移行後は、CMP 170HX 単体で4並列を保ったまま、この23.3%を制限なく受けられます。
5. MTP 投機デコードの検証と不採用の理由
llama.cpp の FastMTP による高い decode 性能を SGLang でも再現すべく、SGLang の Multi-Token Prediction 機能()のパラメータ掃引検証を行いました。
静的メモリ比率()を統一し、ステップ数(steps)、探索幅(topk)、ドラフトトークン数(draft_tokens)を振って性能を測定した結果が以下の表です。
| 設定 | 512 prefill (t/s) | 差 | 8,192 prefill (t/s) | 差 | 512 decode (t/s) | 差 | 8,192 decode (t/s) | 差 | 4並列合計生成 (sum_tg) |
|---|---|---|---|---|---|---|---|---|---|
| 投機なし | 1,049.9 | — | 1,893.0 | — | 69.6 | — | 64.9 | — | 33.0 |
| s1 k1 d2 | 874.3 | -16.7% | 1,672.1 | -11.7% | 108.8 | +56% | 52.3 | -19% | 28.2 |
| s2 k1 d3 | 812.8 | -22.6% | 1,646.6 | -13.0% | 159.2 | +129% | 72.4 | +12% | 28.1 |
| s3 k1 d4 | 752.6 | -28.3% | 1,640.4 | -13.3% | 209.4 | +201% | 94.7 | +46% | 33.2 |
| s5 k1 d6 | 378.5 | -63.9% | 1,532.6 | -19.0% | 254.9 | +266% | 110.9 | +71% | 29.5 |
| s3 k2 d4 | 752.6 | -28.3% | 1,656.8 | -12.5% | 167.7 | +141% | 94.0 | +45% | 32.7 |

MTP を有効にすると短文の decode が最大約3倍(209.4 t/s)になり、代わりに prefill が段数に応じて落ちます。
短文では、s3 k1 d4(3ステップ・topk 1・ドラフト4)の decode 速度が 69.6 t/s から 209.4 t/s(+201%) へ約3倍になりました。
prefill の低下を分解すると、犯人は演算スループットの劣化ではありませんでした。ドラフト起動の固定オーバーヘッドが214 ms から363 ms へ増えた分です。
純 prefill 速度は 1,992 t/s から 1,770 t/s(-11%)で頭打ちになり、ドラフト段数を増やしても悪化しません。
固定コストはプロンプト長に依らず毎リクエスト乗るので、短文ほど比率が悪く見えます。
この-11%だけなら、decode が3倍になる取引として飲めます。
問題は長文でした。
| 入力長 (tokens) | llama.cpp decode (t/s) | SGLang 投機なし decode (t/s) | SGLang s3 decode (t/s) | SGLang s3 + ReplaySSM decode (t/s) |
|---|---|---|---|---|
| 512 | 55.6 | 69.6 | 209.4 | 199.1 |
| 8,192 | 52.9 | 64.9 | 94.7 | 93.6 |
| 32,768 | 48.8 | 54.3 | 38.9 | 32.6 |
| 131,072 | 41.8 | 31.1 | 10.1 | 11.4 |

MTP(s3)は 32K トークンを境に投機なしを下回り、131,072トークンでは 31.1 t/s から 10.1 t/s へ約1/3に落ちます。
ここで妙なのは、accept len(受理されたドラフトトークン長)が長文でも3.0〜3.4(受理率 73〜80%)を保っていたことです。
原因は Qwen3.8-27B のアーキテクチャにあります。
本モデルは64層中48層が Gated DeltaNet(linear attention)です。
SGLang の投機検証ロジックは、ドラフトごとに recurrent(回帰)状態のスナップショットを取ります。
状態量はコンテキスト長に比例するため、長文ほど検証のコピーと計算が膨らみます。
ドラフトごとの全状態スナップショットを避ける実験的オプション も試しましたが、128k の decode は11.4 t/s で、解決しませんでした。
トラフィックの23.3%が 64K を超える本番で、長文 decode が1/3になるのは飲めません。
現在の運用は「MTP 投機デコードを採用せず、投機なしで稼働させる」としています。
短文専用のホストを別に組むなら s3 は有力ですが、長文を主用途とする本機では選べません。
踏んだ罠
安定運用に乗せるまでに、どこにも書かれていない落とし穴をいくつも踏みました。
同じ構成を組む人のために記録しておきます。
1. GGUF モデルは SGLang で読めない
当初は既存の GGUF ファイルをそのまま SGLang でロードして比較する計画でした。
ところが Hugging Face transformers 5.x の に が無く、設定ファイルの構築段階で例外になります。
仮にサイドカーとして を配置しても、SGLang 内部の重みマップ解決ルーチンが (HF 側)と (GGUF 側)の表記ゆれに対応できず で停止します。
さらに SGLang には GGUF 由来の mmproj(マルチモーダルプロジェクタ)ローダが存在しないため、vision 機能が失われます。
このため、GGUF の直接利用を諦め、safetensors 形式の AWQ 量子化モデルへ移行しました。
2. SGLang 0.5.19 で MTP 重みがサイレント破棄される
SGLang 0.5.19 には、compressed-tensors 形式の MTP ヘッドをモジュールマッピングの不備で読み飛ばし、乱数初期化のまま動かす不具合があります(上流 issue #35797)。
エラーも警告も出ません。 起動ログは と正常に表示されます。
気付けるのは稼働後で、 が 1.00 付近に張り付いて投機デコードが機能しません。
Ansible のデプロイタスクでパッチを当てて、重みが載るようにしました。
3. Draft モデルのロードと VRAM 配分の罠
MTP を有効化する際、デフォルトの静的メモリ比率()のままで起動すると、ドラフトモデルのロード中に VRAM が枯渇して OOM エラーが発生します。
ドラフトモデルは本体とは独立した ModelRunner インスタンスとして起動するため、重み(約 0.85GB)だけでなく個別のコンピュートバッファと CUDA Graph 領域を消費します。
投機テスト時はこの比率を 0.80 まで引き下げて対応しました。
投機を切った本番でも同じところで刺さりました。0.9では CUDA Graph キャプチャ後の空き VRAM が約 2GB しか残らず、実トラフィックでアクティベーションの確保に失敗して24時間で118件の OOM が出ています。
現在は に落とし、約 3.2GB を動的メモリ用に空けています。
4. Chat Template の による500エラー
モデルリポジトリに同梱されている既定の には、推論パラメータ が 以外の場合に Jinja の を実行して停止する分岐が存在します。
LLM ゲートウェイ(LiteLLM など)は、OpenAI 形式の語彙( や )や Anthropic 形式の思考バジェット設定をこの単一パラメータへ集約してバックエンドへ送信します。
未加工のテンプレートのままだと、クライアントのリクエストにバックエンドが500 Internal Server Error を返します。
未知のパラメータを受け取った際、例外を発生させずに安全に既知の近い値( など)へ丸めるカスタムテンプレートを作成し、SGLang へ明示的に指定してこの問題を解消しました。
5. sm_80 に対する Hopper 前提カーネルの衝突
CMP 170HX は GA100(sm_80)です。
SGLang の最新リリースにおけるデフォルトの Attention バックエンドや Mamba バックエンド(flashinfer や fa3 など)は、Hopper 世代(sm_90+)を前提としたカーネル実装を引き当てるケースがあり、sm_80 環境では初期化時にクラッシュします。
これを防ぐため、引数として および を明示的に指定し、Ampere 世代で確実に動作する Triton 実装へ固定しています。
6. CUDA 12.0 と FlashInfer JIT の非互換
SGLang のデフォルトのサンプリングバックエンド(flashinfer)は、プロセス起動時にサンプリングカーネルを JIT(Just-In-Time)コンパイルします。
そのコンパイル引数に、CUDA 12.8 以降で新設されたオプション がハードコードされています。
ホスト環境の CUDA Toolkit が12.0である場合、nvcc コンパイラが未知のオプションエラーを出力してビルドが失敗し、モデルのロードが完全に成功した直後にプロセスが死にます。
このクラッシュはヘルスチェックエンドポイントが一瞬200を返した後に発生するため、正常起動したと誤認しやすい罠です。
JIT コンパイルを要求しない を明示指定することで、この問題を回避しました。
7. の不整合によるツールのサイレント消失
Qwen3.8-27B のテンプレートが出力するツール呼び出しの形式は、以下のような XML ライクな記法です。
これに対し、パーサ引数として を指定してしまうと、パーサ内部で の中身を しようとして が発生します。
この際、API レスポンス自体は HTTP 200 で返り、 フィールドが空になって生テキストがそのまま流れるため、外見上は動いているように見えてツール呼び出しだけが静かに壊れます。
XML 記法を前提とした正しいパーサ を指定することで、ツール呼び出しが正常に構造化されることを確認しました。
26万円の選択肢
64GB というメモリ量が現実に可能にしたこと
今回の成果は一つです。27B クラスのモデルを、VRAM 64GB の1枚だけで、長文コンテキストと4並列を両立させて動かせました。
モデル重み(W4A16 で約 19.6GB)を載せても、まだ 44GB 以上が残ります。
この空きをすべて KV キャッシュと CUDA Graph、動的アクティベーションに回せるので、並列数を増やしてもコンテキスト窓が割れません。245,724トークンのリクエストがそのまま入ります。
電力は 250W に制限しています。170W から 250W へ上げてもスループットの伸びは+9.2%なので、排熱や電源容量が厳しい環境なら 170W〜200W に絞る手もあります。
買うか迷っている人へ
ここまでを踏まえて、向き不向きを整理します。
買うべき:
- 20B〜30B クラスの LLM を、長いコンテキスト(数万〜数十万トークン)かつ複数並列で、GPU 1 枚に収めて運用したい人
- カーネルモジュールのパッチビルド、Proxmox/LXC のデバイスノード制御、ドライババージョンのピン留めを自分で面倒みられる人
- 20万円台の予算で 64GB の HBM2e を確保したい人
買わないほうがいい:
- 複数枚を束ねて Tensor Parallelism で 70B 超を分散推論したい人(PCIe Gen2 x16 がボトルネックになります)
- VRAM に載らない重みや KV キャッシュをホスト RAM へオフロードしたい人(同じく PCIe 帯域で破綻します)
- パッケージマネージャからの導入とメーカーサポートを前提にしたい人
終わりに
マイニング専用として作られ、用途を失いかけたカードが、cmpunlocker とひと手間かけたインフラ設計で本番の推論ノードになりました。
鍵は、PCIe Gen2 という弱点を避ける設計に徹したことです。1枚に全部載せる構成なら、この弱点はスループットに出てきません。
ローカル LLM を自前で運用する人の参考になれば幸いです。
SRG にご興味ありましたらぜひこちらからご連絡ください。
採用情報 - CyberAgent SRG #ca_srg
