gpt4 book ai didi

c++ - 在 C++ 中使用内存屏障防止凭空值

转载 作者:行者123 更新时间:2023-11-30 04:59:24 26 4
gpt4 key购买 nike

让我们考虑以下 C++ 中的双线程并发程序:

x,y 是全局变量,r1,r2 是线程局部变量,storeloadint 是原子的。内存模型 = C++11

int x = 0, int y = 0
r1 = x | r2 = y
y = r1 | x = r2

允许编译器将其编译为:

int x = 0, int y = 0
r1 = x | r2 = 42
y = r1 | x = r2
| if(y != 42)
| x = r2 = y

而且,虽然它是线程内一致的,但它可能会导致错误的结果,因为该程序的执行可能导致 (x, y) = (42, 42)

这叫做 Out of Thin Air 值问题。它存在,我们必须忍受它。

我的问题是:内存屏障是否会阻止编译器进行导致凭空值的疯狂优化?

例如:

[fence] = atomic_thread_fence(memory_order_seq_cst);

int x = 0, int y = 0
r1 = x | r2 = y
[fence] | [fence]
y = r1 | x = r2

最佳答案

相关:我在 What formally guarantees that non-atomic variables can't see out-of-thin-air values and create a data race like atomic relaxed theoretically can? 上的回答更详细地解释了 C++ 宽松原子内存模型的正式规则不排除“凭空”值。但他们确实在注释中排除了它们。 这个问题仅适用于使用 mo_relaxed 的程序的形式验证,而不适用于实际实现。即使是非原子变量也是安全的,if你避免了未定义的行为(你没有在这个问题的代码中)。


xy 上存在数据竞争未定义行为,因为它们是非原子 变量,因此 C++11 标准对于允许发生的事情绝对无话可说。

对于没有正式内存模型的旧语言标准,人们会使用 volatile 或普通 int 和编译器 + asm 障碍进行线程处理,这将是相关的,在这种情况下,行为可能取决于编译器以您期望的方式工作。但幸运的是,“恰好在当前实现上工作”线程的糟糕日子已经过去了。


障碍在这里没有帮助,因为没有任何东西可以创建同步;正如@davmac 所解释的那样,在全局操作顺序中,没有什么需要障碍来“排队”。 将屏障视为使当前线程等待其先前的部分或全部操作变为全局可见的操作;屏障不直接与其他线程交互。


凭空值是一种可能由于未定义行为而发生的事情;允许编译器对非原子变量进行软件值预测,并发明写入无论如何肯定会写入的对象。如果有一个发布存储,或者一个宽松的存储 + 一个屏障,编译器可能不允许在它之前发明写入,因为那可以创建

一般来说,从 C++11 语言律师的角度来看,您无法采取任何措施来确保您的程序安全(除了互斥锁或使用原子的手动锁定以防止一个线程读取 x 而另一个正在编写它。)


宽松的原子性足以防止编译器在没有任何其他成本的情况下发明写入。

如果您指望这个变量的其他用途被积极优化,除了可能击败自动矢量化和其他东西。

atomic_int x = 0, y = 0
r1 = x.load(mo_relaxed) | r2 = y.load(mo_relaxed)
y.store(r1, mo_relaxed) | x.store(r2, mo_relaxed)

在线程 2 从 y 中看到该值之前,值预测可以推测性地将 r2 的 future 值放入管道中,但它实际上对其他线程不可见线程,直到软件或硬件确定预测是正确的。 (那将是发明一种写法)。

例如允许线程 2 编译为

r2 = y.load(mo_relaxed);
if (r2 == 42) { // control dependency, not a data dependency
x.store(42, mo_relaxed);
} else {
x.store(r2, mo_relaxed);
}

但正如我所说,x = 42; 在非推测性(硬件或软件推测)之前不能对其他线程可见,因此值预测无法发明其他线程的值可以看到。 C++11 标准保证原子性

我不知道/想不出在 y.load 看到实际 42。(即 LoadStore 使用后来的相关存储重新排序负载)。不过,我不认为 C++ 标准正式保证了这一点。如果编译器能够证明 r2 在某些情况下始终为 42,并且甚至删除控制依赖性,那么也许真正积极的线程间优化?

acquire-load 或 release-store 绝对足以阻止因果关系违规。这不完全是 mo_consume,因为 r2 用作值,而不是指针。

关于c++ - 在 C++ 中使用内存屏障防止凭空值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51232730/

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