我还是很困惑。如果我在 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;
}
Run Code Online (Sandbox Code Playgroud)
这个例子也可以用额外的填充来编写
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;
Run Code Online (Sandbox Code Playgroud)
}
但是完全禁止虚假共享所需的额外填充还是减少条款足够安全?
谢谢
填充不是必需的。
从技术上讲,这不是标准规定的。该标准没有说明每个线程私有副本在内存中的位置。请记住,错误共享不是正确性问题,而是(非常重要的)实际性能问题。
但是,如果任何 OpenMP 实现会犯这样一个新手错误并将私有副本放在同一缓存行上,那将是非常令人惊讶的。
假设实现比程序员更了解平台及其性能特征。如果您通过测量证明惯用解决方案(例如您的第一个解决方案)具有无法通过调整修复的不良性能,则仅编写手动“性能改进”,例如您的第二个解决方案。
实用说明:我相当确定实现通常会将私有副本放在执行线程的(私有)堆栈上,然后每个线程将使用临界区或在支持时以原子方式更新共享变量。
理论上,基于对数时间树的缩减是可能的。然而,实现似乎并没有这样做,至少不是在所有情况下。
归档时间: |
|
查看次数: |
420 次 |
最近记录: |