【GPU】什么是NCCL和Simple, LL, LL128通信协议

​​​​​​​什么是NCCL

简介

NCCL 的原理

机器内通信:

机器间通信:

NCCL通信协议

简介

NCCL通信选择协议规则

​​​​​​​1 自动选择

​​​​​​​2 强制选择

Simple协议

1 介绍

2 Simple 协议的基本格式

2 Simple 协议的示例

Simple 协议的伪代码示例

LL协议(Low Latency)

1 介绍

如何选择LL协议

LL128协议(Low Latency128)

1 介绍

如何选择LL128协议

LL 和 LL128 协议的对比

常见 NIVIDA 指令

nvidia-smi topo -m

NVLINK 查询

GPU 监控

NCCL、RCCL和MCCL的区别

NCCL与 MPI的区别


什么是NCCL

简介

NCCL (NVIDIA Collective Communications Library,NVIDIA 集群通信) 是 NVIDIA 推出的一个用于 GPU 之间高性能通信的库。它和 MPI 一样支持多种高效的集体通信操作,如广播、归约、全收集等。通信的实现方式分为两种类型:机器内通信与机器间通信。

深度学习模型规模巨大,单个 GPU 无法满足训练需求,需要将模型或数据分割到多个 GPU 上进行并行训练,NCCL 就是为了解决这个场景而生的。它主要解决以下问题:

  • 在多 GPU 训练场景下实现高效的数据交换
  • 自动识别并优化 GPU 间的通信拓扑
  • 提供标准化的集合通信接口
  • 支持机内(单机多卡)和机间(多机多卡)通信

NCCL 的原理

以下摘自:NCCL 前言 - https://www.cnblogs.com/sys-123456/p/18655886

NCCL是专为NVIDIA GPU设计的集合通信库,它和 MPI 一样支持多种高效的集体通信操作,如广播、归约、全收集等。通信的实现方式分为两种类型:机器内通信与机器间通信。

机器内通信:

  1. GPU Direct Shared Memory(2010年6月引入):共享内存(QPI/UPI),比如:CPU与CPU之间的通信可以通过共享内存。

    某些工作负载需要位于同一服务器中的两个或多个 GPU 之间进行数据交换,来自 GPU 的数据将首先通过 CPU 和 PCIe 总线复制到主机固定的共享内存。然后,数据将通过 CPU 和 PCIe 总线从主机固定的共享内存复制到目标 GPU,数据在到达目的地之前需要被复制两次。

  1. GPU Direct P2P(2011年)

    有了 GPU Direct P2P 通信技术后,将数据从源 GPU 复制到同一节点中的另一个 GPU 不再需要将数据临时暂存到主机内存中。如果两个 GPU 连接到同一 PCIe 总线,GPUDirect P2P 允许访问其相应的内存,而无需 CPU 参与。前者将执行相同任务所需的复制操作数量减半。

  1. NVLink

    NVLink是NVIDIA开发的一种高速互连技术,它提供了比传统PCIe更高的带宽和更低的延迟。通过NVLink,GPU之间的数据传输不再通过PCIe总线,而是直接通过NVLink连接。NVLink通过NVSwitch设备实现多GPU之间的全互联,这对于高性能计算和深度学习应用中的大规模并行处理尤为重要。通常是GPU与GPU之间的通信,也可以用于CPU与GPU之间的通信。

  2. PCIe

    通常是CPU与GPU之间的通信。

机器间通信:

  1. GPU Direct Storage

    在 NVIDIA GPU Direct 远程直接内存访问技术不可用的多节点环境中,在不同节点的两个 GPU 之间传输数据需要 5 次复制操作:

    • 将数据从源 GPU 传输到源节点中的主机固定内存缓冲区时,将发生第一个副本。

    • 然后,该数据将复制到源节点的 NIC 驱动程序缓冲区。

    • 在第三步中,数据通过网络传输到目标节点的 NIC 驱动程序缓冲区。

    • 将数据从目标节点 NIC 的驱动程序缓冲区复制到目标节点中的主机固定内存缓冲区时,会发生第四次复制。

    • 最后一步需要使用 PCIe 总线将数据复制到目标 GPU。

  2. GPU Direct RDMA (2014年)

    • TCP/IP 网络协议。
    • RDMA (Remote Direct Memory Access) 网络协议。
      • InfiniBand
      • iWARP
      • RoCE v2

    下面以InfiniBand为例:

    GPU Direct RDMA 结合了 GPU 加速计算和 RDMA(Remote Direct Memory Access)技术,实现了在 GPU 和 RDMA 网络设备之间直接进行数据传输和通信的能力。它允许 GPU 直接访问 RDMA 网络设备中的数据,无需通过主机内存或 CPU 的中介。

使用 GPU Direct RDMA 两个 GPU 设备必须共享相同的上游 PCI Express root complex。

推荐文章:

NCCL简介&初始化源码阅读-天翼云开发者社区 - 天翼云

NCCL通信协议

以下内容部分摘抄或参考自:https://zhuanlan.zhihu.com/p/699178659

简介

NCCL确实提供了Simple、LL和LL128这三种通信协议,以满足不同应用场景下的性能需求。以下是对这三种通信协议的简要说明:

  1. Simple:这是NCCL的基础通信协议,实现上相对简单,适用于不需要特别优化的通信场景。
  2. LL(Low Latency):低延迟协议,特别优化了小数据量传输的性能。在数据传输量较小,无法充分利用传输带宽时,LL协议通过减少同步带来的延迟来提高性能。它依赖于CUDA的8字节原子存储操作,将数据排列组合成4B Data+4B Flag的形式进行传输,对端会对Flag值进行校验,以确保数据成功传输。
  3. LL128:这是LL协议的一个扩展或优化版本,特别适用于NVLink环境下的通信。LL128能够以较低的延迟达到较大的带宽率,因此在带有NVLink的机器上,NCCL会默认使用该协议。与LL协议类似,LL128也使用Flag来进行数据校验,但它以128字节为单位进行原子存储操作,从而在某些情况下可能提供更好的带宽效率。

 NCCL 使用 3 种不同的协议:LL、LL128 和 Simple,它们具有不同的延迟(~1us、~2us 和 ~6us)、不同的带宽(50%、95% 和 100%),以及其他影响其性能的差异。

