std::atomic 与非原子变量的性能如何?

Ada*_*ark 4 c++ performance multithreading atomic thread-safety

我很好奇在应用程序中使用std::atomic<float>vs 正常的性能float。我也很好奇是什么影响了这个。我经常看到有关原子与互斥锁性能的主题,但我发现很难找到有关原子与非原子的信息。

我不是用它来选择使我的代码线程安全与否,只是想了解所涉及的开销。

(编辑:在原始问题的这一点上,我给出了一个示例(见下文),该示例应该说明实现的更改,而不是询问有关该代码的具体问题。这似乎让人们对我问的问题感到困惑我已经拿出来了。)

我基本上想知道影响 std::atomic 性能的广泛因素是什么。是平台吗?它们的使用方式?使用由两个线程访问大致相同数量的原子,而不是一个线程在 95% 的时间内访问它们而另一个线程仅偶尔访问它们是否更慢?

另外,在这方面astd::atomic<int>和a之间有什么区别std::atomic<float>吗?

提前致谢,

亚当


来自原始问题的示例:

基本上,我尝试制作一百万个浮点数并向它们写入值 200 次。这对我来说花了 0.87 秒。一旦我将它们更改为std::atomic<float>,这大约需要 2.5 秒。所以这意味着它的使用成本大约是 3 倍std::atomic<float>

我试过这个,但为了读取值而不是写入,发现这是一个正常的float并且std::atomic<float>花费相同的时间。

但这是否受到其他因素的影响?如果另一个线程正在写入/读取我的原子,这是否会减慢其他对同一变量的读取/写入速度?大概是这样,但我怎样才能更好地理解这一点?

LWi*_*sey 5

没有排序参数(即默认值)的原子存储是昂贵的,因为编译器会发出额外的排序指令。On X86,浮点数的默认(顺序一致)原子存储如下所示:

atomic<float> f;
f.store(3.14);
Run Code Online (Sandbox Code Playgroud)

gcc 产生以下指令:

0x00000000004006d0 <+0>:     movl   $0x4048f5c3,0x20096a(%rip)        # 0x601044 <f>
0x00000000004006da <+10>:    mfence
Run Code Online (Sandbox Code Playgroud)

mence指令是昂贵的,因为它确保对其他内核的直接可见性(即导致存储缓冲区被刷新)。

您可以尝试在不订购的情况下运行测试:

f.store(3.14, std::memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)

这将摆脱mfence并可能显示性能的显着差异。它更接近于非原子商店,如果在某些平台上不相等。

在这方面,astd::atomic<int>和 a之间有什么区别std::atomic<float>吗?

假设两者都是无锁的,可能不是。排序约束是导致性能下降的原因。

  • 这在单个实例中是正确的 - 但在更大的应用程序中,您可以选择多线程和单线程解决方案,并且了解与用于多线程的工具相关的开销让您确定哪种方法是最佳方法。 (7认同)
  • 如果没有争用,就没有理由使用原子。如果存在争用,则无法将原子与非原子进行比较,因为非原子不起作用。所以比较“性能”是没有意义的。 (6认同)
  • 考虑原子成本是完全合理的,例如为了推理算法是否值得并行化。但我想表现得高调并告诉OP为什么他的问题(据说)是荒谬的会更容易。遗憾的是,如今这种情况在 SO 上太常见了。 (5认同)
  • 但如果你想从A点到B点,并且它们之间既有湖又有路。您可以选择乘船过湖或开车上路。你想走最快的路。在这种情况下比较它们的速度确实是有意义的。 (3认同)
  • 当然,询问原子指令对性能的影响是有意义的。 (2认同)
  • 如果您正在设计多线程代码并且有多个选项用于设计对浮点数的并发访问,那么这绝对是一个好问题。(例如原子与中间队列或缓冲区)。 (2认同)