背景

2025年後半から2026年にかけて、1兆パラメータ(1T)クラスのMixture-of-Experts(MoE)モデルが相次いでリリースされた。Kimi-K2.5はその代表例で、総パラメータ1.04T、トークンあたりの活性パラメータ32Bという構成を持つ。

GPUで動かすなら話は簡単だが、NVIDIA H200を4枚(推定$150k〜$200k)という投資は個人や小規模チームには現実的ではない。一方で、CPUサーバーなら768GBのDDR5メモリを$15k程度で構築できる。問題は「CPUで1Tモデルを動かして実用になるのか」という点だった。

手元にあったAMD EPYC 9175Fは、16コアという少数精鋭構成に対して512MBという異常に大きなL3キャッシュを持つ。このアーキテクチャ特性がMoE推論に有利に働くのではないかという仮説を立て、検証を行った。

目的

以下の3点を明らかにする。

  1. 「巨大L3キャッシュにMoEのアクティブエキスパートが収まることで推論が加速する」という仮説は正しいか
  2. EPYC 9175FでKimi-K2.5(Q4_K_S)を実行した場合のスループットはバッチ処理に実用的か
  3. スレッド数とメモリ帯域の関係から、運用上の最適設定はどこか

実験環境

ハードウェア

項目仕様
CPUAMD EPYC 9175F(Zen 5, 16C/16T, SMT=OFF)
L3キャッシュ512MB(コアあたり32MB)
メモリDDR5-6400 64GB x 12ch = 768GB
GPURTX PRO 6000 MAX-Q 96GB(本検証では未使用)
TDP320W(cTDP 400W)

ソフトウェア

項目バージョン
OSUbuntu 24.04 LTS
Runtimellama.cpp(server mode)
コンテナPodman rootless

モデル

項目仕様
ModelKimi-K2.5(Moonshot AI)
総パラメータ1.04T(61層:60 MoE + 1 Dense)
活性パラメータ32B(384エキスパート中8個選択)
量子化Q4_K_S(GGUF)
KVキャッシュ量子化Q8_0
コンテキスト長最大256K(本検証は8K〜128K)

CPUトポロジ確認

  ksh3@compute-server:~$ lscpu | egrep 'CPU\(s\)|Thread|Core|Socket|NUMA'
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Model name:                              AMD EPYC 9175F 16-Core Processor
Thread(s) per core:                      1
Core(s) per socket:                      16
Socket(s):                               1
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15

ksh3@compute-server:~$ cat /sys/devices/system/cpu/smt/active
0
  

SMTはOFF。物理16コアがそのまま論理16コアとして見えている。NUMAノードは1つ。

実施内容

llama.cpp起動パラメータ

スレッド数を16, 14, 13, 12と変化させ、それぞれPrefill(入力処理)とDecode(トークン生成)のスループットを計測した。

基本コマンド(th=13の例):

  podman run --rm -p 8081:8080 --shm-size 16g --cap-add=SYS_NICE \
  -v /mnt/data/hf/hub/models--unsloth--Kimi-K2.5-GGUF:/models:Z \
  compute.home.arpa/llamacpp-zen5:latest \
  -m /models/snapshots/386fed8b054275941d6a495a9a7010fbf31b560d/Q4_K_S/Kimi-K2.5-Q4_K_S-00001-of-00013.gguf \
  --cache-type-k q8_0 --cache-type-v q8_0 --flash-attn on \
  --ctx-size 131072 --parallel 1 --threads 13 --threads-batch 13 \
  --batch-size 2048 --ubatch-size 512 --jinja --host 0.0.0.0 --port 8080
  

パラメータの意図:

  • --cache-type-k q8_0 --cache-type-v q8_0: KVキャッシュをQ8_0で量子化し、メモリ消費を抑制
  • --flash-attn on: CPU上でもFlash Attentionを有効化し、長文脈での帯域圧迫を軽減
  • --cap-add=SYS_NICE: スレッド優先度の最適化を許可
  • --batch-size 2048 --ubatch-size 512: Zen 5のAVX-512 VNNIユニットとの組み合わせでPrefillスループットを最大化

プロンプトキャッシュの検証

同一プロンプトプレフィックスでの繰り返しリクエストによるキャッシュ効果も計測した。llama.cppのselected slot by LCP similarity機能により、共通プレフィックスが長いほどTTFTが短縮される。

結果

スレッド数別スループット(ctx=8K, Q4_K_S)

