为什么在某些情况下#pragma omp critical 指令效率低下?

Why in some cases #pragma omp critical directive is inefficient?

我读到在这样的一个语句上使用#pragma omp critical 是低效的,我不知道为什么?

double area, pi, x;
int i, n;
...
area = 0.0;
#pragma omp parallel for private(x)
for (i = 0; i < n; i++) {
   x = (i+0.5)/n;
#pragma omp critical
   area += 4.0/(1.0 + x*x);
}
pi = area / n;

天真的 compiler/runtime 会在每次迭代时做:

  • 拿一把锁
  • 计算`4.0 / (1.0 + x*x)
  • 执行area += ...
  • 解除锁定

另一种方法是不使用锁,而是使用原子指令执行 area += ...

在这两种情况下,这都比使用缩减子句效率低,其中每个线程在没有任何同步的情况下运行,并且缩减(可能基于树)仅发生在 OpenMP 区域的末尾。

扩大 @Gilles 答案,因为所提出的问题比代码更笼统。

omp critical 是一个强制序列化的指令,因此只有一个线程可以在受保护的“临界区”内执行。因此,如果对关键部分的并行性存在争用,则会降低性能。有点悲观的想法是,如果这是临界区中的时间应该添加到 Amdahl's law 中的序列分数。 (这是悲观的,因为关键部分只有在争用点时才会降低并行度。如果它很小而且很少见,那是不太可能的。)

在您的示例中,临界区内的工作(2 次加法、1 次乘法、1 次除法)比外部(2 次加法、1 次除法、1 次比较)要多 [我已经在此处包括了循环开销!] ).然后应用 Amdahl 建议最大并行效率为 50%。

所以 critical 从性能的角度来看是危险的,但对于避免竞争条件是必不可少的,如果关键部分内的执行时间与其他并行执行时间。

正如@Gilles 所说,这里你绝对应该使用缩减。

p.s。 在它们的最小范围内声明你的变量。(这是在 C89 中,所以你不能争辩说你需要向后兼容!)这使得代码更容易阅读并且通常避免需要 private(foo) 指令(因此也使代码更容易正确!)