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
相关推荐
wuyk5556 分钟前
12.归并排序:分治思想的稳定排序算法
开发语言·数据结构·算法·排序算法
吴声子夜歌10 分钟前
ApacheCommons——commons-text(模板替换与文本算法)
java·开发语言·算法·apache
Haooog23 分钟前
Agent 开发中的 Memory:State、短期记忆、长期记忆与 Memory Retrieval
java·agent·memory
砚底藏山河30 分钟前
【量化纯GET实战 #23】多股票相关性:用收益率看板块联动
java·数据库·python·金融·数据分析
JiNan.YouQuan.Soft37 分钟前
数值计算引擎:计算固体力学
人工智能·算法·机器学习
非凡ghost40 分钟前
AI修图不“造假“,摄影师的出片效率加速器
服务器·前端·人工智能·电脑
抠脚小弟1 小时前
Spring Cache 使用详解:从入门到实战
java·spring
和裕1 小时前
光伏储能定制加强型瓦楞纸箱:降低组件运输隐裂率与售后损耗的核心价值
大数据·运维·网络·人工智能·算法
ZGG0031 小时前
MySQL 索引详解:B+ 树、聚簇索引与最左前缀
java·数据库·mysql
Ruiery1 小时前
Linux 6.6内核 PCIe 深度解析(十):PCIe Port Driver(portdrv)— 一个物理 Port,拆出五个逻辑服务
linux·运维·服务器