了解弱内存模型

时间:2020-08-24 20:16:04

标签: multithreading memory-barriers memory-model

假设我们有两个线程,它们在内存中使用两个变量AB

Thread 1       Thread 2
========       ========
1) A = 1       3) B = 1
2) Print(B)    4) Print(A)

我知道您在Sequential consistent (strong) model中会得到 1 -> 2 -> 3-> 4按顺序执行。 x86是TSO,它接近“强”模型(但不如一个强)。

我不知道“周”模式是什么?弱模型是否只是选择随机指令并执行?即可以使用4 -> 2 -> 3 -> 1之类的东西?

关于这个主题,我还有2个问题:

  • CPU使用Out-of-order execution完成的memory reordering due to memory model与可能浪费的指令周期之间有什么区别?或memory reordering仅处理Load/Store条指令?

  • memory model仅在处理多个线程时才有问题吗?为什么在单线程程序中没有问题?

1 个答案:

答案 0 :(得分:2)

顺序一致性并没有告诉您它将完全执行1,2,3,4。

顺序一致性告诉您CPU0正在执行1,2,而CPU1正在执行3,4; CPU将按该顺序执行这些块,并且不会感觉到2之前的副作用(内存存储);并且在3之前不会感觉到4的副作用。

如果是A=B=0之前的版本,则:

Thread 1       Thread 2
========       ========
1) A = 1       3) B = 1
2) Print(A,B)  4) Print(A,B)

所有顺序并发告诉我们可能的输出是:

Thread 1 { 1, 0 }, { 1, 1}
Thread 2 { 0, 1 }, { 1, 1}.

如果我们将其扩展到初始状态A=B=C=D=0

Thread 1       Thread 2
========       ========
A = 1          D = 1
C = 1          B = 1
Print(A,B,C,D) Print(A,B,C,D)

Thread1有效输出:

1: {1, 0, 1, 0}       -- no effects from thread2 seen
2: {1, 0, 1, 1}       -- update of D visible; not B
3: {1, 1, 1, 0}       -- update of B visible; not D
4: {1, 1, 1, 1}       -- update of B and D visible.

Thread2有效输出:

5: {0, 1, 0, 1}       -- no effects from thread1 seen
6: {0, 1, 1, 1}       -- update of C visible; not A
7: {1, 1, 0, 1}       -- update of A visible; not C
8: {1, 1, 1, 1}       -- update of A and C visible.

在顺序一致性中,1,2,4:5,6,8是可能的。 在较弱的一致性中,1,2,3,4:5,6,7,8是可能的。 请注意,在两种情况下,线程都无法按顺序看到自己的更新;但是输出3,7是线程看到其他线程无序更新的结果。

如果您需要维护特定的顺序,则插入 barrier指令 [1]是首选方法。当CPU遇到障碍时,它会影响预取(读取障碍),存储队列(写入障碍)或两者(rw障碍)。

有两次内存写操作:A = 1; C = 1;,您可以将写屏障安装为membar w; store A; store C。这样可以确保在存储到A或C之前,可以看到存储在A的所有之前;但不强制A和C之间的顺序。

您可以将它们安装为store A; membar w; store C,以确保在C之前可以看到A的存储;和store A; store C; membar w确保在任何后续存储之前都可以看到A和C。

那么哪种壁垒或壁垒组合最适合您的情况?

[1]更现代的体系结构将障碍纳入了加载并自己存储了指令;因此您可能有一个store.sc A; store C;。这样做的好处是可以限制存储屏障的范围,以便存储单元仅需序列化这些存储,而不必承受整个队列的延迟。

相关问题