RTX 5080 16GBでの長文コーディング:Qwen3.6-35B-A3Bは128Kで30t/s(新規は89t/s)・品質低下なし

Reddit r/LocalLLaMA / 2026/5/1

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical UsageModels & Research

要点

  • 著者は、RTX 5080 16GB上でQwen3.6-35B-A3Bを長文(ロングコンテキスト)コーディングに適用し、Claude Codeのようなワークフローをローカルで動かして有料のホステッドツールへの依存を減らすことを目的にベンチマークを行いました。
  • Anthropic互換の /v1/messages へ接続するローカル llama.cpp サーバ構成を用い、128Kコンテキスト時に約30トークン/秒、プロンプトが「フレッシュ」な条件では約89トークン/秒のスループットを報告しています。
  • 本記事はリーダーボード目的ではなく、単一のコンシューマGPUで長文コードエージェント運用が現実的に成立するかを重視した実運用ログとして提示されています。
  • Windows 11やCUDA 12.9.1必須(他のCUDAで誤出力やクラッシュが発生)、計算専用のRTX 5080などの環境詳細を記述し、なぜ特定のバージョンが問題になるのかも明らかにしています。
  • 性能面ではTurboQuant系CUDAフォークをベースにNsight Computeでボトルネックを特定し(特定カーネルがメモリよりレジスタ制約で支配的)、小さなコンパイル/インライン化/ベクトル化の改善を積み上げて深い長文での速度を押し上げたと述べています。

自分のコーディングエージェントのワークフローのうち、どれだけをローカルに移して、ホスト型ツールにずっと払い続けるのを減らせるのかを見たかったんです。

もう1つの後押しがありました。Anthropic自身の4月23日のポストモーテムで、3月/4月までの間に製品レイヤーで回帰が起きていたことが確認されたからです。ローカルモデルなら、ベンチマークしたものがそのまま得られます。

制約はコンテキストでもありました。最低でも65K〜128Kの範囲で、実用に耐えるものが必要でした。

昼の大半、RTX 5080 16GBが手つかずで空いていました。Qwen3.6はコーディング面で十分な評価を得ていて、真面目に試す価値があるように思えました。Claude Codeは、ローカルのAnthropic互換/v1/messagesエンドポイントを指すことができます(Unslothがここに良いガイドを用意しています)。そこで方針はシンプルにしました。つまりClaude Codeのワークフローは維持しつつ、モデル提供だけをローカルのllama.cppに切り替える。

これはランキング(リーダーボード)のベンチマークではありません。1台のコンシューマーGPUで長いコンテキストのコーディングエージェントを実用にしようと試したときの、現場ログです。

ハードウェア

  • RTX 5080 16GB(sm_120、コンシューマー向けBlackwell GB203)
  • Ryzen 9700X(8c/16t)
  • 96GB DDR5
  • Windows 11
  • iGPUがディスプレイ駆動、5080は演算専用
  • PCIe Gen 5 x16

重要な注記:私が最終的に使ったフォークにはCUDA 12.9.1が必須です。CUDA 13.xはゴミ出力になり、13.1はMMQカーネルでセグフォールトします。痛い目を見て学びました。

フォーク

メインラインのllama.cppは動かしませんでした。最初はMadreag/turbo3-cudaから始めました(TheTom/llama-cpp-turboquant系譜のTurboQuant CUDAフォーク。TurboQuantはKVキャッシュにTCQ / Trellis-Coded Quantizationを追加し、1値あたり約3.125ビット)。私がパッチしたフォークはこちら:craftogrammer/llama.cpp-adaptive-turboquant。低いコンテキストでは問題なく、だいたい64K前後まではうまく動きましたが、狙っていた長いコンテキストでは速度が急激に落ちました。なぜなのか理解したくて、d=65Kの密な27Bに対してncu(Nsight Compute)でデコードをプロファイルしました。mul_mat_q<IQ3_S>が、プロファイルされたデコード時間の43%を食っていました。さらに掘り下げると、スレッドあたり254レジスタ、理論上のoccupancyは約12.5%、DRAMスループットは7%未満でした。つまりカーネルはレジスタ束縛で、メモリ束縛ではありません。cp.async、プリフェッチ、パイプライン化のようなトリックは効きません。そこで、コミットしたカーネル変更を2つ(共有メモリへのバックトレース、アラインメント修正)と、ローカル実験を1つ(MMQタイルロードでcp.asyncを試してテスト後に戻す)行い、さらにそれぞれきれいに再ベンチしました:合計で+0.16%。結論は無。小さめのインライン化やベクタ化による勝ちを積み上げると(V-dequantのインライン、バイトペアのベクタ化、minBlocksの増加、スコアラーのインライン)、d=0で+0.7%から、d=64Kで+13%までスケールしました。個々は小さいけれど、深さ方向で積み重なって効いていました。

