偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

NVSHMEM 深度解析:初始化流程與核心機(jī)制

發(fā)布于 2025-7-11 07:30
瀏覽
0收藏

?一、背景

在此前的內(nèi)容中,筆者曾介紹過 DeepSeek 的 DeepEP、字節(jié)跳動(dòng)的 Flux 和 Tilelink 等系統(tǒng),這些系統(tǒng)在底層通信實(shí)現(xiàn)中均依賴于 NVIDIA 的 NVSHMEM 庫。事實(shí)上,字節(jié)跳動(dòng)后續(xù)的諸如 Comet、Triton-distributed,以及其他針對(duì)細(xì)粒度計(jì)算與通信重疊(Overlap)優(yōu)化的工作,也都廣泛使用了 NVSHMEM。

本文將深入剖析 NVSHMEM 的初始化流程及其核心概念,以便從開發(fā)者視角理解其機(jī)制,為后續(xù)的定制化改造和工程實(shí)踐打下基礎(chǔ)。?

也可以參考 NVSHMEM 的官方文檔:NVIDIA OpenSHMEM Library (NVSHMEM) Documentation [1]

二、引言

2.1 DeepEP

DeepEP 是 DeepSeek 開源的專為 MoE 和專家并行(Expert Parallelism, EP)設(shè)計(jì)的通信庫。提供了一系列優(yōu)化的通信 Kernel,實(shí)現(xiàn)了以下能力:

  • 高度優(yōu)化的 All2All 通信。
  • 同時(shí)支持不同的通信類型:

節(jié)點(diǎn)內(nèi)(intra-node):使用 NVLink + NVSwitch 通信。

節(jié)點(diǎn)間(inter-node):使用 RDMA 通信。

  • 針對(duì)不同場景的 Kernel:
  • 常規(guī)(高吞吐) Kernel(Normal Kernel):針對(duì) Training 和 Inference Prefill。節(jié)點(diǎn)內(nèi) NVLink + 節(jié)點(diǎn)間 RDMA 通信。
  • 低時(shí)延 Kernel(Low-Latency Kernel):針對(duì) Inference Decoding。使用純 RDMA 通信來最小化時(shí)延。
  • 原生支持 FP8,減少數(shù)據(jù)傳輸需求,相比 FP16 通信量減半。
  • 靈活的 GPU 資源(SM)控制,支持計(jì)算和通信的 Overlap。

代碼庫:DeepEP: an efficient expert-parallel communication library [2]

2.2 NVSHMEM 簡介

NVSHMEM 是 NVIDIA 開發(fā)的一種并行編程接口,基于 OpenSHMEM 標(biāo)準(zhǔn),專為 GPU 集群提供高效且可擴(kuò)展的通信。通過創(chuàng)建一個(gè)跨多個(gè) GPU 內(nèi)存的全局地址空間,實(shí)現(xiàn)細(xì)粒度的 GPU 發(fā)起的數(shù)據(jù)傳輸和同步操作,顯著減少了 CPU 的干預(yù),從而降低同步開銷并提升性能。

NVSHMEM 通常被視為 MPI(Message-Passing Interface) 的替代方案,特別是在 GPU 集群通信中。與 MPI 不同,NVSHMEM 通過 GPU 發(fā)起操作減少了 CPU-GPU 同步開銷。雖然它可以與 MPI 結(jié)合使用(如在 Host 端通信中),但其核心優(yōu)勢(shì)在于 GPU 間的直接通信。相比 NCCL,NVSHMEM 更專注于單邊通信,而 NCCL 則更適合集合通信,具體使用場景取決于應(yīng)用程序的需求。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

安裝指南和源代碼可以參考:NVSHMEM Installation Guide [3]

2.3 IBRC & IBGDA

InfiniBand GPUDirect Async(IBGDA)是 NVSHMEM 中的一種新的通信方法,構(gòu)建在 GPUDirect Async 技術(shù)之上。IBGDA 在 NVSHMEM 2.6.0 中引入,并在 NVSHMEM 2.7.0 和 2.8.0 中得到顯著改進(jìn)。它可以使 GPU 繞過 CPU 進(jìn)行節(jié)點(diǎn)間 NVSHMEM 通信,而無需對(duì)現(xiàn)有應(yīng)用程序進(jìn)行修改,進(jìn)而使得 NVSHMEM 應(yīng)用程序的吞吐和擴(kuò)展得到顯著改進(jìn)。

如下圖所示,在引入 IBGDA 之前,NVSHMEM InfiniBand Reliable Connection (IBRC) 傳輸使用 CPU 上的代理線程來管理通信(PS:也就是數(shù)據(jù)傳輸不同用過主機(jī)內(nèi)存,但是很多控制鏈路還在 CPU 上)。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

與 IBRC 相比,IBGDA 利用 GPUDirect Async–Kernel-Initiated (GPUDirect Async–KI) ,使得 GPU SM 能夠直接與 NIC 交互。如下圖所示為其關(guān)鍵步驟,可以看出,IBGDA 將 CPU 上的通信控制鏈路移到 GPU 上,WQ 和 DBR 緩沖區(qū)也被移動(dòng)到 GPU 內(nèi)存。使用 IBGDA 時(shí),GPU 和 NIC 直接交換通信所需信息,以提高 SM 訪問效率,同時(shí)通過 GPUDirect RDMA 保留 NIC 的訪問。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

