为什么这个OpenMP计划每次给我不同的答案?

时间:2015-10-18 02:14:16

标签: c multithreading parallel-processing openmp

我试图让这个程序运行多个线程。

#include <stdio.h>
#include <time.h>
#include <omp.h>

#define NUM_THREADS 4
static long num_steps = 1000000000;

int main()
{
  int i;
  double x, pi, sum = 0.0;
  double step = 1.0/(double)num_steps;

  clock_t start = clock(), diff;
  #pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum)   
  for (i = 0; i < num_steps; i++)
  {
    x = (i+0.5)*step;
    sum += 4.0/(1.0 + x*x);
  }
  #pragma omp ordered
  pi = step*sum;
  printf("pi = %.15f\n %d iterations\n", pi, num_steps);

  diff = clock() - start;
  int msec = diff * 1000 / CLOCKS_PER_SEC;
  printf("Time taken %d seconds %d milliseconds", msec/1000, msec%1000);

  return 0;
}

添加#pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum)。在for循环之后我也有#pragma omp ordered,我认为我实际上并不需要因为在所有线程完成for循环之前没有线程继续。它是否正确?这也是我将其作为单线程程序运行而只获得第二次性能提升的原因吗?这是6秒,而我只有7秒。

我无法回答的问题是,为什么每次运行这个程序都会给我一个不同的答案?

2 个答案:

答案 0 :(得分:4)

除了吉尔斯指出的错误之外,这里还有一个更基本的问题。

并行线程的减少不一定是确定性的。每个线程贡献的组合顺序可以随着代码的每次执行而改变。如果你不知道为什么重要,请去阅读&#34; What Every Computer Scientist Should Know About Floating-Point Arithmetic&#34;

如果你没有得到这一点,但是,考虑三个线程在十进制算术机器上进行总和减少,它支持三位数的精度。 假设我们按照顺序添加它们(100,-100,0.1),我们将有100 - 100 = 0 + 0.1 = 0.1,但是如果我们按顺序添加它们(100) ,0.1,-100),我们得到100 + 0.1 = 100(三位有效数字,记住!)-100 == 0

如果您正在使用英特尔编译器,则可以设置一个环境变量来请求确定性减少(KMP_DETERMINISTIC_REDUCTION),但这只会在使用相同数量的线程时强制执行确定性。它不会在具有不同线程数的运行之间强制执行它。 (这样做需要强制执行每线程贡献累积的顺序,这需要不同的代码生成和一些线程间同步)。

答案 1 :(得分:2)

您的问题来自于您忘记声明x private这一事实。 如果将OpenMP指令更改为:

#pragma omp parallel for num_threads(NUM_THREADS) reduction(+:sum) private(x)

您的代码有效。

但是,这里仍有两个问题:

  1. #pragma omp ordered毫无意义,因为您未进入parallel区域。你应该删除它。
  2. 使用clock()来测量多线程代码中的时间是危险的,不是因为该函数不是线程安全的,而是因为它返回了 CPU时间当前线程其子项,而不是已用时间。因此,无论是否激活OpenMP,您通常都会得到几乎相同的结果,并且人们想知道为什么他们的代码没有暴露任何加速...所以除非您有充分的理由使用clock() ,请改用omp_get_wtime()