また、測って却下した2つのアイデアも試しました。think-anchorメカニズム(推論トークンにアンカーしたfp16のシンクレンジ。測定値は−0.28%、TG vs 無効で、出荷しないことにしました)と、疎なVのしきい値を調整するランタイムノブ(測定値は−32%デコード回帰、20.4 vs 29.8 t/s。アップストリームで検証済みの定数に戻しました)。どちらも実際に時間を使ったので、負の結果も含めて正直な全体像として言及します。

その過程でsm_120のptxas問題にもぶつかりました。FA vecカーネルではoccupancyのヒントを後退させる必要がありました(minBlocksを高くするとコンパイラがクラッシュ)。また、いくつかのTCQヘルパーは__noinline__のままにしておく必要があり、特定のTUでは--ptxas-options=-O0が必要でした。見落としがちな点を1つ挙げると、prefetch.global.L2はsm_120のSASSでCCTL.E.PF2に落ちます。PRFではなく、CCTLでgrepしてください。

これらの調査結果の上に立って、フォークを適応的KVモード選択、MoEオフロード調整、RTX 5080 16GB向けのタイトなVRAM対策でパッチしました。

最初の試み:Qwen3.6-27B dense

このモデルは16GB向けの自然な選択に見えました。Hybrid Transformer-Mambaで、KVキャッシュを持つのは16/64層だけです。メモリ計算も紙の上では問題なさそうでした。

そして低いコンテキストでは、うまくいきました。NEO-CODE IQ3_M量子化で、空のコンテキスト時に40 t/s。実用可能。

ただ、その後に深さスイープを回して、コンテキストが伸びると実際にどうなるか確認しました:

コンテキスト深さ デコード(t/s)
0 40.5
16K 17.4
32K 10.6
65K 6.0
128K 3.2

128Kで3.2トークン毎秒。実際には、会話が長くなるとClaude Codeはとにかく辛いほど遅く感じました。その理由を、深さベンチを回した後に説明できることがわかりました。カーブが、まさに私が体感していたものと一致していました。

私はこの調整に何日も費やしました。ubatchサイズとスレッド数の9通りの組み合わせを総当たりで試しました。9つすべての間でのばらつきは0.46 t/sしかありませんでした。デコードは完全に帯域(バンド幅)に縛られていて、調整する余地がありませんでした。

IQ3_Mは品質面での選択というより、収まるための唯一の選択肢でした。では16GBで131Kコンテキストだと、量子化の状況はどう見えるかというと:

Quant ファイルサイズ 131Kで収まる?
NEO-CODE IQ3_M(DavidAU提供) 12.0 GiB はい
UD-Q3_K_XL 13.5 GiB はい(きつい)
IQ4_XS 14.3 GiB いいえ(約1.6 GiBオーバー)
Q4_K_S 14.8 GiB いいえ
IQ4_NL 15.0 GiB いいえ
Q4_K_M 15.7 GiB いいえ
Q5 / Q6 19+ GiB 5090の領域

Q4クラス以上の量子化は、実用的なコンテキストで密な27B + 16GBでは手が届きません。IQ4_XSに必要なのはCPUへ約7層をオフロードすることでしたが、それではデコードが約5 t/sまで落ちてしまい、目的が台無しになります。したがって、私はIQ3_Mの品質で固定され、しかも深さカーブのせいでエージェントのループがつらい状態でした。

ついにMoEの道を試す決め手になったのは、具体的なコーディングテストです。どちらのモデルにも、レストランの会計割り勘(整数のpaisa、合計の厳密不変条件、4つのテストケース)を解かせました。denseの27Bは、personSubtotalsではなくpersonSubtitlesを書いてしまい、3回とも実行できないコードになっていました。35B-A3B MoEは、4つすべてのテストに通るクリーンなBigIntコードを書き、しかも54%多くのトークンを生成しているのに、壁時計時間はより短く済みました。この瞬間に、denseルートで頑張って改善するのをやめることにしました。

なぜMoEの道を試したのか

16GBに完全に収まらないモデルでも、一部のエキスパートをシステムRAMにオフロードすれば、長コンテキストのコーディングで役に立つのでしょうか?

私はこの条件(コンシューマーBlackwell、16GBのGPUが1枚、長いコーディングエージェントのコンテキスト、部分的なMoEオフロード)について、十分な数値データを見たことがありませんでした。だから私は、「35B合計」という数字を自動的に無理だと決めつけるのではなく、エンドツーエンドで実際に試しました。