因此,IBGDA 非常適合控制鏈路開銷比較大的小消息的傳輸。?

三、NVSHMEM 關(guān)鍵概念

3.1 Processing Element(PE)

NVSHMEM 程序由多個(gè)進(jìn)程組成,每個(gè)進(jìn)程映射到一個(gè) GPU 上,稱為一個(gè)PE。所有 PE 運(yùn)行相同的程序,在啟動(dòng)時(shí)通過 nvshmem_init 或擴(kuò)展接口 nvshmemx_init_attr 集體初始化環(huán)境。

初始化過程中,每個(gè) PE 都會(huì)分配到唯一的 ID(可以使用 nvshmem_my_pe() 獲取本 PE 的 ID),并且同步總的 PE 數(shù)目(可以使用 nvshmem_n_pes() 獲?。?。假設(shè)有 2 臺(tái) 8 卡 H100 的機(jī)器,則啟動(dòng)后會(huì)有 16 個(gè) PE,對(duì)應(yīng)的 mype 為 [0, 1, 2, …, 15],對(duì)應(yīng)的 npes 為 16。

PE 之間通過從 GPU 內(nèi)存中的對(duì)稱堆(symmetric heap)分配的對(duì)稱內(nèi)存進(jìn)行通信和數(shù)據(jù)共享。此類內(nèi)存需使用 CPU 端的 NVSHMEM 分配 API 進(jìn)行分配。采用其他任何方法分配的內(nèi)存均被視為分配 PE 的私有內(nèi)存,其他 PE 無法訪問。NVSHMEM 使用 CUDA 的統(tǒng)一虛擬地址 (UVA) 和 CUDA IPC 機(jī)制映射這些對(duì)稱緩沖區(qū),以實(shí)現(xiàn) GPU 之間的高速訪問。在多 GPU 或分布式場景中,GPU 可直接發(fā)起通信請(qǐng)求,NVSHMEM 后端負(fù)責(zé)將這種 GPU 觸發(fā)的請(qǐng)求通過網(wǎng)絡(luò)傳輸?shù)侥繕?biāo) PE。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

需要注意,從 2.4.1 版開始,NVSHMEM 支持一個(gè) GPU 上運(yùn)行多個(gè) PE 的情形(通過 CUDA 多進(jìn)程服務(wù) MPS 或時(shí)間共享方式),但這會(huì)對(duì)同步和集合通信的使用有額外限制。

3.2 Team 管理

NVSHMEM 支持在全局 PE 集合之外定義邏輯 Team(nvshmem_team_t),以在一部分 PE 上執(zhí)行集合通信操作。默認(rèn)情況下,NVSHMEM_TEAM_WORLD 表示包含所有 PE 的“全局 Team”,PE 在該 Team 中的編號(hào)即為 nvshmem_my_pe() 返回的值。

用戶可以通過“split”現(xiàn)有 Team 來創(chuàng)建新 Team(如 nvshmem_team_split_strided() 等),也可以使用擴(kuò)展接口 nvshmemx_team_get_uniqueid 和 nvshmemx_team_init 任意指定新 Team 成員。后者需要選定一個(gè) Root PE 來生成 Team 的唯一 ID,并將此 ID 分發(fā)給 Team 內(nèi)所有 PE。創(chuàng)建 Team 時(shí),每個(gè) PE 提供在新 Team 中的編號(hào)(pe_idx_in_team),新 Team 成員按照該編號(hào) 0 到 N–1 重新編號(hào)。

3.3 Transport 機(jī)制

NVSHMEM 支持多種傳輸通道來實(shí)現(xiàn) GPU 間通信。

  • 對(duì)于節(jié)點(diǎn)內(nèi)通信,NVSHMEM 主要利用 CUDA 級(jí)聯(lián)技術(shù)(比如 NVLink/PCIe 上的 P2P 訪問),使得一個(gè)節(jié)點(diǎn)上不同進(jìn)程的 GPU 可以直接通過顯存共享或復(fù)制互相訪問。
  • 對(duì)于節(jié)點(diǎn)間通信,NVSHMEM 默認(rèn)使用 IBRC,并可選用其他協(xié)議如 UCX 或 Libfabric。具體選擇由環(huán)境變量 NVSHMEM_REMOTE_TRANSPORT 控制,允許設(shè)置為 "ibrc"(默認(rèn))、"ucx"、"libfabric"、"ibdevx" 等。

NVSHMEM 對(duì) GPU 內(nèi)存的處理邏輯包括:如果目標(biāo) PE 在本節(jié)點(diǎn),優(yōu)先使用 CUDA IPC 或 P2P 拷貝;如果在遠(yuǎn)端,則調(diào)用網(wǎng)絡(luò)傳輸接口,通過已經(jīng)注冊(cè)的 GPU 映射區(qū)完成遠(yuǎn)程寫入/讀取。所有 GPU 通信都在 NVSHMEM 的對(duì)稱地址空間模型下進(jìn)行:當(dāng)用戶調(diào)用 RMA 操作時(shí),內(nèi)部先將對(duì)稱地址轉(zhuǎn)換為物理地址,然后通過上述通道發(fā)起數(shù)據(jù)傳輸。

