【问题标题】:Can std::atomic cancel out increments with decrements?std::atomic 可以用减量抵消增量吗?
【发布时间】:2016-12-10 12:03:21
【问题描述】:

使用relaxed memory order,例如对于引用计数指针,是否允许编译器优化后续的增量和减量?

std::atomic_int32_t ai;
for (size_t i = 0; i < 10000; i++)
{
    ai.fetch_add(1, std::memory_order_relaxed);
    ai.fetch_sub(1, std::memory_order_relaxed);
}

看反汇编它看起来不像。但是由于允许重新排序并且 atomic 的行为类似于计数器,只是线程安全的,因此有人可能会争辩说他可以像普通 int 一样进行优化。

【问题讨论】:

  • 如果您发现它没有优化,您是否已经有了答案?
  • @rustyx:这不是它的工作原理。仅仅因为 OP 的实现这次没有对其进行优化,并不意味着禁止这样的优化。
  • 试图回答这个问题,但我对第 29.3 节中的术语不够熟悉。期待答案!
  • 我认为编译器可以或可以优化这一点,除非您引入显式内存防护。但同样,我不会完全肯定地说任何关于原子的事情:)
  • 也许这个链接会提供更多的见解。 playingwithpointers.com/ipo-and-derefinement.html

标签: c++ multithreading c++11 atomic compiler-optimization


【解决方案1】:

我相信它可以被优化,除非声明为 volatile。原因是对于任何在其间交错一些线程的调度,都存在不存在的有效调度。我相信 drf-sc 内存模型也是如此。

如果此线程在两者之间读取某些内容,则情况并非如此。

【讨论】:

  • 我同意。此外,通过宽松的内存排序,无法保证修改何时对其他线程可见,并且您无法证明您不会只是从未抓住两次更改之间的时刻。如果有aquire-release语义或其他同步,那就另当别论了。
  • @Revolver_Ocelot:即使获取-释放也不够。有一个微不足道的线程调度,所有 10.000 增量和 10.000 减量都发生在该线程上,然后任何其他线程运行一条指令。 (这甚至是单核处理器上的现实线程调度)
【解决方案2】:

编译器无法优化掉原子,因为这会违反它们的用途。它必须假设另一个线程也可能触及该值,因此不允许删除。

它也无法优化/重新排序从之前到之后(以及其他方式)对 C++ 代码“可见”的内容,因为原子是内存屏障。

【讨论】:

  • 不,您将它们与volatile 混淆了。那些是不可移动的。至于内存屏障,请注意这两个操作是背靠背的。也就是说,中间没有同步点,保证另一个线程能够看到或触摸到中间结果。为了在线程 Y 中看到线程 X 的中间结果,线程 Y 必须在 X 中的中间计算之后排序,但在 X 中的覆盖之前排序。
猜你喜欢
  • 2013-04-03
  • 2021-05-17
  • 1970-01-01
  • 2014-02-28
  • 2015-06-26
  • 2011-04-27
  • 2011-12-15
  • 1970-01-01
相关资源
最近更新 更多