问题描述
假设我们有两个线程,它们在内存中使用两个变量A
和B
:
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,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}.
如果我们将其扩展到初始状态A=B=C=D=0
Thread 1 Thread 2
======== ========
A = 1 D = 1
C = 1 B = 1
Print(A,B,C,D) Print(A,D)
Thread1有效输出:
1: {1,1,0} -- no effects from thread2 seen
2: {1,1} -- update of D visible; not B
3: {1,0} -- update of B visible; not D
4: {1,1} -- update of B and D visible.
Thread2有效输出:
5: {0,1} -- no effects from thread1 seen
6: {0,1} -- update of C visible; not A
7: {1,1} -- update of A visible; not C
8: {1,1} -- update of A and C visible.
在顺序一致性中,1,4:5,6,8是可能的。 在较弱的一致性中,1,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;
。这样做的好处是可以限制存储屏障的范围,以便存储单元仅需序列化这些存储,而不必承受整个队列的延迟。