默認(rèn)情況下,NVSHMEM 借助 CUDA 的 VMM 來映射 PE 間 GPU 對(duì)稱地址。用戶可通過 NVSHMEM_DISABLE_CUDA_VMM 禁用這一機(jī)制。對(duì)稱緩沖區(qū)會(huì)通過 CUDA IPC 注冊(cè)到各進(jìn)程地址空間,從而在同一節(jié)點(diǎn)上實(shí)現(xiàn) ZeroCopy。此外,NVSHMEM 通過 Mellanox OFED、nv_peer_mem 驅(qū)動(dòng)和 GDRCopy 庫支持 GPU 直連 RDMA,將 GPU 顯存注冊(cè)到網(wǎng)卡,可直接發(fā)起 RDMA 操作。也可以使用環(huán)境變量  NVSHMEM_DISABLE_GDRCOPY 來禁止在 IBRC 下使用 GDRCopy。

對(duì)于 GPU 和 NIC 的綁定,NVSHMEM 提供自動(dòng)映射和手動(dòng)配置的方式。默認(rèn)情況下,各 PE 會(huì)分配到“最近的” NIC,但可通過設(shè)置 NVSHMEM_ENABLE_NIC_PE_MAPPING=1 使系統(tǒng)按照輪詢或用戶指定的方式綁定。進(jìn)一步可用 NVSHMEM_HCA_LIST 和 NVSHMEM_HCA_PE_MAPPING 明確指定使用哪些 HCA 設(shè)備 Port,以及每個(gè) Port 分配給哪些 PE。所有這些機(jī)制確保 GPU 的對(duì)稱內(nèi)存可以高效地通過 NIC 通信。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

3.4 集合通信

在 NVSHMEM 程序中,PE 通過兩種方式進(jìn)行通信:

  • 一種是點(diǎn)對(duì)點(diǎn)通信,需明確指定目標(biāo) PE 的編號(hào)。
  • 另一種是集合通信,它們作用于一組 PE 之上。

基于 Team 的集合通信,通過 Team 句柄參數(shù)確定參與通信的 PE,并利用 Team 對(duì)象封裝的資源進(jìn)行操作。

如果未指定 Team 參數(shù),則默認(rèn)作用于所有 PE。

3.5 Bootstrap

NVSHMEM 的 Bootstrap 模塊負(fù)責(zé)在多進(jìn)程環(huán)境中引導(dǎo)通信和 PE 管理。支持多種啟動(dòng)方式:默認(rèn)通過進(jìn)程管理器的 PMI 接口交互式啟動(dòng),也可以直接利用現(xiàn)有的 MPI 通信域、OpenSHMEM 環(huán)境或者 Plugin 模式??梢酝ㄟ^ NVSHMEM_BOOTSTRAP 環(huán)境變量來指定:

  • PMI:可以選擇不同的版本,比如 PMI-1、PMI-2 或 PMIx。
  • MPI:用戶可用 nvshmemx_init_attr 指定 NVSHMEMX_INIT_WITH_MPI_COMM 標(biāo)志以傳入一個(gè)已有的 MPI_Comm 作為全局 Team。
  • OpenSHMEM:同樣可以用 NVSHMEMX_INIT_WITH_SHMEM 指示在 OpenSHMEM 程序內(nèi)啟動(dòng)。
  • UID:在沒有 MPI/SHMEM 的場景下,還可以采用網(wǎng)絡(luò)唯一 ID (UID) 方式,調(diào)用 nvshmemx_get_uniqueid 在一個(gè) PE 上生成 UID,并通過用戶定義的機(jī)制(比如環(huán)境變量 NVSHMEM_BOOTSTRAP_UID_SESSION_ID)分發(fā)給各 PE,然后各 PE 在 nvshmemx_set_attr_uniqueid_args 中設(shè)置該 UID、自己的 PE 編號(hào)和總 PE 數(shù),再集體調(diào)用 nvshmemx_init_attr。這種 UID 模式利用 TCP 套接字自動(dòng)交換初始化信息,無需依賴 MPI。也可以像 NCCL 一樣配合 NVSHMEM_BOOTSTRAP_UID_SOCK_IFNAME 和 NVSHMEM_BOOTSTRAP_UID_SOCK_FAMILY 使用。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

四、DeepEP 初始化

4.1 概覽

如下圖所示,在 DeepEP 中會(huì)調(diào)用 NVSHMEM 的如下幾個(gè)接口完成初始化(后續(xù)會(huì)詳細(xì)解釋這些函數(shù)的作用):

  • nvshmemx_set_attr_uniqueid_args()。
  • nvshmemx_init_attr()。
  • nvshmem_team_split_strided()。
  • nvshmem_barrier_all()。
  • nvshmem_my_pe()。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

