OpenMP标准指定缩减变量的初始值。所以我必须初始化变量,在下列情况下我该怎么做:
int sum;
//...
for(int it=0;i<maxIt;i++){
#pragma omp parallel
{
#pragma omp for nowait
for(int i=0;i<ct;i++)
arrayX[i]=arrayY[i];
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
}
//Use sum
}
请注意,我只使用1个并行区域来最小化开销并允许第一个循环中的nowait。使用这个原样会导致数据竞争(IMO),因为在其他线程启动第二个循环后来自第一个循环的线程将重置总和。
当然,我可以在外循环的顶部执行此操作,但在一般情况下,对于大型代码库,您可能会忘记您需要或已将其设置在那里会产生意外结果。
“omp single”有帮助吗?我怀疑当线程A执行单个时,另一个线程可能已进入减少循环。
“omp barrier”是可能的,但我想避免这种情况,因为它击败了“nowait”。
又一个例子:
#pragma omp parallel
{
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
//Use sum
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
//Use sum
}
我如何(重新)在这里初始化?
答案 0 :(得分:5)
编辑:这个答案错误,因为它假设不在OpenMP规范中。由于无法删除已接受的答案,因此我将其留在此处作为一个例子,应始终怀疑和验证在互联网上找到的代码和/或陈述。
实际上,代码没有展示数据竞赛:
#pragma omp parallel
{
...
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
...
}
这里发生的是sum
的私有副本在工作共享构造内创建,并初始化为0
(+
运算符的初始化值)。每个本地副本由循环体更新。一旦给定的线程完成,它将等待for
结构末尾的隐式屏障。一旦所有线程都到达屏障,其sum
的本地副本将汇总在一起,并将结果添加到共享值。
所有线程可能在不同时间执行sum = 0;
并不重要,因为只有在达到屏障后才会更新其值。想想上面的代码执行类似的事情:
...
sum = 0;
// Start of the for worksharing construct
int local_sum = 0; // ^
for(int i = i_start; i < i_end; i++) // | sum not used here
local_sum += arrayZ[i]; // v
// Implicit construct barrier
#pragma omp barrier
// Reduction
#pragma omp atomic update
sum += local_sum;
#pragma omp barrier
// End of the worksharing construct
...
这同样适用于第二个例子。
答案 1 :(得分:0)
OpenMP规范没有规定原始值何时以及如何更新并强制使用同步(OpenMP,p.205):
为了避免竞争条件,原始列表项的并发读取或更新必须与
reduction
计算结果发生的原始列表项的更新同步。
在这两个示例中,为了防止竞争条件,需要在分配到barrier
或sum
构造(没有single
)之后nowait
以防止竞争条件。