请考虑以下代码:
#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) 系统设置:Linux Mint 18.3 64位,g ++ 5.4.0,四核处理器。
我希望第一个比第二个更快(仅与外部循环并行)并且比第三个快得多。
我做错了什么?第一个和第二个都在所有8个线程上运行。
提前感谢您的帮助!
答案 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)
运行的内部循环没有指定效果,即完整矩阵而不是矩阵的一半。