如何选择 NCCL 协议

  • 数据规模

    • 小规模数据:优先使用 Simple 协议。

    • 大规模数据:使用 LL 或 LL128 协议。

  • 硬件环境

    • 如果硬件支持 NVLink,优先使用 LL 或 LL128 协议。

    • 如果硬件环境较简单,可以使用 Simple 协议。

  • 性能需求

    • 对性能要求较高的场景,避免使用 Simple 协议。

NCCL通信选择协议规则

​​​​​​​1 自动选择

下面是一段使用 NCCL 进行 AllReduce 操作的伪代码示例,代码中没有体现使用哪一种协议:

#include <nccl.h>
#include <cuda_runtime.h>

void allReduceWithSimpleProtocol(float* data, int count, int nGPUs) {
    ncclComm_t comm;
    ncclUniqueId id;
    ncclGetUniqueId(&id);  // 获取唯一的 NCCL ID

    // 初始化 NCCL 通信器
    ncclCommInitAll(&comm, nGPUs, id);

    // 执行 AllReduce 操作
    ncclAllReduce(data, data, count, ncclFloat, ncclSum, comm, 0);

    // 销毁 NCCL 通信器
    ncclCommDestroy(comm);
}

  这是因为:

NCCL 的设计目标是提供高效的集体通信操作,同时隐藏底层协议的复杂性。因此:

  • 协议选择是自动的:NCCL 在运行时根据硬件(如 GPU 型号、NVLink 拓扑)和数据规模自动选择最优的协议(如 Simple、LL、LL128 等)。

  • API 是抽象的:NCCL 的 API(如 ncclAllReduce)并不直接暴露协议的选择,开发者只需调用 API,NCCL 会自动处理底层细节。

​​​​​​​2 强制选择

例如,如何强制使用 Simple 协议?

可以通过设置环境变量来强制 NCCL 使用某种协议,如 Simple 协议。以下是如何操作的步骤:

设置环境变量

export NCCL_PROTO=Simple

验证协议

设置以下环境变量来查看 NCCL 实际使用的协议:

export NCCL_DEBUG=INFO

运行程序时,NCCL 会输出调试信息,包括使用的协议。

验证例子

以下是一个完整的示例,展示如何强制使用 Simple 协议并验证协议选择:

#include <nccl.h>
#include <cuda_runtime.h>
#include <iostream>

int main() {
    // 初始化 CUDA
    cudaSetDevice(0);

    // 分配数据
    int count = 1024;
    float* data;
    cudaMalloc(&data, count * sizeof(float));

    // 初始化 NCCL
    ncclComm_t comm;
    ncclUniqueId id;
    ncclGetUniqueId(&id);  // 获取唯一的 NCCL ID
    ncclCommInitAll(&comm, 1, &id);  // 初始化通信器(单 GPU)

    // 执行 AllReduce 操作
    ncclAllReduce(data, data, count, ncclFloat, ncclSum, comm, 0);

    // 销毁 NCCL 通信器
    ncclCommDestroy(comm);

    // 释放 CUDA 内存
    cudaFree(data);

    std::cout << "NCCL AllReduce completed!" << std::endl;
    return 0;
}

在运行程序之前,设置环境变量:

export NCCL_PROTO=Simple
export NCCL_DEBUG=INFO
./your_program


输出:

NCCL INFO Connected all rings
NCCL INFO Using network Simple
NCCL INFO AllReduce: opSum, datatypeFloat, count=1024, protocol=Simple

Simple协议

1 介绍

1. Simple 协议的作用

Simple 协议是 NCCL 中最基础的通信协议,主要用于以下场景:

  • 小规模数据传输:当数据量较小时,Simple 协议可以提供低开销的通信。

  • 调试和测试:由于其实现简单,Simple 协议常用于调试和测试 NCCL 的基本功能。


2. Simple 协议的特点

  • 实现简单:Simple 协议的实现逻辑较为直接,适合处理简单的通信任务。

  • 低开销:由于协议逻辑简单,通信开销较低,适合小规模数据传输。

  • 通用性:Simple 协议不依赖于特定的硬件优化,可以在各种硬件环境下运行。


3. Simple 协议的工作方式

Simple 协议的核心思想是通过 点对点通信 实现集体通信操作。以下是其工作流程:

  1. 数据分块

    • 将需要传输的数据分成多个小块(chunks)。

  2. 点对点传输

    • 每个 GPU 将数据块发送给目标 GPU,同时接收来自其他 GPU 的数据块。

  3. 数据聚合

    • 在接收端,将来自不同 GPU 的数据块聚合起来,完成集体通信操作(如 AllReduce、Broadcast 等)。

2 Simple 协议的基本格式

Simple 协议的格式可以理解为一种基于 消息分块 和 点对点通信 的简单数据传输机制。以下是其可能的格式和工作流程:

1. 消息分块

  • 数据被划分为多个固定大小的块(chunks)。

  • 每个块的大小通常与硬件特性(如 GPU 的显存带宽)相匹配,以优化传输效率。

2. 消息头(Header)

每个数据块可能包含一个消息头,用于描述数据的元信息。消息头的格式可能包括以下字段:

  • 消息类型:标识通信操作的类型(如 AllReduce、Broadcast 等)。

  • 数据块编号:标识当前数据块在整体数据中的位置。

  • 数据块大小:标识当前数据块的大小。

  • 目标 GPU ID:标识数据块的目标 GPU。

