负载是否可以在获取操作下方滑动/存储是否可以漂浮在C++释放上方?

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

本文关键字:是否 释放 C++ 漂浮 方滑动 操作 获取 负载 存储      更新时间:2023-10-16

TL/DR:获取/发布操作是否只允许 4 次重新排序中的 1 次(而不是 2 次(?如果是这样,为什么?

目前,根据我对获取发布语义的理解,(基本上(

  • 获取操作不允许其下方的负载/存储
  • 浮动在其上方
  • 释放操作不允许其上方的负载/存储滑入其下方

但关于相反方向的说法较少。

从一些来源(Jeff Preshing和其他人的博客,以及一些架构手册似乎暗示(我读到获取/释放操作相当于一个(给定内存位置上的原子操作+内存屏障(/(内存屏障+原子操作(。

他们描述了 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 的文章中所述:

请注意,这些障碍在技术上比 在单个内存上获取和发布语义需要什么 操作,但它们确实达到了预期的效果。

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

是的,这是我演示文稿中的一个错误(单词多于实际 幻灯片(。这个例子很好,但我应该修复"如果 这是一个释放围栏。特别:

从 1:10:30 开始,我说发布围栏存在正确性问题是不正确的,因为它允许商店漂浮起来(它确实如此( 不是,如前所述,该规则在 29.8.2 中;谢谢!– 我应该拥有的 说的是,这仍然是一个性能悲观,因为 围栏与该预期存储无关,但由于我们没有 知道它必须悲观地应用于所有以下商店 随后的普通存储,直到下一个适用的特殊内存 操作同步点 – 它经常将它们全部推低 不需要

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