スレッド数Prefill (tok/s)Decode (tok/s)Latency (ms/tok)備考
16(全コア)24.4312.9477.28最大スループット
1421.3212.5079.97帯域飽和が顕在化
13(推奨)21.5811.6785.70効率と余力のバランス
1214.5811.8684.32演算リソース不足の兆候

注目すべきポイント: Decode速度はth=13〜16の範囲で11.67〜12.94 tok/sとほぼ横ばいになる。これはメモリ帯域がボトルネックになっていることを示す。一方Prefillはth=12で急落しており、AVX-512の演算リソースが不足し始めるラインがここにある。

128Kコンテキストでの長時間安定性(th=13)

項目測定値評価
Prefill22.39 tok/sAVX-512 VNNIの最適点
Decode9.34 tok/s長文脈でも人間の読書速度(約6 tok/s)を上回る
TTFT(39トークン新規入力)1,741 msLCPキャッシュが効いた結果
KV Cache Latency107.10 ms/tok12ch DDR5による安定した帯域制御

128Kコンテキストでも9.34 tok/sを維持している。コンテキストが深まっても急激な速度低下は観測されなかった。

プロンプトキャッシュ効果(ctx=16K)

リクエスト条件Prompt処理Generation備考
1stキャッシュなし22.24 tok/s (823tok/37s)10.27 tok/s (438tok/42.7s)コールドスタート
2ndキャッシュ保存中19.98 tok/s8.76 tok/s保存オーバーヘッド込
3rdLCP similarity hit62 ms (キャッシュルックアップ)10.0+ tok/sTTFTが劇的に短縮

プロンプトキャッシュサイズは1260トークンで159.5 MiB。3回目以降はプレフィックス部分の再計算がスキップされ、TTFTが秒単位から62msまで短縮された。

メモリ消費

項目測定値備考
モデル本体(ウェイト)約522 GBQ4_K_S量子化
KVキャッシュ(16K ctx)約2.0 GBK:1098MiB / V:976MiB
プロンプトキャッシュ約160 MB1.2Kトークン時
合計RSS約523 GiB / 755 GiBスワップ発生なし

768GB搭載環境でスワップなしで動作。OSとバックグラウンドプロセスを差し引いても200GB以上の余裕がある。

考察

当初仮説の検証結果

当初の仮説:

MoEモデルはアクティブパラメータが10〜30B程度なので、EPYC 9175Fの512MB L3キャッシュにエキスパート全体が収まり、高速化する。

結果: 棄却。ただし方向性は正しかった。

Kimi-K2.5の活性パラメータは32B、Q4量子化でも1トークン生成ごとに約16GBのデータが動く。512MBのL3キャッシュに全体が収まるわけがない。

修正版仮説: 部分的・確率的キャッシュヒット

L3キャッシュが実際に効いているのは以下の「高再利用ホット領域」である:

  • Router / Gatingロジック: どのエキスパートを活性化するか決定する高頻度アクセス層。全トークンで参照されるため、L3に常駐しやすい
  • Projection / Biasテンソル: 各レイヤーの入出力境界。サイズが小さく再利用頻度が高い
  • 直前レイヤのweight / 中間テンソル: 時間的局所性により一時的にL3に残る
  • KVキャッシュの再利用部分: 特にアテンション計算で頻繁にアクセスされる部分

MoEモデルがCPU推論で「思ったより速い」理由は、「全体がキャッシュに載る」からではなく、再利用頻度の高い作業セットがL3に確率的にヒットし続けるためである。そしてEPYC 9175Fはコアあたり32MBという異常な量のL3を持つことで、このヒット率を高水準に維持できている。

なぜ「少コア・多キャッシュ」がMoEに向くのか

一般的な多コアEPYC(例えば128コア構成)では、512MBのL3を128コアで共有する。コアあたり4MBしか使えない。MoEの不規則なメモリアクセスパターンでは、コア間のL3競合(スラッシング)が発生し、実効的なキャッシュヒット率が低下する。

EPYC 9175Fは16コアに512MBを割り当てることで:

  1. コア間のL3競合を最小化
  2. ホット領域がL3から追い出されにくい
  3. メモリコントローラへのリクエスト集中を回避

メモリ帯域がボトルネックになる境界

スレッド数別ベンチマークから、Decode速度がth=13〜16で飽和することが明確に分かった。12チャンネルDDR5-6400の理論帯域は614 GB/sだが、実効帯域でth=13付近が飽和点になる。これは演算能力ではなくメモリ帯域がボトルネックであることの直接的な手がかりであり、LLM推論がmemory-boundであるという一般的な理解と一致する。

