背景

Kimi-K2.5(総パラメータ1.04T、トークンあたり32B活性)を EPYC 9175F で動かしました。2025年後半から2026年に登場した1Tクラスの Mixture-of-Experts(MoE)を、CPUでバッチ処理に使えるか確かめます。

当時の見積もりでは、NVIDIA H200 4枚は$150k〜$200k、768GB DDR5のCPUサーバーは約$15kでした。個人や小規模チームのLLM導入を考え、CPUの実用性を調べました。

手元の EPYC 9175F は16コア、L3キャッシュ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速度を狙った設定です。

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

同じprefixでリクエストを繰り返し、selected slot by LCP similarity によるキャッシュ効果も測りました。

結果

スレッド数別スループット(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で急落し、この設定では演算用のスレッドを減らしすぎると遅くなりました。

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回目以降はprefixの再計算が省かれ、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キャッシュにエキスパート全体が収まり、高速化する。

結果:エキスパート全体が収まるという仮説は棄却しました。

活性パラメータ32Bは、Q4でも1トークン生成あたり約16GBです。512MBのL3に全体は収まりません。

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

修正版の仮説では、L3に残る候補を次のように考えました。

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

全体ではなく、再利用の多い作業領域がL3に当たるという仮説です。EPYC 9175Fのコアあたり32MBのL3が、そのヒット率を高めると考えています。今回の速度測定だけではヒット率自体を確定できません。

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

128コアで512MBなら単純計算でコアあたり4MBです。MoEの不規則なアクセスでは、コア間の競合でキャッシュが入れ替わる可能性があります。

16コアに512MBを持つ EPYC 9175F では、次の効果を仮定しています。

  1. コア間のL3競合が減る
  2. 再利用の多い領域が残りやすい
  3. メモリコントローラへの要求集中を減らせる

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

Decodeはth=13〜16で飽和しました。12チャンネルDDR5-6400の理論帯域は614 GB/sです。th=13付近で実効帯域が制約になるという解釈で、推論が 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に全体が収まる仮説は成立しませんでした。MoEのアクセスの局所性を部分的に使うという仮説へ修正しました。DenseよりCPUに合うかどうかも、この解釈の範囲です。

運用にはth=13を選びました。推論性能の90%を維持し、残る3コアをDagster、Trino、ネットワークIOに使えるためです。

128Kで9.34 tok/sは想定以上でした。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が推奨

この構成ではSMTをOFFにしています。ONなら論理32コアですが、L3の利用効率を落とさず物理16コアを使う方針です。/sys/devices/system/cpu/smt/active が 0 か確認します。

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

Dense 70Bはアクセスが均一で、MoEのエキスパート選択による局所性とは異なります。このL3の解釈をDenseへそのまま当てはめず、MoE向けの構成として考えています。

CPU推論でprompt cacheを使う

遅いPrefillはprompt cacheで補います。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以下です。対話用途には遅いものの、夜間バッチやデータセット生成には使える速度でした。