考虑使用SC atomics进行的存储缓冲石蕊测试:
// Initial
std::atomic<int> x(0), y(0);
// Thread 1 // Thread 2
x.store(1); y.store(1);
auto r1 = y.load(); auto r2 = x.load();
这个程序能以r1
r2
和r2
为0结束吗?
我看不出memory_order_seq_cst
在优先选择中的描述是如何禁止这个结果的
具有此内存顺序的加载操作执行获取操作,存储执行释放操作,读-修改-写同时执行获取操作和释放操作,再加上一个单独的总顺序,其中所有线程都以相同的顺序观察所有修改。
在我看来,memory_order_seq_cst
只是在收购-发布,外加一个全球商店订单。我不认为全球商店的订单在这个特定的试金石测试中起作用。
发布于 2021-12-02 23:35:34
SC的偏好摘要的太弱了,确实不够强,不足以禁止这种重新排序。
在我看来,它说的只有x86-TSO (acq_rel +没有IRIW重新排序,即所有读者线程都能同意的总的存储订单)。
ISO C++实际上保证了所有SC操作的总顺序(),包括负载、(以及SC栅栏),这与程序顺序一致。(这基本上是计算机科学中顺序一致性的标准定义;C++程序只使用seq_cst原子操作,它们的非原子访问是无数据竞争的,它们的非原子访问连续执行,即“恢复顺序一致性”,尽管非原子访问允许完全优化。)顺序一致性必须禁止在同一线程中的任何两个SC操作之间的任何重新排序,甚至禁止StoreLoad重排序。
这意味着在每个seq_cst存储之后都会有一个昂贵的全障碍(包括seq_cst),或者例如,AArch64 STLR / LDAR不能彼此重新订购,但在其他情况下只能发布和获取wrt。与其他操作重新排序。(因此,如果以后没有在同一个线程中执行任何SC加载或RMW操作,那么AArch64上的缓存命中SC存储可能比x86便宜得多。)
请参阅https://eel.is/c++draft/atomics.order#4,它清楚地表明SC操作没有重新排序wrt。彼此。目前的标准草案说:
31.4 atomics.order
memory_order::seq_cst
memory_order::seq_cst
操作(包括栅栏)上都有一个单一的总阶S,满足以下约束。首先,如果A和B是memory_order::seq_cst
运算,而A强发生在B之前,则A在S中先于B。其次,对于对象M上的每一对原子运算A和B,其中A是一致性-在B之前排序,S需要满足以下四个条件:
前面的排序意味着之前会发生强烈的情况,所以开头的段落保证S与程序顺序一致。
4.1是指在彼此之前/之后是相干有序的操作。也就是说,碰巧从存储中看到值的负载。这将线程间可见性与总订单S联系起来,使其与程序顺序相匹配。这两种需求的结合迫使编译器使用完整的屏障(包括StoreLoad)从它瞄准的任何较弱的硬件模型中恢复顺序一致性。
(在原文中,所有4.都是一段。我把它分开来强调,这里有两种不同的东西,一种是为了强-发生-之前和行动/障碍的一致性-有序-之前。)
这些保证,加上以前发生的同步,足以恢复整个程序的顺序一致性,如果它是无数据竞争的(这将是UB),如果您不使用任何较弱的内存顺序。
如果程序涉及较弱的订单,这些规则仍然有效,但例如,两个relaxed
操作之间的SC围栏不如两个SC负载强。例如,关于不排除IRIW重新排序的PowerPC只使用SC操作;IIRC PowerPC在加载SC之前和之后都需要设置屏障。
因此,拥有一些SC操作并不一定足以在任何地方恢复顺序一致性;这正是使用较弱操作的意义所在,但其他操作可以重新排序wrt,这可能有点令人惊讶。军控行动。特种部队不是警戒线。还请参阅这是一个具有相同“存储缓冲区”测试的例子的问答。:将一个商店从seq_cst
削弱为release
允许重新排序。
https://stackoverflow.com/questions/70204442
复制