なぜEPYC 9175Fの512MB L3キャッシュはMoE推論に効くのか:1Tモデル実測による仮説検証
AMD EPYC 9175FでKimi-K2.5(1T級MoE)をCPUオンリー実行し、「巨大L3キャッシュがMoE推論を加速する」という仮説を実測データで検証した記録。当初仮説の棄却から修正版への過程と、スレッド数別ベンチマーク、再現手順を含む。
背景
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点を明らかにする。
- 「巨大L3キャッシュにMoEのアクティブエキスパートが収まることで推論が加速する」という仮説は正しいか
- EPYC 9175FでKimi-K2.5(Q4_K_S)を実行した場合のスループットはバッチ処理に実用的か
- スレッド数とメモリ帯域の関係から、運用上の最適設定はどこか
実験環境
ハードウェア
| 項目 | 仕様 |
|---|---|
| CPU | AMD EPYC 9175F(Zen 5, 16C/16T, SMT=OFF) |
| L3キャッシュ | 512MB(コアあたり32MB) |
| メモリ | DDR5-6400 64GB x 12ch = 768GB |
| GPU | RTX PRO 6000 MAX-Q 96GB(本検証では未使用) |
| TDP | 320W(cTDP 400W) |
ソフトウェア
| 項目 | バージョン |
|---|---|
| OS | Ubuntu 24.04 LTS |
| Runtime | llama.cpp(server mode) |
| コンテナ | Podman rootless |
モデル
| 項目 | 仕様 |
|---|---|
| Model | Kimi-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.43 | 12.94 | 77.28 | 最大スループット |
| 14 | 21.32 | 12.50 | 79.97 | 帯域飽和が顕在化 |
| 13(推奨) | 21.58 | 11.67 | 85.70 | 効率と余力のバランス |
| 12 | 14.58 | 11.86 | 84.32 | 演算リソース不足の兆候 |
注目すべきポイント: Decode速度はth=13〜16の範囲で11.67〜12.94 tok/sとほぼ横ばいになる。これはメモリ帯域がボトルネックになっていることを示す。一方Prefillはth=12で急落しており、AVX-512の演算リソースが不足し始めるラインがここにある。
128Kコンテキストでの長時間安定性(th=13)
| 項目 | 測定値 | 評価 |
|---|---|---|
| Prefill | 22.39 tok/s | AVX-512 VNNIの最適点 |
| Decode | 9.34 tok/s | 長文脈でも人間の読書速度(約6 tok/s)を上回る |
| TTFT(39トークン新規入力) | 1,741 ms | LCPキャッシュが効いた結果 |
| KV Cache Latency | 107.10 ms/tok | 12ch 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/s | 8.76 tok/s | 保存オーバーヘッド込 |
| 3rd | LCP similarity hit | 62 ms (キャッシュルックアップ) | 10.0+ tok/s | TTFTが劇的に短縮 |
プロンプトキャッシュサイズは1260トークンで159.5 MiB。3回目以降はプレフィックス部分の再計算がスキップされ、TTFTが秒単位から62msまで短縮された。
メモリ消費
| 項目 | 測定値 | 備考 |
|---|---|---|
| モデル本体(ウェイト) | 約522 GB | Q4_K_S量子化 |
| KVキャッシュ(16K ctx) | 約2.0 GB | K:1098MiB / V:976MiB |
| プロンプトキャッシュ | 約160 MB | 1.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を割り当てることで:
- コア間のL3競合を最小化
- ホット領域がL3から追い出されにくい
- メモリコントローラへのリクエスト集中を回避
メモリ帯域がボトルネックになる境界
スレッド数別ベンチマークから、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/active が 0 であることを確認すること。
Dense 70BクラスではL3の恩恵が薄れる
Dense(高密度)モデルではアクセスパターンが均一で、L3キャッシュのヒット率が低下する。MoE特有の「エキスパート選択による局所性」がL3活用の鍵であり、この構成はMoEモデル専用と考えた方がよい。
プロンプトキャッシュの活用がCPU推論の真の武器
CPUの弱点であるPrefill速度を、プロンプトキャッシュで補うことが重要。Dagsterパイプラインで数千件のドキュメントを処理する場合、共通のSystem Promptをキャッシュしておくことで、全体の実行時間を大幅に短縮できる。
コスト比較
| プラットフォーム | 構成 | 推定Decode速度 | ハードウェアコスト |
|---|---|---|---|
| AMD EPYC 9175F | 1x CPU, 768GB RAM | 10〜13 tok/s | 約$15k |
| Mac Studio M3 Ultra | 2台構成 (512GB) | 約21 tok/s | 約$20k |
| NVIDIA GPUクラスター | 4x H200 | 40+ tok/s | $150k〜$200k |
GPUクラスターの1/10以下のコストで「動作する」環境が得られる。対話UXには不向きだが、夜間バッチ処理やデータセット生成には十分な速度。

