什么限制了这个简单的OpenMP程序的扩展?

时间:2013-11-05 01:38:19

标签: c multithreading performance openmp smp

我正在尝试理解48核系统上的并行化限制(4xAMD Opteron 6348,2.8 Ghz,每个CPU 12个核心)。我写了这个微小的OpenMP代码来测试加速,我认为这是最好的情况(任务是令人尴尬的并行):

// Compile with: gcc scaling.c -std=c99 -fopenmp -O3                                                                                               

#include <stdio.h>
#include <stdint.h>

int main(){

  const uint64_t umin=1;
  const uint64_t umax=10000000000LL;
  double sum=0.;
#pragma omp parallel for reduction(+:sum)
  for(uint64_t u=umin; u<umax; u++)
    sum+=1./u/u;
  printf("%e\n", sum);

}

我惊讶地发现缩放是高度非线性的。使用48个线程运行代码需要大约2.9s,使用36个线程运行3.1s,使用24个线程运行3.7s,使用12个线程运行4.9s,使用1个线程运行代码需要57s。

不幸的是,我不得不说计算机上有一个进程使用100%的一个核心运行,因此可能会影响它。这不是我的过程,所以我无法结束它来测试差异,但不知何故,我怀疑它在19~20倍加速和理想的48倍加速之间有所区别。

为了确保它不是OpenMP问题,我同时运行了两个程序副本,每个程序有24个线程(一个用umin = 1,umax = 5000000000,另一个用umin = 5000000000,umax =百亿)。在这种情况下,程序的两个副本在2.9s之后完成,因此它与使用单个程序实例运行48个线程完全相同。

使用这个简单程序阻止线性缩放是什么?

3 个答案:

答案 0 :(得分:3)

我不确定这是否属于答案,但感觉不仅仅是评论,所以我们走了。

我从来没有注意到任何项目中线程数量的特别线性表现。首先,在我看来,调度程序是严格公平的。 OpenMP可能在一开始就在其线程团队之间平均分配任务,然后加入每个任务。在我喜欢的每个Linux机器上,我希望有一些线程可以提前完成,还有一些线程可以延迟。其他平台会有所不同。无论如何,当然你正在等待最慢的追赶。所以随机说来,在一条钟形曲线上有一个线程处理脉冲,我应该思考的线越多,你就越不会做到,直到后缘越过终点线。

top说什么?它是否告诉你你的进程在20个线程上获得2000%CPU,在40时获得4000%?我打赌它逐渐消失。顺便说一句,htop通常显示一个进程总计,并为每个线程分隔行。这可能很有趣。

通过像这样的小循环,你可能不会遇到缓存捶打或任何这样的烦恼。但是另一个问题必然会削弱一些性能:就像任何现代的多核CPU一样,Opteron在很酷的情况下以更高的时钟频率运行。你加热的核心越多,你会看到的涡轮模式越少。

答案 1 :(得分:3)

我有两个重点,两个为什么你的结果不是线性的。第一个是关于英特尔超线程和AMD模块。下一个是关于英特尔和AMD的turbo频率模式

1.。超线程和AMD模块/核心

太多人将模块中的英特尔超线程和AMD内核混淆为真正的内核,并期望线性加速。具有超线程的英特尔处理器可以运行两倍于内核的超线程/硬件线程。 AMD也拥有自己的技术,其基本单元称为模块,每个模块都有AMD不诚实地称之为核心What's a module, what's a core。容易混淆的一个原因是,例如在具有超踩踏的窗口中使用Task Mangager,它显示了硬件线程的数量,但它表示CPU。这是误导性的,因为它不是CPU核心的数量。

我没有足够的AMD知识进入细节,但据我所知,每个模块都有一个浮点单元(但是两个整数单元)。因此,对于浮点运算,您无法真正期望线性加速超过英特尔核心或AMD模块的数量。

在您的情况下,Opteron 6348每个处理器有2个芯片,每个芯片有3个模块,每个模块为2个“核心”。虽然这提供了12个内核,但实际上只有6个浮点单元。

我在单插槽Intel Xeon E5-1620 @ 3.6 GHz上运行了您的代码。这有4个内核和超线程(所以八个硬件线程)。我明白了:

1 threads: 156s 
4 threads: 37s  (156/4 = 39s)
8 threads: 30s  (156/8 = 19.5s)

请注意,对于4个线程,缩放几乎是线性的,但对于8个线程,超线程只能帮助一点(至少它有帮助)。另一个奇怪的观察是我的单线程结果远低于你的(MSVC2013 64位发布模式)。我希望更快的单线程常春藤网桥核心可以轻松胜过更慢的AMD打桩机核心。这对我没有意义。

2.)英特尔Turbo Boost和AMD Turbo Core。

英特尔拥有一项名为Turbo Boost的技术,可根据正在运行的线程数更改时钟频率。当所有线程都在运行时,涡轮增压器处于最低值。在Linux上,我知道的唯一可以在运行操作时测量它的应用程序是powertop。获得真正的工作频率并不容易衡量(对于需要root访问权限的人来说)。在Windows上,您可以使用CPUz。无论如何,结果是,与运行最大数量的实际核心相比,只运行一个线程时,您不能指望线性扩展。

再一次,我对AMD处理器的经验不多,但据我所知,他们的技术称为Turbo Core,我希望效果类似。这就是在比较线程代码时,良好的基准测试禁用turbo频率模式(如果可以,在BIOS中)的原因。

答案 2 :(得分:2)

我终于有机会使用完全卸载的系统对代码进行基准测试: enter image description here

对于我使用schedule(dynamic,1000000)的动态时间表。对于静态计划,我使用了默认值(在核心之间均匀)。对于线程绑定,我使用了export GOMP_CPU_AFFINITY="0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47"

此代码高度非线性扩展的主要原因是AMD称之为“内核”的实际上不是独立内核。这是redrum答案的第(1)部分。从24个线程的突然加速平台上看,这在上图中清晰可见;动态调度非常明显。从我选择的线程绑定中也很明显:事实证明我上面写的内容对于绑定来说是一个糟糕的选择,因为你最终在每个“模块”中都有两个线程。

第二大减速来自具有大量线程的静态调度。最慢和最快的线程之间不可避免地存在不平衡,当使用默认静态调度将迭代划分为大块时,在运行时间中引入大的波动。这部分答案来自Hristo的评论和Salt的回答。

我不知道为什么“Turbo Boost”的影响不是更明显(Redrum答案的第2部分)。此外,我不是100%肯定在哪里(可能是在开销中)缩放的最后一位丢失(我们得到22倍的性能而不是预期的24倍来自模块的线性缩放)。但是否则这个问题得到了很好的回答。