如何测试代码的问题规模缩放性能
How to test the problem size scaling performance of code
我是 运行 一个简单的内核,它添加了两个双精度复数值流。我使用带有自定义调度的 OpenMP 将其并行化:slice_indices
容器包含不同线程的不同索引。
for (const auto& index : slice_indices)
{
auto* tens1_data_stream = tens1.get_slice_data(index);
const auto* tens2_data_stream = tens2.get_slice_data(index);
#pragma omp simd safelen(8)
for (auto d_index = std::size_t{}; d_index < tens1.get_slice_size(); ++d_index)
{
tens1_data_stream[d_index].real += tens2_data_stream[d_index].real;
tens1_data_stream[d_index].imag += tens2_data_stream[d_index].imag;
}
}
目标计算机具有 Intel(R) Xeon(R) Platinum 8168 CPU @ 2.70GHz,24 核,L1 缓存 32kB,L2 缓存 1MB 和 L3 缓存 33MB。总内存带宽为115GB/s。
以下是我的代码如何随着问题大小 S = N x N x N 进行缩放。
在以下情况下,任何人都可以用我提供的信息告诉我:
- 扩展性很好,and/or
- 我怎样才能知道它是否利用了所有可用的资源?
提前致谢。
编辑:
现在我绘制了 GFLOP/s 中 24 核和 48 核(两个 NUMA 节点,同一处理器)的性能。看起来是这样的:
现在强弱比例图:
注意:我测了一下BW,结果是105GB/S
问题: weak scaling plot 中 6 threads/problem size 90x90x90x16 B 处的奇怪峰的含义对我来说并不明显。有人可以清除这个吗?
您的图形大致具有正确的形状:微型数组应该适合 L1 缓存,因此可以获得非常高的性能。大约一兆字节的数组适合 L2 并获得较低的性能,超过它你应该从内存流式传输并获得低性能。所以问题大小和 运行time 之间的关系确实应该随着大小的增加而变得更陡峭。但是,生成的图(顺便说一句,ops/sec 比单纯的 运行 时间更常见)在您达到连续缓存边界时应该具有逐步结构。我会说你没有足够的数据点来证明这一点。
此外,通常您会重复每个“实验”几次,以 1. 消除统计问题和 2. 确保数据确实在缓存中。
既然你标记了这个“openmp”,你还应该探索采用给定的数组大小,并改变核心数。然后,您应该或多或少地获得性能的线性增长,直到处理器没有足够的带宽来维持所有内核。
一位评论者提出了 strong/weak 缩放的概念。强扩展意味着:给定一定的问题规模,使用越来越多的核心。这应该会给你提高性能,但随着开销开始占主导地位,returns 会减少。弱缩放意味着:保持每个 process/thread/whatever 的问题大小不变,并增加处理元素的数量。这应该会给你带来几乎线性增长的性能,直到——正如我所指出的——你 运行 超出带宽。你似乎做的实际上不是这些:你在做“乐观缩放”:增加问题的大小,其他一切都不变。这应该会给你带来越来越好的性能,除了我指出的缓存效果。
因此,如果您想说“此代码可伸缩”,则必须决定在什么情况下。就其价值而言,您的 200Gb/秒数字是合理的。这取决于您的体系结构的细节,但对于一个相当新的 Intel 节点来说这听起来很合理。
我是 运行 一个简单的内核,它添加了两个双精度复数值流。我使用带有自定义调度的 OpenMP 将其并行化:slice_indices
容器包含不同线程的不同索引。
for (const auto& index : slice_indices)
{
auto* tens1_data_stream = tens1.get_slice_data(index);
const auto* tens2_data_stream = tens2.get_slice_data(index);
#pragma omp simd safelen(8)
for (auto d_index = std::size_t{}; d_index < tens1.get_slice_size(); ++d_index)
{
tens1_data_stream[d_index].real += tens2_data_stream[d_index].real;
tens1_data_stream[d_index].imag += tens2_data_stream[d_index].imag;
}
}
目标计算机具有 Intel(R) Xeon(R) Platinum 8168 CPU @ 2.70GHz,24 核,L1 缓存 32kB,L2 缓存 1MB 和 L3 缓存 33MB。总内存带宽为115GB/s。
以下是我的代码如何随着问题大小 S = N x N x N 进行缩放。
在以下情况下,任何人都可以用我提供的信息告诉我:
- 扩展性很好,and/or
- 我怎样才能知道它是否利用了所有可用的资源?
提前致谢。
编辑:
现在我绘制了 GFLOP/s 中 24 核和 48 核(两个 NUMA 节点,同一处理器)的性能。看起来是这样的:
现在强弱比例图:
注意:我测了一下BW,结果是105GB/S
问题: weak scaling plot 中 6 threads/problem size 90x90x90x16 B 处的奇怪峰的含义对我来说并不明显。有人可以清除这个吗?
您的图形大致具有正确的形状:微型数组应该适合 L1 缓存,因此可以获得非常高的性能。大约一兆字节的数组适合 L2 并获得较低的性能,超过它你应该从内存流式传输并获得低性能。所以问题大小和 运行time 之间的关系确实应该随着大小的增加而变得更陡峭。但是,生成的图(顺便说一句,ops/sec 比单纯的 运行 时间更常见)在您达到连续缓存边界时应该具有逐步结构。我会说你没有足够的数据点来证明这一点。
此外,通常您会重复每个“实验”几次,以 1. 消除统计问题和 2. 确保数据确实在缓存中。
既然你标记了这个“openmp”,你还应该探索采用给定的数组大小,并改变核心数。然后,您应该或多或少地获得性能的线性增长,直到处理器没有足够的带宽来维持所有内核。
一位评论者提出了 strong/weak 缩放的概念。强扩展意味着:给定一定的问题规模,使用越来越多的核心。这应该会给你提高性能,但随着开销开始占主导地位,returns 会减少。弱缩放意味着:保持每个 process/thread/whatever 的问题大小不变,并增加处理元素的数量。这应该会给你带来几乎线性增长的性能,直到——正如我所指出的——你 运行 超出带宽。你似乎做的实际上不是这些:你在做“乐观缩放”:增加问题的大小,其他一切都不变。这应该会给你带来越来越好的性能,除了我指出的缓存效果。
因此,如果您想说“此代码可伸缩”,则必须决定在什么情况下。就其价值而言,您的 200Gb/秒数字是合理的。这取决于您的体系结构的细节,但对于一个相当新的 Intel 节点来说这听起来很合理。