OpenMP 减慢速度

标签 openmp

openmp代码有缩减和不缩减两种版本。

//减少

#pragma omp parallel for reduction(+:sum)
  for (i=1;i<= num_steps; i++){
      x = (i-0.5)*step;
      sum = sum + 4.0/(1.0+x*x);
  }

//不减少

#pragma omp parallel private(i)
{
  int id = omp_get_thread_num();
  int numthreads = omp_get_num_threads();
  double x;

  double partial_sum = 0;

  for (i=id;i< num_steps; i+=numthreads){
      x = (i+0.5)*step;
      partial_sum += + 4.0/(1.0+x*x);
  }
#pragma omp critical
      sum += partial_sum;
}

我使用 8 个内核运行代码,缩减版本的总时间加倍。什么原因?谢谢。

最佳答案

OpenMP 中的标量缩减通常非常快。在您的案例中观察到的行为是由于两件事以两种不同的方式出错。

在您的第一个代码中,您没有将 x 设为私有(private)。因此,它在线程之间共享,除了得到不正确的结果外,执行还会受到数据共享的影响。每当一个线程写入 x 时,它执行的核心就会向所有其他核心发送一条消息,并使它们使该缓存行的副本无效。当它们中的任何一个稍后写入 x 时,必须重新加载整个缓存行,然后所有其他内核中的缓存行都会失效。等等。这会显着减慢速度。

在您的第二个代码中,您使用了 OpenMP critical 构造。相对于atomic adds,这是一个比较重量级的,通常用来实现最后的reduction。 x86 上的原子添加是使用 LOCK 指令前缀执行的,所有内容都在硬件中实现。另一方面,关键部分是使用互斥锁实现的,需要几条指令和经常繁忙的等待循环。这远低于原子添加的效率。

最后,由于数据共享条件不佳,您的第一个代码速度变慢了。由于使用不正确的同步原语,您的第二个代码速度变慢。碰巧在您的特定系统上,后者的影响没有前者严重,因此您的第二个示例运行得更快。

关于OpenMP 减慢速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20416277/

相关文章:

c++ - 如何以最佳方式并行化嵌套循环?

c++ - 在 OpenMP 线程之间通信

c++ - 混合 C++11 原子和 OpenMP

c++ - 如何使用 OpenMP 屏障

multithreading - SGE/UGE/etc.. 将 OpenMP 作业提交到多个内核的标准化方式?

for-loop - OpenMP 中的并行 while/for - 目录中的文件

c - MPI_文件_写C

c - 像 OpenMP 这样的 golang 中有一个简单的 `parallel for` 吗?

gcc - OpenMP GCC GOMP 浪费屏障

c++ - OpenMP:循环遍历 'std::map' 基准(动态调度)