NCCL 系列之深入解析 NCCL 拓撲建模
一、概覽
1.1 引言
在大規模分布式訓練中,NCCL 的拓撲發現和建模是確保高效通信的核心機制,可以幫助建立硬件感知的通信優化,比如充分利用異構帶寬,避免通信成為瓶頸,會直接影響訓練的性能和擴展性。
對應的核心代碼位于:
- init.cc:nccl/src/init.cc at master · NVIDIA/nccl · GitHub [1]
- topo.cc:nccl/src/graph/topo.cc at master [2]
1.2 NCCL 初始化拓撲建模
如下圖所示為 NCCL 初始化時拓撲相關的核心流程,主要負責系統拓撲發現、路徑建模、設備親和性設置、通信通道構建、特性檢測與初始化等,它是 NCCL 多 GPU 通信的基礎。
- ncclTopoGetSystem 自動探測或解析 XML,構建 NCCL 內部的系統拓撲結構(CPU、GPU、PCIe、NIC、NVLink等)。(PS:本文的工作也主要聚集在這一部分)
- ncclTopoComputePaths 基于拓撲結構,計算所有 GPU 與 NIC 之間的最短/最優通信路徑(考慮帶寬、跳數等)。
- ncclTopoTrimSystem 移除不可達的 GPU、未用到的 NIC,精簡拓撲。
- ncclTopoComputePaths 修剪后重新計算路徑,確保路徑信息準確。
- ncclTopoSearchInit 為后續的通信算法(如 Ring、Tree、CollNet 等)初始化搜索輔助結構。
- ncclTopoPrint 輸出最終的系統拓撲,便于調試和分析。
- 設置 CPU 親和性:將當前線程綁定到本地 GPU 最近的 NUMA 節點,保證主機內存分配的局部性。
- 檢測 CollNet 支持:檢測當前環境和硬件是否支持 CollNet(多節點高效集合通信)。
- 檢測 NVLS 支持:檢測并初始化 NVLS(NVIDIA NVLink Switch)支持。
1.3 關鍵類型(node & link & path)
在 NCCL 拓撲中有 3 個比較關鍵的概念,分別為 node、link 和 path:
- node:表示系統中的一個硬件組件,是 NCCL 拓撲的基本單元。
- link:表示兩個 node 之間的直接物理連接。
- path:表示從一個 node 到另一個 node 的通信路徑,可以經過多個 link。
如下圖所示,node 類型常見的 GPU、NIC、CPU 外還可以是 PCI、NVS(NVSwitch)以及 NET:
- NIC 通常指的是物理網卡,比如 Mellanox 或 Broadcom 的 PCIe 網卡。
- NET 通常是指網絡端點,比如 NIC 上的一個網絡端口,一個 NIC 可以有多個物理端口。
如下圖所示,link 類型包括如下幾種:
- LOC:表示 Local 自環連接。
- NVL:表示 NVLink 連接,包括 GPU 與 GPU 之間或者 GPU 與 NVSwitch 之間。
- PCI:表示 PCIe 連接,比如 GPU 與 NIC 之間的 PCIe 通道。
- SYS:表示系統總線連接,比如 NUMA 節點間的 QPI/UPI/Infinity Fabric 等。
- NET:表示通過網絡設備(如以太網,IB 或 RoCE)進行互聯。
如下圖所示,path 類型包括如下幾種(與通信路徑密切相關):
1.4 硬件拓撲
1.4.1 常見 8 GPU 服務器拓撲
如下圖所示為常見的 8 GPU 服務器拓撲,通常包括:
- 2 個 CPU,CPU 之間通過 QPI 連接。
- 每個 CPU 下有 2 個 PCIe Switch(比如常見的 Broadcom PEX89104,PEX89144),總共 4 個。
- 每個 PCIe Switch 下連接 2 個 GPU,2 個 NIC(如果一臺服務器 4 個后向 NIC,這里每個 PCIe Switch 有 1 個 NIC),一些 NVMe。
- 8 個 GPU 通過 NVLink + NVSwitch 實現全互聯。
如果每個 PCIe Switch 兩個 GPU + 兩個 NIC,那么 GPU 通過 NIC 通信時應該選擇哪一個呢?有兩種常見的方式:
- 直接使用 8 個 物理 PCIe Switch,每個下面都有一個 GPU 和 一個 NIC。(PS:這種方式如果只有 4 個 NIC,則兩個 GPU 對應一個 NIC 的距離會不同。)
- 將一個物理 PCIe Switch 虛擬化為兩個邏輯 Virtual PCIe Switch。(PS:這種方式的好處是如果只有 4 個 NIC,只要調整虛擬化,兩個 GPU 與 NIC 的距離就可以相同。)
1.4.2 IBM Power9
如下圖所示,IBM Power System AC922 Introduction and Technical Overview [3] 包含了 Power9 服務器中 CPU 和 GPU 的連接方式,可以看出,其 Power9 芯片會與 GPU 直接通過 NVLink 連接。
如下圖所示為完整的拓撲:
1.3.3 NVIDIA Grace CPU & GPU
NVIDIA 從 Hopper 架構開始推出了 Grace 架構,比如 GH200 以及后續的 GB200。如下圖所示為 GH200 的架構,其 Grace CPU 與 GPU 之間可以通過 NVLink C2C 連接;而 GPU 與 GPU 之間依舊可以通過 NVLink 連接。
1.4.4 GB200 NVL72 Superpod
常見的 NVLink + NVSwitch 全互聯通常在一臺單機內,比如常見的單機 8 GPU 服務器。而 Superpod 中,比如常見的 NVL72,將 NVLink + NVSwitch 全互聯的范圍進一步擴展到單個機柜甚至多個機柜內。如下圖所示,DGX GB200 NVL72 將其擴展到一個機柜的 72 個 B200 GPU。
二、拓撲構建入口函數 ncclTopoGetSystem
NCCL 拓撲構建相關代碼在 topo.cc 文件中,其入口函數為 ncclTopoGetSystem,包含幾個主要的部分:
1??初始化 xmlTopo 文件:
- 如果指定對應的 NCCL_TOPO_FILE 文件,則從中加載。
- 否則嘗試從默認路徑加載。
- 如果都沒有加載成功,創建相應節點。
2??GPU 自動檢測:
- 遍歷所有通信證中的 Rank,篩選出和當前進程處于同一個 Host(hostHash)的所有 GPU。
獲取 PCI Bus ID
創建對應的 XML 節點
設置屬性
keep 為 1 表示保留節點。
rank 為對應的 rank 值。
gdr 表示是否支持 GPUDirect RDMA。
3??NIC 自動檢測:
- 優先處理 CollNet 網絡設備
通過 collNetGetProperties 獲取每個 NIC 的屬性,比如 pciPath、名稱、速率、port、guid、最大連接數、gdr 支持等。
使用 ncclTopoFillNet 填充 XML 文件,并設置節點屬性。
- 補充常規網絡設備
- 通過 comm->ncclNet->devices 獲取常規網絡設備屬性。
- 使用 ncclTopoFillNet 填充 XML 文件,并設置節點屬性。
4??MNNVL 信息補全:
- NCCL 在啟用 MNNVL(Multi-Node NVLink Clique,比如 Support Pod,NVL72, NVL256)功能時,對多節點 NVLink 拓撲信息的收集與融合。
為整個 clique(NVLink 互聯的節點集合)分配一個足夠大的共享內存,每個相關 rank 都有對應空間。
使用 bootstrapIntraNodeAllGather 進行所有 rank 的 XML 結構同步。
分配一塊新的區域存放融合后的 clique 拓撲。
遍歷所有 rank,依次融合到 clique 的 XML 結構中,形成全局的 clique 拓撲。
釋放原有 XML 拓撲。
5??Dump XML 拓撲:
- 如果配置了 NCCL_TOPO_DUMP_FILE,將 XML 拓撲保存到文件中。
6??根據 XML 生成 NCCL 拓撲:
- 調用 ncclTopoGetSystemFromXml 生成 NCCL 拓撲。
三、XML 拓撲 -> NCCL 拓撲
如下圖所示,ncclTopoGetSystemFromXml 主要包含 8 個步驟:
1??初始化 ncclTopoSystem 結構。
2??查找系統頂層節點 “system”。
3??遍歷 system 節點下所有子節點,查找并調用 ncclTopoAddCpu 添加 <cpu> 節點(包括 <pic> 和 <nic>)。
4??設置本地 Host 的 systemId。
- systemId 用于唯一標識當前進程所在的 Host(或 NUMA 域)在整個多節點拓撲中的位置。
- 在多機多卡場景下,systemId 能幫助 NCCL 管理和區分不同主機的硬件資源,實現跨主機的高效通信和調度。
5??添加 NVLink 拓撲連接。
- 調用 ncclTopoAddNvLinks 遞歸添加 NVLink 連接,建立 GPU-GPU、GPU-CPU、GPU-NVS 等 NVLink 拓撲。
6??添加 C2C 拓撲連接。
- 調用 ncclTopoAddC2c 遞歸解析 C2C(CPU-to-CPU 或 GPU-to-CPU)連接信息,并建立相應的鏈路。
7??拓撲結構優化與補全。
- ncclTopoFlattenBcmSwitches:將 BCM Gen4/Gen5 PCIe 交換芯片的多級結構“扁平化”,避免多級結構影響路徑搜索。
- ncclTopoConnectCpus:將同一系統下的所有 CPU 節點互聯,建模 NUMA 節點之間的系統總線。
- ncclTopoSortSystem:對整個系統的拓撲進行排序,優化后續遍歷和搜索效率。
四、添加 CPU 節點(ncclTopoAddCpu)
ncclTopoAddCpu 用于將一個 CPU 節點(通常對應一個 NUMA 節點)及其下屬的 PCI、NIC 子節點遞歸添加到 NCCL 內部的系統拓撲結構中。
- 解析 XML 拓撲描述文件中的 <cpu> 節點,提取 NUMA id、架構、廠商、親和性等屬性。
- 創建并初始化 NCCL 拓撲中的 CPU 節點,并遞歸添加其下掛的 PCI 設備、NIC 設備等。
- 為后續 NCCL 通信路徑建模和優化提供準確的硬件結構基礎。
1??獲取 NUMA 節點編號。
- 從 XML 節點讀取 numaid 屬性,確定該 CPU 節點的 NUMA id。
2??獲取 systemId。
- 通過 Host 哈希等信息,確定該 CPU 節點屬于哪個物理 Host。
3??創建 CPU 節點。
- 在 NCCL 拓撲結構中創建一個新的 CPU 節點,唯一 id 由 systemId 和 numaId 組合而成。
4??設置 CPU 親和性(affinity)。
- 如果 XML 中有 affinity 屬性,則解析為 CPU 親和性掩碼,記錄該 NUMA 節點可用的 CPU Core 集合。
5??設置 CPU 架構、廠商和型號。
- 讀取并設置 CPU 架構(如 x86_64、arm64、ppc64)。
- 如果是 x86 架構,還會進一步讀取廠商(Intel、AMD)和型號(如 Skylake、Broadwell)等信息。
6??遞歸處理子節點。
- 如果是 <pci> 節點,遞歸調用 ncclTopoAddPci,將 PCI 設備(如 GPU、PCIe Switch、NIC 等)掛到該 CPU 節點下。
- 如果是 <nic> 節點,查找或創建 NIC 節點,并與 CPU 節點建立 PCI 鏈路,然后遞歸添加 NIC 下的網絡設備。
五、添加 PCI 節點(ncclTopoAddPci)
ncclTopoAddPci 用于遞歸解析 XML 拓撲文件中的 <pci> 節點,將 PCI 設備(包括 PCIe Switch、GPU、NIC 等)及其下屬設備添加到 NCCL 內部的系統拓撲結構中,并建立相應的鏈路。
- 支持多層級 PCIe 結構(如 PCIe Switch 下掛 GPU/NIC/子 Switch 等)。
- 能自動識別并處理 GPU、NIC 等特殊設備,并遞歸處理其下屬節點。
- 為 NCCL 拓撲建模提供完整的 PCIe 層次結構。
1??解析設備類型和 BusId。
- 讀取 <pci> 節點的 class 屬性,確定設備類型(PCI、GPU、NIC、NVS 等)。
- 讀取 busid 屬性,轉為唯一的 64 位 BusId。
2??處理 GPU 子節點。
- 如果該 PCI 設備下有 <gpu> 子節點,則將其視為 GPU 設備。
- 創建 GPU 節點(ncclTopoCreateNode),并遞歸設置其屬性(ncclTopoAddGpu,如 rank、sm、dev、gdr 等)。
3??處理 NIC 子節點。
- 如果該 PCI 設備下有 <nic> 子節點,則將其視為 NIC 設備。
- 合并多端口 NIC(忽略 sub device id),保證同一物理 NIC 只建一個節點。
- 創建 NIC 節點,并遞歸添加其下屬網絡設備(ncclTopoAddNic)。
4??處理普通 PCI 設備(如 PCIe Switch)。
- 如果是普通 PCI 設備(如 PCIe Switch),則創建節點,并遞歸處理其所有子 PCI 設備。
- 解析并組合 vendor/device/subsystem_vendor/subsystem_device 字段,唯一標識該 PCI 設備。
5??建立連接。
- 如果本節點有效,則根據 link_width 和 link_speed 屬性,計算 PCIe 鏈路帶寬(單位 GB/s)。
- 在本節點和父節點之間建立雙向 PCI 鏈路(ncclTopoConnectNodes)。
六、添加 NVLink 拓撲連接(ncclTopoAddNvLinks)
ncclTopoAddNvLinks 函數的作用是遞歸解析 XML 拓撲描述中的 NVLink 連接信息,并在 NCCL 內部拓撲結構中建立 GPU-GPU、GPU-CPU、GPU-NVS 等 NVLink 鏈路。具體流程如下:
1??判斷節點類型:
- 如果當前 XML 節點是 "nvlink",說明需要添加一條 NVLink 連接。如下圖紅框中所示節點。
2??定位本地 GPU 節點:
- 通過 parentBusId 和 systemId 計算出本地 GPU 的唯一 ID,并在系統拓撲中查找對應的 GPU 節點。
3??獲取 NVLink 連接目標類型和目標節點:
- 讀取 “tclass” 屬性,判斷目標類型(nvlink 的節點類型只可能是這 3 種,GPU、CPU 或 NVS)。
- 如果目標是 GPU,則根據 target 屬性找到目標 GPU 節點。(PS:對應 GPU 直接通過 NVLink 連接的方式,沒有 NVSwitch)
- 如果目標是 CPU,則查找與本地 GPU 最近的 CPU 節點。(PS:目前這種方式主要是 IBM Power8/9 等場景中存在 CPU 和 GPU 直接通過 NVLink 連接的方式)
- 如果目標是 NVS(NVSwitch),則查找或創建 NVS 節點。(PS:主要是指包含 NVSwitch 的場景)
4??建立 NVLink 連接:
- 計算 NVLink 帶寬(根據 GPU 計算能力和 NVLink 數量)。
- 在本地 GPU 和目標節點之間建立 NVLink 鏈路(雙向或單向,取決于目標類型)。
5??遞歸處理子節點:
- 如果當前節點不是 <nvlink>,則遞歸處理其所有子節點,繼續查找和添加 NVLink 連接。
七、添加 C2C 拓撲連接(ncclTopoAddC2c)
ncclTopoAddC2c 作用是遞歸解析 XML 拓撲描述中的 C2C(CPU-to-CPU 或 GPU-to-CPU)連接信息,并在 NCCL 內部拓撲結構中建立相應的鏈路。
1??處理 c2c 節點(如果當前節點名為 "c2c",則表示需要添加一個 C2C 連接):
- 首先通過 parentBusId 和 systemId 計算出本地 GPU 的唯一 ID,并在系統拓撲中查找對應的 GPU 節點。
- 讀取該連接的 count(鏈路數量)和 bw(單鏈路帶寬),計算總帶寬 c2cBw。
- 查找與該 GPU 最近的 CPU 節點(NUMA 歸屬)。
- 在 GPU 和 CPU 之間建立雙向的 NVL 類型鏈路(沒有 c2c 鏈路,依然是 NVLink),帶寬為 c2cBw。
2??遞歸處理子節點(如果當前節點不是 "c2c",則遞歸處理其所有子節點):
- 如果當前節點是 <cpu>,則更新 systemId(用于多主機場景)。
- 獲取當前節點的 busid 屬性,作為后續遞歸的父 busId。
- 對每個子節點遞歸調用 ncclTopoAddC2c,傳遞合適的 busId 和 systemId。
八、BCM PCIe 結構扁平化(ncclTopoFlattenBcmSwitches)
ncclTopoFlattenBcmSwitches 的作用是將 Broadcom(BCM)Gen4/Gen5 PCIe 交換芯片的兩級(多級)結構“扁平化”,簡化為單層結構。
- 這些 BCM PCIe 交換芯片(如 PLX/PEX)在硬件上可能以多級樹狀結構呈現,但實際上它們可以提供全帶寬的任意端口互聯。
- 如果不做處理,NCCL 拓撲搜索時會被多級結構誤導,導致通信路徑選擇不最優,甚至出錯。
- 該函數通過識別并合并這些多級結構,把所有下掛設備直接連接到頂層交換芯片節點,消除中間多余的“子交換芯片”節點。
遍歷所有 PCI 類型節點(依次檢查系統中每一個 PCI 交換芯片節點):
1??通過getBcmGen函數判斷當前節點是否為 BCM Gen4/Gen5 芯片。
2??查找所有子交換芯片。
- 遍歷當前交換芯片的所有鏈路,找出所有下掛的、同型號的子交換芯片節點(即二級交換芯片)。
- 記錄這些子交換芯片的 id,并從父交換芯片的鏈路中移除對這些子交換芯片的連接。
3??將所有子交換芯片下掛的設備提升到父交換芯片下,對每一個子交換芯片。
- 遍歷其所有鏈路,把所有下掛設備(除了父交換芯片本身)都直接連接到父交換芯片。
- 同時,把這些設備指向子交換芯片的鏈路改為指向父交換芯片。
4??刪除子交換芯片節點。
- 從系統中移除這些已經被“吸收”的子交換芯片節點。
5??標記父交換芯片已處理。
- 修改父交換芯片的 device 字段,防止后續重復處理。
6??重啟遍歷。
- 由于節點數組發生變化,從頭開始遍歷,直到所有可合并的結構都被處理完。
九、建立 CPU 連接(ncclTopoConnectCpus)
ncclTopoConnectCpus 的作用是在 NCCL 拓撲系統中,將同一物理主機(systemId 相同)下的所有 CPU 節點(通常對應 NUMA 節點)兩兩互聯,為每對 CPU 節點之間建立一條系統總線(SYS)類型的鏈路,并設置合適的帶寬。這樣可以準確建模多路 NUMA 架構下 CPU 之間的互聯關系,為 NCCL 后續的通信路徑選擇和帶寬估算提供基礎。
十、拓撲排序(ncclTopoSortSystem)
ncclTopoSortSystem 的主要作用是對 NCCL 內部的系統拓撲結構進行排序和規范化,以便后續遍歷、路徑搜索和調度更加高效和一致。具體來說,它會遞歸地對每個 CPU 節點(通常是 NUMA 節點)為根的拓撲樹進行排序,調整各節點的鏈路順序,使得:
- PCIe 下行鏈路、上行鏈路、NVLink、SYS(系統總線)等類型的鏈路排列有序。
- 拓撲結構的遍歷順序更加合理,便于后續算法(如最短路徑、帶寬統計等)高效執行。
十一、建立連接(ncclTopoConnectNodes)
ncclTopoConnectNodes 函數用于在 NCCL 拓撲結構中為兩個 node 之間建立一條鏈路(link),并設置鏈路類型(如 NVLink、PCI、NET 等)和帶寬(bw)。如果鏈路已存在,則聚合帶寬;否則新建鏈路。最后,所有鏈路會按照帶寬從高到低排序,便于后續路徑選擇和優化。
如下圖所示,在不同的場景中創建對應的連接類型就行:
十二、參考鏈接
- https://github.com/NVIDIA/nccl/blob/master/src/init.cc
- https://github.com/NVIDIA/nccl/blob/master/src/graph/topo.cc
- https://www.redbooks.ibm.com/redpapers/pdfs/redp5472.pdf
本文轉載自????????AI閑談????????,作者:AI閑談
