在 C++ 中,加载是否可以低于获取操作/存储是否可以浮动高于释放?

Can loads slip beneath an acquire operation / can stores float above a release in C++?

TL/DR:acquire/release 操作只允许 4 次重新排序中的 1 次(而不是 2 次)是真的吗?如果是,为什么?

目前我对获取-释放语义的理解是(基本上)

但反方向的说法较少

从一些来源(Jeff Preshing 和其他人的博客,以及一些体系结构手册似乎暗示)我读到 acquire/release 操作等同于(原子操作 + 给定内存位置上的内存屏障) /(memory barrier + atomic op) 对应。

他们描述了 4 种内存障碍,例如获取操作使用类似于 LoadLoad + LoadStore 的屏障(对于释放也类似)。

据我了解,这些障碍(LoadLoad + LoadStore 和 StoreStore + LoadStore 相应)只允许:

并且负载不能滑到获取下方/商店不能浮在释放上方。

这通常是正确的吗?这对 C++ 来说正确吗? C++ 与一般含义不同吗?

(因为例如 回答说负载可以滑到收购之下(据我所知)。我也有几个消息来源说任何东西都可以滑到收购之下(反之亦然) ))

如果那是正确的,那么这样做的理由是什么?我试图想出类似(发布)的东西:

x.store(5, std::memory_order_release);
y.store(true, std::memory_order_relaxed);

不同的线程以不同的顺序读取它们将是一件坏事,考虑到它用于双重检查锁定等模式。

这接近于一个原因吗?如果可以,有人可以提供有关获取和发布的可靠示例吗?

虽然存储在获取下方滑动/加载在发布上方浮动,但(可能)没有这样的缺点...

内存屏障可用于实现加载-获取和存储-释放语义,但它们提供的保证比要求的更严格,如 Jeff Preshing's article:

中所述

Please note that these barriers are technically more strict than what’s required for acquire and release semantics on a single memory operation, but they do achieve the desired effect.

如果在加载获取和后续内存操作之间放置一个 LoadLoad + LoadStore 屏障,则程序顺序中屏障之前的所有加载都不能在屏障之后重新排序,并且所有以后的内存访问都不能重新排序在屏障之前。这比为特定加载操作实现获取语义所需的更严格,因为屏障对所有先前的加载进行排序,而不仅仅是需要获取语义的特定加载。所以它们并不完全等价。商店发布语义也是如此。 Herb Stutter 对此发表了评论:

Yes, this is a bug in my presentation (the words more than the actual slide). The example is fine but I should fix the description of "if this was a release fence." In particular:

starting at 1:10:30, I was incorrect to say that a release fence has a correctness problem because it allows stores to float up (it does not, as noted the rule is in 29.8.2; thanks!) – what I should have said was that it’s a still a performance pessimization because the fence is not associated with THAT intended store, but since we don’t know which following store it has to pessimistically apply to ALL ensuing ordinary stores until the next applicable special memory operation synchronization point – it pushes them all down and often doesn’t need to

根据 LoadLoad、LoadStore 和 StoreStore 屏障实现加载获取和存储释放语义的原因是因为 ISA 仅提供此类屏障。有针对更灵活或可配置的障碍的研究建议,这些障碍可能仅适用于特定的内存操作或指令范围或指令块,但它们尚未进入任何 ISA。