gpt4 book ai didi

Reorder relaxed atomic operations on the same object(对同一对象上的松散原子操作进行重新排序)

转载 作者:bug小助手 更新时间:2023-10-25 17:50:35 26 4
gpt4 key购买 nike



http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync

Http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync


Assuming x is initially 0:

假设x最初为0:


-Thread 1-
x.store (1, memory_order_relaxed)
x.store (2, memory_order_relaxed)

-Thread 2-
y = x.load (memory_order_relaxed)
z = x.load (memory_order_relaxed)
assert (y <= z)

The assert cannot fail.

断言不能失败。


I don't understand why the two loads cannot be reordered so that z is read before y which can give z = 1 and y = 2, and so y <= z fails.

我不明白为什么不能对这两个加载重新排序,以便在y之前读取z,这可以得到z=1和y=2,所以y<=z失败。


更多回答

Maybe you are confusing relaxed ops on C/C++ atomics with regular (= non volatile) field ops in Java? Relaxed is way stronger than non volatile Java fields.

也许您混淆了C/C++原子上的宽松操作和Java中的常规(=非易失性)字段操作?RELAX比非易失性Java字段强大得多。

@curiousguy Not any Java here. It's mainly about C++ memory models.

@好奇的家伙,这里没有爪哇人。它主要是关于C++内存模型的。

What do you believe I meant? My whole point is that C/C++ is different.

你认为我的意思是什么?我的全部观点是,C/C++是不同的。

优秀答案推荐

Operations on the same object by the same thread be reordered; the modification-order for each object separately is some interleaving of program-order across threads.

同一线程对同一对象的操作被重新排序;每个对象单独的修改顺序是跨线程的程序顺序的某种交错。


