我仍然感到困惑。如果我在OpenMP中使用reduce子句可以进行错误共享吗? (两个代码片段都给出了正确的结果。)
一个小例子,需要最大数组:
double max_red(double *A, int N){
double mx = std::numeric_limits<double>::min();
#pragma omp parallel for reduction(max:mx)
for(int i=0; i<N; ++i){
if(A[i]>mx) mx = A[i];
}
return mx;
}
此示例也可以使用额外填充
编写double max_padd(double *A, int N){
omp_set_num_threads(NUM_THREADS);
double local_max[NUM_THREADS][8];
double res;
#pragma omp parallel
{
int id = omp_get_thread_num();
local_max[id][0] = std::numeric_limits<double>::min();
#pragma omp for
for(int i=0; i<N; ++i){
if(A[i]>local_max[id][0])local_max[id][0]=A[i];
}
#pragma omp single
{
res = local_max[0][0];
for(int i=0; i<NUM_THREADS; ++i){
if(local_max[i][0]> res)res = local_max[i][0];
}
}
}
return res;
}
但完全禁止虚假共享或减少条款是否足够安全所需的额外填充?
由于
答案 0 :(得分:4)
填充不是必需的。
从技术上讲,这不是标准规定的。该标准没有说明每个线程私有副本在内存中的位置。请记住,错误共享不是正确性问题,而是(非常重要的)实际性能问题。
但是,如果任何OpenMP实现会造成这样的菜鸟错误并将私有副本放在同一个缓存行上,那将是非常令人惊讶的。
假设实现比程序员更好地理解平台及其性能特征。如果您通过测量证明惯用解决方案(例如您的第一个解决方案)具有无法通过调整修复的不良性能,则只编写手动“性能改进”,例如您的第二个解决方案。
实用注意事项:我非常确定实现通常会将私有副本放在执行线程的(私有)堆栈上,然后每个线程都会在关键部分更新共享变量,或者在支持时以原子方式更新。
理论上,基于对数时间树的减少是可能的。但是implementations don't seem to do that,至少在所有情况下都不是。