NCCL SHARP 和 TREE算法

H100开始一代用于机内allreduce操作。

主要流程:

Scatter->reduce-> broadcast -> gather。其中reduce和broadcast由Nvswitch做,但由GPU发起控制指令驱动。

其中GPU有一个UC(unicast)内存地址和MC(multicast) 内存地址, 都映射到同一块物理地址,可以由GPU和nvswitch共同访问。

详细流程:

复制代码
初始状态:每个 GPU 有 sendbuff(本 GPU 的输入数据)

═══════════════════════ 阶段一:Scatter ═══════════════════════
【全部 GPU 并行,纯 GPU 本地操作】

  GPU-k:  sendbuff[0..N] ──GPU拷贝──→ ucBuff[0..N]  (UC 地址,本地内存)
  更新 send.tail,通知"我已写入 ucBuff"

═══════════════════════ 阶段二:Reduce + Broadcast ═══════════
【GPU 发指令 + NVSwitch 硬件执行计算】

  Step 2a: 等待所有人 Scatter 完成
  ┌─────────────────────────────────────────────────────────┐
  │ GPU-k 发: multimem.ld_reduce.min([MC_tail])             │ ← GPU 发请求
  │ NVSwitch: min(tail_GPU0, tail_GPU1, ..., tail_GPU7)     │ ← NVSwitch 计算
  │ 返回: 全局最小 step,确认所有人写完                     │ ← NVSwitch 返回
  └─────────────────────────────────────────────────────────┘

  Step 2b: 从 MC 地址读数据(触发硬件规约)
  ┌─────────────────────────────────────────────────────────┐
  │ GPU-k 发: multimem.ld_reduce.add.f32 %reg, [MC_addr]   │ ← GPU 发请求
  │ NVSwitch: 读所有 GPU ucBuff[i],执行 add 规约            │ ← NVSwitch 计算
  │ 返回: sum(GPU0[i], GPU1[i], ..., GPU7[i]) → %reg        │ ← NVSwitch 返回
  └─────────────────────────────────────────────────────────┘
  ← GPU SM 寄存器里得到全局 sum,GPU 自身没做加法计算!

  Step 2c: 将规约结果写回 MC 地址(触发硬件广播)
  ┌─────────────────────────────────────────────────────────┐
  │ GPU-k 发: multimem.st.b32 [MC_addr], %reg              │ ← GPU 发请求
  │ NVSwitch: 将 sum 广播写入所有 GPU 的 ucBuff[i]          │ ← NVSwitch 执行
  └─────────────────────────────────────────────────────────┘

  更新 send.tail,通知"规约结果已写入 ucBuff"

═══════════════════════ 阶段三:Gather ═══════════════════════
【全部 GPU 并行,纯 GPU 本地操作】

  等待 MC_tail min 确认广播完成(GPU 发请求,NVSwitch 做 min)
  GPU-k:  ucBuff[0..N] ──GPU拷贝──→ recvbuff[0..N] (本地内存,UC 地址)

最终状态:每个 GPU 的 recvbuff = sum(所有 GPU 的 sendbuff)
二. 机间CollNet Sharp算法

目前只有NV的ib交换机支持交换机Sharp协议,CollNet有两种实现模式。

1.CollNet Direct (机内过Nvswitch场景,如全局AllReduce) , 这种是假设只有一个head网卡接交换机。

复制代码
时间 →

Node0: GPU0(leaf)  GPU1(leaf)  GPU2(Head)   IB/SHARP   GPU4(Node1-Head)
       |           |           |               |             |
       ├──Scatter─→|           |               |             |
       ├──────────Scatter──────→|               |             |
       |           ├──Scatter─→|               |             |
       |           |           |               |             |
       |           |     Reduce locally        |             |
       |           |           ├──iallreduce──→|←──iallreduce─|
       |           |           |     SHARP硬件内Reduce        |
       |           |           |←─ result ────┤             |
       |           |           |               |             |
       |←─────Bcast/Gather─────|               |             |
       |           |←──Gather─ |               |             |
       ↓           ↓           ↓               ↓             ↓
    [done]      [done]      [done]          [done]        [done]

节点内的数据通过 Head GPU做local Reduce, 然后发给 IB switch。

