NVIDIA 集合通信库 (NCCL)
可实现针对 NVIDIA GPU 和网络进行优化的多 GPU 和多节点通信基元。NCCL 是多 GPU 深度学习训练软件的核心部分。它可以处理任何类型的 GPU 间通信,无论是通过 PCI、NVLink 还是网络进行通信。它采用先进的拓扑检测、优化的通信图形和调整模型,可在 NVIDIA GPU 平台上直接获得开箱即用的最佳性能。
在本文中,我们将讨论 NCCL 2.23 中发布的新功能和修复程序。查看
NVIDIA/nccl
Github 存储库。
NVIDIA Magnum IO NCCL
是一个旨在优化 GPU 间和多节点通信的库,对于 AI 和高性能计算 (HPC) 应用中的高效并行计算至关重要。此版本的价值在于其新功能:
-
适用于 ReduceScatter 和 AllGather 的新 PAT 算法 :
我们为 AllGather 和 ReduceScatter 引入了基于 Brucks 的 Parallel Aggregated Trees (PAT) 算法,实现了对数缩放。
-
加速初始化:
改进了初始化性能,包括使用带内网络进行引导通信的能力。
-
ncclCommInitRankScalable:
一种新的初始化 API,用于使用多个 ncclUniqueId 来加速大规模初始化。
-
节点内用户缓冲区注册 :
利用已注册的用户缓冲区进行节点内操作。
-
新的分析器插件 API :
用于测量细粒度 NCCL 性能的 API hook。
以下各节将深入探讨新功能的详细信息:
PAT 对数缩放 for ReduceScatter 和 AllGather
PAT 算法是 Bruck 算法的变体,该算法具有对数数量的网络步长,适用于大规模的小型网络,随着规模的增加,网络传输的数量会逐渐增加,从而尽可能减少缓冲需求。它同时适用于 AllGather 和 ReduceScatter。在使用 PAT 时,小到中型的消息大小预计会表现得更好,随着工作负载的扩展,这种改进也会增加。
此算法会针对每个秩执行已偏移的二项树。与递归翻倍等类似算法相比,它的优势在于可在任意数量的秩上运行,且不需要 2 的幂。
最初,PAT 仅支持每个节点一个 GPU。在每节点一个 GPU 的情况下,ReduceScatter 和 AllGather 对于
大语言模型 (LLM)
训练非常重要,在这种训练中,工作流并行性和张量并行性的维度与数据并行性垂直。张量并行维度通常与节点内 NVLink 连接对齐,这意味着其他维度的每个节点仅有一个 GPU。
请查看我们即将发表的介绍算法细节的论文。
新的 ncclCommInitRankScalable API
此功能添加了新的初始化函数
ncclCommInitRankScalable
,以便在通信器创建期间利用多个唯一 ID。这一新增功能避免了初始化期间的多对一通信模式,并提供了更可扩展的初始化性能。
在创建通信器时,NCCL 需要获取所有通信器 rank 的地址 (bootstrap step)。为此,NCCL 依赖于所有等级已知的唯一 ID。在 communicator 初始化的 bootstrap 步骤中,每个秩都会将其地址与已知的唯一 ID 进行交换,从而创建一对一通信模式,并在规模上造成重大瓶颈。
借助
ncclCommInitRankScalable
,用户现在可以自由提供多个唯一 ID,以便在启动过程中使用。为实现最高增益,NCCL 会将负载分散到多个唯一 ID,从而在所提供的唯一 ID 数量随通信器大小而扩展时,实现大规模的恒定引导时间。
这种新 API 需要多个 rank 才能创建唯一 ID。为获得最佳性能,我们建议在 rank 中尽可能均匀地分配唯一 ID。
在 2.23 版本中,我们改进了初始化代码的整体性能。我们消除了所需的部分引导群集,并在引导步骤中进行了性能调整。
现在,您可以使用快速网络 (IB/RoCE/…) 进行带外通信,以加速初始化的两个线性步骤:bootstrap 和 allgather。默认禁用该功能,以避免使用错误配置的设备 (在拓扑检测之前使用
ncclNet
设备)。您可以使用
NCCL_OOB_NET_ENABLE=1
启用它。
此外,您可以指定应与
NCCL_OOB_NET_IFNAME
一起使用的接口。默认情况下,NCCL 将使用该网络上找到的第一个
ncclNet
设备。
内用户缓冲区注册(
Intranode user buffer registration
)
NCCL 从不要求您作为用户来注册和维护任何持久性缓冲区,以便正常运行。这是一项出色的功能,易于使用,但也需要进行性能权衡。如果没有直接访问,NCCL 传输数据时必须出现更多的控制流和缓冲。与显式注册和映射缓冲区相比,这会消耗更多的 GPU 资源,并导致在移动同等数据量时产生更高的开销。
建议 NCCL 开发者尽可能使用
ncclCommRegister
注册缓冲区,以允许 NCCL 使用所有可用的优化。NCCL 团队一直致力于为已注册的用户缓冲区添加更多用例。2.23 版本为 NvLink 和 PCIe P2P 传输实现了节点内用户缓冲区(UB)注册支持。
注册 Intranode UB 的主要好处是避免在对等节点之间产生额外副本。这可减轻内存子系统的压力,提高 NCCL 通信性能,并改善计算和通信重叠。除
ncclReduce
和
ncclReduceScatter
(将无法受益) 外,支持所有 NCCL 集合和基于 sendrecv 的操作。
启用节点内 UB 注册的方法有两种。第一种是通过
ncclCommRegister
显式注册缓冲区,仅在调用相应的 NCCL 群集时注册缓冲区。第二种方法是通过 CUDA Graphs 捕获 NCCL 运算,并在图形捕获期间自动注册所有用户缓冲区。如需了解更多指南和要求,请参阅 NCCL 文档 。
除了通过 NVLink 和 PCIe 进行节点内通信外,该功能还适用于每个 NVLink 域内的多节点 NVLink(MNNVL)系统。
随着 GPU 集群规模的增加,性能异常变得越来越难以检测和找出根本原因。需要特定于域的监控和诊断工具来收集和分析遥测数据,同时尽可能减少运行作业的用度。NCCL 分析器插件接口旨在解决这些问题。该界面设计还可让 DL 框架分析器如 PyTorch Kineto 等轻松采用。
新的
NCCL_PROFILER_PLUGIN
环境变量控制 profiler 插件的加载和初始化,与加载和初始化其他 NCCL 插件的方式相同。加载后,profiler 插件可以通过设置 NCCL 在初始化期间向 profiler 公开的事件激活掩码来启用 NCCL 事件分析。事件激活掩码是一个 32 位整数,其中每个位都表示 NCCL profiler 事件。目前,NCCL 支持以下事件:
-
ncclProfileGroup (bit-0):
群组事件
-
ncclProfileColl (bit-1):
集合事件
-
ncclProfileP2p (bit-2):
点对点事件
-
ncclProfileProxyOp (bit-3):
代理进度通道事件
-
ncclProfileProxyStep (bit-4):
代理进度步骤事件
-
ncclProfileProxyCtrl (bit-5):
代理进度内部状态事件
NCCL 以分层形式表示事件。例如,collectives 可以分组在一起,而 proxy 操作可协助 GPU 跨可用网络通信通道进行单个数据块的点对点传输。因此,NCCL 会将相应的事件呈现给 profiler,并保留此关系。NCCL 事件层次结构的示意图如下所示:
ncclProfileGroup
|
+- ncclProfileColl
| |
| +- ncclProfileProxyOp
| |
| +- ncclProfileProxyStep
|
+- ncclProfileP2p
|
+- ncclProfileProxyOp
|
+- ncclProfileProxyStep
ncclProfileProxyCtrl
这种分层表示使 profiler plugins 能够以更有意义和更易于理解的形式向用户呈现 events。
NCCL 还在 ext-profiler/example 目录中提供了分析器插件示例,可用作开发第三方分析器插件的模板。
分析器插件接口总共定义了以下五个函数回调:
ncclResult_t (*init)(
void** context,
int* eActivationMask);
ncclResult_t (*startEvent)(
void* context,
void** eHandle,
ncclProfilerEventDescr_t* eDescr);
ncclResult_t (*stopEvent)(
void* eHandle);
ncclResult_t (*recordEventState)(
void* eHandle,
ncclProfilerEventState_t eState,
NcclProfilerEventStateArgs_t* eStateArgs);
ncclResult_t (*finalize)(void* context);
分析器
init
函数接收事件激活掩码指针,并将不透明上下文对象返回至 NCCL。上下文在分析器实例之间提供隔离,而分析器则使用事件激活掩码来通知 NCCL 应分析哪些事件;例如,设置 *
eActivationMask = ncclProfileColl | ncclProfileProxyOp
。
分析器
startEvent
函数采用分析器上下文和事件描述符。分析器使用描述符信息来分配新的事件对象并对其进行初始化。之后,分析器会返回一个不透明的句柄,NCCL 可使用该句柄对事件执行进一步运算,例如,记录状态更新。
分析器
stopEvent
函数获取事件句柄并将事件标记为已完成。之后,事件句柄将无法再使用 (分析器可能会在内部回收相应的对象以用于未来的事件)。