而 DeepEP 中在 Buffer 的 sync 中會(huì)調(diào)用該 init() 接口,并且調(diào)用之前會(huì)完成 rank 和 num_ranks 的計(jì)算。這里會(huì)針對(duì)上述的“高吞吐”和“低時(shí)延”模式進(jìn)行不同的處理。假設(shè) 2 臺(tái)機(jī)器,各 8 個(gè) GPU,則與 PyTorch 對(duì)應(yīng)的 rank 為 [0, 15],num_ranks 為 16。

  • 高吞吐模式:將上述 rank 轉(zhuǎn)為 rdma_rank([0, 1]) 和 nvl_rank;num_ranks 轉(zhuǎn)為 num_rdma_ranks(為 2,其中 NUM_MAX_NVL_PEERS  為 8)。并且使用 rdma_rank 和 num_rdma_ranks 初始化。(PS:節(jié)點(diǎn)內(nèi)使用 NVLink)
  • 低時(shí)延模式:直接使用 rank 和 num_ranks。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

PS:DeepEP 針對(duì)高吞吐模式的特殊邏輯導(dǎo)致其與 NVSHMEM 的拓?fù)溆成浞椒?nbsp;NVSHMEM_HCA_PE_MAPPING 不兼容,后續(xù)會(huì)具體介紹。

4.2 nvshmemx_set_attr_uniqueid_args

如下圖所示(nvshemem_src/src/host/init/init.cu),nvshmemx_set_attr_uniqueid_args 其實(shí)就是設(shè)置了 id、myrank 和 nranks:

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

PS:如果是高吞吐模式,這里的 myrank 和 nranks 已經(jīng)不再等價(jià)于 PyTorch 里的 rank 和 num_ranks。

4.3 nvshmemx_init_attr

如下圖所示(nvshemem_src/src/include/host/nvshmemx_api.h),nvshmemx_init_attr 實(shí)際是調(diào)用了 nvshmemi_init_thread:

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

4.4 nvshmem_team_split_strided

如果是 low_latency_mode,并且 num_ranks > NUM_MAX_NVL_PEERS(8),也就是多機(jī)時(shí)才會(huì)執(zhí)行 nvshmem_team_split_strided,將 NVSHMEM 的 team 劃分為多組 sub-RDMA team,這樣可以實(shí)現(xiàn)每組內(nèi)部的高效通信,同時(shí)保證整體的可擴(kuò)展性和性能。

4.5 nvshmem_barrier_all

如下圖所示(nvshmem_src/src/host/coll/barrier/barrier.cpp),使用 nvshmem_barrier_all 進(jìn)行全局同步。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

4.6 nvshmem_my_pe

如下圖所示(nvshmem_src/src/host/init/query_host.cpp),用 nvshmem_my_pe 返回當(dāng)前的 pe: 

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

五、nvshmemi_init_thread

5.1 概覽

nvshmemi_init_thread(位于 nvshmem_src\src\device\init\init_device.cu) 是 NVSHMEM 初始化流程中的關(guān)鍵函數(shù),主要作用是初始化運(yùn)行環(huán)境,確保后續(xù)在 GPU 上能夠安全、高效地進(jìn)行 NVSHMEM 通信和同步操作。其主要功能和步驟如下:

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

nvshmemid_hostlib_init_attr:負(fù)責(zé)完成 NVSHMEM Host 端初始化流程的主控函數(shù)。

nvshmemid_init_status:Host 端初始化狀態(tài)檢查,如果狀態(tài)大于 NVSHMEM_STATUS_IS_BOOTSTRAPPED,說明 Host 端初始化已完成,可以安全地初始化 Device 端狀態(tài)。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

_nvshmemi_init_device_only_state:進(jìn)一步設(shè)置 Device 端的集合通信等內(nèi)部狀態(tài)。主要是調(diào)用 nvshmemi_setup_collective_launch。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)


cudaGetDevice:調(diào)用 cudaGetDevice 獲取當(dāng)前 GPU 的 device id,存入 

nvshmemi_device_only_state.cuda_device_id,便于后續(xù) Device 端操作定位到正確的 GPU。

5.2 nvshmemid_hostlib_init_attr

包括版本兼容性檢查、bootstrap 啟動(dòng)、全局和本地狀態(tài)初始化、設(shè)備狀態(tài)注冊(cè)、以及多種初始化的管理。包括以下幾個(gè)關(guān)鍵步驟。

5.2.1 IBGDA 狀態(tài)初始化

如果啟用 IBGDA,則提前設(shè)置一下狀態(tài)信息:

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

如下圖所示(nvshmem_src/src/host/init/init.cu):

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

5.2.2 nvshmemi_options_init 環(huán)境變量初始化

如下圖所示:該函數(shù)的作用是批量初始化 NVSHMEM 的所有環(huán)境變量配置項(xiàng),并將結(jié)果寫入 options 結(jié)構(gòu)體。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

5.2.3 nvshmemi_bootstrap_preinit 預(yù)初始化

如下圖所示(位于 nvshmem_src/src/host/init/init.cu),實(shí)際調(diào)用 bootstrap_preinit:

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

