cuML 多节点多 GPU(MNMG)集群启用 InfiniBand 通信的完整配置指南
【免费下载链接】cumlNVIDIA cuML: GPU-Accelerated Machine Learning项目地址: https://gitcode.com/GitHub_Trending/cu/cuml
[!WARNING] 本文所述步骤源自 cuML 仓库 wiki/mnmg/Using_Infiniband_for_MNMG.md,该页面明确标注为弃用(deprecated):其中的命令与 API 面向早期版本(UCX/CUDA 9.2 时代)编写,无法直接用于最新版 cuML。本文保留其完整技术脉络供理解 MNMG 通信原理与历史演进,并在文末给出与当前仓库源码的差异对照,帮助读者正确迁移到新版 API。若要在生产环境部署,请以当前版本官方文档为准。
导读
cuML 的多节点多 GPU(Multi-Node Multi-GPU,MNMG)训练依赖 Dask 在多个计算节点之间协调 GPU 数据交换。当节点间通过传统 TCP/IP 以太网互联时,PCIe/网络带宽往往成为大规模训练的瓶颈;而 InfiniBand 提供的 RDMA(Remote Direct Memory Access)能力可以让数据在 GPU 显存、主机内存与网卡之间以远高于 TCP 的带宽和远低于 CPU 的参与度直接传输。本文围绕 cuML 仓库中的历史运维文档,系统讲解从安装 UCX、编译 gdrcopy、配置 NCCL、启用 IP over IB,到启动 UCX 协议 Dask 集群、并在 cuML/RAFT 通信层上完成点对点(P2P)与集合通信(Allreduce)连通性测试的完整闭环,让你理解 MNMG 场景下 InfiniBand 的每一层配置细节与验证方法。
1. MNMG 通信架构概览:为什么需要 InfiniBand
cuML 的 MNMG 实现(见 python/cuml/cuml/dask 目录下的cluster、linear_model、decomposition、neighbors等子模块)采用「Dask 负责任务调度、底层通信库负责 GPU 间数据交换」的两层架构:
- Dask负责将算法分解为跨节点的任务图,并把数据分片调度到各个 worker;
- 通信层(历史上为 UCX + NCCL,当前仓库中由 RAPIDS 的 raft-dask 提供
Comms封装)负责 worker 之间真正的 GPU 张量交换,包括点对点 send/recv 与集合 allreduce。
当训练数据需要跨节点聚合(例如 KMeans 的中心点同步、DBSCAN 的标签合并、PCA 的协方差归约)时,节点间通信量会随规模快速增长。InfiniBand 相比以太网的核心优势在于:
- RDMA 卸载:数据从 GPU 显存经 GPUDirect RDMA 直达网卡,无需经过 CPU 内存拷贝,降低延迟与 CPU 占用;
- 高带宽低延迟:文档示例环境为 100 Gb/sec(4X EDR)链路,远超当时常见千兆/万兆以太网;
- 内核旁路:绕过操作系统网络协议栈,减少中断与上下文切换开销。
在 cuML 中,跨节点通信的初始化与测试入口正是文档第 7 节使用的Comms对象——它管理一个跨所有 Dask worker 的通信会话(session),并为每个 worker 建立 Raft 通信句柄(handle)。后续所有 MNMG 算法都会复用该句柄执行聚合操作。
2. 安装 UCX(Unified Communication X)
UCX 是本文方案最底层的通信库,它统一封装了 InfiniBand(rc/ud等 RDMA 传输)、CUDA 内存拷贝(cuda_copy、cuda_ipc)与 GPUDirect RDMA(gdr_copy)等多种传输通道。cuML 的 MNMG 通过 UCX 建立 Dask worker 之间的底层数据通路。
注意(原文提示):该 conda 包当时处于实验阶段,最终会由 rapidsai 频道提供支持;当时需要 CUDA 9.2,CUDA 10 包也在开发中。
2.1 通过 Conda 安装
conda install -c conda-forge -c jakirkham/label/ucx cudatoolkit=9.2 ucx-proc=*=gpu ucx python=3.7关键参数说明:
| 参数 | 含义 |
|---|---|
cudatoolkit=9.2 | 与当时 cuML 依赖的 CUDA 工具链版本对齐 |
ucx-proc=*=gpu | 选择启用 CUDA/GPU 支持的 UCX 构建变体(而非仅 CPU 版) |
python=3.7 | 锁定 Python 版本以匹配当时 rapidsai 生态 |
2.2 从源码编译
当 conda 渠道没有可用的实验包时,可以自行编译。首先安装编译工具链:
sudo apt-get install autogen autoconf libtool(可选)安装 gdrcopy 以加速 GPU 与网卡间的数据搬运
根据 UCX 的 NVIDIA GPU 支持文档,安装gdrcopy可以(并且可能有必要)启用更快的 GPU-网卡数据搬运。gdrcopy 提供了一套让 CPU 端代码可以直接读写 GPU 显存的库,UCX 借助它实现gdr_copy传输:
git clone https://github.com/NVIDIA/gdrcopy.git cd gdrcopy make -j PREFIX=$CONDA_INSTALL_PREFIX CUDA=/usr/local/cuda && make -j install sudo ./insmod.sh其中PREFIX指定安装前缀(示例指向 conda 环境),CUDA指向 CUDA 安装目录,insmod.sh用于加载内核模块。
编译带 CUDA 与多线程支持的 UCX:
git clone https://github.com/cjnolet/ucx-py.git cd ucx git checkout fea-ext-expose_worker_and_ep ./autogen.sh mkdir build && cd build ../configure --prefix=$CONDA_PREFIX --with-cuda=/usr/local/cuda --enable-mt --disable-cma CPPFLAGS="-I//usr/local/cuda/include" make -j install配置选项说明:
| 选项 | 作用 |
|---|---|
--prefix=$CONDA_PREFIX | 安装到当前 conda 环境,便于后续 ucx-py 通过UCX_PATH找到它 |
--with-cuda=/usr/local/cuda | 启用 CUDA 支持,编译出cuda_copy/cuda_ipc传输 |
--enable-mt | 启用多线程安全支持 |
--disable-cma | 禁用 cross-memory attach(避免与 CUDA 注册内存的兼容问题) |
--with-gdrcopy=/path/to/gdrcopy | 若已安装 gdrcopy,追加该选项即可启用gdr_copy传输 |
2.3 验证 UCX 传输能力:ucx_info -d
编译完成后,用ucx_info -d列出所有可用传输。应当能看到rc(InfiniBand Reliable Connection)传输,例如:
# Transport: rc # # Device: mlx5_0:1 # # capabilities: # bandwidth: 11794.23 MB/sec # latency: 600 nsec + 1 * N # overhead: 75 nsec # put_short: <= 124 # put_bcopy: <= 8K # put_zcopy: <= 1G, up to 8 iov # put_opt_zcopy_align: <= 512 # put_align_mtu: <= 4K # get_bcopy: <= 8K # get_zcopy: 65..1G, up to 8 iov # get_opt_zcopy_align: <= 512 # get_align_mtu: <= 4K # am_short: <= 123 # am_bcopy: <= 8191 # am_zcopy: <= 8191, up to 7 iov # am_opt_zcopy_align: <= 512 # am_align_mtu: <= 4K # am header: <= 127 # domain: device # connection: to ep # priority: 30 # device address: 3 bytes # ep address: 4 bytes # error handling: peer failure同时应看到 CUDA 相关传输。cuda_copy用于 CPU 内存与 GPU 显存之间的拷贝:
# Transport: cuda_copy # # Device: cudacopy0 # # capabilities: # bandwidth: 6911.00 MB/sec # latency: 10000 nsec # overhead: 0 nsec # put_short: <= 4294967295 # put_zcopy: unlimited, up to 1 iov # put_opt_zcopy_align: <= 1 # put_align_mtu: <= 1 # get_short: <= 4294967295 # get_zcopy: unlimited, up to 1 iov # get_opt_zcopy_align: <= 1 # get_align_mtu: <= 1 # connection: to iface # priority: 0 # device address: 0 bytes # iface address: 8 bytes # error handling: nonecuda_ipc用于同一节点内跨进程的 GPU 显存共享(通过 CUDA IPC 机制):
# Memory domain: cuda_ipc # component: cuda_ipc # register: <= 1G, cost: 0 nsec # remote key: 104 bytes # # Transport: cuda_ipc # # Device: cudaipc0 # # capabilities: # bandwidth: 24000.00 MB/sec # latency: 1 nsec # overhead: 0 nsec # put_zcopy: <= 1G, up to 1 iov # put_opt_zcopy_align: <= 1 # put_align_mtu: <= 1 # get_zcopy: <= 1G, up to 1 iov # get_opt_zcopy_align: <= 1 # get_align_mtu: <= 1 # connection: to iface # priority: 0 # device address: 8 bytes # iface address: 4 bytes # error handling: none若在configure时指定了--with-gdrcopy,列表中还会出现gdr_copy传输,它让数据可以直接从 GPU 显存经由 GPUDirect RDMA 搬运到 InfiniBand 网卡:
# Memory domain: gdr_copy # component: gdr_copy # register: unlimited, cost: 0 nsec # remote key: 32 bytes # # Transport: gdr_copy # # Device: gdrcopy0 # # capabilities: # bandwidth: 6911.00 MB/sec # latency: 1000 nsec # overhead: 0 nsec # put_short: <= 4294967295 # get_short: <= 4294967295 # connection: to iface # priority: 0 # device address: 0 bytes # iface address: 8 bytes # error handling: none判读要点:确认列表同时包含rc(跨节点 IB)、cuda_ipc(节点内 GPU 共享)与cuda_copy(CPU/GPU 拷贝);若配置了 gdrcopy 还应看到gdr_copy。缺少任何一项都意味着对应数据通路不可用,后续 Dask 集群将退化为较低效的传输组合。
3. 安装 ucx-py:把 UCX 暴露给 Python/Dask
ucx-py是 UCX 的 Python 绑定,它把 UCX worker/endpoint 暴露给 Python 层,是 Dask 的ucx://协议得以工作的桥梁。
3.1 通过 Conda 安装
conda install -c conda-forge -c jakirkham/label/ucx cudatoolkit=9.2 ucx-py python=3.73.2 从源码安装
git clone git@github.com:rapidsai/ucx-py cd ucx-py export UCX_PATH=$CONDA_PREFIX make -j installUCX_PATH必须指向第 2 节中 UCX 的安装前缀(即--prefix指定的路径),这样 ucx-py 才能在运行时定位到 UCX 库。
4. 安装并配置 NCCL
NCCL(NVIDIA Collective Communications Library)负责 cuML MNMG 中的集合通信(如 allreduce)。原文特别强调:必须安装 NCCL 2.4 及以上版本,且库路径上不能残留旧版本 NCCL,否则会在 cuML 构建阶段引发编译错误。
conda install -c nvidia nccl随后在主目录创建.nccl.conf,把 NCCL 的 socket 接口绑定到 InfiniBand 设备ib0:
NCCL_SOCKET_IFNAME=ib0这一步确保 NCCL 的集合通信流量走 IB 接口而非默认的以太网接口,与第 5 步中 UCX 的接口选择保持一致。
5. 为 IB 设备启用 IP over IB 接口(ib0)
NCCL 与 Dask 的控制面通信需要 IP 地址,因此需要在 InfiniBand 设备上启用 IP over IB。当 IP over IB 内核模块已经安装好后,把 IB 设备映射到 IP 接口非常简单:
sudo ifconfig ib0 10.0.0.50/24用ifconfig ib0验证接口是否创建正确,输出形如:
ib0 Link encap:UNSPEC HWaddr 80-00-00-68-FE-80-00-00-00-00-00-00-00-00-00-00 inet addr:10.0.0.50 Bcast:10.0.0.255 Mask:255.255.255.0 inet6 addr: fe80::526b:4b03:f5:ce9c/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:65520 Metric:1 RX packets:2655 errors:0 dropped:0 overruns:0 frame:0 TX packets:2697 errors:0 dropped:10 overruns:0 carrier:0 collisions:0 txqueuelen:256 RX bytes:183152 (183.1 KB) TX bytes:194696 (194.6 KB)注意MTU:65520正是 InfiniBand 的大 MTU(相比以太网 1500 字节),这也是 IB 高吞吐的来源之一。每个节点都需要为各自的 IB 设备配置 IP,例如本例中 scheduler 节点使用10.0.0.50,其他节点使用同一子网内的不同地址。
6. 设置 UCX 环境变量
先运行ibstatus查看本机可用的 IB 设备及端口状态:
Infiniband device 'mlx5_0' port 1 status: default gid: fe80:0000:0000:0000:506b:4b03:00f5:ce9c base lid: 0xf sm lid: 0x1 state: 4: ACTIVE phys state: 5: LinkUp rate: 100 Gb/sec (4X EDR) link_layer: InfiniBand Infiniband device 'mlx5_1' port 1 status: default gid: fe80:0000:0000:0000:506b:4b03:0049:4236 base lid: 0x6 sm lid: 0x1 state: 4: ACTIVE phys state: 5: LinkUp rate: 100 Gb/sec (4X EDR) link_layer: InfiniBand Infiniband device 'mlx5_2' port 1 status: default gid: fe80:0000:0000:0000:506b:4b03:00f5:cf04 base lid: 0x2 sm lid: 0x1 state: 4: ACTIVE phys state: 5: LinkUp rate: 100 Gb/sec (4X EDR) link_layer: InfiniBand Infiniband device 'mlx5_3' port 1 status: default gid: fe80:0000:0000:0000:506b:4b03:0049:3eb2 base lid: 0x11 sm lid: 0x1 state: 4: ACTIVE phys state: 5: LinkUp rate: 100 Gb/sec (4X EDR) link_layer: InfiniBand关键状态判读:state: 4: ACTIVE与phys state: 5: LinkUp表示端口已激活并完成链路协商,rate: 100 Gb/sec (4X EDR)给出链路速率。
把设备与端口写入UCX_NET_DEVICES,指定 UCX 实际使用的 IB 设备列表:
export UCX_NET_DEVICES=mlx5_0:1,mlx5_3:1,mlx5_2:1,mlx5_1:1设置 UCX 启用的传输(TLS,transport layer selection):
export UCX_TLS=rc,cuda_copy,cuda_ipcrc:跨节点的 InfiniBand Reliable Connection,是节点间 GPU 数据交换的主力通道;cuda_copy:CPU/GPU 内存拷贝传输,作为非 RDMA 路径的兜底;cuda_ipc:节点内跨进程 GPU 显存共享。
注意:若安装了
gdrcopy,需把gdr_copy追加到UCX_TLS末尾,即export UCX_TLS=rc,cuda_copy,cuda_ipc,gdr_copy,以启用 GPU 显存直达 IB 网卡的 GPUDirect RDMA 路径。
这些环境变量需要**在启动 Dask 的每个节点上(scheduler 与所有 worker)**预先导出,并随进程传递给 Dask 运行时。
7. 在 ib0 接口上启动 Dask 集群
在作为调度器(scheduler)的节点上运行:
dask-scheduler --protocol ucx --interface ib0--protocol ucx:让 Dask 使用 UCX 作为通信协议,而非默认的 TCP;--interface ib0:绑定到 IP over IB 接口,确保控制面与数据面流量都走 IB。
然后在每个 worker 节点上启动 GPU worker,连接地址使用 scheduler 的 IP over IB 地址(示例中为10.0.0.50,端口为 Dask 默认的 8786):
dask-cuda-worker ucx://10.0.0.50:8786dask-cuda-worker是 RAPIDS 生态提供的 GPU 感知 worker,它会为每个 GPU 启动一个 worker 进程,并在启动阶段初始化 UCX 与 NCCL 环境。
8. 验证通信:cumlCommunicator 测试
集群就绪后,用一段 Python 脚本创建 DaskClient与 cuML 的Comms通信会话,然后分别测试点对点(P2P)与集合(Allreduce)通信。
8.1 创建 Client 与 Comms
from dask.distributed import Client, wait from cuml.raft.dask.common.comms import Comms from cuml.dask.common import get_raft_comm_state from cuml.dask.common import perform_test_comms_send_recv from cuml.dask.common import perform_test_comms_allreduce import random c = Client("ucx://10.0.0.50:8786") cb = Comms(comms_p2p=True) cb.init()Client("ucx://10.0.0.50:8786"):通过 UCX 协议连接 scheduler;Comms(comms_p2p=True):创建跨 worker 的通信管理器,comms_p2p=True表示同时初始化点对点通信能力;cb.init():在所有 worker 上建立 Raft 通信会话(sessionId),各 worker 可通过get_raft_comm_state(sessionId)获取通信句柄(handle)。
8.2 测试点对点通信(send/recv)
n_trials = 2 def func_test_send_recv(sessionId, n_trials, r): handle = get_raft_comm_state(sessionId)["handle"] return perform_test_comms_send_recv(handle, n_trials) p2p_dfs=[c.submit(func_test_send_recv, cb.sessionId, n_trials, random.random(), workers=[w]) for wid, w in zip(range(len(cb.worker_addresses)), cb.worker_addresses)] wait(p2p_dfs) p2p_result = list(map(lambda x: x.result(), p2p_dfs)) print(str(p2p_result)) assert all(p2p_result)该测试让每个 worker 通过 UCX 向其他所有 worker 发送/接收数据,验证节点间的 RDMA 数据通路。worker 上应看到类似输出(每个 Trial 中,每个 rank 收到的数据集合恰好缺失自身 rank,证明全连通且数据正确):
========================= Trial 0 Rank 0 received: [1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 8, 9, 14, 15] Rank 1 received: [0, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 8, 9, 14, 15] Rank 2 received: [0, 1, 3, 4, 5, 6, 7, 10, 11, 12, 13, 8, 9, 14, 15] Rank 3 received: [0, 1, 2, 4, 5, 6, 7, 10, 11, 12, 13, 8, 9, 14, 15] Rank 4 received: [0, 1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 8, 9, 14, 15] Rank 5 received: [0, 1, 2, 3, 4, 6, 7, 10, 11, 12, 13, 8, 9, 14, 15] Rank 6 received: [0, 1, 2, 3, 4, 5, 7, 10, 11, 12, 13, 8, 9, 14, 15] Rank 7 received: [0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 8, 9, 14, 15] ========================= ========================= Trial 1 Rank 0 received: [11, 2, 13, 12, 9, 10, 15, 14, 1, 8, 5, 4, 3, 6, 7] Rank 1 received: [2, 12, 11, 10, 9, 14, 13, 8, 15, 4, 5, 6, 3, 0, 7] Rank 2 received: [12, 1, 11, 10, 9, 14, 13, 8, 15, 4, 5, 6, 3, 0, 7] Rank 3 received: [2, 11, 12, 10, 9, 14, 13, 8, 15, 4, 1, 6, 5, 0, 7] Rank 4 received: [2, 11, 12, 9, 13, 10, 15, 14, 1, 8, 3, 6, 5, 0, 7] Rank 5 received: [2, 11, 12, 9, 10, 14, 13, 8, 15, 4, 1, 6, 3, 0, 7] Rank 6 received: [2, 11, 12, 9, 10, 13, 15, 14, 1, 8, 5, 4, 3, 0, 7] Rank 7 received: [2, 11, 12, 9, 10, 13, 14, 8, 15, 4, 1, 6, 5, 0, 3] =========================8.3 测试集合通信(Allreduce)
def func_test_allreduce(sessionId, r): handle = get_raft_comm_state(sessionId)["handle"] return perform_test_comms_allreduce(handle) coll_dfs = [c.submit(func_test_allreduce, cb.sessionId, random.random(), workers=[w]) for wid, w in zip(range(len(cb.worker_addresses)), cb.worker_addresses)] wait(coll_dfs) coll_result = list(map(lambda x: x.result(), coll_dfs)) coll_result assert all(coll_result)该测试通过 NCCL 在所有 worker 之间执行 allreduce 归约,验证集合通信通路。worker 上应看到类似输出:
Clique size: 16 Clique size: 16 Clique size: 16 Clique size: 16 Clique size: 16 Clique size: 16 final_size: 16 Clique size: 16 Clique size: 16 final_size: 16 final_size: 16 final_size: 16 final_size: 16 final_size: 16 final_size: 16 final_size: 16Clique size表示参与归约的 rank 数量,final_size: 16表示 16 个 rank(示例为 8 节点 × 每节点多 GPU 或单节点多 GPU 的总数)全部正确参与了归约。两个断言assert all(...)全部通过,即代表 InfiniBand 数据通路配置成功,可以在此基础上运行 cuML 的 MNMG 算法。
9. 与当前仓库源码的差异:API 演进对照
原文档编写时使用的是cuml.raft.dask.common.comms等旧导入路径。在当前版本的 cuML 仓库中,通信层已经迁移为独立的 raft-dask 库,导入路径变为raft_dask.common.comms。以仓库源码为证:
- python/cuml/cuml/dask/cluster/kmeans.py 使用
from raft_dask.common.comms import Comms, get_raft_comm_state; - python/cuml/cuml/dask/cluster/dbscan.py 同样导入
Comms, get_raft_comm_state; - python/cuml/cuml/dask/neighbors/nearest_neighbors.py、python/cuml/cuml/dask/decomposition/base.py、python/cuml/cuml/dask/common/base.py 等文件也统一使用
raft_dask命名空间。
因此,原文档第 8 节测试脚本中perform_test_comms_send_recv/perform_test_comms_allreduce这两个函数在当前 cuML 源码中已不存在,其对应能力由raft_dask提供的通信测试 API 承担。从源码结构看,当前 MNMG 算法(KMeans、DBSCAN、PCA、kNN、线性模型等)统一通过get_raft_comm_state(sessionId)获取通信句柄,再调用 raft 底层的comms完成聚合操作——这一「会话 + 句柄」的架构与文档第 8 节的测试逻辑一脉相承,只是封装库与导入路径发生了变化。
此外,python/cuml/cuml/dask/common/utils.py 提供了parse_host_port、get_client、wait_and_raise_from_futures等 MNMG 通用工具;python/cuml/cuml/tests/dask 目录下的测试(如test_kmeans.py、test_pca.py)与 CI 脚本 ci/run_cuml_dask_pytests.sh 展示了当前 MNMG 功能的验证方式,可作为理解新版通信层用法的参考入口。
10. 总结:Infiniband 配置链路一览
| 层级 | 组件 | 配置/验证要点 |
|---|---|---|
| 传输库 | UCX | conda 或源码安装;ucx_info -d确认rc/cuda_copy/cuda_ipc/gdr_copy |
| GPU 搬运 | gdrcopy(可选) | 编译安装后configure --with-gdrcopy,启用gdr_copy |
| Python 绑定 | ucx-py | UCX_PATH指向 UCX 前缀,make -j install |
| 集合通信 | NCCL 2.4+ | conda install -c nvidia nccl;~/.nccl.conf设NCCL_SOCKET_IFNAME=ib0 |
| 网络接口 | IP over IB | ifconfig ib0 10.0.0.50/24;ifconfig ib0确认 MTU 65520 |
| 环境变量 | UCX | UCX_NET_DEVICES=mlx5_0:1,...;UCX_TLS=rc,cuda_copy,cuda_ipc[,gdr_copy] |
| 集群启动 | Dask | dask-scheduler --protocol ucx --interface ib0;dask-cuda-worker ucx://<scheduler-ip>:8786 |
| 连通性验证 | Comms 测试 | P2P send/recv 与 Allreduce 测试断言全通过 |
按此链路逐层验证,即可在具备 InfiniBand 硬件的环境中为 cuML 的 MNMG 训练建立高带宽、低延迟的 GPU 间通信基础。再次提醒:以上命令与 API 面向旧版本,部署最新版 cuML 时应参考当前官方文档,并将通信导入迁移至raft_dask.common.comms。
【免费下载链接】cumlNVIDIA cuML: GPU-Accelerated Machine Learning项目地址: https://gitcode.com/GitHub_Trending/cu/cuml
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考