この記事の要点
- 12GB に何が載るか— 3B から 14B クラスまでは 1 枚にほぼ丸ごと載り、速度はパラメータ数の順におおむね並んだ。12GBに載りきらないモデルでは、dense か MoE かで生成速度が大きく変わる。載りきるモデル同士では、この作りの違いが速度にどう効くかは今回の計測からは分からない。
- 載らなくなると何が起きるか— あふれた後の速度は 2 種類に割れ、常駐率が 41〜54% に収まる 8 本では MoE と dense が 約 8〜14 倍離れた。
- 次に何を見るか— dense と MoE のどちらかは、GGUF ヘッダの expert_count を読むのが確実だった。同じ architecture 文字列でも中身が違う実例がある。
この結果が言える範囲: VRAM 12GB のカード1枚 (OCuLink 接続) で 2026年9月9日に測った値 (動画生成の測り直しは9月28日に追記)。断らないかぎりコンテキスト8192・Q4_K_M。速度はこのカードの値であり、同じ 12GB でも世代が違えば変わる。学習・同時実行・回答の質・画像生成の限界は測っていない。MoE と dense の差は 10 本の観測 (常駐率の近い行で比べたのは 8 本) によるもので、作りだけが原因と確認したわけではない。
12GBの挙動を分けるのは、大きさの境目と、モデルの作り
VRAM 12GB では、まず「収まるか、あふれるか」で挙動が二つに分かれる。収まった 12 本では、生成速度はパラメータ数なりにおおむね順に落ちていった (使用量が近くても 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 出るかは書けない。
動画生成は、9月9日の構成でフレーム数を伸ばした条件だけ失敗した
ここまでは LLM の話である。同じカードで動画生成も測っており、こちらは収まるかどうかではなく、どの条件で生成が失敗し、それが構成で変わるかが問題になった。
9月9日の構成で失敗したのは、フレーム数を伸ばした条件だけだった
9月9日の構成 (ComfyUI 0.31.1・PyTorch 2.9.1+cu128・comfy-aimdo 0.4.13) で失敗に当たったのは、864x480 でフレーム数を 168 まで伸ばした条件だけだった (ComfyUI・PyTorch・comfy-aimdo を上げた9月28日の構成では 264 フレームまで通った。この節の最後の追記を参照)。864x480 では 136 フレームまで生成でき、1280x720 の 56 フレームも生成できている。解像度は 1280x720 より上を試していないため、解像度の限界がどこかは測っていない。解像度とフレーム数を同時には上げていない。解像度とフレーム数はどちらも 1 本の動画で扱う量 (横 × 縦 × フレーム数) を増やす方向に働き、失敗した 864x480・168 フレームはこの量が9月9日に試した中で最も大きかった。解像度だけを上げてこの量に届く条件は試していないため、解像度とフレーム数のどちらが先に限界に当たるかは、この測定からは区別できない。
| 条件 | 量子化 | ステップ | 生成時間 | ピークVRAM |
|---|---|---|---|---|
| 1024x576 56frames | FP8 | 4 | 45.3 秒 | 11532 MiB |
| 1280x720 56frames | FP8 | 4 | 75.3 秒 | 11564 MiB |
| 864x480 104frames | FP8 | 4 | 70.9 秒 | 11660 MiB |
| 864x480 136frames | FP8 | 4 | 90.3 秒 | 11564 MiB |
| 864x480 56frames | FP8 | 4 | 40.6 秒 | 11268 MiB |
| 864x480 72frames | FP8 | 4 | 51.1 秒 | 11108 MiB |
| 864x480 88frames | FP8 | 4 | 57.1 秒 | 11756 MiB |
測定条件: VRAM 12GB のカード1枚 / ComfyUI 0.31.1 / MiniMax H3 (拡散モデルは FP8 の minimax_h3_fl2va_pruned_fp8_scaled、LoRA なし) / 4 ステップ / res_multistep + simple / 1 本ごとにプロセスを立て直す。
864x480 のピーク VRAM は 56 フレームの 11,268 MiB から 136 フレームの 11,564 MiB まで、フレーム数に対して単調には増えていない (最大は 88 フレームの 11,756 MiB)。表のピーク VRAM は 11,108〜11,756 MiB と、どの条件でもカードの容量 (nvidia-smi の total で 12,282 MiB) の近くに集まっている。この値からは、フレーム数を増やしたときに必要な量がどれだけ増えたかは読めない。
解像度とフレーム数を別々に上げた測定で、両方を同時に上げてはいない。生成時間は ComfyUI の実行開始から完了までで、1 本ごとに立て直したプロセスでのモデルの読み込みを含む (プロセスの起動そのものは含まない)。1 本ずつの測定で、回数を重ねていない条件がある。
864x480 で 168 フレームを指定した回は 2 回とも生成に失敗した。ログには VRAM の確保に失敗した旨と、ピーク VRAM が 11,692 MiB だったことが記録されている。成功した 88 フレームのピーク (11,756 MiB) より低いが、記録しているピークは確保できた分までの値で、失敗した確保の要求量は含まない。ピークの比較だけでは VRAM が足りていたとは言えず、どれだけ足りなかったかは記録に残っていない。原因は特定していない。
ステップ数を減らしてもVRAMは空かない
864x480・56 フレームに固定して、ステップ数と量子化だけを変えた測定が次の表である。
| 条件 | 量子化 | ステップ | 生成時間 | ピークVRAM |
|---|---|---|---|---|
| 864x480 56frames | FP8 | 4 | 40.6 秒 | 11268 MiB |
| 864x480 56frames | FP8 | 8 | 57.6 秒 | 11300 MiB |
| 864x480 56frames | FP8 | 20 | 107.8 秒 | 11350 MiB |
| 864x480 56frames | INT8 | 4 | 55.1 秒 | 11468 MiB |
| 864x480 56frames | INT8 | 8 | 89.8 秒 | 11500 MiB |
測定条件: 上の表と同じ環境で、864x480・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 世代で、別世代のカードで同じ向きになるとは限らない。
(2026年9月25日追記)この向きは PyTorch の版にも左右される。この測定の PyTorch は cu128 版で、cu128 版では ComfyUI が INT8 などの量子化形式向けの高速化処理を使わず、汎用の経路で計算する。上の INT8 の値はその状態で測ったものである。同じ 864x480・56 フレームの条件を RTX 5060 Ti・ComfyUI 0.37.1 で測ると、PyTorch が cu128 版では INT8 のほうが遅く (4 ステップで 66.1 秒対 46.2 秒)、ComfyUI の README の手順どおり cu130 版に入れ替えると INT8 のほうが速かった (4 ステップで 34.7 秒対 41.7 秒、8 ステップで 49.0 秒対 63.0 秒。MiniMax H3 Turbo LoRA の記事の追記)。このカード (Ada 世代) で cu130 版に替えたときに同じく逆転するかは測っていない。
(2026年9月28日追記)9月28日の構成では 264 フレームまで通った
同じカードを ComfyUI 0.37.1・PyTorch 2.14.0+cu130・comfy-aimdo 0.5.5 (ComfyUI の DynamicVRAM が有効な既定の状態) で測り直すと、864x480・FP8・4 ステップで 136・168・200 フレーム (各 2 回) と 264 フレーム (1 回) がすべて生成できた。上の失敗は 9月9日の構成 (ComfyUI 0.31.1・PyTorch 2.9.1+cu128・comfy-aimdo 0.4.13) での結果で、12GB のカードならどこでも 168 フレームで止まる、という限界ではない。
| 条件 | 回数 | 生成時間 (中央値) | ピークVRAM | ComfyUI プロセスの RAM (ピーク) |
|---|---|---|---|---|
| 864x480 136frames | 2 | 88.1 秒 | 10920 MiB | 23.3 GiB |
| 864x480 168frames | 2 | 111.7 秒 | 10920 MiB | 24.4 GiB |
| 864x480 200frames | 2 | 139.3 秒 | 10932 MiB | 25.1 GiB |
| 864x480 264frames | 1 | 201.4 秒 | 10920 MiB | 26.7 GiB |
測定条件: 2026年9月28日 / 上と同じ VRAM 12GB のカード1枚 / ComfyUI 0.37.1・PyTorch 2.14.0+cu130・comfy-aimdo 0.5.5 / MiniMax H3 (拡散モデルは FP8 の minimax_h3_fl2va_pruned_fp8_scaled、LoRA なし) / 4 ステップ / res_multistep + simple / 1 本ごとにプロセスを立て直す。生成時間は ComfyUI の実行開始から完了までで、1 本ごとに立て直したプロセスでのモデルの読み込みを含む (プロセスの起動そのものは含まない)。9月9日の表とは ComfyUI・PyTorch・comfy-aimdo の版と測定日が違うため、生成時間は並べて比べない。
測り直した構成では、ピーク VRAM がどの長さでも 10,920〜10,932 MiB (約 10.7GiB) でほぼ一定だった。フレーム数によらず同じ値に止まっており、この生成に必要な VRAM の量を示す値ではない (容量の違うカードでどこに止まるかは測っていない)。フレーム数を伸ばすと、ComfyUI プロセスの RAM は 23.3GiB から 26.7GiB へ増えた。9月9日の表には RAM の記録が無いので、VRAM に載らない分を RAM へ回すようになったのかは比べられない。264 フレームのときプロセスの RAM は 26.7GiB に達していた。これはこのプロセスが使った量で、必要なメインメモリの量ではない。検証機よりメインメモリの少ない環境で同じ結果になるかは測っていない。9月9日とは版と測定日が同時に違い、どれで結果が変わったのかは切り分けていない。264 フレームより長い条件と、解像度を上げた条件は測っていない。
なお、動画の測定 (9月9日・9月28日とも) で見ているのは所要時間とメモリの使用量であり、出来上がった映像そのものの比較は含まない。動画や画像の生成環境をどう組むかは、ComfyUI の推奨スペックを VRAM 別に実測した記事で扱っている。
まとめ
LLM では分かれ目が 3 か所ある。1 つめは容量で、収まる範囲はおおむね重みとキャッシュの大きさで決まる。ただし物理容量ぎりぎりまで載るわけではない (14B の 1 本は 2 GiB 以上を残した状態で常駐率 95% だった)。その余白が何に使われているかは測っていない。ここは同じ 12GB なら大枠では同じように読める。
2 つめが、収まらなくなった後である。常駐率が近い条件で比べても MoE と dense は 約 8〜14 倍離れていた。あふれる大きさのモデルを使う場合、どちらの作りかは最初に確認する材料になる。ただし作りのほかにパラメータ数も重みの大きさも同時に違っており、作りだけが差を生んでいると確かめたわけではない。この差はあふれた分の読み出しがカードの外を通る構成で測った値でもあり、接続の仕方が違えば大きさは変わりうる。
3 つめがコンテキストで、KV キャッシュを 1 段量子化するごとに上限が伸び (測れた 1 本ではほぼ倍ずつ)、モデルによって余裕はまるで違った。枠が収まることと、その枠を実際に埋めたときに使える速さであることは、別々に確認する必要がある。
動画生成は当たり方が違い、9月9日の構成で試した中では、いちばん大きい条件 (864x480・168 フレーム) だけが失敗した。ComfyUI・PyTorch・comfy-aimdo を上げた9月28日の構成では、同じカードで 264 フレームまで通った (このとき ComfyUI プロセスは RAM を 26.7GiB 使っており、メインメモリの少ない環境で同じ結果になるかは測っていない)。この失敗は 12GB という容量だけで決まる限界ではない。構成と測定日を変えた測り直しでは結果が変わった (どの違いで変わったのかは切り分けていない)。解像度は失敗するところまで上げておらず、どちらが先に限界に当たるかは区別できていない。9月9日の構成では、ステップ数を減らしてもピーク VRAM は動かなかったので、フレーム数で落ちたときにステップを削って回避する筋道は、測った範囲では見えていない。
この記事の数値が言える範囲は、最後まで変わらない。速度はここで使った 1 枚のカードの値であり、学習用途、複数の同時実行、回答の質、画像生成の限界は測っていない。
この記事の測り方と、測っていないこと
本記事の実測は、動画生成の測り直し (2026年9月28日) を除いて 2026年9月9日に、VRAM 12GB のカード1枚 (OCuLink 接続) で計測したものである。検証機は 1 枚構成で、カードの容量は nvidia-smi が total として 12,282 MiB と報告する。カードは OCuLink 接続の外付けドックに搭載している。
9月9日に使用したソフトのバージョンは、Ollama 0.32.3、llama.cpp b10356、ComfyUI 0.31.1 (PyTorch 2.9.1+cu128・comfy-aimdo 0.4.13)。9月9日の測定の途中では更新していない。9月28日の測り直しは ComfyUI 0.37.1・PyTorch 2.14.0+cu130・comfy-aimdo 0.5.5 で行った。
LLM の既定条件は、Q4_K_M・コンテキスト 8192・think=false・生成 512 トークン・乱数の種 42 で、3 回計測の中央値を採っている。節ごとに条件を変えた測定については、各表の下に測定条件を書いてある。常駐率は Ollama の報告値、カードの使用量は nvidia-smi がカードについて報告する値で、いずれもモデル単体の所要量ではない。メモリクロックを変えた測定では、コアのクロックは固定していない。
測っていないことも書いておく。学習用途、複数の同時実行、回答の質、画像生成の限界は測っていない。dense と MoE の比較についても対照実験は行っておらず、10 本の観測にとどまる。
生成速度はこのカードで測った値であり、同じ 12GB のカードでも世代が違えば変わる。容量で決まること (収まる・収まらない、コンテキストの上限) は、同じエンジンの版と設定なら主に VRAM の量に制約されるため、同じ 12GB なら大枠では近い読み方ができる。速度はそうではない。動画のフレーム数の上限は、失敗の原因を特定しておらず、9月28日の構成 (ComfyUI・PyTorch・comfy-aimdo を更新) では 264 フレームまで通っているため、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 世代) を PyTorch cu128 版で測った 2 点では、同じステップ数で INT8 のほうが遅かった。4 ステップで 40.6 秒に対して 55.1 秒、8 ステップで 57.6 秒に対して 89.8 秒である。INT8 は 2 点しか測っておらず、別世代のカードで同じ向きになるとは限らない。PyTorch の版でも向きは変わりうる。RTX 5060 Ti では、ComfyUI の版をそろえたまま PyTorch を cu130 版に替えると INT8 のほうが速くなった (このカードでは、cu130 版で INT8 と FP8 を比べていない)。出力そのものの比較もしていない。
モデル名だけで dense か MoE か見分けられるか
手元で確認した範囲では、タグ名から分かるのは -a3b のように活性パラメータ数が入っている場合だけだった。architecture の文字列も当てにならず、gemma4:26b (MoE) と gemma4:31b (dense) はどちらも gemma4 と記録されている。確実なのは GGUF ヘッダの expert_count を読む方法で、本文のスクリプトは判定材料としてこのキーだけを使う。
この記事の速度を、別の12GBカードにそのまま当てはめられるか
当てはめられるのは容量についてだけである。何が収まるか、コンテキストがどこまで伸びるかは、同じエンジンの版と設定なら主に VRAM の量に制約されるため、同じ 12GB なら大枠では近い読み方ができる。動画のフレーム数の上限は別で、9月9日の構成で 864x480・168 フレームが失敗したときのログには VRAM の確保に失敗した旨が残っているものの、どれだけ足りなかったかは記録されておらず、失敗の原因も特定していないため、VRAM の量だけで決まるとは書けない。同じカードでも、ComfyUI 0.37.1・PyTorch 2.14.0+cu130・comfy-aimdo 0.5.5 の構成では 264 フレームまで通っている。ただしそのとき ComfyUI プロセスは RAM を最大 26.7GiB 使っており、メインメモリの少ない環境で同じ長さが通るかは測っていない。生成速度はメモリの読み出し速度に強く追随していたので、メモリの世代が違えば変わる。公称スペックの並びからも、12GB・192-bit という括りの中でメモリの種別が違うことは確認できる。この記事は 1 枚しか測っていないため、ほかのカードの速度は書いていない。
VRAM 12GB の RTX 4070 SUPER 1 枚で、三値の重みの 30B-A3B モデルを、全部の層を GPU に置く指定のまま文脈長の設定 368,640 トークンまで動かした例はTernary Nemotron 3.5 LightningをVRAM 12GBで動かすで測った。29万トークンの入力では、読み込みの速さから計算して約2分かかった。
動画モデル4つを VRAM 12GB の RTX 4070 SUPER と 16GB の2枚で動かし、動画生成用の PC の GPU とメインメモリの選び方にまとめた記事は動画生成AI用PCのおすすめスペックにある。

