【问题标题】:Are these allowed optimizations in C++? [duplicate]这些是 C++ 中允许的优化吗? [复制]
【发布时间】:2020-08-19 14:19:52
【问题描述】:

std::atomic<std::int64_t> num{0}; 定义在代码中可访问/可见的某个地方。 C++ 编译器是否允许将以下两个代码中的每一个替换为空代码(什么都不做)?同样,这些优化是否允许在运行时发生? 我只是想更好地了解事情的运作方式。

num.fetch_add(1,std::memory_order_relaxed);
num.fetch_sub(1,std::memory_order_relaxed);

num.fetch_add(1,std::memory_order_relaxed);
std::this_thread::yield();
num.fetch_sub(1,std::memory_order_relaxed);

【问题讨论】:

  • 运行时不做任何优化。它们都是由编译器完成的。
  • num.fetch_add(1,std::memory_order_relaxed); num.fetch_sub(1,std::memory_order_relaxed); 这不是 NOOP,因为其他人可以在操作之间访问它并对其进行处理。所以它不应该被 NOOP 取代。 Yielding 只是试图让其他线程继续执行。否则,在无限循环中读取/写入原子直到满足某些条件 - 可能会占用太多资源。
  • @ALX23z 实际上这个 is 可以用 NOOP 替换,因为它只使用 memory_order_relaxed。 JF Bastien 在N4455 No Sane Compiler Would Optimize Atomics 中提出了一个类似的例子。
  • 理论上这样的优化是可能的。在实践中,编译器似乎不会这样做:godbolt.org/z/Gg2Sub
  • 如果你有更强的内存顺序,它可能会阻止其他操作在 add/sub 对中重新排序,有点像atomic_thread_fence()。 (但请注意,栅栏比单一操作更强大)。但是在放松的情况下,编译器将被允许决定每个其他线程总是看到这些背靠背并取消的顺序。请参阅我在Can num++ be atomic for 'int num'? 上的答案底部以进行讨论。请注意,volatile atomic 肯定会阻止此优化。

标签: c++ multithreading c++11 stdatomic


【解决方案1】:

我认为理论上是的,甚至yield 也无济于事。

但实际上不是今天,而是将来可能。

见:

如果修改合并,可能会发生“运行时优化”。我不知道这种情况在实践中是否会发生。无论如何,它与“没有其他线程在更改回之前设法观察修改后的值”没有太大区别

实际上,优化效果相当于“没有其他线程在修改值变回之前设法观察到修改值”,无论是编译器优化,还是运行时。产量并不能保证有帮助,至少因为它只是“提供了重新安排的机会”,而实现可能会决定忽略它。而且理论上yield 和原子操作之间没有同步。

另一方面,您希望通过此实现什么目标?

【讨论】:

  • 我还想添加对N4455 No Sane Compiler Would Optimize Atomics 的引用,因为它包含更多关于可能优化的示例和详细信息——其中许多不仅是可能的,而且是鼓励的。所以我会打“实际上不”。即使今天的编译器不执行这些优化,也不能保证这不会随着新版本而改变。
  • @mpoeter:我认为编译器开发人员之间有一个默契,直到 C++ 发明了一种方法让程序员清楚地表达他们的意思,并且绝对防止编译器将宽松的进度条存储下沉到循环之外例如,编译器应该继续像 volatile 一样对待原子。
  • @PeterCordes 我理解 P0062R1 中讨论的问题,但我认为 N4455 中提出的一些案例仍然完全有效。 P0062R1 中的结论似乎建议我们应该标记应该优化的代码。但这意味着当前编写的代码将来可能会被优化,除非它被改编,所以我们至少应该注意可能的优化。
  • @mpoeter:是的,N4455 展示了一些可以从编译器优化中受益的清晰示例。我认为该机制可能是一些新的语法或类型属性,可以让新代码选择进行此类优化,因此现有代码不会有被它破坏的风险。即,似乎很多人都同意 ISO C++ 标准目前还不够强大,无法满足程序员希望atomic 在每种情况下都能做到的事情,尽管volatile atomic 加上不会在很长的循环中移动存储的理智编译器已经接近了。但是是的,编译器可能开始优化纯 atomic
  • 在 MSVC 中用于控制类似问题的大小/对齐 volatile 是否是获取/释放原子 /volatile:ms/volatile:iso 开关是否使用。编译器可能会通过 switch 或 #pragma 来执行此操作,而不是使用另一个属性。
猜你喜欢
  • 2019-11-22
  • 2020-06-09
  • 1970-01-01
  • 2019-04-21
  • 1970-01-01
  • 1970-01-01
  • 2019-08-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多