コンテキスト深さ 27B dense(旧パス) 35B-A3B MoE(最終パス)
0 40.5 91.8
16K 17.4 76.9
32K 10.6 54.1
65K 6.0 46.2
128K 3.2 30.4

制御された「単一変数」の比較ではありません。モデル、量子化、オフロード分割、そしてKVレイアウトを変更しました。結論は実用的です。denseはエージェントのコンテキストでは使えず、MoEはチューニング後に使えるようになりました。

オフロードのバランスがすべて

UD-Q4_K_XL GGUF(20.81 GiB)で、d=16Kにおける ncmoe のスイープ:

ncmoe tg32(t/s) 注記
40(全CPU) 36.4 ベースライン
20 53.2
16 58.9 このファイルでのスイートスポット
12 36.1 VRAMの崖にヒット
8 5.9 壊滅的なスピル

その「崖」は鋭いです。スイートスポットは、GGUFファイルのサイズと、KV割り当て後に使えるVRAMの量の関係に依存します。

APEX-I-Compact(クレジット: mudler(Hugging Face上))が勝ったのは、そのファイルが小さかった(20.8 GiBではなく16.1 GiB)ため、ncmoe=16ではなくncmoe=8を使えたからです。これによりPCIeへの負荷が十分に下がって効きました:

コンテキスト深さ UD-Q4_K_XL(ncmoe=16) APEX-I-Compact(ncmoe=8)
0 51.6 92.3
16K 58.9 75.9
32K 49.3 64.2
65K 39.4 48.0
128K 31.3

APEX-I-Quality(Q6_K、21.25 GiB)もテストしました。ncmoe=20が必要で、VRAMのスラッシングを避けるためです。そのオフロードレベルでは、共有テストハーネス上で、同じ品質のUDと同程度の速度でした。どの軸でも、どちらのキーパーにも勝てませんでした。削除しました。

僕のコーディング・ベンチマークは間違っていた(あなたのもそうかもしれない)

当初、UDの方が明らかに品質が良いと思っていました:APEX-I-Compactが29/32だったのに対し、33/34でした。差は6.5パーセントポイント。

しかし実際に何が起きているかを見ると、状況が変わりました。各モデルは自分自身のテストスイートを作り、自分自身の実装も作っていたのです。壊れているテストを4つ含む19テストを書いたモデルは15/19、きれいな11テストを書いたモデルは11/11でした。このベンチマークは (実装品質 × テスト品質) を採点して、それを実装品質と呼んでいました。

見つけた具体的なバグ:

  • APEX-I-Compactには本当の実装バグがありました:購読(サブスクリプション)により b.priorityoptions.priority として保存されるため未定義になっていました。ソートのコンパレータがNaNを返し、ソートが発生しませんでした。
  • APEX-I-Qualityは、ノーオペのハンドラが配列を埋めるはずだったケースで、ハンドラの削除後に宣言された配列を対象にしていました。テストが壊れており、実装ではありませんでした。
  • プロンプトに、スナップショットを「emit中に取る」セマンティクスについて矛盾した条項があり、各モデルが違う解釈をしたものの、いずれも一貫していました。

プロンプトを修正した後、サンプリングを決定的に固定(temp=0、seed=42)し、3つすべてを1つの共有11テスト・ハーネスで採点しました:

モデル デコード t/s 共有ハーネス
UD-Q4_K_XL 64.5 11/11
APEX-I-Compact 86.7 11/11
APEX-I-Quality 53.4 11/11

品質のギャップは消えました。速度のギャップは消えませんでした。

ローカルでコーディング評価をするなら:共有のテストハーネスを使い、サンプリングを固定し、プロンプトを曖昧さなくしてください。 自作のテストは品質のシグナルになりません。

「すべてを圧縮する」トラップ

セットアップから得られた発見の1つで、他でも試す価値があるかもしれない点があります。KVの圧縮を増やしても、長いコンテキストでは必ずしも速くならない、ということです。

フォーク側で異なるKVキャッシュのレイアウトをテストしました。「TCQで全ての注意(attention)レイヤーを圧縮する」から、「一部のK+Vレイヤーをq8_0に昇格させる」までの範囲です。ここでは正確なモードマップは意図的に載せません。フォーク固有で、しかもまだ変わっているためです。ただ、結果の形はこうです:

KVレイアウト d=0 d=16K d=32K d=65K d=128K
全て圧縮 86.8 55.2 42.3 28.3 16.6
ハイブリッド(いくつかのレイヤーをq8_0) 91.8 76.9 54.1 46.2 30.4