3. 数据块(Payload)

  • 数据块是实际传输的数据部分。

  • 数据块的大小通常是固定的,以简化传输逻辑。

4. 点对点传输

  • 每个 GPU 将数据块发送给目标 GPU,同时接收来自其他 GPU 的数据块。

  • 数据传输可能通过 PCIe 或 NVLink 进行,具体取决于硬件环境。


2 Simple 协议的示例

协议选择,NCCL 根据环境情况自动选择或用户通过设置环境变量指定使用Simple 协议。详情见本文《NCCL通信选择协议规则》的相关说明。

Simple 协议的工作流程

以下是一个典型的工作流程:

    初始化:
    NCCL 初始化通信器(ncclCommInitAll),确定参与通信的 GPU 和拓扑结构。


    数据分块:
    将需要传输的数据划分为多个固定大小的块。


    消息头生成:
    为每个数据块生成消息头,包含元信息(如目标 GPU ID、数据块编号等)。


    数据传输:
    每个 GPU 将数据块发送给目标 GPU,同时接收来自其他 GPU 的数据块。


    数据聚合:
    在接收端,将来自不同 GPU 的数据块聚合起来,完成集体通信操作(如 AllReduce、Broadcast 等)。

    Simple 协议的伪代码示例

    以下是一个简化的伪代码示例,展示 Simple 协议的可能实现:

    struct SimpleProtocolHeader {
        int messageType;  // 消息类型(如 AllReduce、Broadcast)
        int chunkId;      // 数据块编号
        int chunkSize;    // 数据块大小
        int targetGpuId;  // 目标 GPU ID
    };
    
    void simpleProtocolSend(void* data, int size, int targetGpuId) {
        int chunkSize = 1024;  // 假设每个数据块大小为 1024 字节
        int numChunks = (size + chunkSize - 1) / chunkSize;
    
        for (int i = 0; i < numChunks; i++) {
            // 生成消息头
            SimpleProtocolHeader header;
            header.messageType = ALLREDUCE;
            header.chunkId = i;
            header.chunkSize = chunkSize;
            header.targetGpuId = targetGpuId;
    
            // 发送消息头和数据块
            sendHeaderAndData(&header, data + i * chunkSize, chunkSize);
        }
    }
    
    void simpleProtocolReceive(void* buffer, int size) {
        int chunkSize = 1024;  // 假设每个数据块大小为 1024 字节
        int numChunks = (size + chunkSize - 1) / chunkSize;
    
        for (int i = 0; i < numChunks; i++) {
            // 接收消息头和数据块
            SimpleProtocolHeader header;
            void* chunkData = receiveHeaderAndData(&header);
    
            // 将数据块写入缓冲区
            memcpy(buffer + header.chunkId * chunkSize, chunkData, header.chunkSize);
        }
    }

    LL协议(Low Latency)

    1 介绍

    LL 协议(Low Latency) 的出现是为了解决多 GPU 和多节点通信中的 延迟问题。

    以往NCCL为了保证同步,会引入 memory fence,这就导致延迟比较大。而在小数据量下,往往打不满传输带宽,此时优化点在于同步带来的延迟。

    LL协议依赖前提是 CUDA 的memory 8Bytes大小的操作是atomic的,因此通信时会将数据排列组合成 4B Data + 4B Flag 进行传输。

    而对端则会对Flag值进行校验,当达到预期值后,代表4B Data已经成功传输过来,便可进行下一步的操作。

    一些相关代码实现在 prims_ll.h

    存储数据的代码为:

    __device__ void storeLL(union ncclLLFifoLine* dst, uint64_t val, uint32_t flag) {
        asm volatile("st.volatile.global.v4.u32 [%0], {%1,%2,%3,%4};" :: "l"(&dst->i4), "r"((uint32_t)val), "r"(flag), "r"((uint32_t)(val >> 32)), "r"(flag));
      }

    读取远端数据的代码为:

    __device__ uint64_t readLL(int offset, int i) {
        union ncclLLFifoLine* src = recvPtr(i) + offset;
        uint32_t flag = recvFlag(i);
        uint32_t data1, flag1, data2, flag2;
        int spins = 0;
        do {
          asm("ld.volatile.global.v4.u32 {%0,%1,%2,%3}, [%4];" : "=r"(data1), "=r"(flag1), "=r"(data2), "=r"(flag2) : "l"(&src->i4));
          if (checkAbort(spins, 0)) break;
        } while ((flag1 != flag) || (flag2 != flag));
        uint64_t val64 = data1 + (((uint64_t)data2) << 32);
        return val64;
      }
    
    
    • 使用volatile关键字来保证相关内存操作不会被编译器优化重排
    • CUDA支持向量化加载128bit数据,因此用的是 u32x4 指令
    • 存储的时候,按照 DATA1 | FLAG1 | DATA2 | FLAG2 形式重排组合进128bit寄存器里
    • 读取的时候,当flag1 和 flag2 为预期值后,将data1 和 data2 组合到一起,得到真正的数据

    因为 Flag 占了整个数据包的一半,因此有效带宽是 50%,LL协议也因为这个不适用大数据量的传输。

    如何选择LL协议

    协议选择,NCCL 根据环境情况自动选择或用户通过设置环境变量指定使用LL 协议。详情见本文《NCCL通信选择协议规则》的相关说明。

    LL128协议(Low Latency128)

    1 介绍

            ​​​​​​​LL128 协议(Low Latency 128) 的出现是为了进一步优化大规模数据传输的通信效率,它是 LL 协议(Low Latency) 的扩展,旨在解决 LL 协议在大规模数据传输中的局限性。(ll效带宽是 50%,ll128是93.75%

     该协议与LL特别像,但是又依赖于一些特殊硬件(NVLink)实现。

    在NVLink下,memory operation 是以 128B 的粒度顺序可见的。考虑每个thread依旧是用128bit(16B)传输,那么128B这个粒度只需要每8个thread为一组,并且让最后一个thread承担flag校验的任务即可。

    计算下来可以得到有效数据为:16B * 7 + 8B = 120B

    Flag校验位为:8B

    有效带宽为:120B / 128B = 93.75%

    LL128能够以较低的延迟达到较大的带宽率,NCCL会在带有NVLink的机器上默认使用该Protocol

    相关代码位于 prims_ll128.h 头文件内

    在类初始化的时候,会以每8个thread的最后一个thread作为FlagThread,只有该thread进行Flag位校验:

    bool flagThread; 
    
    flagThread((tid%8)==7)

    加载数据到寄存器代码为:

    template<int WordPerThread>
    __device__ __forceinline__ void loadRegsBegin(uint64_t(&regs)[WordPerThread], T const *src, int eltN) {
      constexpr int EltPer16B = 16/sizeof(T);
      if(reinterpret_cast<uintptr_t>(src)%16 == 0) {
        /* We are aligned to 16 bytes, so load directly to registers no shmem.
         * Flag threads load half as much data which gets shuffled to the even
         * registers during Finish. The point of splitting into two phases is to
         * defer that shuffle, which incurs a dependency stall, until after other
         * memops are launched by the caller.
         */
        #pragma unroll
        for(int g=0; g < WordPerThread/2; g++) {
          int ix = g*WARP_SIZE - 4*(g/2) + wid - (g%2)*(wid/8); 
          if(!flagThread || g%2==0) {
            if(ix*EltPer16B < eltN)
              load128((uint64_t*)(src + ix*EltPer16B), regs[2*g+0], regs[2*g+1]);
          }
        }
      }

    这里的ix为:0,32,60,92。对相邻的ix做差可得到 32, 28, 32。考虑到这是以Warp为单位操作,可得第一次加载32个线程都参与,第二次加载只有4*(8-1)个线程参与,同理推第三次/第四次加载。

    每个thread有 uint64_t regs[8] 寄存器,主要区别就在于flagThread加载逻辑,第一次加载满,第二次不加载,第三次加载满,第四次不加载,那么整个寄存器情况为:

    在 recvReduceSendCopy 方法里,会调用一次 loadRegsFinish 完成整个寄存器加载:

    template<int WordPerThread>
      __device__ __forceinline__ void loadRegsFinish(uint64_t(&regs)[WordPerThread]) {
        // Move data out of flag registers into the vacant registers.
        #pragma unroll
        for (int g=1; g < WordPerThread/2; g+=2) {
          if (flagThread) regs[2*g] = regs[2*g-1];
        }
      }

    其实就是交换了下,regs[2]/[1], regs[6]/[5], 得到:

    作者在解释这里操作原因是为了避免shuffle数据依赖导致的stall

    The point of splitting into two phases is to
    defer that shuffle, which incurs a dependency stall, until after other
    memops are launched by the caller.

    发送时候再填充Flag:

    store128(ptr+u*WARP_SIZE, v[u], flagThread ? flag : v[u+1]);

    读取远端数据:

    if (RECV) {
      uint64_t* ptr = recvPtr(0)+ll128Offset;
      uint64_t flag = recvFlag(0);
      bool needReload;
      int spins = 0;
      do {
        needReload = false;
        #pragma unroll
        for (int u=0; u<ELEMS_PER_THREAD; u+=2) {
          load128(ptr+u*WARP_SIZE, vr[u], vr[u+1]);
          needReload |= flagThread && (vr[u+1] != flag);
        }
        needReload &= (0 == checkAbort(spins, 0, 0));
      } while (__any_sync(WARP_MASK, needReload));
    
      #pragma unroll
      for (int u=0; u<ELEMS_PER_THREAD; u+=2)
        load128(ptr+u*WARP_SIZE, vr[u], vr[u+1]);
    }
    • 一次性加载128bit,needReload配合while循环看flagThread里的flag是否为预期值,如果是则校验通过

    存储寄存器的时候,我们需要把flagThread的寄存器再反shuffle回来:

    template<int WordPerThread>
      __device__ __forceinline__ void storeRegs(T *dst, uint64_t(&regs)[WordPerThread], int eltN) {
        constexpr int EltPer16B = 16/sizeof(T);
        // Reverse Finish() register permuatation.
        #pragma unroll
        for (int g=1; g < WordPerThread/2; g+=2) {
          if (flagThread) regs[2*g-1] = regs[2*g];
        }
        // ...

    Reference: What is LL128 Protocol?

    如何选择LL128协议

    协议选择,NCCL 根据环境情况自动选择或用户通过设置环境变量指定使用LL128 协议。详情见本文《NCCL通信选择协议规则》的相关说明。

    LL 和 LL128 协议的对比

    特性LL 协议LL128 协议
    数据块大小较小(通常为 128 字节)较大(通常为 128 字节的倍数,如 128 * N)
    延迟较低
    带宽利用率较高极高
    适用数据规模中等规模(几百 KB 到几 MB)大规模(几 MB 到几百 MB)
    硬件优化优化 NVLink 和 PCIe 的低延迟特性优化 NVLink 和 PCIe 的高带宽特性
    适用场景单节点多 GPU、中等规模数据传输多节点 GPU 集群、大规模数据传输

    为什么 LL128 协议的数据块较大?

    LL128 协议是 LL 协议的扩展,其核心思想是通过增加数据块大小来减少通信开销,从而提高带宽利用率。具体来说:

    • 减少通信次数:较大的数据块意味着每次传输的数据量增加,从而减少通信次数,降低通信开销。

    • 提高带宽利用率:大数据块能够更好地利用 NVLink 和 PCIe 的高带宽特性,最大化传输效率。

    ​​​​​​​

    常见 NIVIDA 指令

    摘自:https://zhuanlan.zhihu.com/p/6160835906

    这里参考了 WeLearnNLP 的指南。

    nvidia-smi topo -m

    最典型的当然有 nvidia-smi 和 nvidia-smi topo -m。前者都非常熟悉了,这里我对比下两台集群的 nvidia-smi topo -m 的输出:

    GPU0    GPU1    GPU2    GPU3    GPU4    GPU5    GPU6    GPU7    CPU Affinity    NUMA Affinity   GPU NUMA ID
    GPU0     X  SYS SYS SYS SYS SYS SYS SYS 0-15,32-47  0       N/A
    GPU1    SYS  X  SYS SYS SYS SYS SYS SYS 0-15,32-47  0       N/A
    GPU2    SYS SYS  X  SYS SYS SYS SYS SYS 0-15,32-47  0       N/A
    GPU3    SYS SYS SYS  X  SYS SYS SYS SYS 0-15,32-47  0       N/A
    GPU4    SYS SYS SYS SYS  X  SYS SYS SYS 16-31,48-63 1       N/A
    GPU5    SYS SYS SYS SYS SYS  X  SYS SYS 16-31,48-63 1       N/A
    GPU6    SYS SYS SYS SYS SYS SYS  X  SYS 16-31,48-63 1       N/A
    GPU7    SYS SYS SYS SYS SYS SYS SYS  X  16-31,48-63 1       N/A
    
    Legend:
    
      X    = Self
      SYS  = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
      NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
      PHB  = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
      PXB  = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
      PIX  = Connection traversing at most a single PCIe bridge
      NV#  = Connection traversing a bonded set of # NVLinks
    GPU0    GPU1    GPU2    GPU3    GPU4    GPU5    GPU6    GPU7    CPU Affinity    NUMA Affinity   GPU NUMA ID
    GPU0     X      NV18    NV18    NV18    NV18    NV18    NV18    NV18    0-47,96-143     0               N/A
    GPU1    NV18     X      NV18    NV18    NV18    NV18    NV18    NV18    0-47,96-143     0               N/A
    GPU2    NV18    NV18     X      NV18    NV18    NV18    NV18    NV18    0-47,96-143     0               N/A
    GPU3    NV18    NV18    NV18     X      NV18    NV18    NV18    NV18    0-47,96-143     0               N/A
    GPU4    NV18    NV18    NV18    NV18     X      NV18    NV18    NV18    48-95,144-191   1               N/A
    GPU5    NV18    NV18    NV18    NV18    NV18     X      NV18    NV18    48-95,144-191   1               N/A
    GPU6    NV18    NV18    NV18    NV18    NV18    NV18     X      NV18    48-95,144-191   1               N/A
    GPU7    NV18    NV18    NV18    NV18    NV18    NV18    NV18     X      48-95,144-191   1               N/A
    
    Legend:
    
      X    = Self
      SYS  = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
      NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
      PHB  = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
      PXB  = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
      PIX  = Connection traversing at most a single PCIe bridge
      NV#  = Connection traversing a bonded set of # NVLinks

    可以读出很多有趣的信息:

    通过对比这两个集群的拓扑信息,我可以得出以下几个重要结论:

    1. 互联方式
    2. 第一个集群:所有 GPU 之间通过 PCIe 和 NUMA 节点间的 SMP 互联(标记为 SYS)
    3. 第二个集群:所有 GPU 之间通过 18 条 NVLink 连接(标记为 NV18)
    4. 性能影响:第二个集群的 GPU 间通信性能显著优于第一个集群,因为 NVLink 的带宽和延迟都优于 PCIe+SMP 方案
    5. NUMA 架构
    6. 两个集群都采用双 NUMA 节点设计:
    7. GPU 0-3 属于 NUMA 节点 0
    8. GPU 4-7 属于 NUMA 节点 1
    9. GPU 通信:应尽量将相关任务分配到同一 NUMA 节点内的 GPU,以避免跨 NUMA 节点的频繁数据传输
    10. CPU 核心分配
    11. 第一个集群:每个 NUMA 节点分配 32 个核心(如 0-15,32-47)
    12. 第二个集群:每个 NUMA 节点分配 96 个核心(如 0-47,96-143)
    13. 系统规模
    14. GPU 数量:两个集群都是 8 GPU 配置
    15. CPU 核心总数:
    16. 第一个集群:64 核心
    17. 第二个集群:192 核心
    18. 拓扑完整性
    19. 每个 GPU 都与其他所有 GPU 直接相连

    NVLINK 查询

    nvidia-smi nvlink --status -i 0
    nvidia-smi nvlink --capabilities -i 0

    nvlink 查询结果

    GPU 0: NVIDIA H100 80GB HBM3 (UUID: GPU-5a10e6e5-95f7-2785-ed63-6f6147f304f7)
             Link 0: 26.562 GB/s
             Link 1: 26.562 GB/s
             Link 2: 26.562 GB/s
             Link 3: 26.562 GB/s
             Link 4: 26.562 GB/s
             Link 5: 26.562 GB/s
             Link 6: 26.562 GB/s
             Link 7: 26.562 GB/s
             Link 8: 26.562 GB/s
             Link 9: 26.562 GB/s
             Link 10: 26.562 GB/s
             Link 11: 26.562 GB/s
             Link 12: 26.562 GB/s
             Link 13: 26.562 GB/s
             Link 14: 26.562 GB/s
             Link 15: 26.562 GB/s
             Link 16: 26.562 GB/s
             Link 17: 26.562 GB/s
    GPU 0: NVIDIA H100 80GB HBM3 (UUID: GPU-5a10e6e5-95f7-2785-ed63-6f6147f304f7)
             Link 0, P2P is supported: true
             Link 0, Access to system memory supported: true
             Link 0, P2P atomics supported: true
             Link 0, System memory atomics supported: true
             Link 0, SLI is supported: true
             Link 0, Link is supported: true
             Link 1, P2P is supported: true
             Link 1, Access to system memory supported: true
             Link 1, P2P atomics supported: true
             Link 1, System memory atomics supported: true
             Link 1, SLI is supported: true
             Link 1, Link is supported: true
             Link 2, P2P is supported: true
             Link 2, Access to system memory supported: true
             Link 2, P2P atomics supported: true
             Link 2, System memory atomics supported: true
             Link 2, SLI is supported: true
             Link 2, Link is supported: true
             Link 3, P2P is supported: true
             Link 3, Access to system memory supported: true
             Link 3, P2P atomics supported: true
             Link 3, System memory atomics supported: true
             Link 3, SLI is supported: true
             Link 3, Link is supported: true
             Link 4, P2P is supported: true
             Link 4, Access to system memory supported: true
             Link 4, P2P atomics supported: true
             Link 4, System memory atomics supported: true
             Link 4, SLI is supported: true
             Link 4, Link is supported: true
             Link 5, P2P is supported: true
             Link 5, Access to system memory supported: true
             Link 5, P2P atomics supported: true
             Link 5, System memory atomics supported: true
             Link 5, SLI is supported: true
             Link 5, Link is supported: true
             Link 6, P2P is supported: true
             Link 6, Access to system memory supported: true
             Link 6, P2P atomics supported: true
             Link 6, System memory atomics supported: true
             Link 6, SLI is supported: true
             Link 6, Link is supported: true
             Link 7, P2P is supported: true
             Link 7, Access to system memory supported: true
             Link 7, P2P atomics supported: true
             Link 7, System memory atomics supported: true
             Link 7, SLI is supported: true
             Link 7, Link is supported: true
             Link 8, P2P is supported: true
             Link 8, Access to system memory supported: true
             Link 8, P2P atomics supported: true
             Link 8, System memory atomics supported: true
             Link 8, SLI is supported: true
             Link 8, Link is supported: true
             Link 9, P2P is supported: true
             Link 9, Access to system memory supported: true
             Link 9, P2P atomics supported: true
             Link 9, System memory atomics supported: true
             Link 9, SLI is supported: true
             Link 9, Link is supported: true
             Link 10, P2P is supported: true
             Link 10, Access to system memory supported: true
             Link 10, P2P atomics supported: true
             Link 10, System memory atomics supported: true
             Link 10, SLI is supported: true
             Link 10, Link is supported: true
             Link 11, P2P is supported: true
             Link 11, Access to system memory supported: true
             Link 11, P2P atomics supported: true
             Link 11, System memory atomics supported: true
             Link 11, SLI is supported: true
             Link 11, Link is supported: true
             Link 12, P2P is supported: true
             Link 12, Access to system memory supported: true
             Link 12, P2P atomics supported: true
             Link 12, System memory atomics supported: true
             Link 12, SLI is supported: true
             Link 12, Link is supported: true
             Link 13, P2P is supported: true
             Link 13, Access to system memory supported: true
             Link 13, P2P atomics supported: true
             Link 13, System memory atomics supported: true
             Link 13, SLI is supported: true
             Link 13, Link is supported: true
             Link 14, P2P is supported: true
             Link 14, Access to system memory supported: true
             Link 14, P2P atomics supported: true
             Link 14, System memory atomics supported: true
             Link 14, SLI is supported: true
             Link 14, Link is supported: true
             Link 15, P2P is supported: true
             Link 15, Access to system memory supported: true
             Link 15, P2P atomics supported: true
             Link 15, System memory atomics supported: true
             Link 15, SLI is supported: true
             Link 15, Link is supported: true
             Link 16, P2P is supported: true
             Link 16, Access to system memory supported: true
             Link 16, P2P atomics supported: true
             Link 16, System memory atomics supported: true
             Link 16, SLI is supported: true
             Link 16, Link is supported: true
             Link 17, P2P is supported: true
             Link 17, Access to system memory supported: true
             Link 17, P2P atomics supported: true
             Link 17, System memory atomics supported: true
             Link 17, SLI is supported: true
             Link 17, Link is supported: true

    可以分析看到一些对开发实用的特性:

    • P2P(点对点)通信
    • 系统内存访问
    • P2P原子操作
    • 系统内存原子操作
    • SLI(多GPU并行)
    • 完整的链路支持

    GPU 监控

    可以监控 GPU 的方式很多,这里推荐 nvitop,非常方便,pip 安装即可,看着最赏心悦目。

    NCCL、RCCL和MCCL的区别

    NCCL、RCCL和MCCL是用于高性能计算的通信库,主要区别在于支持的硬件平台和优化目标:

    1. NCCL (NVIDIA Collective Communications Library)

      • 硬件支持:专为NVIDIA GPU设计,支持多GPU和多节点通信。

      • 优化目标:针对NVIDIA GPU的NVLink和PCIe拓扑进行优化,适合深度学习和大规模并行计算。

      • 应用场景:主要用于深度学习训练,支持跨节点通信。

    2. RCCL (ROCm Collective Communications Library)

      • 硬件支持:专为AMD GPU设计,基于ROCm平台。

      • 优化目标:针对AMD GPU的Infinity Fabric和PCIe拓扑进行优化,支持多GPU和多节点通信。

      • 应用场景:适用于AMD GPU的深度学习和高性能计算。

    3. MCCL (Machine Collective Communications Library)

      • 硬件支持:专为机器学习加速器(如TPU、FPGA等)设计。

      • 优化目标:针对特定机器学习硬件的通信需求进行优化,支持多设备通信。

      • 应用场景:主要用于机器学习加速器的高性能计算任务。

    总结

    • NCCL:适用于NVIDIA GPU。

    • RCCL:适用于AMD GPU。

    • MCCL:适用于机器学习加速器。

    选择库时需根据硬件平台和具体需求决定。

    NCCL与 MPI的区别

            NCCL(NVIDIA Collective Communications Library) 和 MPI(Message Passing Interface) 都是用于并行计算和分布式计算的通信库,但它们的应用场景、设计目标和实现方式有显著区别。以下是它们的详细对比:


    1. 设计目标和应用场景

    特性NCCLMPI
    主要目标优化多 GPU 和多节点之间的通信,特别是深度学习中的分布式训练。通用的并行计算通信标准,适用于各种分布式计算场景(如科学计算、仿真等)。
    应用场景深度学习框架(如 TensorFlow、PyTorch)中的多 GPU 训练。高性能计算(HPC)、科学计算、大规模并行计算。
    硬件优化针对 NVIDIA GPU 和 NVLink 进行深度优化。不特定于硬件,支持多种硬件架构(如 CPU、GPU、InfiniBand 等)。

    2. 通信模式

    特性NCCLMPI
    通信操作专注于集体通信(Collective Communication),如 AllReduce、Broadcast 等。支持点对点通信(Point-to-Point)和集体通信(Collective Communication)。
    通信范围主要用于单节点多 GPU 或多节点 GPU 集群。支持任意规模的分布式计算,包括 CPU 和 GPU 集群。
    通信效率针对 GPU 通信高度优化,性能极高。通用性强,但可能需要额外配置以优化 GPU 通信。

    3. 硬件支持

    特性NCCLMPI
    GPU 支持专门为 NVIDIA GPU 设计,支持 NVLink 和 PCIe。通过 CUDA-aware MPI 实现 GPU 支持,但需要额外配置。
    多节点支持支持多节点通信,但主要针对 GPU 集群。支持多节点通信,适用于各种硬件(如 CPU、GPU、InfiniBand 等)。
    硬件优化深度优化 NVIDIA GPU 和 NVLink 的通信性能。通用性强,但需要针对特定硬件进行优化。

    4. 编程模型和集成

    特性NCCLMPI
    编程模型提供简单的 API,专注于 GPU 集体通信。提供丰富的 API,支持点对点和集体通信,编程模型更复杂。
    集成性与深度学习框架(如 TensorFlow、PyTorch)深度集成。需要手动集成到应用程序中,适合自定义并行计算任务。
    易用性对深度学习开发者更友好,API 简单易用。需要更多编程经验,适合高性能计算领域的开发者。

    5. 性能对比

    特性NCCLMPI
    GPU 通信性能针对 NVIDIA GPU 优化,性能极高,延迟低。性能依赖于实现(如 OpenMPI、MVAPICH2),可能需要额外优化。
    多节点性能针对 GPU 集群优化,但在纯 CPU 集群中性能不如 MPI。在多节点 CPU 集群中性能优异,支持多种网络协议(如 InfiniBand、以太网)。
    扩展性适合中小规模 GPU 集群,大规模扩展性有限。适合超大规模分布式计算,扩展性极强。

    6. 典型使用场景

    场景NCCLMPI
    深度学习训练用于多 GPU 分布式训练,如 TensorFlow、PyTorch 中的 AllReduce 操作。可用于分布式训练,但需要更多手动配置。
    科学计算不常用。广泛用于科学计算、仿真和大规模数值计算。
    通用并行计算不适用。适用于各种并行计算任务,灵活性高。

    总结

    特性NCCLMPI
    定位GPU 优化的集体通信库,专注于深度学习。通用的并行计算通信标准,适用于多种场景。
    硬件支持针对 NVIDIA GPU 和 NVLink 优化。支持多种硬件架构,通用性强。
    易用性对深度学习开发者更友好。需要更多编程经验。
    性能在 GPU 集群中性能优异。在大规模 CPU 集群中性能优异。

    选择建议:

    • 如果你的应用场景是 深度学习 或 多 GPU 训练,优先选择 NCCL

    • 如果你的应用场景是 科学计算 或 通用并行计算,优先选择 MPI

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984044.html

    如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

    相关文章

    aws(学习笔记第三十一课) aws cdk深入学习(batch-arm64-instance-type)

    aws(学习笔记第三十一课) aws cdk深入学习 学习内容&#xff1a; 深入练习aws cdk下部署batch-arm64-instance-type 1. 深入练习aws cdk下部署batch-arm64-instance-type 代码链接 代码链接 代码链接 -> batch-arm64-instance-type之前代码学习 之前学习代码链接 -> aw…

    单细胞多数据集整合和去除批次效应教程,代做各领域生信分析

    单细胞多数据集整合和去除批次效应教程 每个数据集的数据分别单独进行读取单细胞数据构建Seurat分析对象 读取各种来源的单细胞数据构建Seurat分析对象的教程 做这一步的时候可以查看我这篇写的非常详细的教程文章&#xff1a; 【腾讯文档】单细胞分析步骤1读取各种来源格式…

    什么是OFD文件?2025年我推荐新版OFD阅读器和PDF阅读器,双合一

    说起文档格式&#xff0c;大家第一反应肯定是PDF&#xff0c;但你知道吗&#xff1f;现在OFD也越来越常见了&#xff0c;特别是在一些正式文件上。刚开始我也是一脸懵&#xff0c;心想这OFD文件咋看啊&#xff1f;网上一搜&#xff0c;发现原来有专门对付它的阅读器&#xff0c…

    计算机网络----主要内容简介

    这里写目录标题 章节概览每章的大体结构结构功能与服务的关系 一些概念概念一概念二传统的网络层的工作方式&#xff08;路由IP&#xff09;现代的网络层的工作方式&#xff08;SDN&#xff09; 二级目录二级目录 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二…

    每日一练之合并两个有序链表

    题目描述&#xff1a; 方法&#xff1a;双指针 代码实例&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> struct ListNode {int val;struct ListNode* next; }; typedef struct ListNode ListNode; struct ListNode* merg…

    基于Spring3的抽奖系统

    注&#xff1a;项目git仓库地址&#xff1a;demo.lottery 小五Z/Spring items - 码云 - 开源中国 目录 注&#xff1a;项目git仓库地址&#xff1a;demo.lottery 小五Z/Spring items - 码云 - 开源中国 项目具体代码可参考仓库源码&#xff0c;本文只讲解重点代码逻辑 一…

    Vue使用jsts,将wkt转为geojson

    jsts库相关官方资料&#xff1a; JSTS是一个ECMAScript空间谓词和函数库&#xff0c;用于处理符合开放地理空间联盟发布的SQL简单特征规范的几何图形。JSTS也是成熟的Java库JTS的移植。 npm库的地址&#xff1a;https://www.npmjs.com/package/jsts Github开源项目地址&…

    【移动WEB开发】rem适配布局

    目录 1. rem基础 2.媒体查询 2.1 语法规范 2.2 媒体查询rem 2.3 引入资源&#xff08;理解&#xff09; 3. less基础 3.1 维护css的弊端 3.2 less介绍 3.3 less变量 3.4 less编译 3.5 less嵌套 3.6 less运算 4. rem适配方案 4.1 rem实际开发 4.2 技术使用 4.3 …

    数字电子技术基础(二十六)——TTL门电路的输入特性和扇出系数

    1 TTL门电路的输入特性 如下图所示为输入端伏安特性曲线的测试电路&#xff1a; 图1 输入端伏安特性曲线测试电路 以流入输入端的方向作为输入电流的参考方向&#xff0c;调节测试电路当中&#xff0c;电位器滑动端的位置&#xff0c;可以为这个与非门的B输入端提供一个可调的…

    DeepSeek-R1本地化部署(Mac)

    一、下载 Ollama 本地化部署需要用到 Ollama&#xff0c;它能支持很多大模型。官方网站&#xff1a;https://ollama.com/ 点击 Download 即可&#xff0c;支持macOS,Linux 和 Windows&#xff1b;我下载的是 mac 版本&#xff0c;要求macOS 11 Big Sur or later&#xff0c;Ol…

    tp8 + easywechat6.17 token 验证失败

    按照文档死活不行&#xff1a; 调整为以下就成功了&#xff08;return也是失败&#xff09;&#xff1a;

    C++:入门详解(关于C与C++基本差别)

    目录 一.C的第一个程序 二.命名空间&#xff08;namespace&#xff09; 1.命名空间的定义与使用&#xff1a; &#xff08;1&#xff09;命名空间里可以定义变量&#xff0c;函数&#xff0c;结构体等多种类型 &#xff08;2&#xff09;命名空间调用&#xff08;&#xf…

    Redis 数据持久化之AOF

    AOF&#xff08;Append Only File&#xff09; 以日志的形式来记录每个写操作&#xff0c;将Redis执行过的所有写指令记录下来&#xff08;读操作不记录&#xff09;&#xff0c;只许追加文件但不可以改写文件&#xff0c;redis启动之初会读取该文件重新构建数据&#xff0c;换…

    网格图学习(附题单与做题思路)

    文章目录 一、DFS 经典题型695. 岛屿的最大面积 二、BFS 经典题型994. 腐烂的橘子**算法选择对照表** 一、DFS 经典题型 岛屿的最大面积 LeetCode 695描述&#xff1a;求网格中最大的陆地连通区域面积解题&#xff1a;DFS 遍历所有相邻陆地&#xff0c;标记已访问关键点&#…

    开发者社区测试报告(功能测试+性能测试)

    功能测试 测试相关用例 开发者社区功能背景 在当今数字化时代&#xff0c;编程已经成为一项核心技能&#xff0c;越来越多的人开始学习编程&#xff0c;以适应快速变化的科技 环境。基于这一需求&#xff0c;我设计开发了一个类似博客的论坛系统&#xff0c;专注于方便程序员…

    pyecharts 中设置 ​Map 图表的宽高

    在 pyecharts 中设置 ​Map 图表的宽高&#xff0c;需要通过 InitOpts 初始化参数实现。以下是具体方法&#xff1a; &#x1f3af; 完整代码示例 from pyecharts import options as opts from pyecharts.charts import Map# 创建地图时设置宽高 map_chart (Map(init_optsopt…

    FPGA|Verilog-SPI驱动

    最近准备蓝桥杯FPGA的竞赛&#xff0c;因为感觉官方出的IIC的驱动代码思路非常好&#xff0c;写的内容非常有逻辑并且规范。也想学习一下SPI的协议&#xff0c;所以准备自己照着写一下。直到我打开他们给出的SPI底层驱动&#xff0c;我整个人傻眼了&#xff0c;我只能说&#x…

    python语言总结(持续更新)

    本文主要是总结各函数&#xff0c;简单的函数不会给予示例&#xff0c;如果在平日遇到一些新类型将会添加 基础知识 输入与输出 print([要输出的内容])输出函数 input([提示内容]如果输入提示内容会在交互界面显示&#xff0c;用以提示用户)输入函数 注释 # 单行注释符&…

    NO.26十六届蓝桥杯备战|字符数组七道练习|islower|isupper|tolower|toupper|strstr(C++)

    P5733 【深基6.例1】自动修正 - 洛谷 小写字母 - 32 大写字母 大写字母 32 小写字母 #include <bits/stdc.h> using namespace std;const int N 110; char a[N] { 0 };int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cin >> a;int i 0;while (a…

    笔记四:C语言中的文件和文件操作

    Faye&#xff1a;只要有正确的伴奏&#xff0c;什么都能变成好旋律。 ---------《寻找天堂》 目录 一、文件介绍 1.1程序文件 1.2 数据文件 1.3 文件名 二、文件的打开和关闭 2.1 文件指针 2.2.文件的打开和关闭 2.3 文件读取结束的判定 三、 文件的顺序读写 3.1 顺序读写…