← 一覧へ戻る

Claude が書いた記事

壊れる前提の GPU クラスタ ── チェックポイントは HBM から RAM へ「避難」する

1 パケットのロスも許さない GPU クラスタが、なぜ「潰れたら作り直す」k8s の上で回るのか。その鍵であるチェックポイントの足回り (HBM → ホスト RAM の退避) を、network エンジニアの目で追った学習ログ。

俺の最初の疑問

GPU クラスタのネットワークは、遅延もパケロスも致命的だ。1 ノードの通信が遅れただけで、同期 (All-Reduce) のせいで数千枚の GPU が全部待たされる。だから InfiniBand や RoCE v2 でロスレスにガチガチに固める。ここまではわかる。

ほな、なんでサーバー上に「潰れたら作り直せばいい」っていう真逆の思想のコンテナ (k8s) が乗ってんねん? 1 パケットも許さん繊細なシステムなら、生のサーバー (ベアメタル) でガチガチに固めたほうが筋が通るやろ。

そこから話を進めるうちに、最後はこの疑問に行き着いた ── 「数分おきに HBM の中身をホストの RAM に退避させる」って、どの線を、何を宛先にして通ってるんや?

まず一言でいうと

二つの思想は、別のレイヤーの話だから矛盾しない

  • 通信は止めない: GPU 間のデータ移動は 1 パケットも落とさない。ここはロスレス。
  • システムは壊れる前提: ハードは確実に壊れる。壊れたら k8s が切り離して、計算は直近のチェックポイントからやり直す

この「やり直す」を成立させるのがチェックポイントで、その正体は HBM (GPU のメモリ) からホスト RAM への「避難 (退避)」。そしてこの退避は、外向きの口である SmartNIC を通らず、筐体の中で CPU を通って完結する

そもそもチェックポイントとは

LLM の事前学習は、数千〜数万基の GPU を数週間〜数ヶ月ぶっ続けで回す長丁場。その間、ハードは確実に壊れる (GPU のサイレント障害・メモリエラー)。もし途中で 1 台が死んで最初からやり直しになったら、数日分の計算 (= 数億円) がドブに落ちる。

そこで、学習の途中経過を定期的に保存しておく。これがチェックポイント。ゲームのセーブポイントと同じで、死んでも最後にセーブした所から再開できる。

保存するのは「再開に必要なものだけ」。

  • 重み (パラメータ) ── 学習で育てたモデルの本体。
  • Optimizer 状態 ── 「次にどっち方向へ、どれくらいの勢いで重みを動かすか」の内部状態。これが無いと再開時に学習がガクッとブレる。

逆に、計算途中のアクティベーション (中間出力) や一時バッファは保存しない。もう一度計算すれば作り直せるから。

「壊れたら作り直す (k8s)」が成立するのは、この「最後のセーブ」が常にどこかに残っているから。チェックポイントが無ければ、k8s がノードを切り離して別の GPU に再スケジュールできても、計算はゼロからやり直しになってしまう。だから「数分おきに保存する」ことが要になる。

そして、この「保存する」を物理で言い換えると 「学習状態が乗っている HBM の中身を、いったん外(ホスト RAM)へ避難させる」 こと。重みも Optimizer 状態も GPU の HBM の中にあるから、保存とは結局 HBM からの退避にほかならない。チェックポイント = HBM の中身の退避。この記事のタイトルの「避難」は、まさにこのチェックポイントのことを指している。

では、その退避をどうやって GPU を止めずにやるのか ── ここからが本題。

何と比べるとわかるか

同じ「データを運ぶ」でも、ホットパス (計算中の通信) とチェックポイントは真逆の経路を通る。ここが今回の肝。

ホットパス(計算中の通信)チェックポイント(状態の保存)
何を運ぶGPU 間で同期する勾配など学習状態(重み + Optimizer 状態)
経路HBM ↔ HBM(GPU 同士)HBM → ホスト RAM(GPU → 母艦)
CPU通らない(GPUDirect RDMA でバイパス)通る(メモリコントローラを経由)
NIC / SmartNIC通る(サーバー間は NIC 経由)通らない(筐体内で完結)
速さの目的レイテンシ最小(μs を争う)GPU を止めない(稼働率を守る)

前に書いた LLM 推論サーバーの中身:HBM 同士が話している では「CPU はホットパスから外れる」と整理した。チェックポイントは、その記事で外したはずの CPU をわざわざ通る、例外の経路になっている。だからこそ別の足回りが要る。

何が問題なのか

学習状態は GPU の超高速メモリ HBM の中にある。これを保存したい。素直にやると、こうなる。

[GPU の HBM] ──(遅いネットワーク)──> [共有ストレージ]
   ↑ この書き込みの間ずっと GPU の計算は停止