bootstrap_preinit 核心就是通過 dlopen 提前加載動(dòng)態(tài)庫,當(dāng)然,這里主要是 UID 模式的動(dòng)態(tài)庫:

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

5.2.4 nvshmemi_bootstrap 初始化

如下圖所示(位于 nvshmem_src/src/host/init/init.cu),nvshmemi_bootstrap 負(fù)責(zé)完成 NVSHMEM 啟動(dòng)階段的“進(jìn)程組通信環(huán)境”初始化。通過底層 bootstrap 機(jī)制(如 MPI、UID、SHMEM 等)建立全局進(jìn)程組信息,確定每個(gè) PE 的全局編號(hào)、節(jié)點(diǎn)內(nèi)編號(hào)、節(jié)點(diǎn)內(nèi) PE 數(shù)量等,并進(jìn)行一致性檢查,為后續(xù)通信和資源分配打下基礎(chǔ)。

  • bootstrap_set_bootattr:根據(jù) flags 和 attr 構(gòu)造 bootstrap 所需的屬性結(jié)構(gòu)體,會(huì)針對(duì) MPI、SHMEM、UID、PMI、PLUGIN 等不同 mode 對(duì)應(yīng)處理。
  • bootstrap_init:傳入 flags、屬性和 nvshmemi_boot_handle,由底層 Plugin/機(jī)制完成通信環(huán)境初始化(根據(jù) MPI、SHMEM、UID、PMI、PLUGIN 等不同 mode 調(diào)用不同的初始化 bootstrap_loader_init),填充 nvshmemi_boot_handle(包括 allgather、barrier、pg_rank、pg_size 等回調(diào)和參數(shù))。
  • 計(jì)算全局和節(jié)點(diǎn)內(nèi)編號(hào):

通過 nvshmemi_boot_handle.pg_rank 和 pg_size 獲取本進(jìn)程全局編號(hào)和總進(jìn)程數(shù)。

通過 getHostHash 獲取本節(jié)點(diǎn)唯一標(biāo)識(shí)(host hash)。

使用 allgather 收集所有進(jìn)程的 host hash,統(tǒng)計(jì)每個(gè)節(jié)點(diǎn)上的 PE 數(shù)量(npes_node)和本 PE 在節(jié)點(diǎn)內(nèi)的編號(hào)(mype_node)。

將 mype_node 和 npes_node 寫入 nvshmemi_boot_handle。

  • 檢查所有節(jié)點(diǎn) PE 數(shù)一致性:遍歷所有 host hash,確保每個(gè)節(jié)點(diǎn)上的 PE 數(shù)量一致,否則報(bào)錯(cuò)退出。
  • 設(shè)置 PE 分布類型:檢查 PE 分布是否為 block 或 round robin,設(shè)置 nvshmemi_pe_dist,用于后續(xù)通信優(yōu)化。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

5.2.5 nvshmemi_try_common_init 初始化

實(shí)際上是調(diào)用 nvshmemi_common_init,這是最核心的初始化操作,后面會(huì)詳細(xì)介紹:

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

5.3 nvshmemi_setup_collective_launch

nvshmemi_setup_collective_launch 是 NVSHMEM  Device 端集合通信相關(guān)的初始化函數(shù)。它的主要作用是為后續(xù)在 GPU 上安全、高效地發(fā)起通信 kernel 啟動(dòng)做準(zhǔn)備,確保相關(guān) CUDA 資源和屬性已正確配置。主要包括以下幾個(gè)部分:

  • 檢查當(dāng)前 GPU 的 SM 數(shù)量。
  • 檢查當(dāng)前 GPU 是否支持 Cooperative Launch。
  • 檢查 CUDA Stream 優(yōu)先級(jí)范圍。
  • 創(chuàng)建一個(gè)最高優(yōu)先級(jí)的非阻塞 CUDA Stream。
  • 創(chuàng)建兩個(gè) CUDA Event,用于同步 Collective  Launch 的起止。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

六、nvshmemi_common_init

6.1 概覽

