如何使用OpenMP并行更新总和

How to parallelize updating sums with OpenMP

本文关键字:更新 并行 何使用 OpenMP      更新时间:2023-10-16

下面的循环遍历图的所有边,确定末端节点是否属于同一组,然后将边权重添加到该组的总边权重中。

// TODO: parallel
FORALL_EDGES_BEGIN((*G)) {
node u = EDGE_SOURCE;
node v = EDGE_DEST;
cluster c = zeta[u];
cluster d = zeta[v];
if (c == d) {
// TODO: critical section
intraEdgeWeight[c] += G->weight(u, v);
} // else ignore edge
} FORALL_EDGES_END();

我想将它与OpenMP并行化。我认为if语句中的代码是一个关键部分,如果线程在其中中断,可能会导致竞争条件和错误的结果(正确吗?)。

如果+=操作可以原子化地进行,我相信问题已经解决了(对吗?)。然而,我查阅了atomic指令,其中指出:

不幸的是,原子性设置只能指定为通常可以编译到单个处理器中的表达式操作码,例如递增、递减、异或等等。对于例如,它不能包括函数调用、数组索引、重载运算符、非POD类型或多个语句。

我应该使用什么来正确地并行化这个循环?

实际上,atomic update的可接受语法是:

x++;

x--;

++x

--x

xbinop=expr

x=xbinopexpr

其中x是标量l-value表达式,expr为任何表达式,包括函数调用,唯一的限制是它必须是标量类型。编译器会注意将中间结果存储在一个临时变量中。

有时最好查阅标准文档,而不是在互联网上阅读教程。遵守OpenMP 3.1标准扩展示例A.22.1c:

float work1(int i)
{
return 1.0 * i;
}
...
#pragma omp atomic update
x[index[i]] += work1(i);

我还认为,如果块是关键部分,则不应该在不序列化写入的情况下从多个线程写入向量。您可以使用#pragma omp critical将if块中+=的执行限制为一次只能执行一个线程。

您可以将表达式拆分为两部分:将结果赋值为临时值的函数调用,以及将该临时值添加到累加器。在这种情况下,第二个表达式将足够简单,可以使用omp atomic,假设加法本身不是自定义类型的复杂重载运算符。当然,只有在G->weight(u,v)是线程安全调用的情况下才能这样做,否则必须使用omp critical或互斥锁。