これがダメな理由は二つ。

  1. 書いている間、計算を止めるしかない。メモリの中身が書き換わると保存が壊れるので、保存中は GPU をブロックする。
  2. 帯域がパンクする。数百・数千台が一斉に数十 TB を外のストレージへ投げると、スイッチやストレージ側で詰まる。書き込みに数分かかったら「一瞬計算して、すぐストレージ待ちで大停止」の繰り返しになる。

数億円のクラスタを「保存待ち」で遊ばせるのは許されない。そこで編み出されたのが非同期チェックポイント

図で見る

考え方はシンプルで、ホスト RAM を「一時避難所」に使う。遅いストレージへの書き込みを GPU に待たせない。

① 一瞬だけ GPU を止めて HBM → ホスト RAM へ退避(筐体内なので数秒以下)。② RAM に乗った時点で「保存完了」とみなし、GPU はすぐ次の計算へ。③ RAM からストレージへの遅い書き込みは、CPU が裏で非同期に片付ける。GPU はもう待っていない。

ポイントは、「RAM へのコピーが終わった瞬間」をチェックポイント完了とみなすこと。GPU は遅いストレージ書き込みを待たずに計算へ戻れる。これが稼働率 (MFU) を落とさないためのハック。

③「裏でじわじわ」は何をやっているのか

ここで「③ の RAM → ストレージは何のためにやるん? RAM に置いたままでええやん」と引っかかる。やっているのは、一時避難所 (RAM) に置いたコピーを、本当の保存先 (外の共有ストレージ) へ本移しすること。理由は三つ。

  • RAM は揮発するし、最終保存先ではない。GPU でなくホスト本体ごと落ちたら RAM の中身は消える。「壊れても残る」コピーは、外のストレージに着いて初めて成立する。
  • 代わりのノードが読むのは外のストレージ。壊れたノードの計算を別ノードが引き継ぐとき、そいつは死んだノードの RAM を読めない。共有ストレージにあるからこそ、別の GPU が拾って再開できる。
  • RAM を空ける必要がある。次のチェックポイントの退避先を確保するため。

この本移しは遅い (ネットワーク経由で外へ出すから)。でも GPU はもう待っていないので問題にならない。CPU が裏で、データをまとめて圧縮・直列化しながら NIC 経由で少しずつ送り出す。①② で「速さ」を GPU から切り離したから、③ の遅さを CPU が肩代わりできる、という分担になっている。

次に、その ① の退避が物理的にどの線を通るか。ここで network エンジニアの直感が効く。

HBM → ホスト RAM は筐体内の移動。GPU の DMA エンジンが PCIe スイッチ経由で、ホスト CPU のメモリコントローラを通して RAM へ書き込む。外向きの口である SmartNIC は、この段では出番がない。後で RAM → 外のストレージへ出すとき初めて SmartNIC が働く。

混乱しやすいポイント

① 「SmartNIC を通らない」=「CPU を通る」

SmartNIC (DPU) の仕事は、外のネットワークから来るパケットの処理 (TCP/IP・RDMA・ストレージプロトコル) を CPU の代わりに引き受けること。つまり「筐体の外」とやり取りするための口。

ところが HBM → ホスト RAM は同じマザーボードの上の移動で、ネットワークのヘッダを解釈する必要が一切ない。だから外向きの SmartNIC まで引っ張る理由がない。その代わり、データはホスト CPU のメモリコントローラを通って RAM に着く。

RDMA で「CPU をバイパスできる」のは、あくまで外のネットワークをまたぐときの話。筐体内の退避には効かない。

② 「CPU を通る」=「CPU コアがバイト転送する」ではない

経路上は CPU を通るが、実際にバイトを運ぶのは GPU 側の DMA エンジン (ハードウェア)。CPU の演算コアがコピー作業で潰れるわけではない。CPU コアが汗をかくのは、③ の「RAM → ストレージ」を圧縮・直列化しながら送り出す裏方フェーズのほう。

③ ホスト RAM の容量、足りるの?

GPU は 1 台のホストに 8 基。H100 (80GB) なら HBM 合計は 80GB × 8 = 640GB。一見ギリギリだが、二段構えで捌く。

  • RAM をデカく積む: DGX H100 はホスト RAM が 2TB ある。640GB を丸ごと退避しても余る。
  • そもそも全部は保存しない: 保存が要るのは「再開に必要なもの」= 重みと Optimizer 状態だけ。計算途中のアクティベーション (中間出力) や一時バッファは捨てていい。結果、実際の転送量は HBM 物理容量の半分〜1/3 程度に間引かれる。

それでも逼迫する巨大モデルなら、RAM を小さな通り道 (リングバッファ) にして、ローカル NVMe へ流し続ける手もある。

④ CPU すら通らない変態ルートもある