Zen 5 AVX-512の寄与

Zen 5は物理的な512ビットデータパスを持つ。前世代(Zen 4まで)の256ビットx2の疑似実装とは根本的に異なる。BF16ネイティブ処理とAVX-512 VNNI命令により、Q4_K_Sのデ量子化と内積演算がコアクロック(最大5.0GHz)に近いスループットで実行される。Prefillで24.43 tok/sという値はこの恩恵によるところが大きい。

感想

当初は「L3にエキスパートが全部載るから速い」と思っていたが、冷静に計算すれば32Bの活性パラメータが512MBに収まるわけがない。ただ、仮説の方向性自体は間違っていなかった。MoEモデルはアクセスパターンに局所性があり、ホット領域がL3に残りやすいという点で、DenseモデルよりCPU推論との相性が良い傾向がある。

スレッド数の最適化では、th=13が運用上のスイートスポットだった。推論性能の90%を維持しつつ、残り3コアをDagster、Trino、ネットワークIOに割り当てられる。これはバッチ処理サーバーとしての安定運用に直結する。

128Kコンテキストで9.34 tok/sを維持できたのは想定以上だった。1Tモデルがコンテキストを深めても急激に遅くならないのは、MLA(Multi-head Latent Attention)によるKVキャッシュ圧縮の効果が大きい。

再現方法

1. ハードウェア要件

  • AMD EPYC 9175F搭載サーバー(768GB DDR5-6400推奨)
  • ストレージ: モデルファイルに約600GB必要(NVMe推奨)

2. モデル取得

  # Hugging Faceからダウンロード
huggingface-cli download unsloth/Kimi-K2.5-GGUF \
  --include "Q4_K_S/*" \
  --local-dir /mnt/data/hf/hub/models--unsloth--Kimi-K2.5-GGUF
  

3. llama.cppコンテナビルド(Zen 5最適化)

AVX-512 VNNI / BF16を有効にしたビルドが必要。-march=znver5 を指定するか、-DGGML_NATIVE=ON でビルドする。

4. 実行(推奨設定: th=13, ctx=128K)

  podman run --rm -p 8081:8080 --shm-size 16g --cap-add=SYS_NICE \
  -v /mnt/data/hf/hub/models--unsloth--Kimi-K2.5-GGUF:/models:Z \
  compute.home.arpa/llamacpp-zen5:latest \
  -m /models/snapshots/386fed8b054275941d6a495a9a7010fbf31b560d/Q4_K_S/Kimi-K2.5-Q4_K_S-00001-of-00013.gguf \
  --cache-type-k q8_0 --cache-type-v q8_0 --flash-attn on \
  --ctx-size 131072 --parallel 1 --threads 13 --threads-batch 13 \
  --batch-size 2048 --ubatch-size 512 --jinja --host 0.0.0.0 --port 8080
  

5. 動作確認

  curl -s http://localhost:8081/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"kimi-k2.5","messages":[{"role":"user","content":"Hello"}],"max_tokens":100}'
  

補足ノウハウ

SMT(Hyper-Threading)はOFFが推奨

EPYC 9175Fの場合、SMT=ONにすると論理32コアになるが、L3キャッシュの実効利用効率が落ちる。16物理コアをそのまま使う方がMoE推論では安定する。/sys/devices/system/cpu/smt/active0 であることを確認すること。

Dense 70BクラスではL3の恩恵が薄れる

Dense(高密度)モデルではアクセスパターンが均一で、L3キャッシュのヒット率が低下する。MoE特有の「エキスパート選択による局所性」がL3活用の鍵であり、この構成はMoEモデル専用と考えた方がよい。

プロンプトキャッシュの活用がCPU推論の真の武器

CPUの弱点であるPrefill速度を、プロンプトキャッシュで補うことが重要。Dagsterパイプラインで数千件のドキュメントを処理する場合、共通のSystem Promptをキャッシュしておくことで、全体の実行時間を大幅に短縮できる。

コスト比較

プラットフォーム構成推定Decode速度ハードウェアコスト
AMD EPYC 9175F1x CPU, 768GB RAM10〜13 tok/s約$15k
Mac Studio M3 Ultra2台構成 (512GB)約21 tok/s約$20k
NVIDIA GPUクラスター4x H20040+ tok/s$150k〜$200k

GPUクラスターの1/10以下のコストで「動作する」環境が得られる。対話UXには不向きだが、夜間バッチ処理やデータセット生成には十分な速度。