2.CollNet Chain (机内不过Nvswitch场景,原理和direct类似)

但是我们经常的硬件架构是一个GPU接一个网卡出,CollNet Direct变为以下情况

复制代码
Node0:   rank0(head0)  rank1(head1)  rank2(head2)  rank3(head3)
             │               │               │               │
             │               │               │               │
             └───────────────┴───────────────┴───────────────┘
                                     │
                              IB/SHARP Switch
                         (4路并行 iallreduce)
                                     │
             ┌───────────────┬───────────────┬───────────────┐
             │               │               │               │
         rank0(head0)  rank1(head1)  rank2(head2)  rank3(head3)
cpp 复制代码
const int hasUp = (direct->up[0] >= 0) ? 1 : 0;   // = 0,无叶节点需要汇聚
const int hasDn = (direct->down[0] >= 0) ? 1 : 0;  // = 0,无叶节点需要广播

// 线程分配:
// nThreadsScatter = WARP_SIZE (hasUp=0, hasDn=0,只分配最少线程)
// nThreadsGather  = 0
// nThreadsBcast   = WARP_SIZE
// nThreadsReduce  = 剩余所有线程

// Reduce 路径 (hasUp=0, hasDn=0):直接发网络,无本地 reduce 操作
} else {
  // Directly send to network
  prims.send(offset, nelem);  // 直接 send 到 out=nRanks(IB 网络出口)
}

// Bcast 路径:直接从网络接收
} else {
  prims.recv(offset, nelem, /*postOp=*/true);  // 直接从 nRanks 接收结果
}

没有任何 Scatter/Gather/LocalReduce 步骤,GPU 直接将数据送给 SHARP,再直接从 SHARP 接收。

三. Tree算法

比较简单,机内机间都可以用,如图单树

复制代码
Level 0:            GPU0 (根)
                   /    \
Level 1:        GPU4    (GPU? 未画出)
               /    \
Level 2:     GPU2   GPU6
            /  \    /  \
Level 3:   GPU1 GPU3 GPU5 GPU7

实际 NCCL 特点 :不一定是完全二叉树(取决于 GPU 拓扑),通常会使用 Kary 树(每个父节点有 4-8 个子节点)来减少层数,根节点选择:通常是离 NVSwitch 最近的 GPU

两步过程:Reduce 阶段(向上规约)、 Broadcast 阶段(向下广播)

多channel时,会生成2个树,Double Tree, 多channel共用这两颗树,这样为了更好地利用带宽。

复制代码
Tree 0 (Btree):              Tree 1 (镜像):

0---------------8            3----------------11
         ______/ \           / \______
        4         \         /         7
      /   \        \       /        /   \
    2       6       10    1        5      9
   / \     / \     /  \  / \      / \    / \
  1   3   5   7   9   11 0   2   4   6  8   10
相关推荐
一 乐13 小时前
家政服务管理系统|基于springboot + vue家政服务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·家政服务管理系统
dxxt_yy13 小时前
千兆光/电口+OTDR一体——成都鼎讯 SZT-1000A 千兆以太网测试仪助力风电能源光缆管理
运维·服务器·能源
Matrix_1113 小时前
手机里的计算摄影:广角形变校正算法
人工智能·算法·智能手机·计算摄影
WBluuue13 小时前
数据结构与算法:有序表(二):跳表
数据结构·c++·算法·skiplist
tedcloud12313 小时前
taste-skill部署教程:打造个性化AI推荐工作流
服务器·前端·人工智能·系统架构·edge
碳基硅坊14 小时前
Spring AI:把大模型接进 Spring 应用
java·人工智能·spring ai
黄毛火烧雪下14 小时前
Java 核心知识点总结(一)
java·开发语言
x1387028595714 小时前
c语言中srtlen(指针使用计算字符长度)、传值和传址调用
c语言·开发语言·算法·visual studio
海兰14 小时前
【实用程序】电商销售分析仪表盘 — 从零搭建一个AI参与的全栈数据洞察系统
人工智能·学习·算法
凡人叶枫15 小时前
Effective C++ 条款28:避免使用 handles 指向对象内部
linux·服务器·开发语言·c++·嵌入式开发