121

OpenMP 中的 atomic 和 critical 有什么区别?

我可以做这个

#pragma omp atomic
g_qCount++;

但这不一样吗

#pragma omp critical
g_qCount++;

?

4

7 回答 7

189

The effect on g_qCount is the same, but what's done is different.

An OpenMP critical section is completely general - it can surround any arbitrary block of code. You pay for that generality, however, by incurring significant overhead every time a thread enters and exits the critical section (on top of the inherent cost of serialization).

(In addition, in OpenMP all unnamed critical sections are considered identical (if you prefer, there's only one lock for all unnamed critical sections), so that if one thread is in one [unnamed] critical section as above, no thread can enter any [unnamed] critical section. As you might guess, you can get around this by using named critical sections).

An atomic operation has much lower overhead. Where available, it takes advantage on the hardware providing (say) an atomic increment operation; in that case there's no lock/unlock needed on entering/exiting the line of code, it just does the atomic increment which the hardware tells you can't be interfered with.

The upsides are that the overhead is much lower, and one thread being in an atomic operation doesn't block any (different) atomic operations about to happen. The downside is the restricted set of operations that atomic supports.

Of course, in either case, you incur the cost of serialization.

于 2011-10-17T20:11:40.390 回答
34

在 OpenMP 中,所有未命名的临界区都是互斥的。

关键和原子之间最重要的区别是原子只能保护单个赋值,您可以将它与特定运算符一起使用。

于 2012-02-27T14:40:20.973 回答
27

关键部分:

  • 确保代码块的序列化。
  • 可以通过正确使用“名称”标签来扩展以序列化块组。

  • 慢点!

原子操作:

  • 速度要快得多!

  • 仅确保特定操作的序列化。

于 2013-12-23T02:41:51.347 回答
7

最快的方法既不是关键的也不是原子的。大约,临界区加法比简单加法贵 200 倍,原子加法比简单加法贵 25 倍。

最快的选择(并不总是适用)是给每个线程自己的计数器,并在需要总和时进行减少操作。

于 2014-01-26T14:39:29.947 回答
6

的限制atomic很重要。它们应该在OpenMP 规范中详细说明。MSDN提供了一份快速备忘单,如果这不会改变,我不会感到惊讶。(Visual Studio 2012 从 2002 年 3 月开始实施 OpenMP。)引用 MSDN:

表达式语句必须具有以下形式之一:

x二进制操作=expr

x++

++x

x--

--x

在前面的表达式中:x是一个lvalue标量类型的表达式。expr是标量类型的表达式,它不引用由 指定的对象xbinop不是重载运算符,它是+, *, -, /, &, ^, |, <<, 或中的一个>>

我建议尽可能使用atomic,否则命名关键部分。命名它们很重要;这样可以避免调试头痛。

于 2013-11-01T19:31:22.807 回答
1

这里已经有了很好的解释。但是,我们可以更深入地研究。要理解 OpenMP 中原子临界区概念的核心区别,我们首先要了解锁的概念。让我们回顾一下为什么我们需要使用

一个并行程序正在由多个线程执行。当且仅当我们在这些线程之间执行同步时,才会出现确定性结果。当然,线程之间的同步并不总是需要的。我们指的是那些需要同步的情况。

为了同步多线程程序中的线程,我们将使用lock。当一次只需要一个线程限制访问时,lock就会发挥作用。锁定概念的实现可能因处理器而异。让我们从算法的角度来看看一个简单的锁是如何工作的。

1. Define a variable called lock.
2. For each thread:
   2.1. Read the lock.
   2.2. If lock == 0, lock = 1 and goto 3    // Try to grab the lock
       Else goto 2.1    // Wait until the lock is released
3. Do something...
4. lock = 0    // Release the lock

给定的算法可以用如下的硬件语言实现。我们将假设一个处理器并分析其中的锁行为。对于这种做法,让我们假设以下处理器之一:MIPSAlphaARMPower

try:    LW R1, lock
        BNEZ R1, try
        ADDI R1, R1, #1
        SW R1, lock

这个程序似乎还可以,但事实并非如此。上面的代码遇到了前面的问题;同步。让我们找出问题所在。假设锁的初始值为零。如果两个线程运行此代码,一个可能会在另一个读取锁定变量之前到达SW R1,锁定。因此,他们俩都认为是免费的。为了解决这个问题,提供了另一条指令,而不是简单的LWSW。它被称为读-修改-写指令。它是一个复杂的指令(由子指令组成),它确保锁获取过程仅由单个一次线程。Read-Modify-Write与简单的ReadWrite指令的不同之处在于它使用了不同的LoadingStorage方式。它使用LL(Load Linked)加载锁定变量,使用SC(Store Conditional)写入锁定变量。额外的链接寄存器用于确保锁获取过程由单个线程完成。算法如下。

1. Define a variable called lock.
2. For each thread:
   2.1. Read the lock and put the address of lock variable inside the Link Register.
   2.2. If (lock == 0) and (&lock == Link Register), lock = 1 and reset the Link Register then goto 3    // Try to grab the lock
       Else goto 2.1    // Wait until the lock is released
3. Do something...
4. lock = 0    // Release the lock

当链接寄存器被重置时,如果另一个线程假定锁是空闲的,它就不能再次将递增的值写入锁。这样就获得了访问变量的并发性。

关键原子之间的核心区别来自以下想法:

为什么要使用锁(一个新变量)而我们可以使用实际变量(我们正在对其执行操作)作为锁变量?

对锁使用变量会导致临界区,而使用实际变量作为锁会导致原子概念。当我们对实际变量执行大量计算(多于一行)时,临界区很有用。这是因为,如果这些计算的结果未能写入实际变量,则应重复整个过程以计算结果。与在进入高度计算区域之前等待释放锁相比,这可能会导致性能下降。因此,建议在您想要执行单个计算(x++、x--、++x、--x 等)时使用atomic指令并使用当密集部分正在完成计算更复杂的区域时,关键指令。

于 2018-05-02T08:13:06.327 回答
-6

atomic 是单个语句的关键部分,即您锁定一个语句执行

临界区是代码块上的锁

一个好的编译器会像第一个一样翻译你的第二个代码

于 2013-12-18T17:17:04.737 回答