为什么OpenMP版本较慢

Why OpenMP version is slower?

本文关键字:版本 OpenMP 为什么      更新时间:2023-10-16

我正在试用OpenMP。我写了一些代码来检查它的性能。在使用Kubuntu 11.04的4核单英特尔CPU上,以下使用OpenMP编译的程序比未使用OpenMP的程序慢约20倍。为什么?

我通过g++-g-O2-funroll循环-fmit帧指针-march=native-fopenmp 编译了它

#include <math.h>
#include <iostream>
using namespace std;
int main ()
{
  long double i=0;
  long double k=0.7;
  #pragma omp parallel for reduction(+:i)
  for(int t=1; t<300000000; t++){       
    for(int n=1; n<16; n++){
      i=i+pow(k,n);
    }
  }
  cout << i<<"t";
  return 0;
}

问题是变量k被认为是一个共享变量,因此它必须在线程之间同步。避免这种情况的可能解决方案是:

#include <math.h>
#include <iostream>
using namespace std;
int main ()
{
  long double i=0;
#pragma omp parallel for reduction(+:i)
  for(int t=1; t<30000000; t++){       
    long double k=0.7;
    for(int n=1; n<16; n++){
      i=i+pow(k,n);
    }
  }
  cout << i<<"t";
  return 0;
}

根据Martin Beckett在下面评论中的提示,您也可以声明k const和循环外,而不是在循环内声明k。

否则,ejd是正确的——这里的问题似乎不是糟糕的并行化,而是代码并行化时的糟糕优化。请记住,gcc的OpenMP实现还很年轻,远未达到最佳状态。

最快的代码:

for (int i = 0; i < 100000000; i ++) {;}

稍慢的代码:

#pragma omp parallel for num_threads(1)
for (int i = 0; i < 100000000; i ++) {;}

2-3倍慢的代码:

#pragma omp parallel for
for (int i = 0; i < 100000000; i ++) {;}

不管它在{和}之间是什么。简单的;或者更复杂的计算得到相同的结果。我在Ubuntu 13.10 64位下编译,使用gcc和g++,尝试不同的参数-ansi-迂腐错误-Wall-Wextra-O3,并在英特尔四核3.5GHz上运行。

我想线程管理开销出了问题?OMP在每次需要线程时都创建一个线程,然后销毁它,这似乎并不明智。我以为会有四个(或八个)线程在需要的时候运行或睡觉。

我在GCC上观察到类似的行为。然而,我想知道在我的情况下,它是否在某种程度上与模板或内联函数有关。您的代码也在模板或内联函数中吗?请看这里。

然而,对于非常短的循环,您可能会观察到与线程切换相关的一些小开销,如您的情况:

#pragma omp parallel for
for (int i = 0; i < 100000000; i ++) {;}

如果您的循环执行时间非常长,甚至只有几毫秒甚至几秒钟,那么在使用OpenMP时,您应该观察到性能的提升。但只有当你有一个以上的CPU。您拥有的内核越多,OpenMP的性能就越高。