如何解决openMP循环慢于串行版本?

时间:2019-02-10 19:37:20

标签: c++ openmp

我正在尝试使用用于计算永久物的数组来实现计算。

问题是我用C ++(串行)编写的循环比带有openMP的并行化版本快。

这是代码的一部分。

    int t,k,j,i;
    double result = 0;

    #pragma omp parallel for private (t,k,j,i)
    for (int t = 0; t < cols; ++t) {
        double delta = 1;

        #pragma omp parallel for reduction(*: delta)
        for (int k = 0; k < m; ++k) {
            delta *= trasposta[k][t];
        }

        double p = 1;
        for (int j = 0; j < m; j++) {
            double s = 0;

            #pragma omp parallel for reduction(+: s)
            for (int i = 0; i < m; i++) {
                s += trasposta[i][t] * matr[i][j];
            }
            p *= s;
        }
        result += delta * p;
    }

    permanent = result / cols;
    cout << permanent << endl;

带有openMP的执行时间并行版本:

real    0m0,334s
user    0m0,387s
sys     0m0,207s

执行时间序列号版本(没有所有的#pragma omp)

real    0m0,100s
user    0m0,095s
sys     0m0,005s

如何解决opeMP以获得更好的结果?

编辑:我使用以下命令编译了openMP版本:

g++ -fopenmp permanent.cpp -o permanent

2 个答案:

答案 0 :(得分:0)

我建议您检查一下代码:

  • 变量tkji被声明两次;
  • 循环增量运算符有时带有前缀,有时带有后缀。

答案 1 :(得分:0)

不确定,它会完全回答您的问题,但是您的代码有很多问题。

关于并行化,您有几个嵌套的omp节。使用嵌套并行机制,每个线程在每个节中创建max_threads新线程。因此,如果max_threads = 10,具有3个嵌套级别,您最终将拥有1000个线程!如果没有嵌套并行机制(这似乎是您的情况),那么内部omp并行机制将被忽略。所以删除它们。

关于omp,内部减少涉及线程局部变量并且可以被抑制。但是result+=delta*p呢?这是对全局变量的减少,应该这样处理。当前代码有错误。

也许代码中最糟糕的是访问数据的方式。您对矩阵进行的 All 访问均以不友好的缓存方式完成。也许更糟的是,它禁止编译器进行优化(例如simd矢量化)。最低要求是对矩阵进行转置(我做了什么),但是您可能会重新考虑算法,以使缓存更容易被忽略。如果您不知道这些问题,请查看what is a cache friendly code

一些随机的评论。 tijk声明是没有用的。通过const分配双精度数时,它不应为int(对于结果,为delta,s)。

请勿使用time(1)来衡量执行时间。使用rdtsc,times(2)或clock(3)(我使用过)有更精确的度量。我怀疑您的代码非常快,一旦适当地进行了优化,并且为了得到公平的比较,您应该考虑几个循环。根据需要调整循环数。并运行几次您的程序。您将看到执行时间不是确定的。并删除异常值。

最后,但并非最不重要。您应该永远不要在不优化代码的情况下衡量性能。使用gcc -O2或-O3。

#include <time.h>

// transposed versions of matrices. To get efficient transpose code, look
// at https://stackoverflow.com/questions/5200338/a-cache-efficient-matrix-transpose-program
// it is a quick and dirty hack,  
// but you should rethink your algorithms to use properly caches
double Tmatr[m][m];
double Ttrasposta[cols][m];

clock_t start, end;

start=clock(); //tic

#define NLOOPS 100
// to adjust

// run the code several times
for(int l=0; l<NLOOPS; l++)
{ 

double result = 0.0;

#pragma omp parallel for reduction(+:result)
for (int t = 0; t < cols; ++t) {
  double delta = 1.0;

  for (int k = 0; k < m; ++k) {
    delta *= Ttrasposta[t][k];
  }

  double p = 1.0;
  for (int j = 0; j < m; j++) {
    double s = 0.0;
    for (int i = 0; i < m; i++) {
      s += Ttrasposta[t][i] * Tmatr[j][i];
    }
    p *= s;
  }
  result += delta * p;
 }

permanent = result / cols;

 }

end=clock();//toc

cout << permanent << endl;
cout << "Time: " << (double)(end-start) << endl ;

最后一句话。使用干净的代码,您现在可以尝试使用程序。我无法执行此操作,因为缺少重要数据:例如,矩阵的大小。 m = 4或100000吗?同上为cols。它可以严重改变并行化策略。这就是为什么您应该始终提供Minimum, Complete, Verifiable Example的原因。

根据这些值,也许最好collapse进行“并行运算”。