04 NCCL API
以下各节描述 NCCL 的方法与操作。
- 通信器创建与管理函数
- ncclGetLastError
- ncclGetErrorString
- ncclGetVersion
- ncclGetUniqueId
- ncclCommInitRank
- ncclCommInitAll
- ncclCommInitRankConfig
- ncclCommInitRankScalable
- ncclCommSplit
- ncclCommShrink
- ncclCommGetUniqueId
- ncclCommGrow
- ncclCommRevoke
- ncclCommFinalize
- ncclCommDestroy
- ncclCommAbort
- ncclCommGetAsyncError
- ncclCommCount
- ncclCommCuDevice
- ncclCommUserRank
- ncclCommRegister
- ncclCommDeregister
- ncclCommWindowRegister
- ncclCommWindowDeregister
- ncclMemAlloc
- ncclMemFree
- ncclCommSuspend
- ncclCommResume
- ncclCommMemStats
- 集合通信函数
- ncclAllReduce
- ncclBroadcast
- ncclReduce
- ncclAllGather
- ncclReduceScatter
- ncclAlltoAll
- ncclGather
- ncclScatter
- 组调用
- ncclGroupStart
- ncclGroupEnd
- ncclGroupSimulateEnd
- 点对点通信函数
- 双侧点对点操作
- 单侧点对点操作(RMA)
- 类型
- ncclComm_t
- ncclResult_t
- ncclDataType_t
- ncclRedOp_t
- ncclScalarResidence_t
- ncclConfig_t
- ncclSimInfo_t
- ncclCommMemStat_t
- ncclWindow_t
- 用户自定义归约运算符
- ncclRedOpCreatePreMulSum
- ncclRedOpDestroy
- NCCL API 支持的标志
- 窗口注册标志
- NCCL 通信器 CTA 策略标志
- 通信器收缩标志
- 设备 API
- 设备 API ------ 主机端设置
- 设备 API ------ 内存与 LSA
- 设备 API ------ GIN
- 设备 API - CFT
- 设备 API ------ 远程归约与拷贝:自定义通信内核的构建块
- NCCL 参数 API
- 类型
- 基于句柄的 API
- 基于键的 API
0401 通信器创建与管理函数
以下函数是 NCCL 公开的用于创建和管理集合通信操作的公共 API。
ncclGetLastError
const char *ncclGetLastError(ncclComm_t comm)
返回与 NCCL 中最近发生的错误相对应的人类可读字符串。注意:调用此函数不会清除该错误。请注意,ncclGetLastError 返回的字符串可能与当前调用无关,而可能是先前启动的异步操作(如果有)产生的结果。
ncclGetErrorString
const char *ncclGetErrorString(ncclResult_t result)
返回与传入错误码相对应的人类可读字符串。
ncclGetVersion
ncclResult_t ncclGetVersion(int *version)
ncclGetVersion 函数返回当前链接的 NCCL 库的版本号。NCCL 版本号通过 version 返回,编码为一个整数,包含 NCCL_MAJOR、NCCL_MINOR 和 NCCL_PATCH 级别。返回的版本号将与 nccl.h 中定义的 NCCL_VERSION_CODE 相同。可以使用提供的宏 NCCL_VERSION 以 NCCL_VERSION(MAJOR,MINOR,PATCH) 的形式比较 NCCL 版本号。
ncclGetUniqueId
ncclResult_t ncclGetUniqueId(ncclUniqueId *uniqueId)
生成一个用于 ncclCommInitRank 的 ID。创建通信器时应调用一次 ncclGetUniqueId,并且在调用 ncclCommInitRank 之前,应将该 ID 分发给通信器中的所有 rank。uniqueId 应指向由用户分配的 ncclUniqueId 对象。
ncclCommInitRank
ncclResult_t ncclCommInitRank(ncclComm_t *comm, int nranks, ncclUniqueId commId, int rank)
创建一个新的通信器(多线程/多进程版本)。rank 必须介于 0 和 nranks-1 之间,并且在通信器组(clique)内唯一。每个 rank 关联一个 CUDA 设备,该设备必须在调用 ncclCommInitRank 之前设置。ncclCommInitRank 会隐式地与其他 rank 同步,因此必须由不同的线程/进程调用,或在 ncclGroupStart/ncclGroupEnd 内使用。
ncclCommInitAll
ncclResult_t ncclCommInitAll(ncclComm_t *comms, int ndev, const int *devlist)
以阻塞方式创建一组通信器(单进程版本)。这是一个用于创建单进程通信器组的便捷函数。在 comms 中返回 ndev 个新初始化的通信器组成的数组。comms 应预先分配至少 ndev*sizeof(ncclComm_t) 的大小。devlist 定义与每个 rank 关联的 CUDA 设备。如果 devlist 为 NULL,则按顺序使用前 ndev 个 CUDA 设备。
ncclCommInitRankConfig
ncclResult_t ncclCommInitRankConfig(ncclComm_t *comm, int nranks, ncclUniqueId commId, int rank, ncclConfig_t *config)
此函数的工作方式与 ncclCommInitRank 相同,但接受一个配置参数,用于为通信器指定额外属性。如果 config 传入 NULL,通信器将具有默认行为,等同于调用了 ncclCommInitRank。
有关配置选项的详细信息,请参见「使用选项创建通信器」一节。
ncclCommInitRankScalable
ncclResult_t ncclCommInitRankScalable(ncclComm_t *newcomm, int nranks, int myrank, int nId, ncclUniqueId *commIds, ncclConfig_t *config)
此函数的工作方式与 ncclCommInitRankConfig 相同,但接受一个 ncclUniqueId 列表而非单个 ID。如果只传入一个 ncclUniqueId,通信器的初始化将等同于调用了 ncclCommInitRankConfig。所提供的所有 ncclUniqueId 都将用于初始化参数中给定的那个通信器。
有关如何创建和分发 ncclUniqueId 列表的详细信息,请参见「使用选项创建通信器」一节。
ncclCommSplit
ncclResult_t ncclCommSplit(ncclComm_t comm, int color, int key, ncclComm_t *newcomm, ncclConfig_t *config)
ncclCommSplit 是一个集合函数,从现有通信器创建一组新通信器。传入相同 color 值的 rank 将属于同一组;color 必须是非负值。如果传入 NCCL_SPLIT_NOCOLOR ,表示该 rank 不属于任何组,因此 newcomm 返回 NULL。key 的值决定 rank 顺序,key 越小,在新通信器中的 rank 越小。如果 rank 之间的 key 相等,则使用原通信器中的 rank 来排序。如果新通信器需要特殊配置,可以通过 config 传入;否则将 config 设为 NULL 会使新通信器继承原通信器的配置。拆分时,comm 上不应存在任何未完成的 NCCL 操作,否则可能导致死锁。
ncclCommShrink
ncclResult_t ncclCommShrink(ncclComm_t comm, int *excludeRanksList, int excludeRanksCount, ncclComm_t *newcomm, ncclConfig_t *config, int shrinkFlags)
ncclCommShrink 函数通过从现有通信器中移除指定 rank 来创建新通信器。它是一个集合函数,必须由新创建通信器中的所有参与 rank 调用。属于 excludeRanksList 的 rank 不应调用此函数。excludeRanksList (大小为 excludeRanksCount )中列出的原始 rank 将被排除在新通信器之外。在新通信器内,rank 将被更新以保持一组连续的 ID。如果新通信器需要特殊配置,可以通过 config 传入;否则,将 config 设为 NULL 会使新通信器继承父通信器的配置。
shrinkFlags 参数控制操作的行为。常规操作使用 NCCL_SHRINK_DEFAULT (或 0 );在父通信器发生错误后进行收缩时使用 NCCL_SHRINK_ABORT 。具体来说,使用 NCCL_SHRINK_DEFAULT 时,comm 上不应存在任何未完成的 NCCL 操作,以避免潜在死锁。此外,如果父通信器的标志 config.shrinkShare 设置为 1,NCCL 将复用父通信器的资源。另一方面,使用 NCCL_SHRINK_ABORT 时,NCCL 将自动中止父通信器上任何未完成的操作,且父通信器与新创建的通信器之间不共享任何资源。
ncclCommGetUniqueId
ncclResult_t ncclCommGetUniqueId(ncclComm_t comm, ncclUniqueId *uniqueId)
ncclCommGetUniqueId 函数为扩展现有通信器生成一个仅使用一次的唯一标识符。在现有通信器上执行每次扩展操作之前,此函数必须仅由一个 rank(协调者)调用。协调者负责在新 rank 通过 ncclCommGrow 加入通信器之前,将 uniqueId 分发给所有新 rank。仅当通信器上没有未完成的 NCCL 操作时才应调用此函数。
ncclCommGrow
ncclResult_t ncclCommGrow(ncclComm_t comm, int nRanks, const ncclUniqueId *uniqueId, int rank, ncclComm_t *newcomm, ncclConfig_t *config)
ncclCommGrow 函数通过向现有通信器添加新 rank 来创建新通信器。它必须由现有 rank(来自父通信器)和新 rank(加入通信器)共同调用。
对于现有 rank:
- comm 应为父通信器
- rank 必须设置为 -1(现有 rank 在新通信器中保留其原始 rank)
- uniqueId 应为 NULL(现有 rank 在内部接收协调信息)
- 该函数以与父通信器中相同的 rank 创建 newcomm
对于新 rank:
- comm 应为 NULL
- rank 必须设置为新通信器中期望的 rank(必须 >= 父通信器大小)
- uniqueId 必须是从协调者调用的 ncclCommGetUniqueId 获得的唯一标识符
nRanks 参数指定新通信器中 rank 的总数,必须大于父通信器的大小。如果新通信器需要特殊配置,可以通过 config 传入;否则,将 config 设为 NULL 会使新通信器继承父通信器的配置(对于现有 rank)或使用默认配置(对于新 rank)。
调用此函数时,父通信器上不应存在任何未完成的 NCCL 操作,以避免潜在死锁。扩展操作完成后,应使用 ncclCommDestroy 销毁父通信器以释放资源。
示例工作流程:
- 协调者 rank 调用 ncclCommGetUniqueId 生成扩展标识符
- 协调者将 uniqueId 分发给所有新 rank(带外方式)
- 所有现有 rank 以 comm =父通信器、rank =-1、uniqueId =NULL 调用 ncclCommGrow (协调者 rank 除外,它传入 uniqueId)
- 所有新 rank 以 comm =NULL、rank =新 rank、uniqueId =收到的 ID 调用 ncclCommGrow
ncclCommRevoke
ncclResult_t ncclCommRevoke(ncclComm_t comm, int revokeFlags)
撤销通信器上正在传输中的操作,而不销毁资源。成功返回时可能是 ncclInProgress (非阻塞),撤销将异步完成;应用程序可以查询 ncclCommGetAsyncError ,直到它返回 ncclSuccess。
revokeFlags 必须设置为 NCCL_REVOKE_DEFAULT(0)。其他值保留供将来使用。
撤销完成后,通信器进入静默状态,可以安全地执行销毁、拆分和收缩。在被撤销的通信器上启动新的集合通信将返回 ncclInvalidUsage 。不支持在撤销之后调用 ncclCommFinalize 。当父通信器被撤销时,通过 splitShare /shrinkShare 进行的资源共享将被禁用。
ncclCommFinalize
ncclResult_t ncclCommFinalize(ncclComm_t comm)
终结通信器对象 comm 。当通信器被标记为非阻塞时,ncclCommFinalize 是非阻塞函数。其成功返回会将通信器状态设置为 ncclInProgress ,表示通信器正在终结过程中,所有未完成的操作和网络相关资源正在被冲刷和释放。一旦所有 NCCL 操作完成,通信器将转换到 ncclSuccess 状态。用户可以使用 ncclCommGetAsyncError 查询该状态。
此函数是节点内集合调用。当单个线程终结多个 rank(每线程多个 GPU)时,必须用 ncclGroupStart /ncclGroupEnd 将这些调用分组,以避免挂起。
ncclCommDestroy
ncclResult_t ncclCommDestroy(ncclComm_t comm)
销毁通信器对象 comm 。如果用户调用了 ncclCommFinalize ,则用户应保证在调用 ncclCommDestroy 之前通信器状态变为 ncclSuccess 。在所有情况下,ncclCommDestroy 返回之后不应再访问该通信器。建议用户先调用 ncclCommFinalize ,然后再调用 ncclCommDestroy。
ncclCommDestroy 将在内部调用 ncclCommFinalize ,除非先前已在该通信器上调用过 ncclCommFinalize 。如果先前已在通信器对象 comm 上调用过 ncclCommFinalize ,则 ncclCommDestroy 是纯本地操作。
此函数是节点内集合调用,同一节点上的所有 rank 都应调用它以避免挂起。
ncclCommAbort
ncclResult_t ncclCommAbort(ncclComm_t comm)
ncclCommAbort 释放分配给通信器对象 comm 的资源,并在销毁通信器之前中止任何未完成的操作。所有活跃 rank 都需要调用此函数,才能成功中止 NCCL 通信器。更多使用场景请查看「容错」。
ncclCommGetAsyncError
ncclResult_t ncclCommGetAsyncError(ncclComm_t comm, ncclResult_t *asyncError)
查询异步 NCCL 操作的进度和潜在错误。不需要流参数的操作(例如 ncclCommFinalize)在该函数返回 ncclSuccess 时即可视为已完成;带流参数的操作(例如 ncclAllReduce)在操作被提交到流时即返回 ncclSuccess ,但在完成之前仍可能通过 ncclCommGetAsyncError() 报告错误。如果任何 NCCL 函数的返回码为 ncclInProgress ,表示该操作正在后台排队过程中,用户必须查询通信器状态,直到所有状态变为 ncclSuccess 之后才能调用另一个 NCCL 函数。在状态变为 ncclSuccess 之前,用户不允许向 NCCL 正在使用的流发射 CUDA 内核。如果通信器上发生了错误,用户应使用 ncclCommAbort() 销毁通信器。如果通信器上发生错误,则不能对该通信器上已排队操作的完成情况或正确性做任何假设。
ncclCommCount
ncclResult_t ncclCommCount(const ncclComm_t comm, int *count)
在 count 中返回 NCCL 通信器 comm 中的 rank 数量。
ncclCommCuDevice
ncclResult_t ncclCommCuDevice(const ncclComm_t comm, int *device)
在 device 中返回与 NCCL 通信器 comm 关联的 CUDA 设备。
ncclCommUserRank
ncclResult_t ncclCommUserRank(const ncclComm_t comm, int *rank)
在 rank 中返回调用者在 NCCL 通信器 comm 中的 rank。
ncclCommRegister
ncclResult_t ncclCommRegister(const ncclComm_t comm, void *buff, size_t size, void **handle)
在通信器 comm 下以 size 注册缓冲区 buff ,用于零拷贝通信;返回 handle 供将来注销使用。有关 buff 和 size 的要求以及更多说明,请参见「用户缓冲区注册」。
ncclCommDeregister
ncclResult_t ncclCommDeregister(const ncclComm_t comm, void *handle)
在通信器 comm 下注销由 handle 表示的缓冲区。
ncclCommWindowRegister
ncclResult_t ncclCommWindowRegister(ncclComm_t comm, void *buff, size_t size, ncclWindow_t *win, int winFlags)
在通信器 comm 下将本地缓冲区 buff (大小为 size )集合式地注册到 NCCL 窗口。由于这是集合调用,通信器中的每个 rank 都需要参与注册。各 rank 的 size 可以不同;调用方负责确保后续操作只访问对参与该操作的 rank 有效的范围。返回 win 供将来注销使用(如果在组内调用,该值可能要到 ncclGroupEnd() 完成后才会填入)。有关 buff 的要求和更多说明,请参见「用户缓冲区注册」。用户还可以传入不同的窗口标志来控制注册行为。更多窗口标志信息请参阅「窗口注册标志」。主机 API 不接受被对称注册超过一次的缓冲区。将此类缓冲区传给主机 API 会导致未定义行为。
ncclCommWindowDeregister
ncclResult_t ncclCommWindowDeregister(ncclComm_t comm, ncclWindow_t win)
在通信器 comm 下注销由 win 表示的 NCCL 窗口。注销是 rank 本地的操作,调用方需要确保窗口内对应的缓冲区未被任何 NCCL 操作访问。
ncclMemAlloc
ncclResult_t ncclMemAlloc(void **ptr, size_t size)
分配大小为 size 的 GPU 缓冲区。分配的缓冲区首地址将通过 ptr 返回;由于各类 NCCL 优化对缓冲区粒度的要求,实际分配的大小可能大于请求的大小。
ncclMemFree
ncclResult_t ncclMemFree(void *ptr)
释放由 ncclMemAlloc() 分配的内存。
ncclCommSuspend
ncclResult_t ncclCommSuspend(ncclComm_t comm, int flags)
挂起通信器操作以释放资源。通信器在挂起期间不能用于任何 NCCL 操作。调用此函数时,comm 上不应存在未完成的 NCCL 操作。
flags 参数控制释放哪些资源:
- NCCL_SUSPEND_MEM (
0x01)------释放通信器持有的动态 GPU 内存分配。
可以通过调用 ncclCommResume 将被挂起的通信器恢复到活跃状态。
ncclCommResume
ncclResult_t ncclCommResume(ncclComm_t comm)
恢复通信器 comm 上所有先前被挂起的资源。此调用成功返回后,通信器完全恢复正常运行,可以再次用于 NCCL 操作。
ncclCommMemStats
ncclResult_t ncclCommMemStats(ncclComm_t comm, ncclCommMemStat_t stat, uint64_t *value)
查询通信器内存统计信息。stat 参数选择要检索的统计项,结果写入 *value。可用统计项列表请参见 ncclCommMemStat_t。
0402 集合通信函数
以下 NCCL API 提供一些常用的集合通信操作。
ncclAllReduce
ncclResult_t ncclAllReduce(const void *sendbuff, void *recvbuff, size_t count, ncclDataType_t datatype, ncclRedOp_t op, ncclComm_t comm, cudaStream_t stream)
使用 op 操作对 sendbuff 中长度为 count 的数据数组进行归约,并将结果的相同副本留在每个 recvbuff 中。
如果 sendbuff == recvbuff,将执行原地操作。
相关链接:AllReduce。
ncclBroadcast
ncclResult_t ncclBroadcast(const void *sendbuff, void *recvbuff, size_t count, ncclDataType_t datatype, int root, ncclComm_t comm, cudaStream_t stream)
将 root rank 上 sendbuff 中的 count 个元素复制到所有 rank 的 recvbuff。sendbuff 只在 root rank 上使用,其他 rank 忽略它。
如果 sendbuff == recvbuff,将执行原地操作。
ncclResult_t ncclBcast(void *buff, size_t count, ncclDataType_t datatype, int root, ncclComm_t comm, cudaStream_t stream)
ncclBroadcast 的旧式原地版本,风格类似于 MPI_Bcast。调用
c
ncclBcast(buff, count, datatype, root, comm, stream)
等价于
c
ncclBroadcast(buff, buff, count, datatype, root, comm, stream)
相关链接:Broadcast
ncclReduce
ncclResult_t ncclReduce(const void *sendbuff, void *recvbuff, size_t count, ncclDataType_t datatype, ncclRedOp_t op, int root, ncclComm_t comm, cudaStream_t stream)
使用 op 操作将 sendbuff 中长度为 count 的数据数组归约到 root rank 的 recvbuff 中。recvbuff 只在 root rank 上使用,其他 rank 忽略它。
如果 sendbuff == recvbuff,将执行原地操作。
相关链接:Reduce。
ncclAllGather
ncclResult_t ncclAllGather(const void *sendbuff, void *recvbuff, size_t sendcount, ncclDataType_t datatype, ncclComm_t comm, cudaStream_t stream)
从所有 GPU 收集 sendcount 个值,并将结果的相同副本留在每个 recvbuff 中,来自 rank i 的数据存放在偏移量 i*sendcount 处。
注意:这假定接收计数等于 nranks*sendcount,意味着 recvbuff 的大小至少应为 nranks*sendcount 个元素。
如果 sendbuff == recvbuff + rank * sendcount,将执行原地操作。
相关链接:AllGather、「原地操作」。
ncclReduceScatter
ncclResult_t ncclReduceScatter(const void *sendbuff, void *recvbuff, size_t recvcount, ncclDataType_t datatype, ncclRedOp_t op, ncclComm_t comm, cudaStream_t stream)
使用 op 操作归约来自所有 GPU 的 sendbuff 中的数据,并将归约结果分散到各设备上,使 rank i 上的 recvbuff 包含结果的第 i 块。
注意:这假定发送计数等于 nranks*recvcount,意味着 sendbuff 的大小至少应为 nranks*recvcount 个元素。
如果 recvbuff == sendbuff + rank * recvcount,将执行原地操作。
相关链接:ReduceScatter、「原地操作」。
ncclAlltoAll
ncclResult_t ncclAlltoAll(const void *sendbuff, void *recvbuff, size_t count, ncclDataType_t datatype, ncclComm_t comm, cudaStream_t stream)
每个 rank 向所有其他 rank 发送 count 个值,并从所有其他 rank 接收 count 个值。发往目标 rank j 的数据取自 sendbuff+j*count,来自源 rank i 的数据存放在 recvbuff+i*count。
注意:这假定总发送计数和接收计数都等于 nranks*count,意味着 sendbuff 和 recvbuff 的大小至少应为 nranks*count 个元素。
目前不支持原地操作。
相关链接:AlltoAll。
ncclGather
ncclResult_t ncclGather(const void *sendbuff, void *recvbuff, size_t count, ncclDataType_t datatype, int root, ncclComm_t comm, cudaStream_t stream)
每个 rank 从 sendbuff 向 root rank 发送 count 个元素。在 root rank 上,来自 rank i 的数据存放在 recvbuff + i*count。在非 root rank 上,不使用 recvbuff。
注意:这假定接收计数等于 nranks*count,意味着 recvbuff 的大小至少应为 nranks*count 个元素。
如果 sendbuff == recvbuff + root * count,将执行原地操作。
相关链接:Gather。
ncclScatter
ncclResult_t ncclScatter(const void *sendbuff, void *recvbuff, size_t count, ncclDataType_t datatype, int root, ncclComm_t comm, cudaStream_t stream)
每个 rank 从 root rank 接收 count 个元素。在 root rank 上,sendbuff + i*count 处的 count 个元素被发送到 rank i。在非 root rank 上,不使用 sendbuff。
注意:这假定发送计数等于 nranks*count,意味着 sendbuff 的大小至少应为 nranks*count 个元素。
如果 recvbuff == sendbuff + root * count,将执行原地操作。
相关链接:Scatter。
0403 组调用
组原语定义当前线程的行为以避免阻塞。因此,它们可以被多个线程独立使用。
相关链接:「组调用」。
ncclGroupStart
ncclResult_t ncclGroupStart()
开始一个组调用。
在 ncclGroupEnd 之前的所有后续 NCCL 调用都不会因 CPU 间同步而阻塞。
ncclGroupEnd
ncclResult_t ncclGroupEnd()
结束一个组调用。
当自 ncclGroupStart 以来的所有操作都被处理后返回。这意味着通信原语已被排入所提供的流,但不一定已完成。
与 ncclCommInitRank 调用一起使用时,ncclGroupEnd 调用会等待所有通信器完成初始化。
ncclGroupSimulateEnd
ncclResult_t ncclGroupSimulateEnd(ncclSimInfo_t *simInfo)
模拟一次 ncclGroupEnd() 调用,并将 NCCL 的模拟信息返回到作为参数传入的结构体中。
0404 点对点通信函数
NCCL 提供两类点对点通信原语:双侧操作和单侧操作。
双侧点对点操作
(自 NCCL 2.7 起)当 rank 之间需要相互发送和接收任意数据、而无法用 broadcast 或 allgather 表达时(即所有发送和接收的数据都不同时),需要使用双侧点对点通信原语。发送方和接收方都必须显式参与。
ncclSend
ncclResult_t ncclSend(const void *sendbuff, size_t count, ncclDataType_t datatype, int peer, ncclComm_t comm, cudaStream_t stream)
将数据从 sendbuff 发送到 rank peer。
rank peer 需要以与此 rank 相同的 datatype 和相同的 count 调用 ncclRecv。
此操作对 GPU 是阻塞的。如果多个 ncclSend() 和 ncclRecv() 操作需要并发推进才能完成,它们必须融合在 ncclGroupStart()/ncclGroupEnd() 区间内。
相关链接:「点对点通信」。
ncclRecv
ncclResult_t ncclRecv(void *recvbuff, size_t count, ncclDataType_t datatype, int peer, ncclComm_t comm, cudaStream_t stream)
将来自 rank peer 的数据接收到 recvbuff。
rank peer 需要以与此 rank 相同的 datatype 和相同的 count 调用 ncclSend。
此操作对 GPU 是阻塞的。如果多个 ncclSend() 和 ncclRecv() 操作需要并发推进才能完成,它们必须融合在 ncclGroupStart()/ncclGroupEnd() 区间内。
相关链接:「点对点通信」。
单侧点对点操作(RMA)
单侧远程内存访问(RMA)操作使 rank 能够直接访问远程内存,而无需目标进程显式参与。这些操作要求目标内存预先使用 ncclCommWindowRegister() 注册到对称内存窗口中。
ncclPutSignal
ncclResult_t ncclPutSignal(const void *localbuff, size_t count, ncclDataType_t datatype, int peer, ncclWindow_t peerWin, size_t peerWinOffset, int sigIdx, int ctx, unsigned int flags, ncclComm_t comm, cudaStream_t stream)
将数据从 localbuff 写入 rank peer 的已注册内存窗口 peerWin 的偏移量 peerWinOffset 处,随后更新一个远程信号。
目标内存窗口 peerWin 必须使用 ncclCommWindowRegister() 注册。
注意
localbuff 目前也必须使用 ncclCommWindowRegister() 注册。此要求可能在未来版本中放宽。
sigIdx 是操作的信号索引标识符。在 NCCL 2.31 之前,它必须设置为 0。自 NCCL 2.31 起,它可以设置在 [0, numRmaSig) 范围内(参见 numRmaSig)。
ctx 选择通信上下文。在 NCCL 2.31 之前,它必须设置为 0。自 NCCL 2.31 起,它可以设置在 [0, numRmaCtx) 范围内(参见 numRmaCtx)。
flags 参数保留供将来使用。目前必须设置为 0。
ncclPutSignal() 向 CPU 线程返回,表示该操作已成功排入 CUDA 流。当 ncclPutSignal() 在 CUDA 流上完成时,localbuff 可以安全复用或修改。当远程对端上的信号被更新时,即保证对应 ncclPutSignal() 操作的数据已被投递到远程内存。所有先前发往同一对端和同一上下文的 ncclPutSignal() 和 ncclSignal() 操作也都已完成其信号更新。
相关链接:「点对点通信」。
ncclSignal
ncclResult_t ncclSignal(int peer, int sigIdx, int ctx, unsigned int flags, ncclComm_t comm, cudaStream_t stream)
向 rank peer 发送信号,不传输数据。
sigIdx 是操作的信号索引标识符。在 NCCL 2.31 之前,它必须设置为 0。自 NCCL 2.31 起,它可以设置在 [0, numRmaSig) 范围内(参见 numRmaSig)。
ctx 选择通信上下文。在 NCCL 2.31 之前,它必须设置为 0。自 NCCL 2.31 起,它可以设置在 [0, numRmaCtx) 范围内(参见 numRmaCtx)。
flags 参数保留供将来使用。目前必须设置为 0。
当远程对端上的信号被更新时,所有先前发往同一对端和同一上下文的 ncclPutSignal() 和 ncclSignal() 操作也都已完成其信号更新。
相关链接:「点对点通信」。
ncclWaitSignal
type ncclWaitSignalDesc_t
描述符,指定在给定信号索引和上下文上等待来自特定 rank 的多少次信号操作。
int opCnt
要等待的信号操作次数。
int peer
要等待其信号的目标对端。
int sigIdx
信号索引标识符。在 NCCL 2.31 之前,它必须设置为 0。自 NCCL 2.31 起,它可以设置在 [0, numRmaSig) 范围内(参见 numRmaSig)。
int ctx
通信上下文。在 NCCL 2.31 之前,它必须设置为 0。自 NCCL 2.31 起,它可以设置在 [0, numRmaCtx) 范围内(参见 numRmaCtx)。
ncclResult_t ncclWaitSignal(int nDesc, ncclWaitSignalDesc_t *signalDescs, ncclComm_t comm, cudaStream_t stream)
按照信号描述符数组中的描述等待信号。
nDesc 参数指定 signalDescs 数组中信号描述符的数量。每个描述符指示在特定信号索引(sigIdx)和上下文(ctx)上预期来自特定 peer 的多少次信号(opCnt)。
ncclWaitSignal() 向 CPU 线程返回,表示该操作已成功排入 CUDA 流。当 ncclWaitSignal() 在 CUDA 流上完成时,所有指定的信号操作都已收到,且对应的数据在本地内存中可见。
相关链接:「点对点通信」。
0405 类型
NCCL 库使用以下类型。
ncclComm_t
type ncclComm_t
NCCL 通信器。指向 NCCL 内部的一个不透明结构。
ncclResult_t
type ncclResult_t
所有 NCCL 函数的返回值。可能的取值有:
ncclSuccess
(0)函数成功。
ncclUnhandledCudaError
(1)对某个 CUDA 函数的调用失败。
ncclSystemError
(2)对系统的调用失败。
ncclInternalError
(3)内部检查失败。这是由 NCCL 的 bug 或内存损坏导致的。
ncclInvalidArgument
(4)某个参数的值无效。
ncclInvalidUsage
(5)对 NCCL 的调用不正确。这通常反映了编程错误。
ncclRemoteError
(6)调用失败,可能由网络错误或远程进程提前退出导致。
ncclInProgress
(7)通信器上的某个 NCCL 操作正在排队,并在后台推进。
每当函数返回错误(既不是 ncclSuccess 也不是 ncclInProgress)时,如果环境变量 NCCL_DEBUG 设置为 "WARN",NCCL 应打印更详细的消息。
ncclDataType_t
type ncclDataType_t
NCCL 定义了以下整型和浮点数据类型。
ncclInt8 ------ 有符号 8 位整数
ncclChar ------ 有符号 8 位整数
ncclUint8 ------ 无符号 8 位整数
ncclInt32 ------ 有符号 32 位整数
ncclInt ------ 有符号 32 位整数
ncclUint32 ------ 无符号 32 位整数
ncclInt64 ------ 有符号 64 位整数
ncclUint64 ------ 无符号 64 位整数
ncclFloat16 ------ 16 位浮点数(半精度)
ncclHalf ------ 16 位浮点数(半精度)
ncclFloat32 ------ 32 位浮点数(单精度)
ncclFloat ------ 32 位浮点数(单精度)
ncclFloat64 ------ 64 位浮点数(双精度)
ncclDouble ------ 64 位浮点数(双精度)
ncclBfloat16 ------ 16 位浮点数(bfloat16 格式的截断精度,CUDA 11 或更高版本)
ncclFloat8e4m3 ------ 8 位浮点数,4 位指数、3 位尾数(CUDA >= 11.8 且 SM >= 90)
ncclFloat8e5m2 ------ 8 位浮点数,5 位指数、2 位尾数(CUDA >= 11.8 且 SM >= 90)
ncclRedOp_t
type ncclRedOp_t
定义归约操作。
ncclSum ------ 执行求和(+)操作
ncclProd ------ 执行乘积(*)操作
ncclMin ------ 执行取最小值操作
ncclMax ------ 执行取最大值操作
ncclAvg ------ 执行求平均操作,即跨所有 rank 求和,再除以 rank 数量。
ncclScalarResidence_t
type ncclScalarResidence_t
指示标量参数驻留在何处(内存空间)以及何时可以解引用。
ncclScalarHostImmediate ------ 标量驻留在主机内存中,应以最直接的方式解引用。
ncclScalarDevice ------ 标量驻留在设备可见内存中,应在需要时解引用。
ncclConfig_t
type ncclHostCftMode_t
hostCftMode 通信器配置的取值。
ncclHostCftDefault ------ 使用特定于版本的默认值。
ncclHostCftEnable ------ 启用主机端 CFT 支持。
ncclHostCftDisable ------ 禁用主机端 CFT 支持。
ncclHostCftFallback ------ 尝试创建 CFT 逻辑端点。如果出错,将禁用主机端 CFT。
type ncclConfig_t
用户可设置的、用于初始化通信器的基于结构体的配置;新创建的配置必须用 NCCL_CONFIG_INITIALIZER 初始化。
NCCL_CONFIG_INITIALIZER ------ 配置宏初始化器,必须赋值给新创建的配置。
blocking ------ 此属性可设置为整数 0 或 1,分别表示非阻塞或阻塞的通信器行为。阻塞是默认行为。
cgaClusterSize ------ 设置 NCCL 启动的内核的协作组数组(CGA)大小。此属性可设置为 0 到 8 之间,自 sm90 架构起默认值为 4,更早的架构默认值为 0。
minCTAs ------ 设置 NCCL 每个内核应使用的最小 CTA 数量。设置为正整数,最大 32。默认值为 1。
maxCTAs ------ 设置 NCCL 每个内核应使用的最大 CTA 数量。设置为正整数,最大 32。默认值为 32。
netName ------ 指定 NCCL 用于网络通信的网络模块名称。netName 的值必须与网络模块的名称完全匹配(不区分大小写)。NCCL 内部网络模块名称为 "IB"(通用 IB verbs)和 "Socket"(TCP/IP 套接字)。外部网络插件定义自己的名称。默认值未定义,NCCL 将自动选择网络模块。
splitShare ------ 指定在通信器拆分期间是否与子通信器共享资源。将 splitShare 的值设置为 0 或 1。默认值为 0。当父通信器在 ncclCommInitRankConfig 期间以 splitShare=1 创建时,子通信器可以在通信器拆分期间共享父通信器的内部资源。拆分出的通信器属于同一家族。共享资源时,中止任何一个通信器都可能导致同一家族中的其他通信器变得不可用。无论是否共享资源,用户都应始终中止/销毁所有不再需要的通信器以释放资源。注意:当父通信器已被撤销(revoke)时,无论此标志如何,拆分期间的资源共享都会被禁用。
shrinkShare ------ 指定在通信器收缩期间是否与子通信器共享资源。将 shrinkShare 的值设置为 0 或 1。默认值为 0。注意:当收缩使用 NCCL_SHRINK_ABORT 时,shrinkShare 的值将被忽略,且不共享任何资源。当父通信器已被撤销时,资源共享也会被禁用。此标志的行为与 splitShare 类似,见上文。
trafficClass ------ 设置通信器上网络操作使用的流量类别(TC)。TC 的含义取决于通信器所使用的网络插件(例如 IB 网络使用服务级别,RoCE 网络使用服务类型)。为每个通信器分配不同的 TC 可以使重叠通信的工作负载受益。TC 由系统配置定义,应大于或等于 0。请注意,NCCL_IB_SL 和 NCCL_IB_TC 等环境变量优先于用户指定的 TC 值。要使用用户定义的 TC,请确保这些环境变量未设置。
collnetEnable ------ 设置为 1/0 以在通信器上启用/禁用 IB SHARP。默认值为 0(禁用)。
CTAPolicy ------ 设置通信器的策略。支持的策略完整列表见「NCCL 通信器 CTA 策略标志」。默认值为 NCCL_CTA_POLICY_DEFAULT。
nvlsCTAs ------ 设置 NCCL 用于 NVLS 内核的 CTA 总数。设置为正整数。默认情况下,NCCL 会根据系统配置自动确定最佳 CTA 数量。
commName ------ 指定通信器的用户定义名称。NCCL 可使用通信器名称来增强日志记录和性能分析。
nChannelsPerNetPeer ------ 设置用于成对通信的网络通道数量。该值必须为正整数,并将向上取整到 2 的下一个幂。默认值针对 AlltoAll 通信模式进行了优化。可考虑增大该值以提高发送/接收通信的带宽。
graphUsageMode ------ 设置通信器的 graph 使用模式。支持三个可能的值:0(不使用 graph)、1(单个 graph)和 2(多个 graph,或 graph 与非 graph 混合)。默认值为 2。如果 NCCL_GRAPH_STREAM_ORDERING 或 graphStreamOrdering 禁用了捕获时的流排序(0),则必须关闭 graph 混用 ------只能使用 graphUsageMode 0 或 1;graphUsageMode=2 不得与排序 0 组合使用(参见 NCCL_GRAPH_STREAM_ORDERING)。
graphStreamOrdering ------(自 2.30 起)
NCCL_GRAPH_STREAM_ORDERING 的逐通信器覆盖。1 保持 NCCL 默认的捕获时通信内核序列化。0 为此通信器禁用它------内核被放置在捕获流上,应用程序必须保证正确的顺序(参见 NCCL_GRAPH_STREAM_ORDERING)。
默认为 NCCL_CONFIG_UNDEF_INT(继承 NCCL_GRAPH_STREAM_ORDERING)。0 或 1 为此通信器覆盖该环境变量。
graphStreamOrdering=0 要求 graphUsageMode 为 0 或 1(关闭 混用)。不支持将其与 graphUsageMode=2 组合;参见 NCCL_GRAPH_STREAM_ORDERING。
同一 GPU 上的混合取值:设置为 1 的通信器仍会为其自身内核获得 NCCL 的内部序列化,但 NCCL 不会 与设置为 0 的对等通信器插入跨通信器排序------后者的内核可能在 NCCL 原本会序列化的情形下发生重叠。仅当应用程序能保证可能在 GPU 上并发运行的所有 NCCL 通信内核的顺序时,才使用 0。
launchOrderImplicit ------(自 2.31 起)
NCCL_LAUNCH_ORDER_IMPLICIT 的逐通信器请求。1 为此通信器启用隐式启动排序;0 禁用它。NCCL_CONFIG_UNDEF_INT 是默认值,其有效行为与 0 相同。
具有不同有效值的通信器可以共存。重叠安全性针对的是可能在同一 GPU 上并发运行的通信操作:
- 禁用/默认通信器上的操作保留现有的多通信器排序保证。
- 如果应用程序遵循 NCCL_LAUNCH_ORDER_IMPLICIT 所述的主机端排序要求,启用的通信器上的操作可以与其他启用的通信器上的操作重叠。
- 启用的通信器上的操作不得与禁用/默认通信器上的操作重叠。应用程序必须对这些操作进行排序或同步,使其不重叠,或者将各通信器配置为一致。
如果某个 CUDA 上下文同时使用了启用和禁用/默认的有效值,NCCL 会记录一条 INFO 消息,但仍会初始化通信器。
如果在环境中设置了 NCCL_LAUNCH_ORDER_IMPLICIT,它会在初始化之前覆盖此字段。
maxP2pPeers ------ 设置任何 rank 使用 P2P 通信并发通信的最大对端数量。设置此值会影响所有 send/recv 以及基于 send/recv 的集合通信(all-to-all、scatter、gather)。小于 1 或大于 rank 数量的值将默认为通信器中的 rank 数量。
numRmaCtx ------(自 2.31 起)
在通信器上配置的单侧 RMA 通信上下文的数量。ncclPutSignal()、ncclSignal() 和 ncclWaitSignal() 的 ctx 参数必须位于 [0, numRmaCtx) 范围内。默认值为 1。
numRmaSig ------(自 2.31 起)
设置每个上下文可用的单侧 RMA 信号索引数量。默认值为 1。ncclPutSignal()、ncclSignal() 和 ncclWaitSignal() 等主机单侧 RMA 操作使用 [0, numRmaSig) 范围内的 sigIdx 值。
rmaEagerInit ------(自 2.31 起)
控制集合式单侧 RMA 信号设置的初始化时机。使用 0(默认)时,它在首次窗口注册(ncclCommWindowRegister())期间初始化,这是一个集合点。使用 1 则改为在通信器初始化时进行初始化;如果通信器在未先注册窗口的情况下发出 ncclSignal() 或 ncclWaitSignal(),则必须这样设置,否则会返回 ncclInvalidUsage。
如果在环境中设置了 NCCL_RMA_EAGER_INIT,它会在初始化之前覆盖此字段。
hostCftMode ------(自 2.31 起)
控制对主机端 Compute Fabric Transport(CFT)查询的支持。ncclHostCftEnable 在支持 CFT 的通信器上首次调用 ncclCommWindowRegister() 期间创建通信器的单播和组播逻辑端点。ncclHostCftDisable 禁用该支持;ncclHostCftFallback 尝试创建逻辑端点,出错时禁用主机端 CFT。ncclHostCftDefault 选择库定义的默认行为。
ncclSimInfo_t
type ncclSimInfo_t
此结构体由 ncclGroupSimulateEnd() 用于返回有关调用的信息。
NCCL_SIM_INFO_INITIALIZER ------ NCCL_SIM_INFO_INITIALIZER 是配置宏初始化器,必须赋值给新创建的 ncclSimInfo_t 结构体。
estimatedTime ------ 组调用中操作的估计时间将在此属性中返回。
ncclCommMemStat_t
type ncclCommMemStat_t
ncclCommMemStats() 的内存统计选择器。
ncclStatGpuMemSuspend ------ 通信器分配的、可通过挂起释放的 GPU 内存(字节)。
ncclStatGpuMemSuspended ------ 通信器分配的 GPU 内存当前是否已挂起(0 = 活跃,1 = 已挂起)。
ncclStatGpuMemPersist ------ 通信器分配的、无法挂起的 GPU 内存(字节)。
ncclStatGpuMemTotal ------ NCCL 跟踪的通信器分配的 GPU 内存总量(字节)。
ncclWindow_t
type ncclWindow_t
用于窗口注册和注销的 NCCL 窗口对象。
0406 用户自定义归约运算符
以下函数是 NCCL 公开的用于创建和销毁自定义归约运算符的公共 API,这些运算符用于归约类集合通信。
ncclRedOpCreatePreMulSum
ncclResult_t ncclRedOpCreatePreMulSum(ncclRedOp_t *op, void *scalar, ncclDataType_t datatype, ncclScalarResidence_t residence, ncclComm_t comm)
创建一个新的归约运算符,它在本地先将输入值预乘一个给定标量,然后再通过与对端值求和进行归约。输入值和标量都为 datatype 类型。仅用于针对 comm 和 datatype 启动的集合通信。residence 参数指示 scalar 指向的内存应在此函数返回之前由主机立即解引用(ncclScalarHostImmediate),还是在归约集合通信执行期间由设备解引用(ncclScalarDevice)。返回时,新创建的运算符句柄存储在 op 中。
ncclRedOpDestroy
ncclResult_t ncclRedOpDestroy(ncclRedOp_t op, ncclComm_t comm)
销毁归约运算符 op 。该运算符必须是由 ncclRedOpCreatePreMul 使用匹配的通信器 comm 创建的。一旦最后一个使用该运算符的 NCCL 函数返回,即可销毁该运算符。
0407 NCCL API 支持的标志
下面列出 NCCL API 支持的所有标志。
窗口注册标志
NCCL_WIN_DEFAULT
以默认行为将缓冲区注册到 NCCL 窗口。默认行为允许用户将相对于缓冲区首地址的任意偏移量作为 NCCL 集合通信操作的输入。但是,由于缓冲区使用不对称,此行为可能导致 NCCL 性能不佳。
NCCL_WIN_COLL_SYMMETRIC
将缓冲区注册到 NCCL 窗口,用户需要保证在调用 NCCL 集合通信操作时,所有 rank 相对于缓冲区首地址的偏移量必须相等。这允许 NCCL 以对称方式操作缓冲区并提供最佳性能。
NCCL_WIN_STRICT_ORDERING
将缓冲区注册到 NCCL 窗口,同时确保使用 IB Verbs 传输的窗口操作具有严格排序。此标志主要用于 GIN VA 信号所用的缓冲区(参见「信号与计数器」)。
NCCL 通信器 CTA 策略标志
NCCL_CTA_POLICY_DEFAULT
为 NCCL 通信器使用默认 CTA 策略。在此策略下,NCCL 将自动调整资源使用以达到最佳性能。此策略适用于大多数应用程序。
NCCL_CTA_POLICY_EFFICIENCY
为 NCCL 通信器使用 CTA 效率策略。在此策略下,NCCL 将优化 CTA 使用,尽可能用最少的 CTA 数量达到不错的性能。此策略适用于需要更好地重叠计算与通信的应用程序。
NCCL_CTA_POLICY_ZERO
为 NCCL 通信器使用零 CTA 策略。在此策略下,NCCL 将尽可能地使用零 CTA,即使这种选择可能牺牲一些性能。当你的应用程序必须为计算内核保留最大数量的 CTA 时,请选择此模式。
通信器收缩标志
这些标志用于修改 ncclCommShrink 操作的行为。
NCCL_SHRINK_DEFAULT
默认行为。收缩父通信器,不影响正在进行的操作。值:0x00。
NCCL_SHRINK_ABORT
首先终止父通信器上正在进行的操作,然后继续收缩通信器。这用于父通信器可能处于挂起状态的错误恢复场景。父通信器的资源仍未被释放,用户应决定是否在收缩后对父通信器调用 ncclCommAbort。值:0x01。