Real hardware does that for free, so C++ makes that guarantee available for free. (And doesn't allow compile-time reordering which could leave the long-term value wrong once the dust settles.)

真正的硬件是免费做这件事的,所以C++保证是免费的。(并且不允许编译时重新排序,一旦尘埃落定,这可能会使长期值出错。)


If the later store was temporarily visible first, the value would have to change twice to have the right final value. Again, real hardware isn't that weak / crazy, thanks to cache coherency. http://eel.is/c++draft/intro.races#19

如果后面的存储首先暂时可见,则该值必须更改两次才能获得正确的最终值。同样,由于高速缓存的一致性,真正的硬件并不是那么弱/疯狂。Http://eel.is/c++draft/intro.races#19



Standard Perspective


From a C++ standard perspective, the reason is that every atomic object has a modification order, which a total order in which all modifications occur.
The two statements ...

从C++标准的角度来看,原因是每个原子对象都有一个修改顺序,这是所有修改发生的总顺序。这两个声明…



x.store (1, memory_order_relaxed)
x.store (2, memory_order_relaxed)


... are sequenced. Namely store(1) is sequenced before store(2), and the two load()s on Thread 2 are also sequenced.
They are sequenced because they are in separate statements, and std::memory_order::relaxed doesn't affect sequencing within one thread.

..。都被测序了。也就是说,存储(1)在存储(2)之前被排序,并且线程2上的两个Load()S也被排序。对它们进行排序是因为它们位于不同的语句中,而std::Memory_Order::Relaced不会影响一个线程内的排序。


The modification order consists of at least:

修改令至少包括:



  1. initialization of x

  2. x.store(1)

  3. x.store(2)


std::memory_order_relaxed doesn't break these rules. It doesn't allow another thread to "time travel" and first see 3., forget about it, and then see 2.

STD::MEMORY_ORDER_RELAX没有违反这些规则。它不允许另一个线程“时间旅行”,首先看到3。忘记它,然后看到2。



If a value computation A of an atomic object M happens before a value computation B of M, and A takes its value from a side effect X on M, then the value computed by B shall either be the value stored by X or the value stored by a side effect Y on M, where Y follows X in the modification order of M.



- [intro.races] p16

-[Intro.race]p16


In your case, the value computation load() (first) happens before load() (second), so load() (second) either needs to take its value from the same side effect as load() (first), or from a later side effect.

在您的例子中,值计算Load()(First)发生在Load()(Second)之前,因此Load()(Second)需要从与Load()(First)相同的副作用中获取它的值,或者从后面的副作用中获取值。


std::memory_order_relaxed means that Thread 2 can selectively see 1., 2., and 3. (and an arbitrary combination of these) when it calls x.load(), but it cannot see 3. and then ignore it.

STD::MEMORY_ORDER_RELAX意味着当线程2调用x.load()时,它可以选择性地看到1.、2.和3.(以及它们的任意组合),但它不能看到3.然后忽略它。


Hardware Perspective


As @PeterCordes has already pointed out, even though the two stores can't be reordered by the compiler, hardware reordering can still take place.
However, any reordering that is done by the CPU cannot change the semantics of the assembly that the compiler has emitted.

正如@PeterCordes已经指出的,即使编译器不能对这两个存储进行重新排序,硬件重新排序仍然可以发生。但是,CPU执行的任何重新排序都不能更改编译器发出的程序集的语义。



A C++ compiler is not permitted to reorder operations in a way that would change the observable behaviour of a program.

C++编译器不允许以会改变程序可观察行为的方式重新排序操作。


However, when using atomic variables, you may observe results that appear to be caused by "reordering". In actual fact, what is happening is that not all threads will see side effects become visible in the same order. This is illustrated by the following well-known example:

然而,在使用原子变量时,您可能会观察到似乎是由“重新排序”引起的结果。事实上,并不是所有的线程都会以相同的顺序看到副作用。下面这个众所周知的例子说明了这一点:


std::atomic<int> x;
std::atomic<int> y;
// thread 1
x.store(1, memory_order_relaxed);
y.store(1, memory_order_relaxed);
// thread 2
if (y.load(memory_order_relaxed) == 1) {
assert(x.load(memory_order_relaxed) == 1); // can fail
}

It is possible that at some point in time thread 2 might observe the result of the side effect on y (i.e. that its value has been changed to 1) yet still observe x as if the side effect had not happened yet. The assertion may fail for this reason—not because two stores somehow got reordered.

在某个时间点,线程2可能观察到对y的副作用的结果(即,它的值已被改变为1),但仍然观察x,好像还没有发生副作用一样。这一断言可能因为这个原因而失败--而不是因为两家商店不知何故被重新排序了。


Again, when two different atomic variables are written to, then two threads might not see the results of those two writes become visible in the same order.

同样,当写入两个不同的原子变量时,两个线程可能看不到这两个写入的结果以相同的顺序可见。


However, once a write to one single atomic variable has become visible to a particular thread, any subsequent reads from that variable will also behave as if that write is visible—meaning that all previous writes to that variable have been superseded, and the old value will not be observed. In the OP's example, if y is set to 2, then it means that the side effect that changed x's value from 1 to 2 has already become visible to thread 2.

但是,一旦对单个原子变量的写入对特定线程可见,从该变量的任何后续读取也将表现为该写入是可见的-这意味着先前对该变量的所有写入都已被取代,并且将不会观察到旧值。在OP的示例中,如果y设置为2,则意味着将x的值从1更改为2的副作用已经对线程2可见。


更多回答

Thanks "even if both operations are relaxed loads" is the key

感谢“即使两次手术都是轻松的负荷”是关键

"any reordering that is done by the CPU cannot change the semantics of the assembly that the compiler has emitted" doesn't sound as a conclusive argument on multi CPU systems

CPU进行的任何重新排序都不能更改编译器发出的程序集的语义。在多CPU系统上,这听起来不是一个决定性的论点

@curiousguy the argument is simply that if a single/multi-CPU system allowed 3. to be unseen and 2. to become the observed side effect later, this would not be a valid implementation of C++. That's not something the developer has to worry about.

@CuriousGuy这个论点很简单,如果单/多CPU系统允许3.看不见,2.成为后来观察到的副作用,这将不是C++的有效实现。这不是开发人员必须担心的事情。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com