--- GPU内からROCEの通信(GPU Direct)はどのように行われているのか? https://deepwiki.com/search/ib-verbs_2ced64b3-c70c-4606-abe3-71e71393876c GPU内からRoCE(RDMA over Converged Ethernet)の通信(GPU Direct)について、NCCLのコードベースから具体的な実装を説明します。 ## GPU DirectとRoCEの統合 NCCLでは、GPU DirectとRoCEの組み合わせにより、GPU内のデータを直接ネットワーク経由で送信することが可能です。これは主に`src/transport/net_ib.cc`で実装されています。 ## GDR(GPU Direct RDMA)サポートの検出 まず、システムがGDRをサポートしているかを確認します: [1](#2-0) この関数では、nv_peer_memモジュールやnvidia_peermemモジュールの存在を確認してGDRサポートを判定しています。 ## DMA-BUFサポートの検出 CUDA 11.7以降では、DMA-BUFを使用したより効率的なGPU Directが利用可能です: [2](#2-1) ## RoCEデバイスの識別 RoCEとInfiniBandを区別するため、リンク層の情報を取得します: [3](#2-2) この定義により、`IBV_LINK_LAYER_ETHERNET`の場合はRoCEとして識別されます。 ## GPU Directメモリ登録 GPU内のメモリをRoCEネットワークで使用するため、メモリ登録が行われます: [4](#2-3) DMA-BUFがサポートされている場合は、より効率的な登録方法が使用されます: [5](#2-4) ## トポロジーベースのGDR判定 システムトポロジーに基づいて、GPU DirectとRoCEの組み合わせが有効かを判定します: [6](#2-5) この関数では、GPUとNICの距離、C2Cシステムでの特別な処理、PCI経由のアクセスなどを考慮してGDRの使用可否を決定します。 ## RoCE接続の確立 RoCE接続では、GIDとEthernetレイヤーの情報が使用されます: [7](#2-6) ## GPU Directの実際の通信 GPU内のデータは、RDMA書き込み操作を通じて直接ネットワーク経由で送信されます: [8](#2-7) ## Notes GPU DirectとRoCEの組み合わせにより、GPU内のデータをCPUを経由せずに直接ネットワーク経由で送信できます。これは、nv_peer_memモジュールやDMA-BUFサポートを通じて実現され、システムトポロジーに基づいて最適な方法が選択されます。RoCEの場合は、Ethernetレイヤー上でRDMA操作が実行され、高いスループットと低いレイテンシを実現しています。 Wiki pages you might want to explore: - [Transport Layer (NVIDIA/nccl)](/wiki/NVIDIA/nccl#2.2) - [Topology System (NVIDIA/nccl)](/wiki/NVIDIA/nccl#2.3) ``` ncclResult_t ncclIbMultiSend(struct ncclIbSendComm* comm, int slot, void* pHandle) { struct ncclIbRequest** reqs = comm->fifoReqs[slot]; volatile struct ncclIbSendFifo* slots = comm->fifo[slot]; int nreqs = slots[0].nreqs; if (nreqs > NCCL_NET_IB_MAX_RECVS) return ncclInternalError; uint64_t wr_id = 0ULL; for (int r=0; r<nreqs; r++) { struct ibv_send_wr* wr = comm->wrs+r; memset(wr, 0, sizeof(struct ibv_send_wr)); struct ibv_sge* sge = comm->sges+r; sge->addr=(uintptr_t)reqs[r]->send.data; wr->opcode = IBV_WR_RDMA_WRITE; wr->send_flags = 0; wr->wr.rdma.remote_addr = slots[r].addr; wr->next = wr + 1; wr_id += (reqs[r] - comm->base.reqs) << (r*8); #ifdef NCCL_ENABLE_NET_PROFILING reqs[r]->pInfo[0].nEventHandles = 0; #endif } // Write size as immediate data. In the case of multi-send, only write // 0 or 1 as size to indicate whether there was data sent or received. uint32_t immData = 0; if (nreqs == 1) { immData = reqs[0]->send.size; } else { int* sizes = comm->remSizesFifo.elems[slot]; for (int r=0; r<nreqs; r++) sizes[r] = reqs[r]->send.size; comm->remSizesFifo.sge.addr = (uint64_t)sizes; comm->remSizesFifo.sge.length = nreqs*sizeof(int); } ```