nvshmemi_common_init 的主要作用是完成 NVSHMEM 運(yùn)行時(shí)的核心初始化工作。負(fù)責(zé)初始化和配置 NVSHMEM 的所有關(guān)鍵組件,使得后續(xù)的通信和內(nèi)存管理功能可以正常工作。其主要任務(wù)包括:

  • CUDA 相關(guān)初始化:加載 CUDA 符號(hào)表、初始化 CUDA 庫、查詢 CUDA 驅(qū)動(dòng)版本,決定對(duì)稱堆類型(如 SYSMEM/VIDMEM),并獲取 CUDA 上下文和流優(yōu)先級(jí)等。
  • Rail 優(yōu)化設(shè)置:如果啟動(dòng)軌道優(yōu)化,則檢查是否滿足軌道優(yōu)化的支持,并完成相關(guān)設(shè)置。
  • 對(duì)稱堆初始化:根據(jù)配置和硬件能力,初始化對(duì)稱堆,為后續(xù)的通信和內(nèi)存操作分配統(tǒng)一的內(nèi)存空間。
  • 設(shè)備狀態(tài)注冊(cè)與更新:注冊(cè)和更新設(shè)備狀態(tài)指針,確保主機(jī)和設(shè)備之間的狀態(tài)同步。
  • 通信與拓?fù)涑跏蓟撼跏蓟?transport,構(gòu)建 transport 映射表,建立與其他 PE 的連接。
  • CUDA 句柄和 Event 初始化:為每個(gè) peer 分配 CUDA stream 和 event,設(shè)置相關(guān)句柄。
  • 集合通信和 Team 初始化:初始化 CPU 端的集合通信,設(shè)置 NVSHMEM 的 Team 結(jié)構(gòu),支持多種 Team 劃分和同步。
  • 每個(gè) GPU 的多 PE(MPG)支持:檢測(cè)和配置多 PE 共享同一 GPU 的場景,包括 MPS 相關(guān)的共享內(nèi)存和事件管理。
  • 性能與兼容性檢查:檢測(cè)如 NVLS、64-bit stream memops 等高級(jí)特性支持,并給出性能建議或警告。
  • 全局同步:在關(guān)鍵階段通過 barrier 保證所有 PE 的同步,確保初始化過程一致。
  • 最終狀態(tài)標(biāo)記:標(biāo)記設(shè)備狀態(tài)為已初始化,確保后續(xù) API 調(diào)用安全。

6.2 nvshmemi_coll_common_cpu_init

初始化 NVSHMEM Host 端的集合通信相關(guān)環(huán)境參數(shù),并根據(jù)環(huán)境和配置決定是否啟用 NCCL 作為底層通信庫,如下圖所示(位于 nvshmem_src\src\host\coll\cpu_coll.cpp)。

  • nvshmemi_coll_common_cpu_read_env:將環(huán)境變量和配置項(xiàng)寫入全局結(jié)構(gòu)體(如 barrier、reduce、broadcast 等算法和閾值參數(shù))。
  • NCCL 相關(guān)支持邏輯(編譯時(shí)需要加上 NVSHMEM_USE_NCCL)

默認(rèn)嘗試啟用 NCCL,如果設(shè)置了 DISABLE_NCCL 選項(xiàng),則禁用 NCCL,直接返回。

NCCL 不支持 MPG,如果設(shè)置 MPG 要禁用 NCCL。

加載 NCCL 動(dòng)態(tài)庫(“l(fā)ibnccl.so.2”)。

檢查 NCCL 版本和兼容性。

通過 dlsym 加載 NCCL 所需的符號(hào),并填充到 nccl_ftable。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

6.3 nvshmemi_transport_init

初始化 NVSHMEM 支持的所有 Host 端 Transport 模塊,包括 P2P、本地和遠(yuǎn)程網(wǎng)絡(luò)通信插件(如 IBRC、UCX、Libfabric、IBDEVX、IBGDA 等),為后續(xù) PE 間數(shù)據(jù)傳輸和同步提供底層支撐(位于 nvshmem_src\src\host\transport\transport.cpp)。包括如下關(guān)鍵步驟:

  • 分配 transport 結(jié)構(gòu)體數(shù)組:

若 state->transports 為空,則分配一個(gè)大小為 NVSHMEM_TRANSPORT_COUNT(6) 的數(shù)組,每個(gè)元素對(duì)應(yīng)一種 Transport。

  • 初始化 P2P 通信(僅用于同一節(jié)點(diǎn)不同 GPU):
  • 如果沒有禁用 P2P,則初始化本地內(nèi)存緩存,并調(diào)用 nvshmemt_p2p_init 初始化 P2P 通信。
  • 初始化成功則填充相關(guān)字段(如 boot_handle、heap_base、cap、index、granularity 等),并將其加入 transports 列表。
  • 初始化遠(yuǎn)程通信插件(用于不同節(jié)點(diǎn)):
  • 支持多種插件(IBRC、UCX、IBDEVX、Libfabric),通過宏控制編譯。
  • 判斷環(huán)境變量 REMOTE_TRANSPORT,決定是否跳過某個(gè)插件。
  • 若選擇某插件,拼接動(dòng)態(tài)庫(如 nvshmem_transport_ucx.so.1),然后進(jìn)行相應(yīng)初始化。
  • 動(dòng)態(tài)加載并初始化遠(yuǎn)程通信插件:
  • 使用 dlopen 加載對(duì)應(yīng)的動(dòng)態(tài)庫,并使用 dlsym 獲取初始化函數(shù)指針(nvshmemt_init)。
  • 初始化本地緩存,調(diào)用插件的初始化函數(shù),填充 transport 結(jié)構(gòu)體相關(guān)字段(heap_base、cap、index、my_pe、n_pes、cache_handle 等)。
  • IBGDA 特殊支持(需要打開編譯選項(xiàng) NVSHMEM_IBRC_SUPPORT):
  • 如果環(huán)境變量啟用 IBGDA,則單獨(dú)加載并初始化 IBGDA 插件,流程與上面類似。
  • 初始化成功后,調(diào)用 nvshmemi_ibgda_get_device_state 獲取設(shè)備狀態(tài),并設(shè)置全局標(biāo)志。
  • 檢查至少有一個(gè) transport 初始化成功。
  • 記錄已初始化的 transport 數(shù)量:
  • 將成功初始化的 transport 數(shù)量寫入 state->num_initialized_transports。

