开放 mp 方法的奇怪基准测试结果
Strange benchmark results for open mp methods
我的基准测试结果很奇怪。一方面,我有用于计算二次形式的串行函数。另一方面,我写了两个平行版本。对于一个线程,所有函数应该需要或多或少相同的 运行ning 时间。但是一个并行函数只需要一半的时间。有"hidden"优化吗?
连载版本:
double quadratic_form_serial(const std::vector<double> & A,const std::vector<double> & v, const std::vector<double> & w){
int N= v.size();
volatile double q=0.0;
for(int i=0; i<N; ++i)
for(int j=0; j<N; ++j)
q +=v[i]*A[i*N+j]*w[j];
return q;
}
并行版本 1:
double quadratic_form_parallel(const std::vector<double> & A,const std::vector<double> & v, const std::vector<double> & w, const int threadnum){
int N= v.size();
omp_set_num_threads(threadnum);
volatile double q[threadnum];
volatile double val = 0.0;
#pragma omp parallel
{
int me = omp_get_thread_num();
q[me] = 0.0;
#pragma omp for collapse(2)
for(int i=0; i<N; ++i)
for(int j=0; j<N; ++j)
q[me]+=v[i]*A[i*N+j]*w[j];
#pragma omp atomic
val+=q[me];
}
return val;
}
并行版本 2:
double quadratic_form_parallel2(const std::vector<double> & A,const std::vector<double> & v, const std::vector<double> & w, const int threadnum){
int N= v.size();
volatile double result =0.0;
omp_set_num_threads(threadnum);
#pragma omp parallel for reduction(+: result)
for (int i=0; i<N; ++i)
for (int j=0; j<N; ++j)
result += v[i] * A[i*N + j] * w[j];
return result;
}
我 运行 N=10000 的代码,我在调用该函数之前刷新了缓存。函数 quadratic_form_parallel2 需要 一个 线程少于其他两个函数所需时间的一半:
threads serial Parallel1 Parallel2
1 0.0882503 0.0875649 0.0313441
这很可能是因为 result
在第二个 OpenMP 版本中是一个 reduction
变量。这意味着,每个线程都获得 result
的私有副本,该副本在并行区域之后合并。此私有副本可能不遵守易失性限制,因此可以进一步优化。我假设 volatile
和 private
之间的详细交互是未指定的。
这表明,将变量标记为 volatile
- 大概是为了避免优化整个代码 - 是一个坏主意。而只是输出结果。
我的基准测试结果很奇怪。一方面,我有用于计算二次形式的串行函数。另一方面,我写了两个平行版本。对于一个线程,所有函数应该需要或多或少相同的 运行ning 时间。但是一个并行函数只需要一半的时间。有"hidden"优化吗?
连载版本:
double quadratic_form_serial(const std::vector<double> & A,const std::vector<double> & v, const std::vector<double> & w){
int N= v.size();
volatile double q=0.0;
for(int i=0; i<N; ++i)
for(int j=0; j<N; ++j)
q +=v[i]*A[i*N+j]*w[j];
return q;
}
并行版本 1:
double quadratic_form_parallel(const std::vector<double> & A,const std::vector<double> & v, const std::vector<double> & w, const int threadnum){
int N= v.size();
omp_set_num_threads(threadnum);
volatile double q[threadnum];
volatile double val = 0.0;
#pragma omp parallel
{
int me = omp_get_thread_num();
q[me] = 0.0;
#pragma omp for collapse(2)
for(int i=0; i<N; ++i)
for(int j=0; j<N; ++j)
q[me]+=v[i]*A[i*N+j]*w[j];
#pragma omp atomic
val+=q[me];
}
return val;
}
并行版本 2:
double quadratic_form_parallel2(const std::vector<double> & A,const std::vector<double> & v, const std::vector<double> & w, const int threadnum){
int N= v.size();
volatile double result =0.0;
omp_set_num_threads(threadnum);
#pragma omp parallel for reduction(+: result)
for (int i=0; i<N; ++i)
for (int j=0; j<N; ++j)
result += v[i] * A[i*N + j] * w[j];
return result;
}
我 运行 N=10000 的代码,我在调用该函数之前刷新了缓存。函数 quadratic_form_parallel2 需要 一个 线程少于其他两个函数所需时间的一半:
threads serial Parallel1 Parallel2
1 0.0882503 0.0875649 0.0313441
这很可能是因为 result
在第二个 OpenMP 版本中是一个 reduction
变量。这意味着,每个线程都获得 result
的私有副本,该副本在并行区域之后合并。此私有副本可能不遵守易失性限制,因此可以进一步优化。我假设 volatile
和 private
之间的详细交互是未指定的。
这表明,将变量标记为 volatile
- 大概是为了避免优化整个代码 - 是一个坏主意。而只是输出结果。