Claude が書いた記事
RDMA ── CPU 処理なしで通信させる (zero copy と kernel bypass)
「そもそも RDMA ってなんやねん? 普通の通信や DMA と何が違う?」から始め、『普通の通信かてデータは最後メモリに入るやろ、なら何が違うんや』で詰まった所までほどく。両方ともデータは最後にメモリへ着く ── 違うのは『途中のコピー』と『CPU が指揮するか』だけ、と立て直した学習ログ。
俺の最初の疑問
そもそも RDMA ってなんやねん? 普通の通信や DMA と何が違うんや?
説明してもらう中で詰まったのはここ ── 普通の通信と並べられても、何が違うのかピンと来んかった。
この記事は、その「RDMA は普通の通信・DMA と何が違うか」を順に潰した記録や。
まず一言でいうと
RDMA (Remote Direct Memory Access) = あるマシンから別のマシンのメモリへ、NIC が直接データを書き込む (読み出す) 仕組み。 データのコピーとプロトコル処理を OS カーネルにやらせず、CPU は転送を指示するだけで運搬には出てこない。
これにより、速くなり、CPU を食わず、スループットが出る。GPU を何千枚も並べる AI 学習クラスタは、CPU を一々経由してたら通信で詰まる。だから RDMA が効く。
NIC オフロードと混同しやすいから差分を提示しておく。
| NIC オフロード | RDMA | |
|---|---|---|
| オフロード対象 | 個別の低レベル処理 (TSO・LRO・チェックサム・RSS・割り込み統合…) | 通信の CPU・カーネル処理ごと (TCP / IP スタック + コピー) |
| CPU に残る仕事 | ACK・再送制御・ルーティング等 | 送受信の開始・完了待ちだけ |
| データのコピー | 残る | 0 (zero copy) |
| TCP / IP スタック | そのまま通る (中で加速) | 丸ごとバイパス |
| レイテンシ | 数十〜数百 µs | 数 µs 以下 |
| スループット | 〜10 Gbps 級 (CPU が頭打ち) | 数十〜200 Gbps 級 |
| リンク層 (L2) | 普通の Ethernet (一般網で可) | InfiniBand か Ethernet (RoCE)・閉じた DC 網が前提 |
何と比べるとわかるか
「いわゆる普通の TCP / IP 通信と何が違うんや」── これが一番のモヤやった。両者をメモリ込みで並べると、違いははっきりする。
普通の通信 (TCP / IP) も RDMA も、ゴールは同じ=アプリのメモリにデータが入る。違うのは「そこに着くまで何回コピーして、誰が指揮するか」だけ。受信側を、メモリ込みで正確に並べるとこうなる。
TCP/IP の受信: NIC → [カーネルのメモリ] → (CPU がコピー) → [アプリのメモリ] ↑コピー① ↑CPU が毎回働く ↑やっと到着 RDMA の受信: NIC → [アプリのメモリ] ↑ NIC が最初からアプリのメモリへ直書き (途中を経由しない)
| TCP / IP | RDMA | |
|---|---|---|
| 最後にメモリへ着くか | 着く | 着く |
| 途中のコピー回数 | 複数回 (カーネル → アプリ) | 0 回 = zero copy |
| CPU が毎回働くか | 働く (指揮役) | 働かない = kernel bypass |
つまり「メモリに直接書く」の直接は、カーネルのメモリという中継地を経由せず、CPU の手も借りず、いきなり目的地のメモリへ置くという意味。両方メモリに着くが、RDMA は寄り道と人手が不要。これが速くて軽い理由や。
ここで出てくる 2 つの「メモリ」は、物理的には同じ RAM の別区画や。カーネルのメモリ (OS が通信処理に使う中継バッファ) とアプリのメモリ (アプリ専用の最終目的地) は、持ち主が違う。アプリは保護されたカーネルの区画を直接は読めないので、普通の通信はカーネル区画からアプリ区画へのコピーが要る。RDMA はこのコピーを飛ばして、NIC からいきなりアプリのメモリへ置く。
何が問題なのか
普通の通信だと、データが届くたびに CPU が叩き起こされ、カーネルのメモリからアプリのメモリへ詰め替える。1 本 2 本なら誤差やが、AI 学習で GPU 数千枚が一斉に通信 (all-reduce 等) するとどうなるか。
- CPU がデータ移動に張り付く ── 計算に回したい CPU が、運送業で潰れる。
- コピーのたびにレイテンシが積む ── 集団通信は「一番遅い 1 本」に全体が引きずられる。
- 帯域が頭打ち ── CPU の処理速度が天井になる。
「線を太くする・本数を増やす」では埋まらん。CPU を経由する構造そのものが天井やから、その構造を外しに行くのが RDMA や。
図で見る
片側 RDMA と両側 RDMA
RDMA の操作には 2 つの型がある。NIC がどう書くかの詳細に入る前に、ここを分けておく。
- 片側 (WRITE / READ) … 送信側が、事前に、宛先側の番地 + rkey を知り、そこへ直接 書く / 読む。宛先側の CPU は起こさんし、書かれた事すら検知しない。普通の通信を裏返した、一番尖った型。
- 両側 (SEND / RECV) … メッセージを渡す型。送る側は SEND するだけ (相手の番地は知らんでいい)。受け手は先に「受け皿バッファをここに置いとくで」という RECV を積んでおく必要があり、届くと完了通知で軽く起きる。普通の通信に近い。
| 片側 (WRITE / READ) | 両側 (SEND / RECV) | |
|---|---|---|
| 相手の番地を知る必要 | ある | なし |
| 送信元 CPU | 指示書を出してドアベルを鳴らす | 同左 (同じ) |
| 宛先 CPU | 何もしない | RECV を出して完了通知を送る |
| 性格 | 普通の通信と別物 (リモートメモリアクセス) | 普通の通信に近い (メッセージパッシング) |
「片側」「両側」は通信の向きを表してるんやない。送信元の CPU だけを使うのが片側 RDMA、送信元と宛先の両方の CPU を使うのが両側 RDMA、という意味や。
その片側 WRITE が一番効くのが、AI 学習の all-reduce や。リング状に「隣のランクのバッファへ自分のチャンクを直接書く」の繰り返しで、相手 GPU / CPU を起こさず回せる (完了の合図もフラグを書いて相手がポーリング)。数千ランクで毎回相手 CPU を起こしてたら詰むから、CPU ゼロにできる片側 WRITE が選ばれる。両側 (SEND / RECV) はこのデータ本線では基本使わへん (NCCL は片側 WRITE 主体。MPI ベースだと両側を使う実装もある)。
ここから先は、その片側 WRITE で「NIC がどうやってメモリに書くか」を見ていく。
NIC は脳 (OS) が無いのに、なぜメモリに書けるんや
次の疑問はこれやった ── NIC には OS の判断力が無い。なのにメモリにどうやって書くんや?
答えは拍子抜けするほど単純で、NIC は判断してない。
片側 RDMA (WRITE) の全体像は、下の図の 8 ステップや。上半分が準備 (control plane・配管を通すだけ)、下半分が本番 (data plane・アプリデータが流れる)。図を見ながら順に追う。
準備(①〜③・control plane)
「下地」を作る段。データはまだ 1 バイトも流れない。
- ① 両アプリ起動 ── 送信側 A も 宛先 B も、RDMA で通信すると決めて書かれた 1 つのプログラムの両端や。
- ② メモリ登録 ── 各アプリが「このメモリを使う」と登録を呼ぶ。すると CPU (OS) が下の 3 つをやる。鍵は 2 種類出る (A は自分用の lkey、B は相手に渡す rkey)。
- ページを固定 (pin) ── そのメモリが OS のスワップで動かされないよう釘で打つ (動いたら NIC が迷子になる)。
- 変換表を作る ── 「仮想アドレス → 物理アドレス」の対応表を用意する。ただし表の本体はホスト RAM に置く (NIC のチップには載りきらん)。NIC はよく使う分だけ TLB のようにキャッシュし、無い番地は RAM から引きに行く。CPU の MMU と TLB と同じ仕掛け。
- 鍵を発行 ── その領域への許可証を作る。
- ③ 番地 + rkey を送信側へ渡す ── 送信側 (A) は B のメモリの空きなんか知らん。空きを把握して割り当てるのは自分のメモリを持つ B。だから B が、ふつうの経路 (TCP ソケットや RDMA-CM) で「この番地から N バイト、鍵は rkey、使ってええ」と A に渡す。
本番(④〜⑧・data plane)
下地ができたら、アプリデータが流れる。CPU が出てくるのは ④ の一瞬だけ。
- ④ 指示書 + ドアベル (制御) ── A の CPU が「番地 X から M バイト転送せよ」という指示書 (descriptor) をキューに置いてドアベルを鳴らすだけ。データ本体は入ってない (ポインタだけ)。データ量に関係なく一定の極小コスト (1GB でも 1 枚)。キューはユーザー空間に見えてるのでカーネルを呼ばず直接書ける (kernel bypass)。
- ⑤ アプリデータを DMA read ── 送信側 NIC が指示書を見て、ペイロードをホスト RAM から DMA で吸い上げる。PCIe デバイスは「バスマスタ」── CPU を介さず自分でバスを駆動して RAM を読める ── やからこれができる。
- ⑥ パケットが線を渡る ──
[番地 + rkey | アプリデータ]が送信 NIC から受信 NIC へ。番地と rkey は宛名 (殻)、その後ろにアプリデータが乗る。 - ⑦ 照合・変換 ── 受信側 NIC が届いた rkey を照合 → 番地を物理に変換。合えば通す、違えば蹴る。
- ⑧ アプリのメモリへ DMA write ── 受信側 NIC が B の CPU をオフロードして、アプリのメモリへ直書き。B の CPU は寝たまま (書かれた事すら知らん)。
NIC がやってるのは「鍵の照合」「番地の変換」「DMA」だけ。どれも判断やなく、配線された専用回路 (ステートマシン) が、指示書や登録情報のとおり機械的に動くだけ。スイッチ ASIC が脳なしでパケットを転送するのと同じや。だから脳が無くてもできる。
片側 RDMA と両側 RDMA の違いをもう少し
RDMA の操作には「片側 (One-Sided)」と「両側 (Two-Sided)」の 2 種類があります。違いは、転送のたびに受信側の CPU が関与するかどうかです。
片側 RDMA (One-Sided)
イニシエータ (送信側) だけが操作を開始し、ターゲット (受信側) は受動的です。送信側が受信側のメモリ番地と rkey を指定してデータを送信し、受信側 NIC が受信側メモリに DMA で書き込みます (または読み出します)。受信側の CPU は関与せず、書かれたことも知りません。
両側 RDMA (Two-Sided)
送信側と受信側の両方が操作を出します。送信側が SEND を出し、受信側はあらかじめ RECV (受け皿バッファ) を出しておきます。データが届くと、受信側に完了通知が入ります。普通のメッセージのやり取り (送る / 受け取る) に近く、受信側の CPU も関与します。
比較
| 項目 | 片側 (WRITE / READ) | 両側 (SEND / RECV) |
|---|---|---|
| 受信側 CPU の関与 | しない (書かれたことを知らない) | する (完了通知で気づく) |
| 受信側の事前準備 | 不要 | RECV (受け皿) を先に出す |
| 送信側が知る情報 | 相手の番地 + rkey | 不要 |
| モデル | リモートメモリアクセス | メッセージパッシング |
| 代表 API | rdma_write / rdma_read | rdma_send / rdma_recv |
どちらを選ぶか
- 片側:相手の CPU を完全にゼロにできます。AI の all-reduce のように、数千ノードで相手を一切起こしたくない場合に使います。代わりに、番地と rkey の事前交換が必要です。
- 両側:受信側が完了通知で受信を知れるため、プログラムが書きやすくなります。番地の交換も不要です。ただし、受信側の CPU が毎回わずかに関与します。
出てきた言葉の変換表
| 出てきた言葉 | つまり何の話? |
|---|---|
| RDMA | Remote Direct Memory Access。相手の CPU・OS カーネルを介さず、相手側の NIC がそのマシンのメモリへ直接読み書きする仕組み |
| zero copy | 途中のカーネルメモリを経由せず、目的地のメモリへ直に置くこと。コピー回数 0 |
| kernel bypass | OS カーネルの通信処理 (TCP/IP スタック) を通らないこと。CPU が経路外になる |
| DMA | デバイスが CPU を介さず RAM を直接読み書きする標準機能。RDMA の「直書き」の実体 |
| メモリ登録 (Memory Registration) | 本番前に、使うメモリを pin し・変換表を NIC に渡し・rkey を発行する段取り。判断の前倒し |
| rkey (remote key) | 登録した領域への許可証。リモート書き込みで照合される鍵 |
| lkey / rkey | 登録で出る 2 つの鍵。lkey は自分用 (自マシンの NIC が読む)、rkey は相手に渡す用 (リモート書き込みで照合) |
| 片側 / 両側 | WRITE / READ (相手 CPU 不在) と SEND / RECV (受け手が受け皿を積み完了通知で起きる) |
| ドアベル (doorbell) | NIC のレジスタへの 1 回の書き込み (MMIO)。「キューを見ろ」と NIC に知らせる合図。比喩でなく正式な用語 |
RDMA の核 ── CPU を飛ばして NIC が直書きする仕組みは掴めた。次は「その RDMA を Ethernet で安定して走らせる所」と「GPU でどう効くか」。
- RoCE の lossless 問題と PFC ── 次はこれが効く。 普通の Ethernet は混んだら落とす。落ちたら困る RDMA を乗せるには、スイッチで「待った」をかけて落とさせない仕組み (PFC) が要る。RoCE 運用のキモで、ここが分かると次の Ultra Ethernet が何を直すのか読める。
- Ultra Ethernet ── RoCE の弱点を Ethernet 陣営が束で作り直す新規格。 「なぜ今 AI 向けに新しい Ethernet が要るのか」を、RoCE の不満点から逆算して読む段。
- GPUDirect RDMA と集団通信 (all-reduce) ── RDMA の直書き先を GPU のメモリにすると何が起きるか。AI 学習で片側 WRITE がなぜ効くかの実地。