C ++ OpenMP:嵌套循环,其中内部迭代器依赖于外部迭代器

时间:2017-12-11 18:18:04

标签: c++ parallel-processing openmp

请考虑以下代码:

#include <iostream>
#include <chrono>
#include <vector>
#include <numeric>
#include <cmath>
#include <omp.h>

using namespace std;

typedef std::chrono::steady_clock myclock;

double measure_time(myclock::time_point begin, myclock::time_point end)
{
    return std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()/(double)1e6;
}

int main()
{
    int n = 20000;
    vector<double> v(n);
    iota(v.begin(), v.end(), 1.5);

    vector< vector<double> > D(n, vector<double>(n,0.0));

    myclock::time_point begin, end;

    begin = myclock::now();

    //#pragma omp parallel for collapse(2)
    //#pragma omp parallel for
    for(size_t i = 0; i < n - 1; i++){
        for(size_t j = i+1; j < n; j++){
            double d = sqrt(v[i]*v[i] + v[j]*v[j] + 1.5*v[i]*v[j]);
            D[i][j] = d;
            D[j][i] = d;
        }
    }

    end= myclock::now();
    double time = measure_time(begin, end);
    cout<<"Time: "<<time<<" (s)"<<endl;
    return 0;
}

编译:

g++ -std=c++11 -fopenmp -o main main.cpp

我获得了以下运行时间:

  • 使用#pragma omp parallel for collapse(2) 7.9425(s)
  • 使用#pragma omp parallel for 3.73262(s)
  • 没有OpenGM: 11.0935(s)

系统设置:Linux Mint 18.3 64位,g ++ 5.4.0,四核处理器。

我希望第一个比第二个更快(仅与外部循环并行)并且比第三个快得多。

我做错了什么?第一个和第二个都在所有8个线程上运行。

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

当迭代依赖于另一个循环时,不应使用collapse子句。请参阅Understanding the collapse clause in openmp

在您的情况下,由于对称性,您正在矩阵的下三角形(不包括对角线)上运行。这将迭代次数减少了一半左右。如果你想融合/折叠双循环,你可以像这样手工完成(有关详细信息,请参阅this answer的末尾)。

for(size_t k=0; k<n*(n-1)/2; k++) {
    size_t i = k/n, j = k%n;
    if(j<=i) i = n - i - 2, j = n - j - 1;
    double d = sqrt(v[i]*v[i] + v[j]*v[j] + 1.5*v[i]*v[j]);
    D[i][j] = d;
    D[j][i] = d;
}

我认为大多数人都认为折叠循环会提供更好的性能,但通常情况并非如此。根据我的经验,大多数时候性能没有差别,但在某些情况下,由于缓存问题,情况会更糟。在少数情况下,它会更好。你必须自己测试一下。

至于为什么你的代码使用collapse子句慢两倍我只能猜测,因为你的OpenMP实现从j [0,n)运行的内部循环没有指定效果,即完整矩阵而不是矩阵的一半。

相关问题