この記事の要点
- 12GB に何が載るか— 3B から 14B クラスまでは 1 枚にほぼ丸ごと載り、速度はパラメータ数の順におおむね並んだ。速度が 2 つに割れるのは、載りきらなくなったあとである。
- 載らなくなると何が起きるか— あふれた後の速度は 2 種類に割れ、常駐率が 41〜54% に収まる 8 本では MoE と dense が 約 8〜14 倍離れた。
- 次に何を見るか— dense と MoE のどちらかは、GGUF ヘッダの expert_count を読むのが確実だった。同じ architecture 文字列でも中身が違う実例がある。
この結果が言える範囲: VRAM 12GB のカード1枚 (OCuLink 接続) で 2026年9月9日に測った値。断らないかぎりコンテキスト8192・Q4_K_M。速度はこのカードの値であり、同じ 12GB でも世代が違えば変わる。学習・同時実行・回答の質・画像生成の限界は測っていない。MoE と dense の差は 10 本の観測 (常駐率の近い行で比べたのは 8 本) によるもので、作りだけが原因と確認したわけではない。
12GBの挙動を分けるのは、大きさの境目と、その先のモデルの作り
VRAM 12GB では、まず「収まるか、あふれるか」で挙動が二つに分かれる。収まっている間は、生成速度はパラメータ数なりにおおむね順に落ちていくだけである (使用量が近くても 1.4 倍ほど違う組はある)。収まった 12 本は dense と MoE を分けて比べていないので、この範囲で作りによって速度が変わるかどうかは測っていない。この 12 本にかぎれば、速度の順序はパラメータ数だけで読めた。
分かれ目はその先にある。ここでいう常駐率は、モデルが必要とする量のうち何割が VRAM 上に載っているかで、100% ならカードだけで動いている。常駐率が 100% を割ったモデルを、常駐率が近い行だけ取り出して並べると、MoE と dense の生成速度は 約 8〜14 倍離れていた。同じ「12GB に載りきらないモデル」でも、待てる速度で返ってくるものと、実質的に止まって見えるものがある。10 本を並べたときに速度がきれいに割れたのは、パラメータ数の順ではなくモデルの作りだった。
ここでいう MoE (Mixture of Experts) は、モデルの中身を expert と呼ぶ単位に分けておき、1 トークンごとにそのうち一部だけを使う設計を指す。GGUF ヘッダに記録される expert_count はこの routed expert の数で、1 トークンあたりに選ばれる数とは別である (モデルによっては共有される expert も別に存在する)。dense はこの分割を持たず、毎回すべての重みを通る。
この記事で測ったのは、ローカル LLM と動画生成の両方である。LLM では何が収まるか・収まらなくなると何が起きるか・コンテキストをどこまで伸ばせるか、動画生成ではどこで失敗するかを、同じ 1 枚のカードで測った。以下、LLM の話を先に置き、そのあと動画の話に移る。
なお、これから 12GB のカードを用意する段階であれば、価格の安いカードから入る場合の見取り図はRTX 3060 12GB で始めるローカルAIの入門記事にまとめてある。実際に使い始めてから何に詰まるかは、Reddit の投稿から 12GB カードの実力を読み解いた記事のほうが具体的である。
12GBに収まるモデルと、収まらないモデル
まず容量の話から見る。コンテキスト8192・Q4_K_M という条件で、12GB のカード1枚に収まったのは 3B から 14B クラスの 12 本で、カードの使用量は 3,317〜10,142 MiB だった。うち 11 本は常駐率 100% で、phi4:14b だけが 95% と 100% には届いていない。
| モデル | 常駐率 | カードの使用量 | 生成速度 |
|---|---|---|---|
| llama3.2:3b | 100% | 3317 MiB | 165.51 tok/s |
| phi4-mini:3.8b | 100% | 3883 MiB | 134.09 tok/s |
| gemma3:4b | 100% | 4056 MiB | 117.64 tok/s |
| Ornith-1.0-9B | 100% | 5512 MiB | 67.18 tok/s |
| mistral:7b | 100% | 5548 MiB | 93.67 tok/s |
| llama3.1:8b | 100% | 5852 MiB | 84.77 tok/s |
| deepseek-r1:8b | 100% | 6210 MiB | 78.86 tok/s |
| qwen3.5:9b | 100% | 6824 MiB | 72.19 tok/s |
| gemma4:12b | 100% | 8600 MiB | 47.35 tok/s |
| gemma3:12b | 100% | 9206 MiB | 48.85 tok/s |
| qwen3:14b | 100% | 10050 MiB | 47.08 tok/s |
| phi4:14b | 95% | 10142 MiB | 41.82 tok/s |
測定条件: VRAM 12GB のカード1枚 (RTX 4070 SUPER・OCuLink 接続) / Ollama 0.32.3 / すべて Q4_K_M / コンテキスト 8192 / think=false / 生成 512 トークン / 乱数の種 42 / 3回計測の中央値 / 常駐率は Ollama の /api/ps が返す VRAM 上の量と合計量の比 / 使用量は nvidia-smi がカードについて報告する値。
コンテキスト 8192 での値である。コンテキストを伸ばせばキャッシュのぶん増える。量子化を変えれば重みのぶんが変わる。使用量はカード全体の値で、モデル単体の所要量ではない。生成速度はこのカードで測った値で、同じ 12GB でも別のカードでは変わる。
次が、同じ条件で常駐率が 81% 以下まで下がった 10 本である。境目の phi4:14b (95%) は収まったモデルの表に残した。生成速度の順に並べ、作り (dense か MoE か) は各モデルの GGUF ヘッダの expert_count で判定した。
| モデル | 作り | 常駐率 | 生成速度 |
|---|---|---|---|
| Ornith-1.0-35B | MoE (256) | 48% | 54.14 tok/s |
| qwen3.6:35b-a3b | MoE (256) | 41% | 45.89 tok/s |
| gemma4:26b | MoE (128) | 51% | 45.38 tok/s |
| qwen3.5:35b-a3b | MoE (256) | 41% | 44.71 tok/s |
| qwen3-coder:30b | MoE (128) | 53% | 40.9 tok/s |
| deepseek-coder-v2:16b-lite-instruct-q4_K_M | MoE (64) | 81% | 26.93 tok/s |
| codestral:22b-v0.1-q4_K_M | dense | 68% | 10.02 tok/s |
| qwen3.5:27b | dense | 54% | 4.93 tok/s |
| qwen3:32b | dense | 47% | 4.07 tok/s |
| gemma4:31b | dense | 42% | 3.93 tok/s |
測定条件: 上の表と同じ条件で、常駐率が 100% を割ったモデルだけを生成速度の順に並べたもの。括弧内は GGUF の expert_count の値。
ここで測ったのは MoE が 6 本、dense が 4 本である。常駐率の範囲は完全には揃っていない (MoE 41〜81% / dense 42〜68%)。同じ条件で測った値だが、モデルごとにパラメータ数も量子化後の重みの大きさも違うため、作りだけが差を生んでいると確かめたわけではない。
容量について言えることは単純で、今回測った Q4_K_M のモデル群では、1 枚に丸ごと載った最大は 14B クラスだった。量子化の方式や内部構造が変われば所要量も変わるので、12GB 一般の上限がここだという意味ではない。これは常駐率の話であって、使えるかどうかの上限ではない。収まらなくなった後の速度を見ると、あふれた状態でも MoE の 6 本は 26.93 tok/s 以上で回っている。2 枚で 32GB 分にしたときに何が変わるかはVRAM 32GB の実測記事で扱っている。もう一段上の容量で何が変わるかは、VRAM 12GB と 16GB を同じ条件で比べた記事と、16GB で何が収まるかをモデル別に並べた記事で扱っている。
収まらなくなった後は、2 種類に分かれる
収まらなかった 10 本の並びは、dense と MoE できれいに割れている。上から 6 本がすべて MoE で 26.93〜54.14 tok/s、下の 4 本がすべて dense で 3.93〜10.02 tok/s だった。境目にあたる deepseek-coder-v2 と codestral の間で 2.7 倍に開き、この並びの中で隣り合う行としては最も大きい段差になっている。
ただし上の 10 本は常駐率の範囲が揃っていない。そこで、常駐率が 41〜54% に収まる 8 本だけを取り出して比べた。MoE 5 本は 40.90〜54.14 tok/s、dense 3 本は 3.93〜4.93 tok/s で、両者は 約 8〜14 倍離れていた。ほぼ同じ割合しか VRAM に載っていない状態でも、この差は残る。
この比較で言えないこと: これは 10 本の観測であり、作りのほかにパラメータ数・量子化後の重みの大きさ・世代が同時に違っている。作りだけが差を生んでいると確かめたわけではなく、対照実験は行っていない。
同じモデルの中でコンテキストだけを変えた比較では、2 本とも常駐率が下がるにつれて速度も下がった。dense の codestral は常駐 71% から 49% へ下がる間に 11.32 から 6.86 tok/s へ動き、MoE の deepseek-coder-v2 は 81% から 68% へ下がる間に 26.93 から 23.97 tok/s へ動いた。ただしこの 2 本は下げ幅そのものが違う (dense は 22 ポイント、MoE は 13 ポイント) うえ、動かしたコンテキストの倍率も 8 倍と 2 倍で違う。落ち方の速さを作りで比べたことにはならず、この 2 本から読めるのは向き (枠を広げて常駐率が下がると、速度も下がる) までである。
実際にモデルを選ぶときは、12GB からあふれる大きさのものを使うなら、MoE は選択肢として残り、dense は同じ扱いにはならない、という順序になる。あくまで 12GB に収まらなくなった条件での観測であって、MoE が dense より速いという一般則ではない。あふれた分の読み出しがカードの外を通る条件での値でもあり、接続の仕方が違えば差の大きさは変わりうる。
denseとMoEを見分ける
ここまで dense と MoE で分けてきたが、あるモデルがどちらなのかは、配布されているタグ名や説明文を見ても確実には分からない。配布元の資料に明記されている場合もあるが、手元にあるファイルがどちらなのかを確かめるなら、ファイルの中身を読むのが早い。手元で確認できた範囲で確実だったのは、GGUF ファイルのヘッダに expert_count というキーの値を直接読む方法である。1 以上なら MoE、キーが無いか 0 なら dense として扱える。手元の 25 タグについて、この方法でヘッダを読んで確認した。
expert_count は GGUF に記録された routed expert の数で、1 トークンあたりに選ばれる数とは別である。選ばれる数は expert_used_count という別のキーに入っており、手元で確認した 4 本ではいずれも 6 か 8 だった。さらにモデルによっては共有される expert が別にあり、こちらは expert_shared_count に記録される (deepseek-coder-v2:16b-lite は expert_count 64 に対して expert_shared_count 2、expert_used_count 6)。収まらなかったモデルの表で括弧に入れた 256 や 128 や 64 は、expert_count の値である。
タグ名や architecture 名が当てにならないことは、実例で確認できる。gemma4:26b は expert_count が 128 の MoE、gemma4:31b は expert_count を持たない dense で、どちらも architecture の文字列は同じ gemma4 だった。同じ文字列で両方が存在する以上、architecture 名では作りを判別できない。
もう一例が deepseek-coder-v2:16b-lite-instruct-q4_K_M で、architecture は deepseek2、expert_count は 64 の MoE だった。タグ名にも architecture 名にも MoE を示す語は入っていない。手元で確認した範囲でタグ名から作りが分かったのは、-a3b のように活性パラメータ数が名前に入っている場合だけだった。
つまり確かめるキーは 1 つだけで済む。
次のスクリプトが判定に使うのは expert_count の 1 つだけだが、実装はヘッダのメタデータを先頭から順に読むため、そこに入っているキーは一度すべて走査する。標準ライブラリだけで動き、テンソル本体は読まないので、読む量はファイル全体ではなくメタデータの分で済む。引数に GGUF ファイルのパスを 1 つ以上渡すと、architecture と判定結果を 1 行ずつ出力する。
"""GGUF ファイルのヘッダだけを読み、そのモデルが MoE かどうかを判定する。
判定材料はメタデータキー `*.expert_count`(例: `<architecture>.expert_count`)で、
1 以上の値が入っていれば MoE、キーが無い(または 0)なら dense と表示する。
あわせて `general.architecture` も出力する。標準ライブラリのみで動作し、
テンソル本体は読まないので、読む量はモデルのテンソル本体のサイズには依存しない。
"""
import os
import struct
import sys
GGUF_MAGIC = b"GGUF"
# GGUF のメタデータ値の型 ID
(T_UINT8, T_INT8, T_UINT16, T_INT16, T_UINT32, T_INT32, T_FLOAT32,
T_BOOL, T_STRING, T_ARRAY, T_UINT64, T_INT64, T_FLOAT64) = range(13)
# 型 ID -> struct のフォーマット(GGUF はリトルエンディアン)
SCALAR_FORMATS = {
T_UINT8: "<B",
T_INT8: "<b",
T_UINT16: "<H",
T_INT16: "<h",
T_UINT32: "<I",
T_INT32: "<i",
T_FLOAT32: "<f",
T_BOOL: "<?",
T_UINT64: "<Q",
T_INT64: "<q",
T_FLOAT64: "<d",
}
def read_exact(f, size):
"""size バイトを必ず読む。足りなければヘッダが壊れていると見なす。"""
data = f.read(size)
if len(data) != size:
raise ValueError("ヘッダの途中でファイルが終わっている")
return data
def read_scalar(f, fmt):
return struct.unpack(fmt, read_exact(f, struct.calcsize(fmt)))[0]
def read_string(f):
"""GGUF の文字列は「長さ(uint64) + UTF-8 バイト列」。"""
length = read_scalar(f, "<Q")
return read_exact(f, length).decode("utf-8", errors="replace")
def read_value(f, value_type):
"""型 ID に応じて値を 1 個読む。array は要素型を読んでから再帰。"""
if value_type in SCALAR_FORMATS:
return read_scalar(f, SCALAR_FORMATS[value_type])
if value_type == T_STRING:
return read_string(f)
if value_type == T_ARRAY:
elem_type = read_scalar(f, "<I")
count = read_scalar(f, "<Q")
return [read_value(f, elem_type) for _ in range(count)]
raise ValueError("未知のメタデータ型 ID: %d" % value_type)
def read_metadata(path):
"""GGUF ヘッダのメタデータ部だけを辞書で返す。"""
with open(path, "rb") as f:
if read_exact(f, 4) != GGUF_MAGIC:
raise ValueError("先頭 4 バイトが 'GGUF' でない(GGUF ファイルではない)")
read_scalar(f, "<I") # version
read_scalar(f, "<Q") # tensor_count(今回は使わない)
kv_count = read_scalar(f, "<Q")
metadata = {}
for _ in range(kv_count):
key = read_string(f)
value_type = read_scalar(f, "<I")
metadata[key] = read_value(f, value_type)
return metadata
def find_expert_count(metadata):
"""`*.expert_count` を探す。アーキテクチャ名が接頭辞なので後方一致で拾う。"""
for key, value in metadata.items():
if key.endswith(".expert_count"):
return value
return None
def describe(path):
metadata = read_metadata(path)
arch = metadata.get("general.architecture", "unknown")
expert_count = find_expert_count(metadata)
if expert_count is None:
verdict = "dense (expert_count なし)"
elif expert_count > 0:
verdict = "MoE (expert_count=%d)" % expert_count
else:
# キーはあるが 0 のケース。MoE ではなく dense として扱う
verdict = "dense (expert_count=0)"
return "%s: architecture=%s, %s" % (os.path.basename(path), arch, verdict)
def main(argv):
if len(argv) < 2:
print("使い方: python code_expert_count.py <model.gguf> [model2.gguf ...]")
return 1
failed = False
for path in argv[1:]:
try:
print(describe(path))
except (OSError, ValueError) as exc:
# 1 ファイルの失敗で全体を止めず、次のファイルへ進む
print("%s: エラー: %s" % (os.path.basename(path), exc), file=sys.stderr)
failed = True
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
本記事の表に付けた「作り」の列は、この方法で判定したものである。expert_count が 0 と書かれている場合は MoE ではなく dense として扱っている。
contextをどこまで伸ばせるか
Ollama の公式 FAQ には、既定のコンテキスト長は 4096 トークンと記載がある。環境変数または API のパラメータで変更できる。ここまでの測定はすべて 8192 で揃えているが、実際に使う場面では、この枠をどこまで広げられるかが容量の質問そのものになる。
KVキャッシュを量子化すると上限が伸びる
Ollama の公式 FAQ には、Flash Attention が有効なときに KV キャッシュを量子化できるとあり、既定の f16 に対して 8 ビット量子化の q8_0 はおよそ 2 分の 1、q4_0 はおよそ 4 分の 1 のメモリで済むと記載がある。同じ項には、GQA のヘッド数が多いモデルでは量子化による精度への影響が大きくなりうるという注記も置かれている。
常駐率 100% を保てる最大のコンテキスト長を、2048 刻みの二分探索で求めた結果が次の表である。
| モデル | KV f16 | KV q8_0 | KV q4_0 |
|---|---|---|---|
| qwen3:14b | 8,192 | 16,384 | 30,720 |
| qwen3.5:9b | 126,976 | 131,072 以上 | 131,072 以上 |
| gemma4:12b | 131,072 以上 | 131,072 以上 | 131,072 以上 |
測定条件: VRAM 12GB のカード1枚 / Ollama 0.32.3 / Q4_K_M / 常駐率 100% を保てた最大のコンテキスト長を 2048 刻みの二分探索で求めた値。
探索の刻みは 2048 トークン。131,072 までしか試していないため、そこまで収まったモデルは「以上」と表記した。常駐率 100% は Ollama の報告値で、実際に長いプロンプトを流したときの速度は別の話になる。
qwen3:14b の行を見ると、上限は 8,192 → 16,384 → 30,720 と、KV キャッシュを f16 から q8_0、q8_0 から q4_0 と落とすごとにほぼ倍になっている。公式 FAQ の「およそ 2 分の 1・およそ 4 分の 1」という記載と向きは合う。
モデルによって余裕がまるで違う
同じ 12GB でも、コンテキストを伸ばしたときの余裕はモデルによって大きく違った。gemma4:12b は KV f16 のままで 131,072 まで常駐率 100% を保ち、qwen3.5:9b も f16 で 126,976 まで保った。131,072 より上は試していないため、その上限がどこかは分からない。一方の qwen3:14b は、f16 では 8,192 で頭打ちになる。
この差のうち gemma4:12b と qwen3:14b の 2 本については、ヘッダの記録が内部構造の違いを示している (qwen3.5:9b のヘッダは確認していない)。gemma4:12b の GGUF ヘッダは、48 層のうち 40 層が窓 1024 の局所注意で、残る 8 層が全域、その 8 層だけ KV のヘッド数が 1 (ほかの層は 8) と記録されている。全域層は 6 層ごとに 1 層置かれている。対して qwen3:14b のヘッダは 40 層すべてが KV のヘッド数 8 で、窓を示す記録を持たない。
この違いは、コンテキストを伸ばしたときの増え方の差と向きが合う。ただし増加量そのものを再現したわけではないので、これが理由だと確かめたことにはならない。読み取れるのは、同じ 12GB でも「長い文脈を扱えるモデル」と「扱えないモデル」があり、それがモデルの設計で決まっているらしい、というところまでである。
枠を広げた時と、実際に流し込んだ時は別々に測る
コンテキストには 2 つの段階がある。枠を確保しただけの状態と、その枠へ実際にトークンを流し込んだ状態で、この 2 つは別々に測らないと混ざる。
まず枠だけを広げた場合。同じモデルでコンテキスト長だけを変えると、常駐率が下がり、生成速度も下がった。qwen3:14b は 8,192 の 47.08 tok/s から、40,960 で 12.93 tok/s になっている。
| モデル | コンテキスト長 | 常駐率 | 生成速度 |
|---|---|---|---|
| qwen3:14b | 8,192 | 100% | 47.08 tok/s |
| qwen3:14b | 18,432 | 82% | 25.56 tok/s |
| qwen3:14b | 32,768 | 69% | 16.54 tok/s |
| qwen3:14b | 40,960 | 63% | 12.93 tok/s |
| phi4:14b | 8,192 | 95% | 41.82 tok/s |
| phi4:14b | 16,384 | 81% | 24.9 tok/s |
| codestral:22b-v0.1-q4_K_M | 4,096 | 71% | 11.32 tok/s |
| codestral:22b-v0.1-q4_K_M | 8,192 | 68% | 10.02 tok/s |
| codestral:22b-v0.1-q4_K_M | 16,384 | 59% | 8.13 tok/s |
| codestral:22b-v0.1-q4_K_M | 32,768 | 49% | 6.86 tok/s |
| deepseek-coder-v2:16b-lite-instruct-q4_K_M | 8,192 | 81% | 26.93 tok/s |
| deepseek-coder-v2:16b-lite-instruct-q4_K_M | 16,384 | 68% | 23.97 tok/s |
測定条件: 冒頭の表と同じ条件で、コンテキスト長だけを変えて測ったもの。同じモデルの中で比べている。
コンテキストの枠を広げただけで、長いプロンプトを実際に流してはいない。deepseek-coder-v2 だけが MoE で、残る 3 本は dense である。
次が、枠を固定して流す量だけを変えた場合である。27B・UD-Q3_K_XL・KV f16 で枠を 32,768 に固定したまま、投入するトークン数を変えて測った。
| 実際に流したトークン | 生成速度 |
|---|---|
| 129 | 7.232 tok/s |
| 1,423 | 4.657 tok/s |
| 2,995 | 3.297 tok/s |
| 6,008 | 2.071 tok/s |
| 12,034 | 2.962 tok/s |
測定条件: VRAM 12GB のカード1枚 / llama.cpp b10356 / Qwen3.8-27B UD-Q3_K_XL / コンテキストの枠 32,768 / KV f16 / 各条件 1 回。
各点 1 回の測定である。6,008 と 12,034 は順序が逆転しており、この 2 点の差は測定のばらつきと区別できない。25,320 トークンを流した回は、プロンプト処理が 900 秒の打ち切りに達して生成まで到達しなかった。900 秒で打ち切ったのはこの測定の設定で、モデルやエンジンの制限ではない。
この表は枠を 32,768 の 1 通りに固定しているため、枠を広げたこと自体の影響はここからは読めない。読めるのは、枠を固定したまま投入量を増やしたときにどう変わるかだけである。129 トークンで 7.232 tok/s、2,995 トークンで 3.297 tok/s、6,008 トークンで 2.071 tok/s と、投入量が増えるにつれて落ちていく。
2 つの表は、片方が Ollama で 14B クラス、もう片方が llama.cpp で 27B クラスと条件が違うため、同じ軸に並べて比べられるものではない。共通して読み取れるのは、「枠を広げても常駐率が保てるか」と「その枠を実際に埋めたときに何 tok/s 出るか」は別々に確認する必要がある、という点である。
27Bを押し込むと、量子化で5倍変わる
12GB に収まらないと分かっているモデルを、それでも動かす場合の値も測った。27B クラスを 3 種類の量子化で回すと、生成速度は 1.431〜7.342 tok/s と 5 倍前後の開きになった。
| 量子化 | 生成速度 | カードの使用量 |
|---|---|---|
| UD-Q4_K_M | 1.431 tok/s | 11888 MiB |
| UD-IQ4_XS | 3.126 tok/s | 11868 MiB |
| UD-Q3_K_XL | 7.342 tok/s | 11892 MiB |
測定条件: VRAM 12GB のカード1枚 / llama.cpp b10356 / Qwen3.8-27B / コンテキスト 4,096 / 同時実行 1。
収まらなかったモデルの表にある qwen3.5:27b (Ollama・Q4_K_M・コンテキスト 8192) とは、モデルもエンジンも、カードの外へ置かれる量も違う。2 つの 27B の数字は直接は比べられない。
27B はこの容量に収まらないため、いずれも一部をカードの外へ置いた状態での値である。カード全体の使用量は 11,868〜11,892 MiB で、nvidia-smi が total として報告する 12,282 MiB のほぼ上限まで使っている。表の使用量は nvidia-smi がカード全体について報告した値で、モデル単体の GPU 常駐量ではない。
なお、この 3 つの比較で測っているのは生成速度と使用量だけで、量子化による回答の質は測っていない。速い量子化が良い量子化だという読み方はできない。
同じ12GBでも、カードによって速度が変わる
ここまでの生成速度は、すべて 1 枚のカードで測った値である。その値がどこから来ているかを確かめるため、同じカードの上でメモリクロックだけを 3 段に固定して測り直した。
| モデル | メモリクロック | 生成速度 | 最速時との比 |
|---|---|---|---|
| llama3.1:8b | 10,251 MHz | 85.84 tok/s | 1.000 |
| llama3.1:8b | 5,001 MHz | 45.88 tok/s | 0.534 |
| llama3.1:8b | 810 MHz | 6.49 tok/s | 0.076 |
| gemma3:12b | 10,251 MHz | 52.1 tok/s | 1.000 |
| gemma3:12b | 5,001 MHz | 28.27 tok/s | 0.543 |
| gemma3:12b | 810 MHz | 4.08 tok/s | 0.078 |
| qwen3:14b | 10,251 MHz | 48.51 tok/s | 1.000 |
| qwen3:14b | 5,001 MHz | 25.05 tok/s | 0.516 |
| qwen3:14b | 810 MHz | 3.46 tok/s | 0.071 |
測定条件: VRAM 12GB のカード1枚 / Ollama 0.32.3 / Q4_K_M / 3 モデルとも常駐率 100% / メモリクロックだけを 3 段に固定して測定。
同じカードの上でメモリクロックだけを変えた測定で、別のカードを測ったものではない。クロックを固定した別の測定回のため、最上段の値は冒頭の表の同じモデルの値とは完全には一致しない (冒頭の表はクロックを固定していない)。最上段は 10,501 MHz を指定したが、負荷中の実測値は 10,251 MHz だった。コアのクロックは固定していない。
クロックを 10,251 MHz から 5,001 MHz、810 MHz へ落とすと、生成速度は最速時のそれぞれ 0.516〜0.543 倍、0.071〜0.078 倍になった。クロックの比は 1.000 / 0.488 / 0.079 で、生成速度の比はこれにおおむね追随している。3 モデルとも常駐率 100% の状態での測定なので、あふれの影響は入っていない。単発の生成の速度は、メモリからどれだけ速く読み出せるかで決まる量だと読める。
同じ192-bitでも、メモリの世代が違う
12GB のカードは 1 種類ではない。NVIDIA の公式製品ページに載っているスペックを並べると、次のようになる。
| カード | メモリ | メモリインターフェイス幅 | CUDA コア |
|---|---|---|---|
| RTX 4070 SUPER | 12GB GDDR6X | 192-bit | 7168 基 |
| RTX 4070 | 12GB (GDDR6 と GDDR6X の 2 種類がある) | 192-bit | 5888 基 |
| RTX 5070 | 12GB GDDR7 | 192-bit | 6144 基 |
| RTX 3060 | 12GB GDDR6 (8GB・128-bit の構成も併記) | 192-bit | 3584 基 |
上の表はいずれも NVIDIA 公式製品ページのスペック表の記載で、実測値ではない。搭載しているメモリ量であって、推論で使える量ではない。
ここで比較している 12GB の構成はいずれも幅が 192-bit で、違うのはメモリの種別 (世代) とコア数である。その先、つまり実効の読み出し速度がどれだけ違うかについては、NVIDIA の公式製品ページはメモリインターフェイス幅とメモリの種別は載せているが、メモリ帯域の数値を載せていない。2026年9月10日に 3 ページを確認した時点の記載である。本記事が測ったのは 1 枚だけなので、ほかのカードで何 tok/s 出るかは書けない。
動画生成で先に止まったのは、解像度ではなくフレーム数だった
ここまでは LLM の話である。同じカードで動画生成も測っており、こちらは収まるかどうかではなく、どこで生成が失敗するかが問題になった。
試した範囲では、フレーム数のほうが先に限界に当たった
動画生成で先に失敗する条件に当たったのは、解像度ではなくフレーム数だった。864×480 では 136 フレームまで生成でき、1280×720 の 56 フレームも生成できている。解像度は 1280×720 より上を試していないため、解像度の限界がどこかは測っていない。解像度とフレーム数を同時には上げていない。
| 条件 | 量子化 | ステップ | 生成時間 | ピークVRAM |
|---|---|---|---|---|
| 1024×576 56frames | FP8 | 4 | 45.3 秒 | 11532 MiB |
| 1280×720 56frames | FP8 | 4 | 75.3 秒 | 11564 MiB |
| 864×480 104frames | FP8 | 4 | 70.9 秒 | 11660 MiB |
| 864×480 136frames | FP8 | 4 | 90.3 秒 | 11564 MiB |
| 864×480 56frames | FP8 | 4 | 40.6 秒 | 11268 MiB |
| 864×480 72frames | FP8 | 4 | 51.1 秒 | 11108 MiB |
| 864×480 88frames | FP8 | 4 | 57.1 秒 | 11756 MiB |
測定条件: VRAM 12GB のカード1枚 / ComfyUI 0.31.1 / MiniMax H3 (FP8 で焼き込んだ単一ファイル) / 4 ステップ / res_multistep + simple / 1 本ごとにプロセスを立て直す。
864×480 のピーク VRAM は 56 フレームの 11,268 MiB から 136 フレームの 11,564 MiB まで、フレーム数に対して単調には増えていない (最大は 88 フレームの 11,756 MiB)。フレーム数の上限が VRAM の増え方として見えているわけではない。
解像度とフレーム数を別々に上げた測定で、両方を同時に上げてはいない。生成時間はプロセスの立ち上げを含む。1 本ずつの測定で、回数を重ねていない条件がある。
864×480 で 168 フレームを指定した回は 2 回とも生成に失敗した。ログには VRAM の確保に失敗した旨と、ピーク VRAM が 11,692 MiB だったことが記録されている。ただし成功した 88 フレームのピークは 11,756 MiB で、失敗した回のほうが低い。VRAM を使い切って落ちた形にはなっておらず、原因は特定していない。
ステップ数を減らしてもVRAMは空かない
864×480・56 フレームに固定して、ステップ数と量子化だけを変えた測定が次の表である。
| 条件 | 量子化 | ステップ | 生成時間 | ピークVRAM |
|---|---|---|---|---|
| 864×480 56frames | FP8 | 4 | 40.6 秒 | 11268 MiB |
| 864×480 56frames | FP8 | 8 | 57.6 秒 | 11300 MiB |
| 864×480 56frames | FP8 | 20 | 107.8 秒 | 11350 MiB |
| 864×480 56frames | INT8 | 4 | 55.1 秒 | 11468 MiB |
| 864×480 56frames | INT8 | 8 | 89.8 秒 | 11500 MiB |
測定条件: 上の表と同じ環境で、864×480・56 フレームに固定してステップ数と量子化を変えたもの。INT8 はステップ 4 と 8 の 2 点しか測っていない。
FP8 でステップ数を 4 から 20 まで変えても、ピーク VRAM は 11,268〜11,350 MiB でほとんど動かなかった。INT8 の 2 点でも 11,468 と 11,500 MiB で、同じくステップ数では動いていない。測った範囲は 4〜20 ステップだが、この範囲では、ステップを減らしても VRAM が空くわけではない。動くのは所要時間のほうである。フレーム数で落ちる条件に当たったときに、ステップを削って回避する筋道は、この測定の範囲では見えていない。
量子化については、同じ 4 ステップでも INT8 は FP8 より遅く、40.6 秒に対して 55.1 秒だった。8 ステップでは 57.6 秒に対して 89.8 秒になっている。INT8 は 2 点しか測っていない。検証に使ったカードは Ada 世代で、別世代のカードで同じ向きになるとは限らない。
なお、この測定で見ているのは所要時間とピーク VRAM であり、出来上がった映像そのものの比較は含まない。動画や画像の生成環境をどう組むかは、ComfyUI の推奨スペックを VRAM 別に実測した記事で扱っている。
まとめ
LLM では分かれ目が 3 か所ある。1 つめは容量で、収まる範囲はおおむね重みとキャッシュの大きさで決まる。ただし物理容量ぎりぎりまで載るわけではない (14B の 1 本は 2 GiB 以上を残した状態で常駐率 95% だった)。その余白が何に使われているかは測っていない。ここは同じ 12GB なら大枠では同じように読める。
2 つめが、収まらなくなった後である。常駐率が近い条件で比べても MoE と dense は 約 8〜14 倍離れていた。あふれる大きさのモデルを使う場合、どちらの作りかは最初に確認する材料になる。ただし作りのほかにパラメータ数も重みの大きさも同時に違っており、作りだけが差を生んでいると確かめたわけではない。この差はあふれた分の読み出しがカードの外を通る構成で測った値でもあり、接続の仕方が違えば大きさは変わりうる。
3 つめがコンテキストで、KV キャッシュを 1 段量子化するごとに上限が伸び (測れた 1 本ではほぼ倍ずつ)、モデルによって余裕はまるで違った。枠が収まることと、その枠を実際に埋めたときに使える速さであることは、別々に確認する必要がある。
動画生成は当たり方が違い、先に止まったのは解像度ではなくフレーム数だった。ステップ数を減らしてもピーク VRAM は動かないので、フレーム数で落ちたときにステップを削って回避する筋道は、測った範囲では見えていない。
この記事の数値が言える範囲は、最後まで変わらない。速度はここで使った 1 枚のカードの値であり、学習用途、複数の同時実行、回答の質、画像生成の限界は測っていない。
この記事の測り方と、測っていないこと
本記事の実測はすべて 2026年9月9日に、VRAM 12GB のカード1枚 (OCuLink 接続) で計測したものである。検証機は 1 枚構成で、カードの容量は nvidia-smi が total として 12,282 MiB と報告する。カードは OCuLink 接続の外付けドックに搭載している。
使用したソフトのバージョンは、Ollama 0.32.3、llama.cpp b10356、ComfyUI 0.31.1。いずれも測定の途中で更新していない。
LLM の既定条件は、Q4_K_M・コンテキスト 8192・think=false・生成 512 トークン・乱数の種 42 で、3 回計測の中央値を採っている。節ごとに条件を変えた測定については、各表の下に測定条件を書いてある。常駐率は Ollama の報告値、カードの使用量は nvidia-smi がカードについて報告する値で、いずれもモデル単体の所要量ではない。メモリクロックを変えた測定では、コアのクロックは固定していない。
測っていないことも書いておく。学習用途、複数の同時実行、回答の質、画像生成の限界は測っていない。dense と MoE の比較についても対照実験は行っておらず、10 本の観測にとどまる。
生成速度はこのカードで測った値であり、同じ 12GB のカードでも世代が違えば変わる。容量で決まること (収まる・収まらない、コンテキストの上限) は主に VRAM の量に制約されるため、同じ 12GB なら大枠では近い読み方ができる。速度はそうではない。動画のフレーム数の上限は、失敗の原因を特定していないため、どちらとも書いていない。さらに、あふれた状態の速度はカードの外にある分の読み出しにも左右されるため、接続の仕方が違う環境では同じにならない可能性がある。
よくある質問
コンテキストの枠を最初から最大まで広げておけば安全か
安全とは言えない。Ollama で 14B クラスの枠だけを広げた測定では、長いプロンプトを流さなくても、枠を広げた分だけ常駐率が下がり、8,192 の 47.08 tok/s が 40,960 では 12.93 tok/s まで落ちた。常駐率 100% を保てる上限自体は、KV キャッシュを量子化すれば伸びる (qwen3:14b は q4_0 で 30,720)。ただしこれは常駐率だけを測った値で、その枠を実際に埋めたときの速度は測っていない。さらに別の測定 (llama.cpp・27B) では、枠を固定したまま投入するトークン数を増やすと速度が落ちた。枠の広さは「入るかどうか」の指標であって、長文を投げたときの速度の指標ではない。この 2 つは別のエンジン・別のモデルでの測定で、同じ軸には並べられない。
27Bクラスを12GBで動かす場合、量子化はどう見ればよいか
測った 3 つの量子化では生成速度が 1.431〜7.342 tok/s まで変わり、いずれも一部をカードの外へ置いた状態だった。速度だけを見れば差は大きい。ただしこの測定は回答の質を測っていないため、速いほうが良い選択だとは書けない。速度がここまで開くという事実までが、この記事で確認できた範囲である。
動画生成でINT8とFP8のどちらを選ぶか
このカード (Ada 世代) で測った 2 点では、同じステップ数で INT8 のほうが遅かった。4 ステップで 40.6 秒に対して 55.1 秒、8 ステップで 57.6 秒に対して 89.8 秒である。INT8 は 2 点しか測っておらず、別世代のカードで同じ向きになるとは限らない。出力そのものの比較もしていない。
モデル名だけで dense か MoE か見分けられるか
手元で確認した範囲では、タグ名から分かるのは -a3b のように活性パラメータ数が入っている場合だけだった。architecture の文字列も当てにならず、gemma4:26b (MoE) と gemma4:31b (dense) はどちらも gemma4 と記録されている。確実なのは GGUF ヘッダの expert_count を読む方法で、本文のスクリプトは判定材料としてこのキーだけを使う。
この記事の速度を、別の12GBカードにそのまま当てはめられるか
当てはめられるのは容量についてだけである。何が収まるか、コンテキストがどこまで伸びるかは主に VRAM の量に制約されるため、同じ 12GB なら大枠では近い読み方ができる。動画のフレーム数の上限は別で、失敗した回のピーク VRAM が成功した回より低く、VRAM を使い切った形になっていないため、VRAM の量だけで決まるとは書けない。生成速度はメモリの読み出し速度に強く追随していたので、メモリの世代が違えば変わる。公称スペックの並びからも、12GB・192-bit という括りの中でメモリの種別が違うことは確認できる。この記事は 1 枚しか測っていないため、ほかのカードの速度は書いていない。