「CPU のメモリ帯域を 1% も汚したくない」という極限では、GPUDirect Storage (GDS) がある。ホスト RAM すら飛ばして、HBM から PCIe 経由でローカル NVMe へ直接書く。HBM →(PCIe スイッチ)→ NVMe という、CPU 完全おいてけぼりの経路。

たとえ話:宛先の指し方が、IP と全然違う

ここが今回いちばん腹落ちした所。IP 通信じゃないなら、宛先 (RAM) を何で指定してるのか。

答えは IP アドレスでも MAC でもなく、メモリの物理アドレス (0x から始まる番地) とサイズ。手順はこう。

  1. ホスト OS が RAM に空き領域を確保し、その先頭の物理アドレスを決める(宛先 IP の代わり)。
  2. CPU が GPU の DMA エンジンに「この番地へ、このサイズ分書け」という命令 (ディスクリプタ) を渡す。
  3. DMA エンジンが HBM から読み、PCIe に TLP (Transaction Layer Packet) という小包で流す。TLP のヘッダに宛先の物理アドレスが入っている
  4. PCIe スイッチが、そのアドレスを見て「これは CPU 直結の RAM 領域だ」と判断し、配線でそこへ流す。

network の感覚に並べると、こうなる。

IP 通信PCIe / DMA
宛先の識別子IP アドレスメモリの物理アドレス
運ぶ単位IP パケットTLP(小包)
中継の判断ルータが IP を見てバケツリレースイッチがアドレスを見て振り分け
仕組みルーティング(経路を選ぶ)アドレスデコード(番地で一意に決まる)
プロトコル負荷ヘッダ・再送制御ありほぼゼロ(筐体内で密結合)

ルータが「次どっち行く?」と経路を選ぶのに対し、PCIe は番地で行き先が一意に決まる。OS とハードが密結合したローカルだからこそ、プロトコルのオーバーヘッドなしで爆速になる。

出てきた言葉の変換表

言葉つまり何の話?
チェックポイント学習状態のスナップショット。壊れたらここからやり直す
非同期チェックポイントHBM → RAM の退避を「保存完了」とみなし、ストレージ書き込みは裏で行う方式
All-Reduce全 GPU の勾配を同期する処理。1 台遅れると全台が待つ
Straggler死んではいないが微妙に遅い GPU。全体のペースを引きずり下げる
HBM (High Bandwidth Memory)GPU 上の超高速メモリ。学習状態の置き場
Optimizer 状態重みと並んで保存が要る、最適化の内部状態
DMA (Direct Memory Access)CPU コアを介さずハードが直接メモリ転送する仕組み
TLP (Transaction Layer Packet)PCIe を流れるパケット。ヘッダに宛先の物理アドレスを持つ
SmartNIC / DPU外向きの通信処理を肩代わりするカード。筐体内の退避には出番がない
GPUDirect StorageHBM → ローカル NVMe を CPU も RAM も介さず直接書く技術
次に答える、設計者の問い で、それがなんやねん?

退避の足回り (PCIe DMA のアドレスデコード) まで追った。次は運用判断として「で、間隔は何分にするんや?」。

  • チェックポイント間隔を「5 分」と決める根拠を言えるか。 「間隔 × 故障率 × やり直しコスト」と「退避が食う稼働率 (MFU)」のトレードオフ。クラスタが 1 万枚に増えたら、間隔は短くすべきか長くすべきか。
  • 責任境界の問い:ノードが死んだとき「切り離す」のは k8s、「どこから再開する」のは計算フレームワーク。 どこまでが k8s の仕事で、どこからがフレームワークの仕事か、線を引けるか。

これに答えられると強い。「チェックポイントを知ってる」の一歩先 ── 間隔と退避先 (RAM / NVMe / GDS) を要件から自分で設計できる側に立てる。

深掘りメニュー 次におすすめのトピック

今回モヤっと残った所を、次に掘るといい順で。

  • IOMMU と IOVA ── 今回「宛先は物理アドレス」と書いたが、実機は間に IOMMU (Intel VT-d / AMD-Vi) が挟まり、DMA が積むのは生の物理アドレスでなく IOVA (I/O 仮想アドレス) のことが多い。IOMMU がそれを物理へ翻訳する。network でいう NAT に近い。SR-IOV で VF を Pod に渡す話とも直結する、今回いちばん地続きの深掘り。
  • All-Reduce の中身 ── なぜ同期型で、Ring / Tree でどこの帯域を食うか。「1 台遅れると全台待つ」の根っこ。
  • Straggler 検知 ── 死んでない遅い GPU を、起動前のストレステストと計算中のステップタイム監視でどう炙り出して即排除するか。
  • k8s から見た GPU ノード ── 8 枚 = 1 ノードをどう扱うか。DevicePlugin・hostNetwork・SR-IOV CNI が、今回の「k8s は通すが通信は貫通させる」二層とどうつながるか。