PS:IBRC、UCX、IBDEVX、Libfabric 只會(huì)選擇其中的一個(gè),主要是因?yàn)轫樞驒z查:代碼會(huì)按照 IBRC -> UCX -> IBDEVX -> LIBFABRIC 的順序,逐一檢查環(huán)境變量,只要檢查到一個(gè)就會(huì)跳轉(zhuǎn)到 transport_init,從而跳過后續(xù)的幾個(gè)。

6.4 nvshmemi_build_transport_map

為每個(gè) PE 建立一張“可達(dá)性”與“可用 Transport”的映射表。檢測(cè)本地 PE 與所有其他 PE 之間,哪些 Transport 能夠訪問對(duì)方,并將結(jié)果記錄下來,最終形成全局的 Transport 映射表(state->transport_map),為后續(xù)高效通信做準(zhǔn)備(位于 nvshmem_src/nvshmem/src/host/topo/topo.cpp)。主要流程包括:

分配映射表內(nèi)存:

  • state->transport_map:大小為 npes × npes,存儲(chǔ)所有 PE 間的 Transport 可達(dá)性信息。
  • local_map:大小為 npes,存儲(chǔ)本地 PE 到所有其他 PE 的可達(dá)性信息。
  • state->transport_bitmap:用來記錄本 PE 能用到的所有 Transport 類型(每一位代表一種 Transport)。

檢查每個(gè) PE 的可達(dá)性:

  • 外層循環(huán)遍歷所有 PE(i),即本地 PE 需要與哪些遠(yuǎn)端 PE 通信。
  • 內(nèi)層循環(huán)遍歷所有已初始化的 transport(j),判斷本地通過 transport j 能否訪問到 PE i。如果能訪問,則在 local_map[i] 的相應(yīng) bit 位置標(biāo)記,并更新 state->transport_bitmap。同時(shí)記錄每個(gè) transport 對(duì)每個(gè) PE 的能力(state->transports[j]->cap[i])。

NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

匯總所有 PE 的可達(dá)性信息:通過 allgather,將每個(gè) PE 的 local_map 匯總到全局的 state->transport_map,這樣每個(gè)進(jìn)程都能知道所有 PE 之間的可達(dá)性和可用 transport。

6.5 nvshmemi_setup_connections

6.5.1 概覽

為每個(gè)可用的 transport 在本 PE 上選擇合適的底層設(shè)備(如 NIC 等),并建立與其他 PE 的通信端點(diǎn)(endpoint)連接。這是 NVSHMEM 初始化流程中,真正“連通”各個(gè)進(jìn)程間網(wǎng)絡(luò)通信的關(guān)鍵步驟(位于 nvshmem_src/src/host/transport/transport.cpp)。關(guān)鍵步驟如下圖所示:

遍歷所有已初始化的 Transport:

  • 只處理 state->transport_bitmap 標(biāo)記為可用的 Transport。

計(jì)算每個(gè) PE 可用的設(shè)備數(shù):

  • tcurr->n_devices 是當(dāng)前 Host 上的 NIC 數(shù),state->npes_node 是當(dāng)前 Host 上的 PE 數(shù)。
  • 平均分配,并且保證每個(gè) PE 至少有 1 個(gè) NIC。假設(shè) 8 個(gè) GPU,2 個(gè) NIC,則會(huì)有 4 個(gè) GPU 共享 1 個(gè) NIC。

選擇本 PE 要使用的 NIC:

  • 如果 Transport 只有一個(gè) NIC,則直接使用。
  • 如果用戶打開了 ENABLE_NIC_PE_MAPPING 環(huán)境變量,則使用輪詢分配方式。
  • 否則,調(diào)用 nvshmemi_get_devices_by_distance,根據(jù) PCIe 拓?fù)渚嚯x等信息,智能選擇最優(yōu)的設(shè)備分配給本 PE。

