【问题标题】:Use constructor in place of atomic.store() when atomicity is not currently needed当当前不需要原子性时,使用构造函数代替 atomic.store()
【发布时间】:2018-05-02 08:50:48
【问题描述】:

我使用std::atomic 来表示原子性。尽管如此,在代码的某个地方,程序逻辑并不需要原子性。在这种情况下,我想知道是否可以,无论是迂腐还是实际,使用构造函数代替 store() 作为优化。例如,

// p.store(nullptr, std::memory_order_relaxed);
new(p) std::atomic<node*>(nullptr);

【问题讨论】:

  • 优化如何?在这部分代码中使用其他变量,然后在完成后将结果存储在p 中。
  • @BoPersson 我认为普通存储可能比原子存储更有效。就我而言,我需要将一个值存储到原子中。根据程序逻辑,在此期间没有其他线程会读/写这个原子。因此,一个普通的商店就足够了。
  • 这听起来像是对未来维护者(包括您自己)的陷阱。
  • @PeteBecker:最隐蔽的陷阱之一

标签: c++ optimization atomic c++17 c++-standard-library


【解决方案1】:

是否符合标准,这完全取决于std::atomic&lt;T&gt;的实现。如果该T 是无锁的,那么实现可能只存储T。如果它不是无锁的,事情就会变得更加复杂,因为它可能会存储 mutex 或其他一些东西。

问题是,你不知道std::atomic&lt;T&gt; 存储了什么。这很重要,因为如果它存储了const-qualified 对象或引用类型,那么在此处重用存储将导致问题。 Placement-new返回的指针当然可以使用,但是如果使用const或引用类型,则原来的对象名p不能

为什么std::atomic&lt;T&gt; 会存储const 或引用类型?谁知道;我的观点是,因为它的实现不在你的控制之下,所以你无法知道任何特定实现的行为方式。

至于“实际上”,这不太可能导致问题。特别是如果atomic&lt;T&gt; 始终是无锁的。

话虽如此,“实际上”还应包括其他用户将如何解释此代码的一些概念。虽然在重复使用存储等方面经验丰富的人能够理解代码在做什么,但他们可能会对为什么你这样做感到困惑。这意味着您需要在该行上添加评论或创建(模板)函数non_atomic_reset

另外,应该注意std::shared_ptr 对其引用计数器使用原子增量/减量。我提出这个问题是因为没有不使用原子的std::single_threaded_shared_ptr,或者不使用原子的特殊构造函数。因此,即使您在纯单线程代码中使用shared_ptr,这些原子仍在触发。 C++ 标准委员会认为这是一个合理的权衡。

Atomics 并不便宜,但也没有那么昂贵(大多数时候),使用像这样的不寻常机制绕过 atomic 存储是一个好主意。和往常一样,分析一下代码混淆是否值得。

【讨论】:

  • 说实话,这种权衡(没有std::single_threaded_shared_ptr 或类似的东西)有点值得怀疑
  • ^^ 它与“不要为不使用的东西付费”的座右铭相冲突
  • @AndriyTylychko:是吗?您多久在热循环中复制一次shared_ptr?请记住:复制shared_ptr 表示向一块内存添加一个额外的共享所有者。这是否经常发生在性能关键代码中?
  • 在极少数情况下我们应该怎么做?
  • @AndriyTylychko:好吧,如果你的代码对性能至关重要,需要共享内存所有权,那么你可能已经在设计级别做错了,应该重构这样操作就没有必要了。这种重构可能会发现设计固有的其他低效率。万一真的没有办法避免它……这只是两个原子增量。无锁原子不是世界上最便宜的东西,但它们不会完全破坏你的表现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-10
  • 2021-05-30
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
  • 2012-10-03
  • 2013-02-12
相关资源
最近更新 更多