C++11 原子:std::memory_order 代码可移植吗?
C++11 atomic: is std::memory_order code portable?
在 std::atomic::compare_exchange
等函数中,有 std::memory_order_release
、std::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++ 中最不一致的部分之一,告诉你很多。)
在 std::atomic::compare_exchange
等函数中,有 std::memory_order_release
、std::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++ 中最不一致的部分之一,告诉你很多。)