d=128Kでは、ハイブリッドレイアウトが全圧縮よりほぼ 2倍速いです。

なぜそうなるのか、確たる説明はまだありません。作業仮説は、TCQのコードブック参照(lookup)のオーバーヘッドが、Kの読み取り数に対して線形に増えるというものです。より深いコンテキストでは、読み取りごとのコストをより多く払うことになります。最もアクセスされるレイヤーをq8_0に昇格させることで、最も効くところでそのコストを回避します。原因が何であれ、測定結果は明確です。もしTCQや圧縮KVの方式を動かしているなら、d=0ではなく、実際に使うコンテキスト深さでテストしてください。

レイアウトを手動で選ぶのを避けるため、オートセレクタを書きました。キャッシュ確保時に ggml_backend_dev_memory で空きVRAMを調べ、KVサイズを、アロケータが使うのと同じ ggml_row_size の式で各レイアウトごとに見積もり、空きVRAMから計算ピーク用マージン(1 GiB)を引いた下で収まる範囲の最も攻めた(aggressive)モードを選びます。検証済み:予測1510 MiB、実際の確保は1509.88 MiB。より大きいカードでは攻めたままです。VRAMがきつい場合は自動的にフォールバックします。手動で制御したい場合は TURBO_LAYER_ADAPTIVE=N で上書きしてください。

現在のところ

日常運用の設定:

  • モデル: Qwen3.6-35B-A3B APEX-I-Compact(16.10 GiB)
  • フォーク: craftogrammer/llama.cpp-adaptive-turboquant, CUDA 12.9.1, sm_120
  • オフロード: CPU上に8つのエキスパートレイヤー(--n-cpu-moe 8
  • コンテキスト: 131072(128K)
  • KV: turbo3_tcq(自動選択されたハイブリッドレイアウト)
  • サンプリング: temp=0.6, top_p=0.95, top_k=20

Claude Codeは ANTHROPIC_BASE_URL=http://127.0.0.1:8080 を通じてこの設定に接続します。実リクエスト1件からのサーバ側ログ:1078トークンのプロンプトプリフィルが1582 t/s、538トークンのデコードが90.7 t/s。

VRAMは、持続的な128Kデコード中に約13.3 / 16.0 GBに収まっています。きついですが、スピルは起きません。

プロンプトキャッシュ(--cache-ram -1)は、最初のターン以降のエージェントループを大幅に高速化します。23Kトークンのプロンプトのコールドプリフィルは1787 t/sで約13秒かかる一方、同様のプレフィックスを持つ次のターンではプレフィルがその差分のみ再実行され、419〜569 t/sになります。ハイブリッドMamba+Attentionでの落とし穴として、プレフィックスの不一致(動的なタイムスタンプやリクエストIDでさえ)だとSSM状態を部分的に巻き戻せないため、必ず全量の再プリフィルが発生します。

現実世界で回帰が起きたときのフォールバック:ncmoe=16のUD-Q4_K_XLで、約62 t/s。共有ハーネス上では同等の品質でした。

天井はハードウェア

PCIe Gen 5 x16は、MoEのデコード中に約89%まで飽和します(理論上の上限が約63 GB/sのところ、56〜61 GB/sのバースト)。SM利用率は93〜97%です。この状況で、明らかなチューニングの余地は残っていないように見えます。

d=65Kで39〜48 t/s、d=128Kで約30 t/s。これがこのハードウェアが出せる性能です。長いコンテキストで50 t/s以上を安定して出すには、より多くの巧妙なカーネルではなく、より多いVRAMが必要です(CPU上のエキスパート数を減らせば、PCIeトラフィックが減るため)。そうなったらMSRPで5090を待ちます。

16GBカードで試したいなら

要点だけ言うと:APEX-I-Compactがうまくいったのであれば、それを参考に、約16 GiBのGGUFでQwen3.6-35B-A3Bを入手し、目標のコンテキスト深さに合わせてncmoeをスイープしてください(d=0ではありません)。最適点は狭く、ファイルサイズに依存します。私の5080では、16 GiBのファイルはncmoe=8、21 GiBのファイルはncmoe=16でした。

TurboQuant由来のフォークで圧縮KVを使っている場合は、実際の稼働深さでテストしてください。全圧縮は128Kではハイブリッド配置より約2倍遅くなることを見つけました。d=0のベンチマークではそれは分かりません。

もう一つ、ちょうど公開されたので先回りしておく価値がある点:私はメインラインのNVFP4(b8967)を、出荷された当日に同じ条件でベンチを取っていました。MoE+offloadでは、フォークが39〜51 t/sなのに対しメインラインは15〜16 t/s。GitHubの