C++11 原子:std::memory_order 代码可移植吗?

C++11 atomic: is std::memory_order code portable?

std::atomic::compare_exchange 等函数中,有 std::memory_order_releasestd::memory_order_relaxed 等运行时参数。 (http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange)

我不确定这些内存顺序标志是否保证存在于各种CPU/architects中,如果某些CPU不支持标志,这个标志会导致崩溃还是?似乎其中一些标志是为英特尔安腾等设计的,不确定 std::memory_order 相关代码是否可移植。

你能给点建议吗?

一般来说,无论您要求什么,编译器都可以自由地只提供最强内存保证。

在某些平台上,有足够的宽松保证。并非所有平台都支持这些宽松的保证。在这些平台上,编译器必须提供更严格的保证。

因此它们是可移植的,因为当您要求特定保证时,符合规范的编译器必须提供或更好的保证。

请注意,关注的不仅仅是硬件。某些优化和重新排序可能合法或不合法,具体取决于您要求的内存顺序保证。我不知道有任何依赖它的编译器,但我不是编译器专家。

C++ 标准确实有 so-called "freestanding" 实现的概念,它可以支持标准库的一个子集。但是,它还定义了 bare-minimum 功能,即使是独立的实现也必须支持这些功能。在该列表中是 <atomic> header 的 entirety

所以是的,实现必须支持这些。

但是,这并不意味着特定标志将完全且仅执行该标志所描述的内容。标志代表最小内存屏障,保证可见的特定事物。如果硬件实现没有 lower-tier 内存屏障,即使对于不需要它的标志,该实现也可以发出完整的内存屏障。

所以你应该按照标准写代码,让编译器整理细节。如果证明它在平台上效率低下,您可以检查程序集以查看是否可以改进问题。

但要回答您的主要问题,是的,atomic-based 代码是可移植的(模编译器错误)。

在实践中,消费语义总是被当前的编译器加强以获取,因为如果不这样做,它很难安全地实现。或者:

  • 该体系结构提供对所有负载的获取,然后负载消耗与负载获取执行相同的操作,而负载获取与负载放松的操作相同:没有什么特别的(如 x86);
  • 该架构甚至在依赖访问时也需要获取屏障(非常非常罕见,可能只有 DEC Alpha):然后编译器将在消费时使用获取屏障;
  • ISA保证了asm加载的依赖顺序,但是full acquire需要一个barrier。 (这就是 consume 试图向程序员公开的内容)。编译器应该为您提供避免逻辑(不是疯狂)使用 consume

    障碍的好处
    • 要么是编译器尝试这样做,但它很棘手,并且在编译器后端优化中断的某些极端情况下会失败(前端通常不会与其后端进行足够的通信以避免这些只是为了消费);
    • 或者你不信任编译器,将优化设置为零,这甚至不能保证后端隐式完成的微不足道的优化不会破坏消耗(并使性能非常糟糕);
    • 或者编译器编写者不关心效率,或者知道他们无法可靠地提供 consume ,所以他们提供 acquire 代替,这在语义上是正确的,但实际上效率较低,而不是标准的意图。

(而且 C++ 的消费语义很疯狂。它是 C++ 中最不一致的部分之一,告诉你很多。)