檢查設(shè)備分配有效性:

  • 如果 transport 有設(shè)備但沒選到任何設(shè)備,報(bào)錯(cuò)退出。
  • 建立 Endpoint 連接:

    調(diào)用 transport 的 connect_endpoints 回調(diào),傳入選中的設(shè)備列表,建立與其他 PE 的通信端點(diǎn)。

    之后進(jìn)行 barrier,同步所有進(jìn)程,確保連接建立一致。

    更新設(shè)備狀態(tài)。

    NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

    6.5.2 拓?fù)溆成?/h4>

    當(dāng)用戶想要通過 ENABLE_NIC_PE_MAPPING 進(jìn)行 PE(實(shí)際也就是 GPU)與 NIC 的映射時(shí),可以通過以下兩個(gè)環(huán)境變量實(shí)現(xiàn):

    NVSHMEM_HCA_LIST:直接提供 NIC name 和 port 索引的列表(PS:內(nèi)部會(huì)進(jìn)行排序)。

    • “^mlx5_0”:排除 mlx5_0 NIC。
    • “mlx5_1:1,mlx5_2:2”:如果有 8 個(gè) GPU,則 4 個(gè) GPU 對(duì)應(yīng) mlx5_1 的 Port 1,4 個(gè) GPU 對(duì)應(yīng) mlx5_2 的 Port 2。

    NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

    NVSHMEM_HCA_PE_MAPPING:和上述的 NVSHMEM_HCA_LIST 類似,只不過可以進(jìn)一步設(shè)置 PE 的數(shù)量(PS:內(nèi)部也會(huì)進(jìn)行排序)。

    • “mlx5_0:1:3,mlx5_0:2:5”:還是 8 個(gè) GPU,則 3 個(gè) GPU 對(duì)應(yīng) mlx5_1 的 Port 1,5 個(gè) GPU 對(duì)應(yīng) mlx5_0 的 Port 2。

    NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

    我們前面提到,DeepEP 的高吞吐模式與 ENABLE_NIC_PE_MAPPING 不兼容也正是這里的問題。

    假設(shè)還是 8 個(gè) GPU,設(shè)置 NVSHMEM_HCA_PE_MAPPING 為 “mlx5_0:1:2,mlx5_0:2:2,mlx5_1:1:2,mlx5_1:2:2”,預(yù)期是 PE0 和 PE1 使用 mlx5_0 的 Port 1, PE6 和 PE7 使用 mlx5_1 的 Port 2。

    由于高吞吐模式時(shí)這里的 mype_node 實(shí)際為 rdma_rank,一個(gè)節(jié)點(diǎn)上所有 PE 的 mype_node 相同,實(shí)際都是 0,導(dǎo)致這里實(shí)際上這里所有 PE 選擇了相同的 NIC,沒有達(dá)到 Mapping 的目的,反而導(dǎo)致了 NIC 熱點(diǎn)。

    NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

    6.5.3 智能拓?fù)涓兄?/h4>

    主要是調(diào)用 nvshmemi_get_devices_by_distance 為本節(jié)點(diǎn)上的每個(gè) PE 分配最優(yōu)的 NIC,以實(shí)現(xiàn)高效的 GPU-NIC 通信。分配策略基于 PCIe 拓?fù)渚嚯x,優(yōu)先選擇距離 GPU 最近的 NIC,并在多 GPU/多 NIC 情況下盡量負(fù)載均衡(位于 nvshmem_src/nvshmem/src/host/topo/topo.cpp)。整體思路與 NCCL 中類似,這里主要包括幾個(gè)步驟:

    收集 GPU 和 NIC 路徑:

    • 獲取本 PE 的 GPU PCIe Bus ID。
    • 通過 allgather 收集所有 PE 的 GPU Bus ID。
    • 遍歷所有 PE,篩選出本節(jié)點(diǎn)的 PE(hostHash 相同),并獲取其 GPU 的 PCIe 路徑,填入 cuda_device_paths。
    • 填充所有 NIC 的 PCIe 路徑。

    計(jì)算所有 GPU-NIC 距離:

    • 針對(duì)每個(gè) PE 的 GPU,都會(huì)通過 get_pci_distance 計(jì)算每個(gè) NIC 與 GPU 的距離,并構(gòu)造一個(gè)三元組 {pe_id, dev_id, distance},插入 pe_dev_pairs,按距離升序排列(距離越小越優(yōu))。
    • PCIe 距離的優(yōu)先級(jí)為 PIX > PXB > PHB > NODE > SYS,和 NCCL 一樣。

    NVSHMEM 深度解析:初始化流程與核心機(jī)制-AI.x社區(qū)

    第一輪分配(優(yōu)先分配最近的 NIC):

    • 遍歷 pe_dev_pairs(距離優(yōu)先),為每個(gè)本地 PE 依次分配最優(yōu)的 NIC。
    • 如果當(dāng)前距離比已分配的更優(yōu),則分配該 NIC,并記錄距離和使用計(jì)數(shù)。
    • 如果遇到更差的距離,后續(xù)的 NIC 都不再考慮,標(biāo)記為無更優(yōu)分配(-2)。
    • 直到所有本地 PE 的分配完成。

    第二輪分配(負(fù)載均衡):

    • 檢查是否有 NIC 被多個(gè) PE 分配(nic_density >= 2)。
    • 嘗試為這些 PE 找到同等距離但負(fù)載更低的 NIC,減少單個(gè) NIC 的壓力。
    • 只在不會(huì)降低距離優(yōu)先級(jí)的前提下進(jìn)行重新分配。

    輸出本 PE 的分配結(jié)果:

    • 將本 PE 分配到的 NIC 索引寫入 device_arr。
    • 統(tǒng)計(jì)本 PE 實(shí)際分配到的 NIC 數(shù)量。

    七、參考鏈接

    1. ??https://docs.nvidia.com/nvshmem/api/index.html??
    2. ??https://github.com/deepseek-ai/DeepEP??
    3. ??https://docs.nvidia.com/nvshmem/release-notes-install-guide/install-guide/abstract.html??

    本文轉(zhuǎn)載自???AI閑談?????,作者:AI閑談


    收藏
    回復(fù)
    舉報(bào)
    回復(fù)
    相關(guān)推薦