jklincn


集合通信性能评估——源自 NCCL 测试报告


阅读论文时发现一个有意义的集合通信性能评估方法,特此记录(翻译)一下。

原文链接:Performance reported by NCCL tests


NCCL 测试结果展示了以毫秒为单位的平均操作时间,以及以 GB/s 为单位的两个带宽:算法带宽和总线带宽。此页面解释了这些数字的含义以及根据所用硬件应期望的性能。

时间

时间在处理小数据量时很有用,可以测量操作中的固定开销(或延迟)。

在大数据量的情况下,时间与数据大小呈线性关系(因为它大致等于固定开销 + 数据大小 / 带宽),此时不仅在衡量延迟,还同时反映了数据大小与带宽之间的关系。

因此,对于大数据量的操作,查看带宽比查看时间更有意义。

带宽

算法带宽

算法带宽使用最常见的带宽公式:数据大小 ( S ) / 时间 ( t )。

$$ algbw = \frac{S}{t} $$

通过简单地将操作的数据大小除以算法带宽,可以计算出任何大型操作所需的时间。

总线带宽

虽然算法带宽对于点对点操作(如发送/接收)是有意义的,但在衡量集体操作的速度时,它并不总是有用,因为理论上的最大算法带宽通常不等于硬件的峰值带宽,通常还取决于节点(ranks)的数量。大多数基准测试只提供时间测量,这在处理大数据量时很难解释。也有一些测试提供算法带宽,但发现随着节点数量的增加,带宽会变化(随着节点数量增加而减少)。

为了提供一个反映硬件使用效率的数字,NCCL 测试引入了“总线带宽”的概念(在测试输出中为“busbw”列)。这个数值(总线带宽)通过对算法带宽应用一个公式得出,用来反映 GPU 之间通信的速度。通过这个总线带宽,我们可以将其与硬件的峰值带宽进行比较,而不受使用节点数量的影响。

公式取决于集体操作。

AllReduce

AllReduce 操作针对 N 个数组中的每个元素(输入为 $i_X$,输出为 $o_X$,每个都位于节点 X 上)执行以下操作(此时是 SUM 操作,所有输入数组相加并把结果赋给输出数组):

$$ o_0 = o_1 = o_2 = ... = o_{n-1} = i_0 + i_1 + i_2 + ... + i_{n-1} $$

注意:这与所使用的算法(环形、树形或其他)无关,只要它们使用点对点操作(发送/接收)。

在环形算法中,这个操作按照环的顺序执行:

$$ i_0 + i_1 + ... + i_{n-1} \rightarrow o_{n-1} \rightarrow o_0 \rightarrow o_1 \rightarrow ... \rightarrow o_{n-2} $$

在树形算法中,它以层次结构进行:

$$ (((((i_{n-1} + i_{n-2}) + (i_{n-3} + i_{n-4})) + ... + (i_1 + i_0))))) \rightarrow o_0 \rightarrow (o_{n/2} \rightarrow (o_{3n/4} ...)) $$

在所有情况下,我们需要对每个元素进行 n−1 次加法和 n 次赋值。由于每一步通常在不同的节点上进行,除了可能的最后一个输入和第一个输出,因此执行全规约操作需要进行 2(n−1) 次数据传输(针对每个元素)。

即 n-1(加法)+ n(赋值)- 1(因为最后一次加法结果对于本地节点来说不需要传输,可以直接赋值给输出数组)

考虑到每个节点对外的带宽为 B,执行 S 个元素的全规约操作的时间最短为:

$$ t = \frac{S\ast2\ast(n-1)}{n\ast B} $$

确实,我们有 S 个元素,每个元素需要进行 2(n−1) 次操作,并且每个节点有 n 条带宽为 B 的连接来执行这些操作。重新排列公式后,我们得到:

$$ t=\frac{S}{B}\ast\frac{2\ast(n-1)}{n} $$

因此,为了获得一个可以与硬件峰值带宽进行比较的 AllReduce 带宽测量,我们可以计算:

$$ B=\frac{S}{t}\ast\frac{2\ast(n-1)}{n}=algbw\ast\frac{2*(n-1)}{n} $$

ReduceScatter

ReduceScatter 操作只需要执行 AllReduce 操作中的加法部分:

$$ o_K = i_0 + i_1 + i_2 + ... + i_{n-1} $$

其中 K 是获得最终结果的节点。($K=offset/recvsize$)

偏移量(offset)和接收大小(recvsize)用于确定在集体操作(如 ReduceScatter、AllGather 等)中每个节点应该接收的数据块或结果。

因此,假设每个节点的带宽为 B,完美的 ReduceScatter 操作时间为:

$$ t=\frac{S*(n-1)}{B*n} $$

因此,总线带宽计算为:

$$ B=\frac{S}{t}\ast\frac{n-1}{n}=algbw\ast\frac{n-1}{n} $$

请注意,这里的 S 是总数组的字节大小,对于 NCCL 来说,等于 $recvcount*sizeof(datatype*n)$,其中 $recvcount$ 是每个节点的计数。

AllGather

AllGather 操作只需要执行全规约操作中的赋值部分:

$$ o_0 = o_1 = o_2 = ... = o_{n-1} = i_K $$

其中 K 是数据来源的节点。($K=offset*sendsize$)

因此,在每个节点带宽为 B 的情况下,完美的 AllGather 操作时间为:

$$ t= \frac{S*(n-1)}{B*n} $$

因此,总线带宽计算为:

$$ B=\frac{S}{t}\ast\frac{n-1}{n}=algbw\ast\frac{n-1}{n} $$

请注意,这里的 S 是总数组的字节大小,对于 NCCL 来说,等于 $sendcount*sizeof(datatype*n)$,其中 $sendcount$ 是每个节点的计数。

Broadcast

Broadcast 操作的表现形式与 AllGather 类似:

$$ o_0 = o_1 = o_2 = ... = o_{n-1} = i_R $$

其中 R 是操作的根节点。

然而,在这种情况下,由于根节点的输入 $i_R$ 在各个计算节点之间分布不均,因此我们不能利用所有的 N 条连接来执行数据传输操作。实际上,所有数据都必须从根节点输出,因此瓶颈出现在根节点上,根节点的输出能力仅为 B:

$$ t=\frac{S}{B} $$

因此

$$ B=\frac{S}{t} $$

Reduce

Reduce 操作表现为

$$ o_R = i_0 + i_1 + i_2 + ... + i_{n-1} $$

其中 R 是操作的根节点。

与 Broadcast 类似,所有数据都需要发送到根节点,所以有:

$$ t=\frac{S}{B} $$

因此

$$ B=\frac{S}{t} $$

总结

为了获得一个与节点数量 n 无关的总线带宽,我们对算法带宽应用了修正因子:

  • AllReduce:2*(n-1)/n
  • ReduceScatter:(n-1)/n
  • AllGather:(n-1)/n
  • Broadcast:1
  • Reduce:1
  • AlltoAll:(n-1)/n

总线带宽应该反映硬件瓶颈的速度,如 NVLink、PCI、QPI 或网络。

修正因子计算出来的总线带宽是在理想情况下的带宽性能,即在理论上能够达到的最大带宽。如果实际测量的总线带宽未能达到这一理想值,则可能表明系统中存在硬件瓶颈或其他限制因素。


本站不记录浏览量,但如果您觉得本内容有帮助,请点个小红心,让我知道您的喜欢。