【问题标题】:std::atomic | compare_exchange_weak vs. compare_exchange_strong标准::原子 | compare_exchange_weak 与 compare_exchange_strong
【发布时间】:2011-06-24 02:32:20
【问题描述】:

我不确定是我不理解还是文档没有明确表述。

以下摘自最新草案(N3126,第 29.6 节):

bool atomic_compare_exchange_weak(volatile A* object, C * expected, C desired);
bool atomic_compare_exchange_weak(A* object, C * expected, C desired);
bool atomic_compare_exchange_strong(volatile A* object, C * expected, C desired);
bool atomic_compare_exchange_strong(A* object, C * expected, C desired);
bool atomic_compare_exchange_weak_explicit(volatile A* object, C * expected, C desired, memory_order success, memory_order failure);
bool atomic_compare_exchange_weak_explicit(A* object, C * expected, C desired, memory_order success, memory_order failure);
bool atomic_compare_exchange_strong_explicit(volatile A* object, C * expected, C desired, memory_order success, memory_order failure);
bool atomic_compare_exchange_strong_explicit(A* object, C * expected, C desired, memory_order success, memory_order failure);
bool A::compare_exchange_weak(C & expected, C desired, memory_order success, memory_order failure) volatile;
bool A::compare_exchange_weak(C & expected, C desired, memory_order success, memory_order failure);
bool A::compare_exchange_strong(C & expected, C desired, memory_order success, memory_order failure) volatile;
bool A::compare_exchange_strong(C & expected, C desired, memory_order success, memory_order failure);
bool A::compare_exchange_weak(C & expected, C desired, memory_order order = memory_order_seq_cst) volatile;
bool A::compare_exchange_weak(C & expected, C desired, memory_order order = memory_order_seq_cst);
bool A::compare_exchange_strong(C & expected, C desired, memory_order order = memory_order_seq_cst) volatile;
bool A::compare_exchange_strong(C & expected, C desired, memory_order order = memory_order_seq_cst);

备注:弱比较和交换 操作可能会虚假失败,即 是,在离开时返回 false 指向的内存内容 预计手术前是 与对象相同 和预期之后的一样 操作。 [注:这个是假的 失败使实施 在更广泛的范围内进行比较和交换 机器类,例如,负载锁定 存储条件机器。一种 虚假失败的后果是 几乎所有使用弱的 比较和交换将在 循环。

那么,这是什么意思?

首先,它“可能”虚假失败?!为什么会失败?他们如何定义“可能”?

其次,我仍然不知道带有“_strong”和“_weak”后缀的函数有什么区别。有人能解释一下区别吗?

编辑: 这就是我在 libstdc++-implementation (atomic_0.h) 中发现的:

bool compare_exchange_weak(
    __integral_type& __i1,
    __integral_type __i2,
    memory_order __m1,
    memory_order __m2
)
{
    __glibcxx_assert(__m2 != memory_order_release);
    __glibcxx_assert(__m2 != memory_order_acq_rel);
    __glibcxx_assert(__m2 <= __m1);
    return _ATOMIC_CMPEXCHNG_(this, &__i1, __i2, __m1);
}

bool compare_exchange_strong(
    __integral_type& __i1,
    __integral_type __i2,
    memory_order __m1,
    memory_order __m2
)
{
    __glibcxx_assert(__m2 != memory_order_release);
    __glibcxx_assert(__m2 != memory_order_acq_rel);
    __glibcxx_assert(__m2 <= __m1);
    return _ATOMIC_CMPEXCHNG_(this, &__i1, __i2, __m1);
}

【问题讨论】:

  • 我添加了 STL 标签,希望它能把 Howard Hinnant 带到那里,他一直在努力在 libc++ 中实现这些,所以他应该知道。

标签: c++ stl c++11 atomic c++-standard-library


【解决方案1】:

注释提供了一个线索,指的是LL/SC架构。来自维基百科的文章:

如果发生任何更新,则保证条件存储失败,即使加载链接读取的值已经恢复。因此,LL/SC 对比读取后跟比较和交换 (CAS) 更强,如果旧值已恢复,CAS 将不会检测更新(请参阅 ABA 问题)。

如果有问题的内存位置没有并发更新,LL/SC 的实际实现并不总是成功。两个操作之间的任何异常事件,例如上下文切换、另一个加载链接,甚至(在许多平台上)另一个加载或存储操作,都会导致条件存储虚假失败。

在 LL/SC 芯片上,compare_exchange 将以 LL/SC 的形式实现,这可能会虚假失败,因此compare_exchange_strong 需要额外的开销来在失败的情况下重试。同时提供compare_exchange_strongcompare_exchange_weak 允许程序员决定他们是希望库处理虚假故障(在这种情况下他们会使用compare_exchange_strong)还是他们想在自己的代码中处理它(在这种情况下他们会使用compare_exchange_weak)

【讨论】:

  • 不错的答案。要讨论的一件事是关于“失败时重试”,应该是“spurious失败的情况下重试”吗?如果由于争用(dest 值不等于预期)而导致真正失败,compare_exchange_strong 应立即返回false,无需任何重试。你同意吗?
  • 我发布了一个关于 compare_exchange_weak here 的问题。您介意看看并与我们分享您所知道的一些知识吗?谢谢。
  • 是的,强表单在虚假失败的情况下重试,而不是在对象没有预期值时重试。
  • 我是从stackoverflow.com/a/16190791 来到这里的。这是实现 atomic_max 函数的问题。我认为这是使用_weak 案例的一个很好的例子。最大值可能由于虚假故障而无法更新,或者由于另一个线程也更新了该值。但是,如果这两种情况您都想做同样的事情:尝试再次计算最大值。在某些机器上使用_strong 效率会很低,因为您并不真正关心虚假的失败案例。
  • @CygnusX1 是的,这在 C++ 标准中由注释的其余部分覆盖,OP 在引用中省略:“当比较和交换处于循环中时,弱版本将在某些平台上产生更好的性能。当弱比较和交换需要循环而强循环不需要时,强循环更可取。推论是,当你无论如何都要循环时,没有理由更喜欢强循环。
【解决方案2】:

这与硬件实现的共享内存一致性模型有关。对于那些实现某种宽松一致性模型(例如发布语义)的硬件架构,您上面提到的强操作可能会有很高的开销,因此专家可以使用较弱的形式来实现在那些宽松一致性上也表现良好的算法架构。

有关详细信息,请参阅例如

http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf

http://kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html中的第12章和附录C

【讨论】:

  • 但是,加强或削弱记忆排序模型不就是memory_order参数的意义吗?
  • 否,由硬件指定。该参数指定您的算法正确运行所需的最小排序。然后是 C++0x 实现,以确保至少提供指定的顺序;对于像 x86 这样的强排序架构,强和弱实现很可能是相同的。
  • 对不起,但这对我来说仍然没有意义。通过 memory_order 参数和强或弱方法指定排序不是多余的吗?也许了解弱和强 compare_exchange 之间的主要(抽象)区别是什么会有所帮助。
  • @FrEEzE:区别正是标准所说的——弱版本允许偶尔失败,如果这使它在你的硬件上运行得更快。不适用于 x86。
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-08
相关资源
最近更新 更多