OpenMP Dot产品和指针

时间:2015-05-13 15:57:14

标签: c pointers for-loop openmp reduction

我正在尝试使用malloc分配的大型数组在OpenMP中实现dotproduct。但是,当我使用reduction(+:result)时,它会为每个程序运行产生不同的结果。为什么我会得到不同的结果?我该如何解决这个问题?这个例子如何优化?这是我的代码:

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

const int N = 1e1;

int main ()
{
  int    i, nthreads, tid;
  double x_seq, x_par, *y, *z, cpu_time_used;
  clock_t start, end;

  y = (double*)malloc(sizeof(double)*N);
  z = (double*)malloc(sizeof(double)*N);

  for (i=0; i<N; i++) {
      y[i] = i * 1.0;
      z[i] = i * 2.0;
  }

  x_seq = 0;
  x_par = 0;
  for (i=0; i<N; i++) x_seq += y[i] * z[i];

  #pragma omp parallel shared(y, z) private(i, tid)
  {
      #pragma omp single
      {
          nthreads = omp_get_num_threads();
      }
      tid = omp_get_thread_num();

      #pragma omp parallel for reduction(+:x_par)
      for (i=tid; i<N; i+=nthreads)
      {
          x_par += y[i] * z[i];
      }

  }
  return 0;
}

1 个答案:

答案 0 :(得分:3)

这里有几个问题。

让我们看看现在的循环:

#pragma omp parallel shared(y, z) private(i, tid)
{
  #pragma omp single
  {
      nthreads = omp_get_num_threads();
  }
  tid = omp_get_thread_num();

  #pragma omp parallel for reduction(+:x_par)
  for (i=tid; i<N; i+=nthreads)
  {
      x_par += y[i] * z[i];
  }
}

所以(1)请注意,您(可能)希望x_par可以在此区域之外访问。因此,您希望外部的reduction(+:x_par)而不是内部。{1}}。如果您还添加了非常有用的default(none)子句,您还会发现没有描述共享nthreads的子句;让我们明确地分享。

让我们再看看:

#pragma omp parallel shared(y, z, nthreads) private(i, tid) reduction(+:x_par) default(none)
{
  #pragma omp single
  {
      nthreads = omp_get_num_threads();
  }
  tid = omp_get_thread_num();

  #pragma omp parallel for 
  for (i=tid; i<N; i+=nthreads)
  {
      x_par += y[i] * z[i];
  }
}

因此,仔细观察,我们现在看到您有两个omp parallel部分。这意味着,如果启用了嵌套并行性,那么您每次启动nthreads时都会执行nthreads个任务来执行该循环;所以如果一切正常,循环将以nthreads次正确答案结束。因此,让我们摆脱并行,只需使用for:

 #pragma omp parallel shared(y, z, nthreads) private(i, tid) reduction(+:x_par) default(none)
{
  #pragma omp single
  {
      nthreads = omp_get_num_threads();
  }
  tid = omp_get_thread_num();

  #pragma omp for 
  for (i=tid; i<N; i+=nthreads)
  {
      x_par += y[i] * z[i];
  }
}

因此,共享是正确的,并且不是嵌套并行性,但它仍然没有给出正确的答案;它给出了一个小得多的结果。怎么了?我们来看看for循环。每个线程都想从tid开始并跳过nthreads,很好;但为什么我们要omp for呢?

让我们来看一个更简单的版本:

#pragma omp parallel shared(y, z) reduction(+:x_par) default(none)
{
  #pragma omp for
  for (i=0; i<N; i++)
  {
      x_par += y[i] * z[i];
  }
}

请注意,这里我们没有使用tid和nthreads明确地分解循环 - 我们不必这样做,因为omp for为我们分解了循环;它将循环迭代分配给线程。

回顾我们所拥有的东西,我们手动分解循环 - 这很好,有时候你需要做的事情; omp for尝试接受该循环并将其拆分为线程。但我们已经这样做了; omp for只是让我们跳过这里的迭代!

所以摆脱omp for

#pragma omp parallel shared(y, z, nthreads) private(i, tid) reduction(+:x_par) default(none)
{
  #pragma omp single
  {
      nthreads = omp_get_num_threads();
  }
  tid = omp_get_thread_num();

  for (i=tid; i<N; i+=nthreads)
  {
      x_par += y[i] * z[i];
  }
}

给我们正确的答案。